[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.bat]\nend_of_line = crlf\n\n[*.md]\n# Trailing whitespace is important in Markdown (they distinguish a new line from a new paragraph)\ntrim_trailing_whitespace = false\n\n[*.{kt,kts}]\nij_kotlin_allow_trailing_comma = false\nij_kotlin_allow_trailing_comma_on_call_site = false\n\n[*.kts]\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text eol=lf\n*.bat text eol=crlf\n*.png binary\n*.key binary\n*.jar binary\n*.ttf binary\nrelease-notes-* merge=union\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Report a bug or regression\nabout: Create a report to help us improve\ntype: Bug\nlabels: [\"type: bug\"]\n---\n\n<!-- Please provide us the version of JUnit you are using and, if possible, a failing unit test with your bug report. Don't forget to describe the rationale for this issue (e.g. expected vs. actual behavior). Please also mention where it's a regression compared to a previous version. -->\n\n## Steps to reproduce\n\n<!-- Please insert a code snippet or a link to another repo along with instructions how to reproduce the issue here. The example should be minimal, complete and verifiable (see https://stackoverflow.com/help/mcve). -->\n\n## Context\n\n - Used versions (Jupiter/Vintage/Platform):\n - Build Tool/IDE:\n\n## Deliverables\n\n- [ ] ...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://github.com/junit-team/junit-framework/discussions/categories/q-a\n    about: Please ask and answer questions here\n  - name: Ask a question (Stack Overflow)\n    url: https://stackoverflow.com/questions/tagged/junit5\n    about: Alternatively, ask questions here\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Request a feature or enhancement\nabout: Suggest an idea for this project\ntype: Feature\nlabels: [\"type: new feature\"]\n---\n\n<!-- Start by telling us what problem you’re trying to solve. Often a solution already exists! Please, don’t send pull requests to implement new features without first getting our support. -->\n\n## Deliverables\n\n- [ ] ...\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/task.md",
    "content": "---\nname: Create a task\nabout: Create a task for a specific piece of work\ntype: Task\nlabels: [\"type: task\"]\n---\n\n<!-- Please describe what needs to be done to consider this task completed. -->\n\n## Deliverables\n\n- [ ] ...\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!-- Please describe your changes here and list any open questions you might have. -->\n\n---\n\nI hereby agree to the terms of the [JUnit Contributor License Agreement](https://github.com/junit-team/junit-framework/blob/002a0052926ddee57cf90580fa49bc37e5a72427/CONTRIBUTING.md#junit-contributor-license-agreement).\n\n---\n\n### Definition of Done\n\n- [ ] There are no TODOs left in the code\n- [ ] Method [preconditions](https://docs.junit.org/snapshot/api/org.junit.platform.commons/org/junit/platform/commons/util/Preconditions.html) are checked and documented in the method's Javadoc\n- [ ] [Coding conventions](https://github.com/junit-team/junit-framework/blob/HEAD/CONTRIBUTING.md#coding-conventions) (e.g. for logging) have been followed\n- [ ] Change is covered by [automated tests](https://github.com/junit-team/junit-framework/blob/HEAD/CONTRIBUTING.md#tests) including corner cases, errors, and exception handling\n- [ ] Public API has [Javadoc](https://github.com/junit-team/junit-framework/blob/HEAD/CONTRIBUTING.md#javadoc) and [`@API` annotations](https://apiguardian-team.github.io/apiguardian/docs/current/api/org/apiguardian/api/API.html)\n- [ ] Change is documented in the [User Guide](https://docs.junit.org/snapshot/) and [Release Notes](https://docs.junit.org/snapshot/release-notes.html)\n"
  },
  {
    "path": ".github/actions/main-build/action.yml",
    "content": "name: Main build\ndescription: Sets up JDKs and runs Gradle\ninputs:\n  arguments:\n    required: true\n    description: Gradle arguments\n    default: :platform-tooling-support-tests:test build eclipse --no-configuration-cache # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318\n  encryptionKey:\n    required: true\n    description: Gradle cache encryption key\nruns:\n  using: \"composite\"\n  steps:\n    - uses: ./.github/actions/setup-test-jdk\n    - uses: ./.github/actions/run-gradle\n      with:\n        arguments: ${{ inputs.arguments }}\n        encryptionKey: ${{ inputs.encryptionKey }}\n    - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n      if: ${{ always() }}\n      with:\n        name: Open Test Reports (${{ github.job }})\n        path: '**/build/reports/open-test-report.html'\n"
  },
  {
    "path": ".github/actions/maven-central-user-token/action.yml",
    "content": "name: Prepare Maven Central user token\ndescription: Compute the Maven Central user token from username and password\ninputs:\n  username:\n    required: true\n    description: Maven Central username\n  password:\n    required: true\n    description: Maven Central password\nruns:\n  using: \"composite\"\n  steps:\n    - shell: bash\n      run: | # zizmor: ignore[github-env]\n        USER_TOKEN=$(printf \"${USERNAME}:${PASSWORD}\" | base64)\n        echo \"::add-mask::$USER_TOKEN\"\n        echo \"MAVEN_CENTRAL_USER_TOKEN=$USER_TOKEN\" >> $GITHUB_ENV\n      env:\n        USERNAME: ${{ inputs.username }}\n        PASSWORD: ${{ inputs.password }}\n"
  },
  {
    "path": ".github/actions/run-gradle/action.yml",
    "content": "name: Run Gradle\ndescription: Sets up Gradle JDKs and runs Gradle\ninputs:\n  arguments:\n    required: true\n    description: Gradle arguments\n    default: build\n  broken-by-issue:\n    required: false\n    description: Expect the build to fail due the referenced GitHub issue\n    default: \"\"\n  encryptionKey:\n    required: true\n    description: Gradle cache encryption key\nruns:\n  using: \"composite\"\n  steps:\n    - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n      id: setup-gradle-jdk\n      with:\n        distribution: temurin\n        java-version: 25\n        check-latest: true\n    - uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0\n      with:\n        cache-encryption-key: ${{ inputs.encryptionKey }}\n    - uses: testlens-app/setup-testlens@d96a555133c275a00949d2cc77b70fe9a4242ebf # v1.9.2\n      with:\n        write-log-files: true\n    - shell: bash\n      env:\n        JAVA_HOME: ${{ steps.setup-gradle-jdk.outputs.path }}\n      run: | # zizmor: ignore[template-injection]\n        ./gradlew \\\n        -Dorg.gradle.java.installations.auto-download=false \\\n        -Pjunit.develocity.buildCache.pushEnabled=${{ github.repository == 'junit-team/junit-framework' }} \\\n        -Pjunit.develocity.predictiveTestSelection.enabled=true \\\n        -Pjunit.develocity.predictiveTestSelection.selectRemainingTests=${{ github.event_name != 'pull_request' }} \\\n        \"-Dscan.value.GitHub job=${{ github.job }}\" \\\n        javaToolchains \\\n        ${{ inputs.arguments }} || error_code=$?\n        \n        \n        if [ -n \"${{ inputs.broken-by-issue }}\" ]; then\n          if [ -n \"$error_code\" ]; then\n            # we expected the build to fail, it failed => pass \n            exit 0\n          else            \n            # we expected the build to fail, it didn't => fail\n            RED='\\033[0;31m'\n            RESET='\\033[0m' # No Color\n            echo \"${RED}Build was expected to fail, but it passed. Was ${{ inputs.broken-by-issue }} resolved?${RESET}\"\n            exit 1            \n          fi\n        fi\n        \n        # exit with the original error code if set\n        if [ -n \"$error_code\" ]; then\n          exit \"$error_code\"\n        fi\n    - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n      if: ${{ always() }}\n      with:\n        name: TestLens logs (${{ github.job }} - ${{ job.check_run_id }})\n        path: '**/build/**/testlens-logs/**'\n        if-no-files-found: ignore\n"
  },
  {
    "path": ".github/actions/setup-test-jdk/action.yml",
    "content": "name: Set up Test JDK\ndescription: Sets up the JDK required to run platform-tooling-support-tests\ninputs:\n  distribution:\n    required: true\n    description: 'The JDK distribution to use'\n    default: 'temurin'\nruns:\n  using: \"composite\"\n  steps:\n    - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n      with:\n        distribution: ${{ inputs.distribution }}\n        java-version: 17\n        check-latest: true\n    - shell: bash\n      run: echo \"JDK17=$JAVA_HOME\" >> $GITHUB_ENV # zizmor: ignore[github-env]\n"
  },
  {
    "path": ".github/codecov.yml",
    "content": "comment: false\n\ncoverage:\n  status:\n    project:\n      default:\n        threshold: \"1\"\n        informational: true\n    patch: off\n\ncodecov:\n  max_report_age: off\n"
  },
  {
    "path": ".github/dco.yml",
    "content": "require:\n  members: false\n"
  },
  {
    "path": ".github/renovate.json5",
    "content": "{\n  $schema: 'https://docs.renovatebot.com/renovate-schema.json',\n  extends: [\n    'github>junit-team/renovate-config',\n  ],\n  baseBranchPatterns: [\n    'main',\n    'docs-site'\n  ],\n  packageRules: [\n    {\n      matchCurrentValue: '/^2\\\\./',\n      allowedVersions: '(,3.0)',\n      matchPackageNames: [\n        'org.codehaus.groovy:{/,}**',\n      ],\n    },\n    {\n      matchCurrentValue: '/^1\\\\./',\n      allowedVersions: '/^1\\\\..*-groovy-2\\\\.*/',\n      matchPackageNames: [\n        'org.spockframework:{/,}**',\n      ],\n    },\n    {\n      allowedVersions: '!/-SNAPSHOT$/',\n      matchPackageNames: [\n        'org.opentest4j.reporting:{/,}**',\n      ],\n    },\n    {\n      matchManagers: 'npm',\n      matchPackageNames: [\n        '@antora/assembler',\n        '@antora/html-single-extension',\n        '@antora/pdf-extension',\n      ],\n      \"groupName\": \"@antora/assembler\",\n    },\n  ],\n}\n"
  },
  {
    "path": ".github/scripts/checkBuildReproducibility.sh",
    "content": "#!/bin/bash -e\n\nrm -f checksums-1.txt checksums-2.txt\n\nSOURCE_DATE_EPOCH=$(date +%s)\nexport SOURCE_DATE_EPOCH\n\nfunction calculate_checksums() {\n    OUTPUT=$1\n\n    ./gradlew \\\n        --configuration-cache \\\n        --no-build-cache \\\n        -Dorg.gradle.java.installations.auto-download=false \\\n        -Dscan.tag.Reproducibility \\\n        clean \\\n        assemble\n\n    find . -name '*.jar' \\\n        | grep '/build/libs/' \\\n        | grep --invert-match 'javadoc' \\\n        | sort \\\n        | xargs sha512sum > \"${OUTPUT}\"\n}\n\n\ncalculate_checksums checksums-1.txt\ncalculate_checksums checksums-2.txt\n\ndiff checksums-1.txt checksums-2.txt\n"
  },
  {
    "path": ".github/scripts/close-github-milestone.js",
    "content": "module.exports = async ({ github, context }) => {\n    const releaseVersion = process.env.RELEASE_VERSION;\n    const query = `\n                query ($owner: String!, $repo: String!, $title: String!) {\n                    repository(owner: $owner, name: $repo) {\n                        milestones(first: 100, query: $title) {\n                            nodes {\n                                title\n                                number\n                                openIssueCount\n                            }\n                        }\n                    }\n                }\n            `;\n    const {repository} = await github.graphql(query, {\n        owner: context.repo.owner,\n        repo: context.repo.repo,\n        title: releaseVersion\n    });\n    const [milestone] = repository.milestones.nodes.filter(it => it.title === releaseVersion);\n    if (!milestone) {\n        throw new Error(`Milestone \"${releaseVersion}\" not found`);\n    }\n    if (milestone.openIssueCount > 0) {\n        throw new Error(`Milestone \"${releaseVersion}\" has ${milestone.openIssueCount} open issue(s)`);\n    }\n    const requestBody = {\n        owner: context.repo.owner,\n        repo: context.repo.repo,\n        milestone_number: milestone.number,\n        state: 'closed',\n        due_on: new Date().toISOString()\n    };\n    console.log(requestBody);\n    await github.rest.issues.updateMilestone(requestBody);\n};\n"
  },
  {
    "path": ".github/scripts/create-github-release.js",
    "content": "module.exports = async ({ github, context }) => {\n    const releaseVersion = process.env.RELEASE_VERSION;\n    const requestBody = {\n        owner: context.repo.owner,\n        repo: context.repo.repo,\n        tag_name: `r${releaseVersion}`,\n        name: `JUnit ${releaseVersion}`,\n        generate_release_notes: true,\n        body: `JUnit ${releaseVersion} = Platform ${releaseVersion} + Jupiter ${releaseVersion} + Vintage ${releaseVersion}\\n\\nSee [Release Notes](https://docs.junit.org/${releaseVersion}/release-notes.html).`,\n        prerelease: releaseVersion.includes(\"-\"),\n    };\n    console.log(requestBody);\n    await github.rest.repos.createRelease(requestBody);\n};\n"
  },
  {
    "path": ".github/scripts/waitForUrl.sh",
    "content": "#!/usr/bin/env bash\n\nURL=$1\nprintf 'Waiting for %s' \"$URL\"\nuntil curl --output /dev/null --silent --location --head --fail \"$URL\"; do\n    printf '.'\n    sleep 5\ndone\necho ' OK'\n"
  },
  {
    "path": ".github/workflows/close-inactive-issues.yml",
    "content": "name: Close inactive issues and PRs\non:\n  schedule:\n    - cron: \"30 1 * * *\"\n  workflow_dispatch:\npermissions: {}\njobs:\n  close-issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0\n        with:\n          only-labels: \"status: waiting-for-feedback\"\n          days-before-stale: 14\n          days-before-close: 21\n          stale-issue-label: \"status: stale\"\n          stale-pr-label: \"status: stale\"\n          stale-issue-message: >\n            If you would like us to be able to process this issue, please provide the requested information.\n            If the information is not provided within the next 3 weeks, we will be unable to proceed and this issue will be closed.\n          close-issue-message: >\n            Closing due to lack of requested feedback.\n            If you would like to proceed with your contribution, please provide the requested information and we will re-open this issue.\n          stale-pr-message: >\n            If you would like us to be able to process this pull request, please provide the requested information or make the requested changes.\n            If the information is not provided or the requested changes are not made within the next 3 weeks, we will be unable to proceed and this pull request will be closed.\n          close-pr-message: >\n            Closing due to lack of requested feedback.\n            If you would like to proceed with your contribution, please provide the requested information or make the requested changes, and we will re-open this pull request.\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "name: \"CodeQL\"\n\non:\n  push:\n    branches:\n      - main\n      - 'releases/**'\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches:\n      - main\n      - 'releases/**'\n  schedule:\n    - cron: '0 19 * * 3'\n\nconcurrency:\n  # Cancels in-progress runs only for pull requests\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\njobs:\n  analyze:\n    name: Analyze (${{ matrix.language }})\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - language: actions\n            build-mode: none\n          - language: java-kotlin\n            build-mode: manual\n          - language: javascript\n            build-mode: none\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        persist-credentials: false\n    - name: Initialize CodeQL\n      uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4\n      with:\n        languages: ${{ matrix.language }}\n        build-mode: ${{ matrix.build-mode }}\n        tools: linked\n    - name: Build\n      if: matrix.build-mode == 'manual'\n      uses: ./.github/actions/run-gradle\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n        arguments: >-\n          --no-build-cache\n          -Dscan.tag.CodeQL\n          classes\n    - name: Perform CodeQL Analysis\n      uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4\n      with:\n        category: \"/language:${{matrix.language}}\"\n"
  },
  {
    "path": ".github/workflows/cross-version.yml",
    "content": "name: Cross-Version\n\non:\n  schedule:\n    - cron: '0 0 * * 6' # Every Saturday at 00:00 UTC\n  push:\n    branches:\n      - main\n      - 'releases/**'\n  pull_request:\n    branches:\n      - '**'\n\nconcurrency:\n  # Cancels in-progress runs only for pull requests\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\njobs:\n  openjdk:\n    strategy:\n      fail-fast: false\n      matrix:\n        jdk:\n        - version: 26\n          type: ga\n        - version: 27\n          type: ea\n        - version: 26\n          type: ea\n          release: leyden\n        - version: 27\n          type: ea\n          release: valhalla\n    name: \"OpenJDK ${{ matrix.jdk.version }} (${{ matrix.jdk.release || matrix.jdk.type }})\"\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          persist-credentials: false\n      - name: Set up Test JDK\n        uses: ./.github/actions/setup-test-jdk\n      - name: \"Set up JDK ${{ matrix.jdk.version }} (${{ matrix.jdk.release || 'ea' }})\"\n        if: matrix.jdk.type == 'ea'\n        uses: oracle-actions/setup-java@fff43251af9936a0e6a4d5d0946e14f1680e9b6b # v1.5.0\n        with:\n          website: jdk.java.net\n          release: ${{ matrix.jdk.release || matrix.jdk.version }}\n          version: latest\n      - name: \"Set up JDK ${{ matrix.jdk.version }} (${{ matrix.jdk.distribution || 'temurin' }})\"\n        if: matrix.jdk.type == 'ga'\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          distribution: ${{ matrix.jdk.distribution || 'temurin' }}\n          java-version: ${{ matrix.jdk.version }}\n          check-latest: true\n      - name: 'Prepare JDK${{ matrix.jdk.version }} env var'\n        shell: bash\n        run: echo \"JDK${{ matrix.jdk.version }}=$JAVA_HOME\" >> $GITHUB_ENV\n      - name: Build\n        uses: ./.github/actions/run-gradle\n        with:\n          encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n          # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318\n          arguments: >-\n            -Ptesting.enableJaCoCo=false\n            -PjavaToolchain.version=${{ matrix.jdk.version }}\n            -Dscan.tag.JDK_${{ matrix.jdk.version }}\n            build\n            --no-configuration-cache\n          broken-by-issue: ${{ matrix.jdk.broken-by-issue }}\n      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        if: ${{ always() }}\n        with:\n          name: Open Test Reports (${{ github.job }} ${{ matrix.jdk.version }} (${{ matrix.jdk.release || matrix.jdk.type }}))\n          path: '**/build/reports/open-test-report.html'\n  openj9:\n    strategy:\n      fail-fast: false\n      matrix:\n        jdk: [ 25 ]\n    name: \"OpenJ9 ${{ matrix.jdk }}\"\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          persist-credentials: false\n      - name: Set up Test JDK\n        uses: ./.github/actions/setup-test-jdk\n        with:\n          distribution: semeru\n      - name: 'Set up JDK ${{ matrix.jdk }}'\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          distribution: semeru\n          java-version: ${{ matrix.jdk }}\n          check-latest: true\n      - name: 'Prepare JDK${{ matrix.jdk }} env var'\n        shell: bash\n        run: echo \"JDK${{ matrix.jdk }}=$JAVA_HOME\" >> $GITHUB_ENV\n      - name: Build\n        uses: ./.github/actions/run-gradle\n        with:\n          encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n          # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318\n          arguments: >-\n            -Ptesting.enableJaCoCo=false\n            -PjavaToolchain.version=${{ matrix.jdk }}\n            -PjavaToolchain.implementation=j9\n            -Dscan.tag.JDK_${{ matrix.jdk }}\n            -Dscan.tag.OpenJ9\n            build\n            --no-configuration-cache\n      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        if: ${{ always() }}\n        with:\n          name: Open Test Reports (${{ github.job }})\n          path: '**/build/reports/open-test-report.html'\n"
  },
  {
    "path": ".github/workflows/deploy-docs.yml",
    "content": "name: Deploy Documentation\n\non: [ workflow_call, workflow_dispatch ]\n\npermissions: {}\n\njobs:\n  deploy_documentation:\n    name: Deploy Documentation\n    runs-on: ubuntu-slim\n    permissions:\n      actions: write\n    steps:\n      - name: Trigger documentation site deployment\n        uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          max_attempts: 10\n          retry_wait_seconds: 5\n          timeout_seconds: 20\n          command: gh workflow run deploy-docs.yml --repo junit-team/junit-framework --ref docs-site\n"
  },
  {
    "path": ".github/workflows/gradle-dependency-submission.yml",
    "content": "name: Gradle Dependency Submission\n\non:\n  push:\n    branches:\n      - main\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\njobs:\n  dependency-submission:\n    if: github.repository == 'junit-team/junit-framework'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Setup Java\n      uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n      with:\n        distribution: temurin\n        java-version: 25\n        check-latest: true\n    - name: Generate and submit dependency graph\n      id: dependency-submission\n      uses: gradle/actions/dependency-submission@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0\n      continue-on-error: true\n    - name: Generate and submit dependency graph (retry)\n      if: steps.dependency-submission.outcome == 'failure'\n      uses: gradle/actions/dependency-submission@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0\n"
  },
  {
    "path": ".github/workflows/label-opened-issues.yml",
    "content": "name: Add label to opened issues\non:\n  issues:\n    types:\n      - opened\npermissions: {}\njobs:\n  label_issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n          script: |\n            const issue = await github.rest.issues.get({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n            });\n            const originalLabels = issue.data.labels.map(l => l.name);\n            const statusLabels = originalLabels.filter(l => l.startsWith(\"status: \"));\n            if (statusLabels.length === 0 && !originalLabels.includes(\"up-for-grabs\")) {\n              github.rest.issues.addLabels({\n                issue_number: context.issue.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                labels: [\"status: new\"]\n              })\n            }\n"
  },
  {
    "path": ".github/workflows/label-pull-request.yml",
    "content": "name: Copy labels from linked issues to PR\n\non:\n  pull_request_target:\n    types: [opened, reopened] # zizmor: ignore[dangerous-triggers]\n\npermissions: {}\n\njobs:\n  copy_labels:\n    name: Copy labels\n    runs-on: ubuntu-latest\n    permissions:\n      pull-requests: write\n    steps:\n      - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n          script: |\n            const query = `\n              query($owner: String!, $repo: String!, $pr: Int!) {\n                repository(owner: $owner, name: $repo) {\n                  pullRequest(number: $pr) {\n                    closingIssuesReferences(first: 10) {\n                      nodes {\n                        labels(first: 100) {\n                          nodes {\n                            name\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            `;\n            \n            const {repository} = await github.graphql(query, {\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                pr: context.issue.number\n            });\n            \n            let labels = Array.from(new Set(repository.pullRequest.closingIssuesReferences.nodes\n                .flatMap((node) => node.labels.nodes.map((label) => label.name))))\n                .filter((label) => !label.startsWith(\"status:\") && label !== \"up-for-grabs\");\n            \n            if (labels.length > 0) {\n                console.log(`Adding labels to PR: ${labels}`);\n                await github.rest.issues.addLabels({\n                    issue_number: context.issue.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    labels: labels\n                });\n            }\n"
  },
  {
    "path": ".github/workflows/lock-resolved-issues.yml",
    "content": "name: Lock resolved issues\n\non:\n  schedule:\n    - cron: \"39 1 * * 0\"\n  workflow_dispatch:\n\npermissions: {}\n\nconcurrency:\n  group: lock-resolved-issues\n\njobs:\n  action:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n      discussions: write\n    steps:\n      - uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0\n        with:\n          process-only: issues\n          log-output: true\n"
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  push:\n    branches:\n      - main\n      - 'releases/**'\n    tags-ignore:\n      - '**'\n  pull_request:\n    branches:\n      - '**'\n\nconcurrency:\n  # Cancels in-progress runs only for pull requests\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\njobs:\n  Linux:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Install GraalVM\n      uses: graalvm/setup-graalvm@bef4b0e916c7dd079bf60fb95d49139f67e32c5f # v1.5.3\n      with:\n        distribution: graalvm\n        version: latest\n        java-version: 21\n    - name: Build\n      uses: ./.github/actions/main-build\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n        # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318\n        arguments: >-\n          :platform-tooling-support-tests:test\n          build\n          jacocoRootReport\n          --no-configuration-cache\n    - name: Upload to Codecov.io\n      uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0\n      with:\n        token: ${{ secrets.CODECOV_TOKEN }}\n\n  Windows:\n    runs-on: windows-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Build\n      uses: ./.github/actions/main-build\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n\n  macOS:\n    runs-on: macos-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Build\n      uses: ./.github/actions/main-build\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n\n  publish_artifacts:\n    name: Publish Snapshot Artifacts\n    needs: macOS\n    runs-on: ubuntu-latest\n    permissions:\n      attestations: write # required for build provenance attestation\n      id-token: write # required for build provenance attestation\n    if: github.event_name == 'push' && github.repository == 'junit-team/junit-framework' && (startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main')\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Publish\n      uses: ./.github/actions/run-gradle\n      env:\n        ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n        ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n        arguments: >-\n          publishAggregationToCentralSnapshots\n          prepareGitHubAttestation\n          -x check\n    - name: Generate build provenance attestations\n      uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0\n      with:\n        subject-path: documentation/build/attestation/*.jar\n\n  build_documentation:\n    name: Build Documentation\n    needs: macOS\n    runs-on: ubuntu-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Install Graphviz\n      run: |\n        sudo apt-get update\n        sudo apt-get install graphviz\n    - name: Install Node.js\n      uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n      with:\n        node-version-file: documentation/.tool-versions\n    - name: Build Documentation\n      uses: ./.github/actions/run-gradle\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n        arguments: >-\n          antora\n          -Pantora.downloadNode=false\n          -Dscan.tag.Documentation\n\n  deploy_documentation:\n    name: Deploy Documentation\n    needs: build_documentation\n    if: github.event_name == 'push' && github.repository == 'junit-team/junit-framework' && github.ref == 'refs/heads/main'\n    uses: ./.github/workflows/deploy-docs.yml\n    permissions:\n      actions: write\n"
  },
  {
    "path": ".github/workflows/ossf-scorecard.yml",
    "content": "name: OpenSSF Scorecard supply-chain security analysis\n\non:\n  branch_protection_rule:\n  schedule:\n    - cron: '31 20 * * 6'\n  push:\n    branches: [ \"main\" ]\n\npermissions: {}\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard (optional).\n      # Commenting out will disable upload of results to your repo's Code Scanning dashboard\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4\n        with:\n          sarif_file: results.sarif\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n    inputs:\n      releaseVersion:\n        description: Version to be released (e.g. \"5.12.0-M1\")\n        required: true\n      deploymentId:\n        description: ID of the Maven Central Publish Portal deployment\n        required: true\n      dryRun:\n        type: boolean\n        description: Enable dry-run mode\n        required: false\n        default: false\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n  STAGING_REPO_URL: https://central.sonatype.com/api/v1/publisher/deployment/${{ inputs.deploymentId }}/download\n  RELEASE_TAG: r${{ inputs.releaseVersion }}\n  RELEASE_VERSION: ${{ inputs.releaseVersion }}\n\njobs:\n\n  verify_reproducibility:\n    name: Verify reproducibility\n    runs-on: ubuntu-latest\n    permissions:\n      attestations: write # required for build provenance attestation\n      id-token: write # required for build provenance attestation\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          ref: \"refs/tags/${{ env.RELEASE_TAG }}\"\n          persist-credentials: false\n      - name: Prepare Maven Central user token\n        uses: ./.github/actions/maven-central-user-token\n        with:\n          username: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n          password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n      - name: Download reference JAR from staging repository\n        id: referenceJar\n        run: |\n          curl --silent --fail --location --output /tmp/reference.jar \\\n            --header \"Authorization: Bearer $MAVEN_CENTRAL_USER_TOKEN\" \\\n            \"${STAGING_REPO_URL}/org/junit/jupiter/junit-jupiter-api/${RELEASE_VERSION}/junit-jupiter-api-${RELEASE_VERSION}.jar\"\n          sudo apt-get update && sudo apt-get install --yes jc\n          unzip -c /tmp/reference.jar META-INF/MANIFEST.MF | jc --jar-manifest | jq '.[0]' > /tmp/manifest.json\n          echo \"createdBy=$(jq --raw-output .Created_By /tmp/manifest.json)\" >> \"$GITHUB_OUTPUT\"\n          echo \"buildTimestamp=$(jq --raw-output .Build_Date /tmp/manifest.json) $(jq --raw-output .Build_Time /tmp/manifest.json)\" >> \"$GITHUB_OUTPUT\"\n      - name: Verify artifacts\n        uses: ./.github/actions/run-gradle\n        with:\n          encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n          arguments: >-\n            --rerun-tasks\n            -Pmanifest.buildTimestamp=\"${{ steps.referenceJar.outputs.buildTimestamp }}\"\n            -Pmanifest.createdBy=\"${{ steps.referenceJar.outputs.createdBy }}\"\n            :verifyArtifactsInStagingRepositoryAreReproducible\n            --remote-repo-url=${{ env.STAGING_REPO_URL }}\n      - name: Generate build provenance attestations\n        if: ${{ inputs.dryRun == false }}\n        uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0\n        with:\n          subject-path: build/repo/**/*.jar\n\n  verify_consumability:\n    name: Verify consumability\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          ref: \"refs/tags/${{ env.RELEASE_TAG }}\"\n          path: junit-framework\n          persist-credentials: false\n      - name: Check out examples repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: ${{ github.repository_owner }}/junit-examples\n          token: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_EXAMPLES_REPO }}\n          fetch-depth: 1\n          path: junit-examples\n          persist-credentials: false\n      - name: Set up JDK\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          java-version: 25\n          distribution: temurin\n      - uses: sbt/setup-sbt@93e926cbdb4a428e41b4ef754124ec82925ffdc2 # v1.1.23\n      - name: Update JUnit dependencies in examples\n        run: java src/Updater.java ${RELEASE_VERSION}\n        working-directory: junit-examples\n      - name: Prepare Maven Central user token\n        uses: ./junit-framework/.github/actions/maven-central-user-token\n        with:\n          username: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n          password: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n      - name: Inject staging repository URL\n        run: java src/StagingRepoInjector.java ${STAGING_REPO_URL}\n        working-directory: junit-examples\n      - name: Build examples\n        run: java src/Builder.java --exclude=junit-jupiter-starter-bazel,junit-jupiter-starter-sbt,junit-source-launcher\n        working-directory: junit-examples\n        env:\n          MAVEN_ARGS: --settings ${{ github.workspace }}/junit-examples/src/central-staging-maven-settings.xml --activate-profiles central-staging\n\n  close_github_milestone:\n    name: Close GitHub milestone\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          persist-credentials: false\n      - name: Close GitHub milestone\n        if: ${{ inputs.dryRun == false }}\n        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n          result-encoding: string\n          script: |\n            const closeGithubMilestone = require('./.github/scripts/close-github-milestone.js');\n            closeGithubMilestone({ github, context });\n\n  publish_deployment:\n    name: Publish to Maven Central\n    needs: [ verify_reproducibility, verify_consumability, close_github_milestone ]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          ref: \"refs/tags/${{ env.RELEASE_TAG }}\"\n          persist-credentials: false\n      - name: Release staging repository\n        if: ${{ inputs.dryRun == false }}\n        uses: ./.github/actions/run-gradle\n        env:\n          ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}\n          ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}\n        with:\n          encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n          arguments: nmcpPublishDeployment -PnmcpDeploymentId=${{ inputs.deploymentId }}\n\n  publish_documentation:\n    name: Publish documentation\n    needs: publish_deployment\n    runs-on: ubuntu-latest\n    permissions:\n      actions: write\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          persist-credentials: false\n      - name: Trigger deployment\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: gh workflow run deploy-docs.yml --repo junit-team/junit-framework --ref docs-site\n      - name: Install Poppler (for pdfinfo)\n        run: |\n          sudo apt-get update\n          sudo apt-get install --yes poppler-utils\n      - name: Wait for deployment\n        if: ${{ inputs.dryRun == false }}\n        timeout-minutes: 30\n        run: ./.github/scripts/waitForUrl.sh \"${URL}\"\n        env:\n          URL: https://docs.junit.org/${{ inputs.releaseVersion }}/\n      - name: Verify integrity of PDF version of User Guide\n        if: inputs.dryRun == false && !contains(inputs.releaseVersion, '-')\n        run: |\n          curl --silent --fail --location --output /tmp/junit-user-guide.pdf \"${PDF_URL}\"\n          pdfinfo /tmp/junit-user-guide.pdf\n        env:\n          PDF_URL: https://docs.junit.org/${{ inputs.releaseVersion }}/_exports/junit-user-guide-${{ inputs.releaseVersion }}.pdf\n\n  update_examples:\n    name: Update examples\n    needs: publish_deployment\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out examples repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: ${{ github.repository_owner }}/junit-examples\n          token: ${{ secrets.JUNIT_BUILDS_GITHUB_TOKEN_EXAMPLES_REPO }}\n          persist-credentials: true\n      - name: Set up JDK\n        uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0\n        with:\n          java-version: 25\n          distribution: temurin\n      - uses: sbt/setup-sbt@93e926cbdb4a428e41b4ef754124ec82925ffdc2 # v1.1.23\n      - name: Update JUnit dependencies in examples\n        run: java src/Updater.java ${RELEASE_VERSION}\n      - name: Build examples\n        if: ${{ inputs.dryRun == false }}\n        run: java src/Builder.java\n      - name: Create release branch\n        run: |\n          git config user.name \"JUnit Team\"\n          git config user.email \"team@junit.org\"\n          git switch -c \"${RELEASE_TAG}\"\n          git status\n          git commit -a -m \"Use ${RELEASE_VERSION}\"\n      - name: Push release branch\n        if: ${{ inputs.dryRun == false }}\n        run: |\n          git push origin \"${RELEASE_TAG}\"\n      - name: Update main branch (only for GA releases)\n        if: ${{ inputs.dryRun == false && !contains(inputs.releaseVersion, '-') }}\n        run: |\n          git fetch origin main\n          git switch main\n          git merge --ff-only \"${RELEASE_TAG}\"\n          git push origin main\n\n  create_github_release:\n    name: Create GitHub release\n    if: ${{ inputs.dryRun == false }}\n    needs: [ publish_documentation, update_examples ]\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - name: Check out repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          fetch-depth: 1\n          persist-credentials: false\n      - name: Create GitHub release\n        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n          script: |\n            const createGithubRelease = require('./.github/scripts/create-github-release.js');\n            createGithubRelease({ github, context });\n"
  },
  {
    "path": ".github/workflows/reproducible-build.yml",
    "content": "name: Reproducible build\n\non:\n  push:\n    branches:\n      - main\n      - 'releases/**'\n  pull_request:\n    branches:\n      - '**'\n\nconcurrency:\n  # Cancels in-progress runs only for pull requests\n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions: {}\n\nenv:\n  DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}\n\njobs:\n  check_build_reproducibility:\n    name: 'Check build reproducibility'\n    runs-on: ubuntu-latest\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n    - name: Restore Gradle cache and display toolchains\n      uses: ./.github/actions/run-gradle\n      with:\n        encryptionKey: ${{ secrets.GRADLE_ENCRYPTION_KEY }}\n        # Disable configuration cache due to https://github.com/diffplug/spotless/issues/2318\n        arguments: >-\n          --quiet\n          --no-configuration-cache\n    - name: Build and compare checksums\n      shell: bash\n      run: ./.github/scripts/checkBuildReproducibility.sh\n"
  },
  {
    "path": ".github/workflows/sanitize-closed-issues.yml",
    "content": "name: Sanitizes assigned labels and milestone on closed issues\non:\n  issues:\n    types:\n      - closed\npermissions: {}\njobs:\n  label_issues:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n    steps:\n      - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n          script: |\n            const issue = await github.rest.issues.get({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n            });\n            const originalLabels = issue.data.labels.map(l => l.name);\n            const newLabels = originalLabels.filter(l => l !== \"status: in progress\" && l !== \"status: new\" && l !== \"status: waiting-for-feedback\" && l !== \"status: waiting-for-interest\");\n            if (newLabels.length !== originalLabels.length) {\n              await github.rest.issues.update({\n                issue_number: issue.data.number,\n                owner: context.repo.owner,\n                repo: context.repo.repo,\n                labels: newLabels,\n              });\n            }\n            if (issue.data.state_reason === \"not_planned\" || issue.data.state_reason === \"duplicate\") {\n              if (issue.data.milestone) {\n                await github.rest.issues.update({\n                  issue_number: issue.data.number,\n                  owner: context.repo.owner,\n                  repo: context.repo.repo,\n                  milestone: null,\n                });\n              }\n              const statusLabels = newLabels.filter(l => l.startsWith(\"status: \"));\n              if (statusLabels.length === 0) {\n                if (issue.data.state_reason === \"not_planned\") {\n                  await github.rest.issues.createComment({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    body: \"Please assign a status label to this issue.\",\n                  });\n                  await github.rest.issues.update({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    state: \"open\",\n                  });\n                } else {\n                  newLabels.push(\"status: duplicate\");\n                  await github.rest.issues.update({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    labels: newLabels,\n                  });\n                }\n              }\n            } else {\n              if (!(newLabels.includes(\"type: task\") || newLabels.includes(\"type: question\")) && !issue.data.milestone) {\n                let collaborator;\n                try {\n                  await github.rest.repos.checkCollaborator({\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    username: context.actor,\n                  });\n                  collaborator = true;\n                } catch (error) {\n                  collaborator = false;\n                }\n                if (collaborator) {\n                  await github.rest.issues.createComment({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    body: \"Please assign a milestone to this issue or label it with `type: task` or `type: question` – or assign a status label and close it as _not planned_.\",\n                  });\n                  await github.rest.issues.update({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    state: \"open\",\n                  });\n                } else {\n                  const statusLabels = newLabels.filter(l => l.startsWith(\"status: \"));\n                  if (statusLabels.length === 0) {\n                    newLabels.push(\"status: invalid\");\n                  }\n                  await github.rest.issues.update({\n                    issue_number: issue.data.number,\n                    owner: context.repo.owner,\n                    repo: context.repo.repo,\n                    labels: newLabels,\n                    state: \"closed\",\n                    state_reason: \"not_planned\",\n                  });\n                }\n              }\n            }\n"
  },
  {
    "path": ".github/workflows/zizmor-analysis.yml",
    "content": "name: GitHub Actions Security Analysis\n\non:\n  push:\n    branches:\n      - main\n      - 'releases/**'\n    paths:\n      - '.github/**'\n  pull_request:\n    paths:\n      - '.github/**'\n\npermissions: {}\n\njobs:\n  zizmor:\n    name: Run zizmor 🌈\n    runs-on: ubuntu-latest\n    permissions:\n      security-events: write\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Run zizmor 🌈\n        uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3\n"
  },
  {
    "path": ".github/zizmor.yml",
    "content": "rules:\n  cache-poisoning:\n    ignore:\n      # reports issues for setup-node which isn't used while releasing\n      - main.yml\n  secrets-outside-env:\n    disable: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# Gradle\n.gradle\n.kotlin\nbuild\n\n# Ignore Gradle GUI config\ngradle-app.setting\n\n# Eclipse\n.classpath\n.settings/\n.project\n/bin/\n/*/bin\n/gradle/plugins/*/bin\n\n# IntelliJ\n*.iml\n*.ipr\n*.iws\n*.uml\n**/.idea/*\n!/.idea/icon.png\n!/.idea/vcs.xml\n/out/\n/*/out/\n\n# Misc\n*.log\n*.graphml\ncoverage.db*\n.metadata\n/.sdkmanrc\n/.tool-versions\n\nchecksums*\n\n# snapshot-tests\n*.snapshot_actual\n*.snapshot_raw\n\n# Antora\n/documentation/node_modules/\n"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CommitMessageInspectionProfile\">\n    <profile version=\"1.0\">\n      <inspection_tool class=\"BodyLimit\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"true\" />\n      <inspection_tool class=\"SubjectBodySeparation\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"true\" />\n      <inspection_tool class=\"SubjectLimit\" enabled=\"true\" level=\"WARNING\" enabled_by_default=\"true\" />\n    </profile>\n  </component>\n  <component name=\"GitSharedSettings\">\n    <option name=\"synchronizeBranchProtectionRules\" value=\"false\" />\n  </component>\n  <component name=\"IssueNavigationConfiguration\">\n    <option name=\"links\">\n      <list>\n        <IssueNavigationLink>\n          <option name=\"issueRegexp\" value=\"#(\\d+)\" />\n          <option name=\"linkRegexp\" value=\"https://github.com/junit-team/junit5/issues/$1\" />\n        </IssueNavigationLink>\n      </list>\n    </option>\n  </component>\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": ".jitpack.yml",
    "content": "before_install:\n  - sdk update\n  - sdk install java 25-open\n  - sdk use java 25-open\ninstall:\n  - |\n    ./gradlew \\\n      --show-version \\\n      -Pjitpack.version=$VERSION \\\n      -Ppublishing.group=$GROUP.$ARTIFACT \\\n      -Ppublishing.signArtifacts=false \\\n      javaToolchains \\\n      publishToMavenLocal\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Getting Started\n\nWe welcome new contributors to the project!\nIf you're interested, please check for [issues labeled with `up-for-grabs`\nthat are not yet in progress](https://github.com/junit-team/junit-framework/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20label%3Aup-for-grabs%20-label%3A%22status%3A%20in%20progress%22).\nGenerally, before you work on an issue, post a comment and ask whether it can be started.\nPlease wait for the core team to respond and assign the issue to you before making any code\nchanges.\n\n## JUnit Contributor License Agreement\n\n- You will only Submit Contributions where You have authored 100% of the content.\n- You will only Submit Contributions to which You have the necessary rights. This means\n  that if You are employed You have received the necessary permissions from Your employer\n  to make the Contributions.\n- Whatever content You Contribute will be provided under the Project License(s).\n\n### Project Licenses\n\n- All modules use [Eclipse Public License v2.0](LICENSE.md).\n\n## Commit Messages\n\nAs a general rule, the style and formatting of commit messages should follow the guidelines in\n[How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).\n\nIn addition, any commit that is related to an existing issue must reference the issue.\nFor example, if a commit in a pull request addresses issue \\#999, it must contain the\nfollowing at the bottom of the commit message.\n\n```\nIssue: #999\n```\n\n## Pull Requests\n\nOur [Definition of Done](https://github.com/junit-team/junit-framework/wiki/Definition-of-Done)\n(DoD) offers some guidelines on what we expect from a pull request.\nFeel free to open a pull request that does not fulfill all criteria, e.g. to discuss\na certain change before polishing it, but please be aware that we will only merge it\nonce the DoD is met.\n\nPlease add the following lines to your pull request description:\n\n```markdown\n---\n\nI hereby agree to the terms of the JUnit Contributor License Agreement.\n```\n\n## Coding Conventions\n\n### Naming Conventions\n\nAcronyms are words.\n\nWhenever an acronym is included as part of a type name or method name, keep the first\nletter of the acronym uppercase and use lowercase for the rest of the acronym. Otherwise,\nit becomes _impossible_ to perform camel-cased searches in IDEs, and it becomes\npotentially very difficult for mere humans to read or reason about the element without\nreading documentation (if documentation even exists).\n\nConsider for example a use case needing to support an HTTP URL. Calling the method\n`getHTTPURL()` is absolutely horrible in terms of usability; whereas, `getHttpUrl()` is\ngreat in terms of usability. The same applies for types `HTTPURLProvider` vs\n`HttpUrlProvider`, etc.\n\nWhenever an acronym is included as part of a field name or parameter name:\n\n- If the acronym comes at the start of the field or parameter name, use lowercase for the\n  entire acronym -- for example, `String url;`.\n- Otherwise, keep the first letter of the acronym uppercase and use lowercase for the\n  rest of the acronym -- for example, `String defaultUrl;`.\n\n### Formatting\n\n#### Code\n\nCode formatting is enforced using the [Spotless](https://github.com/diffplug/spotless)\nGradle plugin. You can use `gradle spotlessApply` to format new code and add missing\nlicense headers to source files. Formatter and import order settings for Eclipse are\navailable in the repository under\n[junit-eclipse-formatter-settings.xml](gradle/config/eclipse/junit-eclipse-formatter-settings.xml)\nand [junit-eclipse.importorder](gradle/config/eclipse/junit-eclipse.importorder),\nrespectively. For IntelliJ IDEA there's a\n[plugin](https://plugins.jetbrains.com/plugin/6546) you can use in conjunction with the\nEclipse settings.\n\nIt is forbidden to use _wildcard imports_ (e.g., `import static org.junit.jupiter.api.Assertions.*;`)\nin Java code.\n\n#### Documentation\n\nText in `*.adoc` and `*.md` files should be wrapped at 90 characters whenever technically\npossible.\n\nIn multi-line bullet point entries, subsequent lines should be indented.\n\n### Spelling\n\nUse American English spelling rules when writing documentation as well as for\ncode -- class names, method names, variable names, etc.\n\n### Javadoc\n\n- Javadoc comments should be wrapped after 80 characters whenever possible.\n- This first paragraph must be a single, concise sentence that ends with a period (`.`).\n- Place `<p>` on the same line as the first line of a new paragraph and precede `<p>` with a blank line.\n- Insert a blank line before at-clauses/tags.\n- Favor `{@code foo}` over `<code>foo</code>`.\n- Favor literals (e.g., `{@literal @}`) over HTML entities.\n- New classes and methods should declare a `@since ...` tag.\n- Use `@since 5.10` instead of `@since 5.10.0`.\n- Do not use `@author` tags. Instead, contributors are listed on the [GitHub](https://github.com/junit-team/junit-framework/graphs/contributors) page.\n- Do not use verbs in third-person form in the first sentence of the Javadoc for a method -- for example, use \"Discover tests...\" instead of \"Discovers tests...\".\n\n#### Examples\n\nSee [`ExtensionContext`](junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java) and\n[`ParameterContext`](junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java) for example Javadoc.\n\n### Constant fields\n\nA constant field is a `static final` field whose value is immutable. If a static final\nfield has a primitive type or an immutable reference type it is a constant field.\n\n- To minimize accessibility and mutability for all non-private `static final` fields\n  under `src/main` should be constant fields.\n- Constant fields should be named using uppercase words separated by underscores. For\n  example `DEFAULT_HTTP_URL_PROVIDER`.\n\nNote: `org.junit.platform.commons.logging.Logger` is considered mutable.\n\n### Nullability\n\nThis project uses JSpecify's annotation to indicate nullability. In general, the approach\nis as follows:\n\n- All packages are annotated with `@NullMarked`\n- Types of fields, parameters, return types etc. may be annotated with `@Nullable`\n\n### Tests\n\n#### Naming\n\n- All test classes must end with a `Tests` suffix.\n- Example test classes that should not be picked up by the build must end with a `TestCase` suffix.\n\n#### Assertions\n\n- Use `org.junit.jupiter.api.Assertions` for simple assertions.\n- Use AssertJ when richer assertions are needed.\n- Do not use `org.junit.Assert` or `junit.framework.Assert`.\n\n#### Mocking and Stubbing\n\n- Use either [Mockito](https://github.com/mockito/mockito) or hand-written test doubles.\n\n### Logging\n\n- In general, logging should be used sparingly.\n- All logging must be performed via the internal `Logger` façade provided via the JUnit [LoggerFactory](https://github.com/junit-team/junit-framework/blob/main/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java).\n- Levels defined in JUnit's [Logger](https://github.com/junit-team/junit-framework/blob/main/junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java) façade, which delegates to Java Util Logging (JUL) for the actual logging.\n  - _error_ (JUL: `SEVERE`, Log4J: `ERROR`): extra information (in addition to an Exception) about errors that will halt execution\n  - _warn_ (JUL: `WARNING`, Log4J: `WARN`): potential usage or configuration errors that should not halt execution\n  - _info_ (JUL: `INFO`, Log4J: `INFO`): information the users might want to know but not by default\n  - _config_ (JUL: `CONFIG`, Log4J: `CONFIG`): information related to configuration of the system (Example: `ServiceLoaderTestEngineRegistry` logs IDs of discovered engines)\n  - _debug_ (JUL: `FINE`, Log4J: `DEBUG`)\n  - _trace_ (JUL: `FINER`, Log4J: `TRACE`)\n\n### Deprecation\n\nThe JUnit project uses the `@API` annotation from [API Guardian](https://github.com/apiguardian-team/apiguardian).\nPublicly available interfaces, classes, and methods have a defined lifecycle\nwhich is described in detail in the [User Guide](https://docs.junit.org/current/api-evolution.html).\n\nThat following describes the deprecation process followed for API items.\n\nTo deprecate an item:\n- Update the `@API.status` to `DEPRECATED`.\n- Update `@API.since`. Please note `since` describes the version when the\n  status was changed and not the introduction of the element.\n- Add the `@Deprecated` Java annotation on the item.\n- Add the `@deprecated` JavaDoc tag to describe the deprecation, and refer to\n  an eventual replacement.\n- If the item is used in existing code, add `@SuppressWarnings(\"deprecation\")`\n  to make the build pass.\n\n## Building the Project\n\nPlease refer to [the readme](README.md#building-from-source) and [the documentation readme](documentation/README.md) for\nthe most common build commands.\n\n### Build Cache\n\nLocal builds can reuse outputs from previous CI builds via Gradle's [Build Cache](https://docs.gradle.org/current/userguide/build_cache.html).\nThe default build cache server is located in the US.\nIf you're in Europe, you can configure the build to use a build cache node located in the EU by adding the following to the `gradle.properties` file in your [Gradle user home](https://docs.gradle.org/current/userguide/directory_layout.html#dir:gradle_user_home):\n\n```properties\njunit.develocity.buildCache.server=https://eu-build-cache-ge.junit.org\n```\n\n### Build Parameters\n\nThe build can be influenced by a number of parameters. For example, measuring\nJaCoCo code coverage of Test tasks can be enabled, or Predictive Test Selection\ndisabled. To see the full list, please run the following task:\n\n`./gradlew :plugins:build-parameters:parameters`\n"
  },
  {
    "path": "KEYS",
    "content": "This file contains the PGP key that is used to sign releases.\n\nImporting: `pgp < KEYS` or `gpg --import KEYS`\n\nAdding a key:\n`pgp -kxa <your name> >> KEYS`,\nor `(pgpk -ll <your name> && pgpk -xa <your name>) >> KEYS`,\nor `(gpg --list-sigs <your name> && gpg --armor --export <your name>) >> KEYS`\n\n================================\n\npub   rsa4096 2018-04-08 [SC]\n      FF6E2C001948C5F2F38B0CC385911F425EC61B51\nuid           [ unknown] Open Source Development <mail@marcphilipp.de>\nsig 3        85911F425EC61B51 2018-04-08  Open Source Development <mail@marcphilipp.de>\nsub   rsa4096 2018-04-08 [E]\nsig          85911F425EC61B51 2018-04-08  Open Source Development <mail@marcphilipp.de>\n\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nmQINBFrKW9IBEACkqUvM7hU1WqOOeb1gZ7pUsRliHuoUvYIrd+hdp+qhPmJ0NG0W\nYhZK5UtJBmqvtHKRkbwYxUuya9zlBmCfQFf0GpFKJ65JSrPSkZADI3aZ4aUkxIUw\nnIRoUHucmr10Xftpebr/zaJk5oR8RdaL5FapapmcZmAaHR9CDWB8XtI318u314jq\nM5rKatnAZMERoPugOvvuAOz4bfZKwdfCmZKfYUM/TMSrSinXrGExSW6z4RhtqmpC\nE5M/7OoVfvDynVJKqNazqgigpmMNhOyzAhQsiKh1K0akyxTZbjeZKsdYfhCXvq0q\nk9+KM/cTllQ54MPnFWiObLkHeK0Waw8bI/vAJ4h4x/XM9iGYpkXv7F2/FVsHQdPe\nYJcwD/CkD8KHyiPaRKMeApiUtZsdAHU0L4X/lNmcooea/7ipskruUgwcm+RdLhRZ\nP949t1e7nqDZfpEHy90NiFxmlRAPSNqBLwefxY/hwBgog2jabDALJVcLCMosFWPj\nMQhFlGSIODiVcW8folGIjzkyNZbNMWkwnl2QnWp/h2TAwYQJOMqcv2MG9o5pyzpx\n97Iz1ngq1FlM/gJnGnNUydP2tAjT2L2U3MP1uX/EdRChdgPqdolqYhdFfwCr0Fpf\nW527bUZpReHCEiQ29ABSnQ711mO+d9+qM6edRyHUoBWz89IHt8sCunuvNwARAQAB\ntC1PcGVuIFNvdXJjZSBEZXZlbG9wbWVudCA8bWFpbEBtYXJjcGhpbGlwcC5kZT6J\nAk4EEwEIADgWIQT/biwAGUjF8vOLDMOFkR9CXsYbUQUCWspb0gIbAwULCQgHAgYV\nCAkKCwIEFgIDAQIeAQIXgAAKCRCFkR9CXsYbUQyRD/9xm3BqdpWcRCE5UyB6nbwV\n8TgzMmbOhpFhhcjzobly/pKAbcofKsjhreENJkfBVUo+zAFx21ToC5tbH20wRtIE\nvQVCP6sAIzhYWU1ohafqVFP4+PztNBuYTnS6vGvSwzp0IXLIIoxSxo0IOED9uUS9\nDTxh1n9NnDLDe2pfjrXBblQtLSW3W5ISDoUvcoyO7Hk1OByW6MNsSoLvXIUNeVhB\nju9TfYxFACJSWBhUxJfgip9Y2GrNBJaYGLZrTAoW1Lh1H1DfLV3wHDClQ1+H+oyx\nIOZULEGYY3MgZTd6Ner2yNAUCB7gVa50NiCZXCS74m+XzMrTEsdWjSMUaOe+dL0I\n9MCrgi4ycUHWIfTKx9gGlIOo3hSDMN+8Nj33XPjLT8kcfoFeUX8jTOvC1HFfTuQJ\nx2t/dKHizdrS3F6A/JQa7v8GNTrZFnEXkwgRTf3ccLoo3gPwzNJeCm2xNjvne1VH\nfvxzwNmq8M05oicEigvEed2VMStMhvT7dSiMAf66rEJHjjaHAoNqbLDEATYrWUP2\nI52txHSSxSJohxVP6Ec6dERnqqYi0mVyzBPo7mmFFBisq74w8RvZXyzvXE3BTiDL\nwe1E/Z/AXbtJye9DickQ/G6RFtVLbUHQfzyRS/65JPtlH8rqJr58YWlylGImVLwE\nOsKNQrwLZ0UkfaWV7wqr3rkCDQRaylvSARAAnQG636wliEOLkXN662OZS6Qz2+cF\nltCWboq9oX9FnA1PHnTY2cAtwS214RfWZxkjg6Stau+d1Wb8TsF/SUN3eKRSyrkA\nxlX0v552vj3xmmfNsslQX47e6aEWZ0du0M8jw7/f7Qxp0InkBfpQwjSg4ECoH4cA\n6dOFJIdxBv8dgS4K90HNuIHa+QYfVSVMjGwOjD9St6Pwkbg1sLedITRo59Bbv0J1\n4nE9LdWbCiwNrkDr24jTewdgrDaCpN6msUwcH1E0nYxuKAetHEi2OpgBhaY3RQ6Q\nPQB6NywvmD0xRllMqu4hSp70pHFtm8LvJdWOsJ5we3KijHuZzEbBVTTl+2DhNMI0\nKMoh+P/OmyNOfWD8DL4NO3pVv+mPDZn82/eZ3XY1/oSQrpyJaCBjRKasVTtfiA/F\ngYqTml6qZMjy6iywg84rLezELgcxHHvjhAKd4CfxyuCCgnGT0iRLFZKw44ZmOUqP\nDkyvGRddIyHag1K7UaM/2UMn6iPMy7XWcaFiH5Huhz43SiOdsWGuwNk4dDxHdxmz\nSjps0H5dkfCciOFhEc54AFcGEXCWHXuxVqIq/hwqTmVl1RY+PTcQUIOfx36WW1ix\nJQf8TpVxUbooK8vr1jOFF6khorDXoZDJNhI2VKomWp8Y38EPGyiUPZNcnmSiezx+\nMoQwAbeqjFMKG7UAEQEAAYkCNgQYAQgAIBYhBP9uLAAZSMXy84sMw4WRH0JexhtR\nBQJaylvSAhsMAAoJEIWRH0JexhtR0LEP/RvYGlaokoosAYI5vNORAiYEc1Ow2McP\nI1ZafHhcVxZhlwF48dAC2bYcasDX/PbEdcD6pwo8ZU8eI8Ht0VpRQxeV/sP01m2Y\nEpAuyZ6jI7IQQCGcwQdN4qzQJxMAASl9JlplH2NniXV1/994FOtesT59ePMyexm5\n7lzhYXP1PGcdt8dH37r6z3XQu0lHRG/KBn7YhyA3zwJcno324KdBRJiynlc7uqQq\n+ZptU9fR1+Nx0uoWZoFMsrQUmY34aAOPJu7jGMTG+VseMH6vDdNhhZs9JOlD/e/V\naF7NyadjOUD4j/ud7c0z2EwqjDKMFTHGbIdawT/7jartT+9yGUO+EmScBMiMuJUT\ndCP4YDh3ExRdqefEBff3uE/rAP73ndNYdIVq9U0gY0uSNCD9JPfj4aCN52y9a2pS\n7Dg7KB/Z8SH1R9IWP+t0HvVtAILdsLExNFTedJGHRh7uaC7pwRz01iivmtAKYICz\nruqlJie/IdEFFK/sus6fZek29odTrQxx42HGHO5GCNyEdK9jKVAeuZ10vcaNbuBp\niP7sf8/BsiEU4wHE8gjFeUPRiSjnERgXQwfJosLgf/K/SShQn2dCkYZRNF+SWJ6Z\n2tQxcW5rpUjtclV/bRVkUX21EYfwA6SMB811mI7AVy8WPXCe8La72ukmaxEGbpJ8\nmdzS2PJko7mm\n=l0XA\n-----END PGP PUBLIC KEY BLOCK-----\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Eclipse Public License - v 2.0\n==============================\n\nTHE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.\n\n### 1. Definitions\n\n“Contribution” means:\n* **a)** in the case of the initial Contributor, the initial content Distributed under this Agreement, and\n* **b)** in the case of each subsequent Contributor:\n\t* **i)** changes to the Program, and\n\t* **ii)** additions to the Program;\nwhere such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works.\n\n“Contributor” means any person or entity that Distributes the Program.\n\n“Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.\n\n“Program” means the Contributions Distributed in accordance with this Agreement.\n\n“Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors.\n\n“Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship.\n\n“Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof.\n\n“Distribute” means the acts of **a)** distributing or **b)** making available in any manner that enables the transfer of a copy.\n\n“Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor.\n\n### 2. Grant of Rights\n\n**a)** Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works.\n\n**b)** Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.\n\n**c)** Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.\n\n**d)** Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.\n\n**e)** Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3).\n\n### 3. Requirements\n\n**3.1** If a Contributor Distributes the Program in any form, then:\n\n* **a)** the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and\n\n* **b)** the Contributor may Distribute the Program under a license different than this Agreement, provided that such license:\n\t* **i)** effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;\n\t* **ii)** effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;\n\t* **iii)** does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and\n\t* **iv)** requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3.\n\n**3.2** When the Program is Distributed as Source Code:\n\n* **a)** it must be made available under this Agreement, or if the Program **(i)** is combined with other material in a separate file or files made available under a Secondary License, and **(ii)** the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and\n* **b)** a copy of this Agreement must be included with each copy of the Program.\n\n**3.3** Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (“notices”) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices.\n\n### 4. Commercial Distribution\n\nCommercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: **a)** promptly notify the Commercial Contributor in writing of such claim, and **b)** allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.\n\nFor example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.\n\n### 5. No Warranty\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.\n\n### 6. Disclaimer of Liability\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n### 7. General\n\nIf any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.\n\nIf Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.\n\nAll Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.\n\nEveryone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version.\n\nExcept as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement.\n\n#### Exhibit A - Form of Secondary Licenses Notice\n\n> “This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.”\n\nSimply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses.\n\nIf it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n"
  },
  {
    "path": "NOTICE.md",
    "content": "Open Source Licenses\n====================\n\nThis product may include a number of subcomponents with separate\ncopyright notices and license terms. Your use of the source code for\nthese subcomponents is subject to the terms and conditions of the\nsubcomponent's license, as noted in the `LICENSE-<subcomponent>[.md]`\nfiles.\n"
  },
  {
    "path": "README.md",
    "content": "<h1>\n  <picture width=\"200\">\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://junit.org/assets/img/junit-logo-inverted-darker.svg\">\n    <source media=\"(prefers-color-scheme: light)\" srcset=\"https://junit.org/assets/img/junit-logo.svg\">\n    <img alt=\"JUnit\" src=\"https://junit.org/assets/img/junit-logo.svg\" width=\"200\">\n  </picture>\n</h1>\n\nThis repository is the home of JUnit Platform, Jupiter, and Vintage.\n\n## Sponsors\n\n[![Support JUnit](https://img.shields.io/badge/%F0%9F%92%9A-Support%20JUnit-brightgreen.svg)](https://junit.org/sponsoring)\n\n* **Gold Sponsors:** [JetBrains](https://jb.gg/junit-logo), [Netflix](https://www.netflix.com/)\n* **Silver Sponsors:** [Micromata](https://www.micromata.de), [Quo Card](https://quo-digital.jp)\n* **Bronze Sponsors:** [Premium Minds](https://www.premium-minds.com), [codefortynine](https://codefortynine.com), [Info Support](https://www.infosupport.com), [Code Intelligence](https://www.code-intelligence.com), [Route4Me](https://route4me.com/), [Testiny](https://www.testiny.io/), [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=junit)\n\n## Latest Releases\n\n- General Availability (GA): [JUnit 6.0.3](https://github.com/junit-team/junit-framework/releases/tag/r6.0.3) (February 15, 2026)\n- Preview (Milestone/Release Candidate): [JUnit 6.1.0-RC1](https://github.com/junit-team/junit-framework/releases/tag/r6.1.0-RC1) (April 25, 2026)\n\n## Documentation\n\n- [User Guide]\n- [Javadoc]\n- [Release Notes]\n- [Examples]\n\n## Contributing\n\nContributions to JUnit are both welcomed and appreciated. For specific guidelines\nregarding contributions, please see [CONTRIBUTING.md] in the root directory of the\nproject. Those willing to use milestone or SNAPSHOT releases are encouraged\nto file feature requests and bug reports using the project's\n[issue tracker](https://github.com/junit-team/junit-framework/issues). Issues marked with an\n<a href=\"https://github.com/junit-team/junit-framework/issues?q=is%3Aissue+is%3Aopen+label%3Aup-for-grabs\">`up-for-grabs`</a>\nlabel are specifically targeted for community contributions.\n\n## Getting Help\n\nAsk JUnit-related questions on [StackOverflow] or use the Q&A category on [GitHub Discussions].\n\n## Continuous Integration Builds\n\n[![CI](https://github.com/junit-team/junit-framework/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/junit-team/junit-framework/actions/workflows/main.yml) [![Cross-Version](https://github.com/junit-team/junit-framework/actions/workflows/cross-version.yml/badge.svg?branch=main)](https://github.com/junit-team/junit-framework/actions/workflows/cross-version.yml)\n\nOfficial CI build server used to perform quick checks on submitted pull requests and for\nbuild matrices including the latest released OpenJDK and early access builds of the next\nOpenJDK.\n\n## Code Coverage\n\nCode coverage using [JaCoCo] for the latest build is available on [Codecov].\n\nA code coverage report can also be generated locally via the [Gradle Wrapper] by\nexecuting `./gradlew clean jacocoRootReport`. The results will be available\nin `build/reports/jacoco/jacocoRootReport/html/index.html`.\n\n## Develocity\n\n[![Revved up by Develocity](https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A)](https://ge.junit.org/scans)\n\nJUnit utilizes [Develocity](https://gradle.com/) for [Build Scans](https://scans.gradle.com/),\n[Build Cache](https://docs.gradle.org/current/userguide/build_cache.html), and\n[Predictive Test Selection](https://docs.gradle.com/enterprise/predictive-test-selection/).\n\nThe latest Build Scans are available on [ge.junit.org](https://ge.junit.org/). Currently,\nonly core team members can publish Build Scans on that server.\nYou can, however, publish a Build Scan to [scans.gradle.com](https://scans.gradle.com/) by\nusing the `--scan` parameter explicitly.\n\nThe remote Build Cache is enabled by default for everyone so that local builds can reuse\ntask outputs from previous CI builds.\n\n## Building from Source\n\nYou need [JDK 25] to build JUnit. [Gradle toolchains] are used to detect and\npotentially download additional JDKs for compilation and test execution.\n\nAll modules can be _built_ and _tested_ with the [Gradle Wrapper] using the following command:\n\n`./gradlew build`\n\nAll modules can be _installed_ in a local Maven repository for consumption in other local\nprojects via the following command:\n\n`./gradlew publishToMavenLocal`\n\n## Dependency Metadata\n\n[![JUnit Jupiter version](https://img.shields.io/maven-central/v/org.junit.jupiter/junit-jupiter/6..svg?color=25a162&label=Jupiter)](https://central.sonatype.com/search?namespace=org.junit.jupiter)\n[![JUnit Vintage version](https://img.shields.io/maven-central/v/org.junit.vintage/junit-vintage-engine/6..svg?color=25a162&label=Vintage)](https://central.sonatype.com/search?namespace=org.junit.vintage)\n[![JUnit Platform version](https://img.shields.io/maven-central/v/org.junit.platform/junit-platform-commons/6..svg?color=25a162&label=Platform)](https://central.sonatype.com/search?namespace=org.junit.platform)\n\nConsult the [Dependency Metadata] section of the [User Guide] for a list of all artifacts\nof the JUnit Platform, JUnit Jupiter, and JUnit Vintage.\n\n\n[Codecov]: https://codecov.io/gh/junit-team/junit-framework\n[CONTRIBUTING.md]: https://github.com/junit-team/junit-framework/blob/HEAD/CONTRIBUTING.md\n[Dependency Metadata]: https://docs.junit.org/current/appendix.html#dependency-metadata\n[GitHub Discussions]: https://github.com/junit-team/junit-framework/discussions/categories/q-a\n[Gradle toolchains]: https://docs.gradle.org/current/userguide/toolchains.html\n[Gradle Wrapper]: https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:using_wrapper\n[JaCoCo]: https://www.eclemma.org/jacoco/\n[Javadoc]: https://api.junit.org\n[JDK 25]: https://javaalmanac.io/jdk/25/\n[Release Notes]: https://docs.junit.org/current/release-notes.html\n[Examples]: https://github.com/junit-team/junit-examples\n[StackOverflow]: https://stackoverflow.com/questions/tagged/junit5\n[User Guide]: https://docs.junit.org\n"
  },
  {
    "path": "RELEASING.md",
    "content": "# Releasing\n\n## Pre-release steps\n\n- [ ] Switch or create the release branch for this feature release (e.g. `releases/5.12.x`)\n- [ ] Change `version` in `gradle.properties` to the versions about to be released\n- [ ] Change release date in Release Notes\n- [ ] Change release date in `README.MD`\n- [ ] Commit with message \"Release ${VERSION}\"\n- [ ] Execute `./gradlew --no-build-cache --no-configuration-cache clean build publishAggregationToCentralPortal`\n- [ ] Tag current commit: `git tag -s -m ${VERSION} r${VERSION}`\n- [ ] Change `version` in `gradle.properties` on release branch to new development versions and commit with message \"Back to snapshots for further development\" or similar\n- [ ] Push release branch and tag to GitHub: `git push --set-upstream --follow-tags origin HEAD`\n- [ ] Trigger a [release build](https://github.com/junit-team/junit-framework/actions/workflows/release.yml): `gh workflow run --ref r${VERSION} -f releaseVersion=${VERSION} -f deploymentId=${DEPLOYMENT_ID} release.yml`\n  - Select the release branch\n  - Enter the version to be released\n  - Enter the staging repository ID from the output of above Gradle build\n\n## Post-release steps\n\n- [ ] Post about the new release:\n    - [ ] [Mastodon](https://fosstodon.org/@junit)\n    - [ ] [Bluesky](https://bsky.app/profile/junit.org)\n\n### Preview releases (milestones and release candidates)\n\n- [ ] Fast-forward merge the release branch to `main` and push to GitHub\n- [ ] Create release notes for the next preview or feature release from the template\n\n### Feature releases (x.y.0)\n\n- [ ] Delete `accepted-breaking-changes.csv` (if it exists), change `previousVersion` in `gradle.properties` to `x.y.0` on the release branch, and commit with message \"Update API baseline and clear accepted breaking changes\"\n- [ ] Fast-forward merge the release branch to `main` and push to GitHub\n- [ ] Update the [security policy](https://github.com/junit-team/junit-framework/blob/main/SECURITY.md) and commit with message \"Update security policy to reflect 5.x release\" or similar\n- [ ] Create release notes for the next feature release from the template\n- [ ] Update [JBang catalog](https://github.com/junit-team/jbang-catalog/blob/main/jbang-catalog.json)\n\n### Patch releases (x.y.z)\n\n- [ ] Cherry-pick the tagged commit from the release branch to `main` and resolve the conflict in `gradle.properties` by choosing the version of the `main` branch\n- [ ] Delete `accepted-breaking-changes.csv` (if it exists), change `previousVersion` in `gradle.properties` to `x.y.z` on the release branch, and commit with message \"Update API baseline and clear accepted breaking changes\"\n- [ ] Include the release notes of the patch release on `main` if not already present\n- [ ] Update [JBang catalog](https://github.com/junit-team/jbang-catalog/blob/main/jbang-catalog.json)\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9607/badge)](https://www.bestpractices.dev/projects/9607) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/junit-team/junit-framework/badge)](https://scorecard.dev/viewer/?uri=github.com/junit-team/junit-framework)\n\n## JAR Signing\n\nJUnit JARs released on Maven Central are signed.\nYou'll find more information about the key here: [KEYS](./KEYS)\n\n## Supported Versions\n\n| Version | Supported          |\n|---------| ------------------ |\n| 6.0.x   | :white_check_mark: |\n| 5.14.x  | :white_check_mark: |\n| < 5.14  | :x:                |\n\n## Reporting a Vulnerability\n\nTo report a security vulnerability, you have two options:\n\n- [Privately report a vulnerability](https://github.com/junit-team/junit-framework/security/advisories/new) on GitHub (see [docs](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) for details)\n- Send an email to security@junit.org. You can use the [published OpenPGP key](https://keys.openpgp.org/search?q=security%40junit.org) with fingerprint `0152DA30EABC7ABADCB09D10D9A6B1329D191D25` to encrypt the message body.\n"
  },
  {
    "path": "build.gradle.kts",
    "content": "import junitbuild.extensions.dependencyProject\n\nplugins {\n\tid(\"junitbuild.base-conventions\")\n\tid(\"junitbuild.build-metadata\")\n\tid(\"junitbuild.checkstyle-nohttp\")\n\tid(\"junitbuild.jacoco-aggregation-conventions\")\n\tid(\"junitbuild.temp-maven-repo\")\n}\n\ndescription = \"JUnit\"\ngroup = \"org.junit\"\n\nval license by extra(License(\n\tname = \"Eclipse Public License v2.0\",\n\turl = uri(\"https://www.eclipse.org/legal/epl-v20.html\"),\n\theaderFile = layout.projectDirectory.file(\"gradle/config/spotless/eclipse-public-license-2.0.java\")\n))\n\nval platformProjects by extra(listOf(\n\t\tprojects.junitPlatformCommons,\n\t\tprojects.junitPlatformConsole,\n\t\tprojects.junitPlatformConsoleStandalone,\n\t\tprojects.junitPlatformEngine,\n\t\tprojects.junitPlatformLauncher,\n\t\tprojects.junitPlatformReporting,\n\t\tprojects.junitPlatformSuite,\n\t\tprojects.junitPlatformSuiteApi,\n\t\tprojects.junitPlatformSuiteEngine,\n\t\tprojects.junitPlatformTestkit\n).map { dependencyProject(it) })\n\nval jupiterProjects by extra(listOf(\n\t\tprojects.junitJupiter,\n\t\tprojects.junitJupiterApi,\n\t\tprojects.junitJupiterEngine,\n\t\tprojects.junitJupiterMigrationsupport,\n\t\tprojects.junitJupiterParams\n).map { dependencyProject(it) })\n\nval vintageProjects by extra(listOf(\n\tdependencyProject(projects.junitVintageEngine)\n))\n\nval mavenizedProjects by extra(listOf(dependencyProject(projects.junitStart)) + platformProjects + jupiterProjects + vintageProjects)\nval modularProjects by extra(mavenizedProjects - setOf(dependencyProject(projects.junitPlatformConsoleStandalone)))\n\ndependencies {\n\tmodularProjects.forEach {\n\t\tjacocoAggregation(it)\n\t}\n\tjacocoAggregation(projects.documentation)\n\tjacocoAggregation(projects.jupiterTests)\n\tjacocoAggregation(projects.platformTests)\n}\n"
  },
  {
    "path": "documentation/.tool-versions",
    "content": "nodejs 24.15.0\n"
  },
  {
    "path": "documentation/README.md",
    "content": "# JUnit User Guide\n\nThis subproject contains the Antora/AsciiDoc sources for the JUnit User Guide.\n\n## Structure\n\n- `modules/ROOT/pages`: AsciiDoc files\n- `src/test/java`: Java test source code that can be included in the AsciiDoc files\n- `src/test/kotlin`: Kotlin test source code that can be included in the AsciiDoc files\n- `src/test/resources`: Classpath resources that can be included in the AsciiDoc files or\n  used in tests\n\n## Usage\n\n### Generate Antora site\n\nThis following Gradle command generates the HTML version of the User Guide as\n`build/antora/build/site`.\n\n```\n./gradlew antora\n```\n\nOn Linux operating systems, the `graphviz` package providing `/usr/bin/dot` must be\ninstalled in order to generate the PlantUML images used in the User Guide.\n"
  },
  {
    "path": "documentation/antora-playbook.yml",
    "content": "antora:\n  extensions:\n  - '@antora/collector-extension'\n  - require: '@antora/lunr-extension'\n    index_latest_only: true\n    index_by_heading: true\n  - require: '@springio/antora-extensions/root-component-extension'\n    root_component_name: junit\n  - require: '@springio/antora-extensions/root-attachments-extension'\n  - require: '@springio/antora-xref-extension'\nsite:\n  title: JUnit User Guide\n  start_page: junit::overview.adoc\ncontent:\n  sources:\n  - url: @GIT_REPO_ROOT@\n    branches: @GIT_BRANCH_NAME@\n    start_path: documentation\n    worktrees: true\nasciidoc:\n  extensions:\n  - '@asciidoctor/tabs'\n  attributes:\n    page-pagination: ''\n    idprefix: '@'\n    idseparator: '-@'\n    table-caption: false\n    table-frame: 'none@'\n    table-grid: 'rows@'\n    table-stripes: 'odd@'\n    figure-caption: false\n    example-caption: false\n    listing-caption: false\n    hide-uri-scheme: '@'\n    attribute-missing: warn\n    tabs-sync-option: ''\nruntime:\n  log:\n    failure_level: warn\nui:\n  bundle:\n    url: https://gitlab.com/antora/antora-ui-default/-/jobs/artifacts/HEAD/raw/build/ui-bundle.zip?job=bundle-stable\n    snapshot: true\n  supplemental_files:\n    - path: css/vendor/tabs.css\n      contents: @GIT_REPO_ROOT@/documentation/node_modules/@asciidoctor/tabs/dist/css/tabs.css\n    - path: js/vendor/tabs.js\n      contents: @GIT_REPO_ROOT@/documentation/node_modules/@asciidoctor/tabs/dist/js/tabs.js\n    - path: partials/footer-scripts.hbs\n      contents: |\n        <script id=\"site-script\" src=\"{{{uiRootPath}}}/js/site.js\" data-ui-root-path=\"{{{uiRootPath}}}\"></script>\n        <script async src=\"{{{uiRootPath}}}/js/vendor/highlight.js\"></script>\n        <script async src=\"{{{uiRootPath}}}/js/vendor/tabs.js\" data-sync-storage-key=\"preferred-code-language\"></script>\n        {{#if env.SITE_SEARCH_PROVIDER}}\n        {{> search-scripts}}\n        {{/if}}\n    - path: partials/head-styles.hbs\n      contents: |\n        <link rel=\"stylesheet\" href=\"{{{uiRootPath}}}/css/site.css\">\n        <link rel=\"stylesheet\" href=\"{{{uiRootPath}}}/css/vendor/tabs.css\">\n"
  },
  {
    "path": "documentation/antora.yml",
    "content": "name: junit\nversion: true\ntitle: JUnit\nnav:\n  - modules/ROOT/nav.adoc\n  - modules/ROOT/extra-site-nav.adoc\next:\n  collector:\n    run:\n      command: gradlew --quiet :documentation:generateAntoraResources\n      local: true\n    scan:\n    - dir: ./build/generated-antora-resources\n      clean: true\n    - dir: ./build/docs/fixedJavadoc\n      clean: true\n      into: modules/ROOT/attachments/api\n    - dir: ./src/test\n      into: modules/ROOT/examples\n    - dir: ./build/generated/asciidoc\n      clean: true\n      into: modules/ROOT/partials\n    - dir: ./build/plantuml\n      clean: true\n      into: modules/ROOT/images\n  assembler:\n    nav:\n      - modules/ROOT/nav.adoc\nasciidoc:\n  attributes:\n    javadoc-root: \"xref:attachment$api\"\n    # Snapshot Repository\n    snapshot-repo: 'https://central.sonatype.com/service/rest/repository/browse/maven-snapshots'\n    # Base Links\n    junit-team: 'https://github.com/junit-team'\n    junit-framework-repo: '{junit-team}/junit-framework'\n    current-branch: '{junit-framework-repo}/tree/{release-branch}'\n    # Platform Commons\n    junit-platform-support-package: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/package-summary.html[org.junit.platform.commons.support]'\n    AnnotationSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/AnnotationSupport.html[AnnotationSupport]'\n    ClassSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ClassSupport.html[ClassSupport]'\n    ConversionSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/conversion/ConversionSupport.html[ConversionSupport]'\n    ModifierSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ModifierSupport.html[ModifierSupport]'\n    ReflectionSupport: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/support/ReflectionSupport.html[ReflectionSupport]'\n    Testable: '{javadoc-root}/org.junit.platform.commons/org/junit/platform/commons/annotation/Testable.html[@Testable]'\n    # Platform Console Launcher\n    junit-platform-console: '{javadoc-root}/org.junit.platform.console/org/junit/platform/console/package-summary.html[junit-platform-console]'\n    ConsoleLauncher: '{javadoc-root}/org.junit.platform.console/org/junit/platform/console/ConsoleLauncher.html[ConsoleLauncher]'\n    # Platform Engine\n    junit-platform-engine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/package-summary.html[junit-platform-engine]'\n    junit-platform-engine-support-discovery: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/package-summary.html[org.junit.platform.engine.support.discovery]'\n    CancellationToken: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/CancellationToken.html[CancellationToken]'\n    ClasspathResourceSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathResourceSelector.html[ClasspathResourceSelector]'\n    ClasspathRootSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClasspathRootSelector.html[ClasspathRootSelector]'\n    ClassSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ClassSelector.html[ClassSelector]'\n    DiscoveryIssue: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/DiscoveryIssue.html[DiscoveryIssue]'\n    DiscoveryIssueReporter: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/discovery/DiscoveryIssueReporter.html[DiscoveryIssueReporter]'\n    DirectorySelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DirectorySelector.html[DirectorySelector]'\n    DiscoverySelectors: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html[DiscoverySelectors]'\n    DiscoverySelectors_selectClasspathResource: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathResource(java.lang.String)[selectClasspathResource]'\n    DiscoverySelectors_selectClasspathRoots: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClasspathRoots(java.util.Set)[selectClasspathRoots]'\n    DiscoverySelectors_selectClass: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectClass(java.lang.String)[selectClass]'\n    DiscoverySelectors_selectDirectory: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectDirectory(java.lang.String)[selectDirectory]'\n    DiscoverySelectors_selectFile: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectFile(java.lang.String)[selectFile]'\n    DiscoverySelectors_selectIteration: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectIteration(org.junit.platform.engine.DiscoverySelector,int\\...)[selectIteration]'\n    DiscoverySelectors_selectMethod: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectMethod(java.lang.String)[selectMethod]'\n    DiscoverySelectors_selectModule: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectModule(java.lang.String)[selectModule]'\n    DiscoverySelectors_selectNestedClass: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedClass(java.util.List,java.lang.Class)[selectNestedClass]'\n    DiscoverySelectors_selectNestedMethod: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectNestedMethod(java.util.List,java.lang.Class,java.lang.String)[selectNestedMethod]'\n    DiscoverySelectors_selectPackage: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectPackage(java.lang.String)[selectPackage]'\n    DiscoverySelectors_selectUniqueId: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUniqueId(java.lang.String)[selectUniqueId]'\n    DiscoverySelectors_selectUri: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/DiscoverySelectors.html#selectUri(java.lang.String)[selectUri]'\n    EngineDiscoveryListener: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryListener.html[EngineDiscoveryListener]'\n    EngineDiscoveryRequest: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/EngineDiscoveryRequest.html[EngineDiscoveryRequest]'\n    FileSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/FileSelector.html[FileSelector]'\n    HierarchicalTestEngine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.html[HierarchicalTestEngine]'\n    IterationSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/IterationSelector.html[IterationSelector]'\n    MethodSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/MethodSelector.html[MethodSelector]'\n    ModuleSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/ModuleSelector.html[ModuleSelector]'\n    NamespacedHierarchicalStore: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.html[NamespacedHierarchicalStore]'\n    NestedClassSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedClassSelector.html[NestedClassSelector]'\n    NestedMethodSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/NestedMethodSelector.html[NestedMethodSelector]'\n    OutputDirectoryCreator: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/OutputDirectoryCreator.html[OutputDirectoryCreator]'\n    PackageSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/PackageSelector.html[PackageSelector]'\n    ParallelExecutionConfigurationStrategy: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.html[ParallelExecutionConfigurationStrategy]'\n    UniqueIdSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UniqueIdSelector.html[UniqueIdSelector]'\n    UriSelector: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/discovery/UriSelector.html[UriSelector]'\n    TestEngine: '{javadoc-root}/org.junit.platform.engine/org/junit/platform/engine/TestEngine.html[TestEngine]'\n    # Platform Launcher API\n    junit-platform-launcher: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/package-summary.html[junit-platform-launcher]'\n    DiscoveryIssueException: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/DiscoveryIssueException.html[DiscoveryIssueException]'\n    Launcher: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/Launcher.html[Launcher]'\n    LauncherConfig: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherConfig.html[LauncherConfig]'\n    LauncherDiscoveryListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryListener.html[LauncherDiscoveryListener]'\n    LauncherDiscoveryRequest: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherDiscoveryRequest.html[LauncherDiscoveryRequest]'\n    LauncherDiscoveryRequestBuilder: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.html[LauncherDiscoveryRequestBuilder]'\n    LauncherExecutionRequest: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherExecutionRequest.html[LauncherExecutionRequest]'\n    LauncherExecutionRequestBuilder: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherExecutionRequestBuilder.html[LauncherExecutionRequestBuilder]'\n    LauncherFactory: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/core/LauncherFactory.html[LauncherFactory]'\n    LauncherInterceptor: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherInterceptor.html[LauncherInterceptor]'\n    LauncherSession: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSession.html[LauncherSession]'\n    LauncherSessionListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/LauncherSessionListener.html[LauncherSessionListener]'\n    LoggingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/LoggingListener.html[LoggingListener]'\n    PostDiscoveryFilter: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/PostDiscoveryFilter.html[PostDiscoveryFilter]'\n    SummaryGeneratingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/SummaryGeneratingListener.html[SummaryGeneratingListener]'\n    TestExecutionListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestExecutionListener.html[TestExecutionListener]'\n    TestPlan: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/TestPlan.html[TestPlan]'\n    UniqueIdTrackingListener: '{javadoc-root}/org.junit.platform.launcher/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.html[UniqueIdTrackingListener]'\n    # Platform Reporting\n    LegacyXmlReportGeneratingListener: '{javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.html[LegacyXmlReportGeneratingListener]'\n    OpenTestReportGeneratingListener: '{javadoc-root}/org.junit.platform.reporting/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.html[OpenTestReportGeneratingListener]'\n    # Platform Suite\n    suite-api-package: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/package-summary.html[org.junit.platform.suite.api]'\n    junit-platform-suite-engine: '{javadoc-root}/org.junit.platform.suite.engine/org/junit/platform/suite/engine/package-summary.html[junit-platform-suite-engine]'\n    Select: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/Select.html[@Select]'\n    SelectClasspathResource: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasspathResource.html[@SelectClasspathResource]'\n    SelectClasses: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectClasses.html[@SelectClasses]'\n    SelectDirectories: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectDirectories.html[@SelectDirectories]'\n    SelectFile: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectFile.html[@SelectFile]'\n    SelectMethod: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectMethod.html[@SelectMethod]'\n    SelectModules: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectModules.html[@SelectModules]'\n    SelectPackages: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectPackages.html[@SelectPackages]'\n    SelectUris: '{javadoc-root}/org.junit.platform.suite.api/org/junit/platform/suite/api/SelectUris.html[@SelectUris]'\n    # Platform Test Kit\n    testkit-engine-package: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/package-summary.html[org.junit.platform.testkit.engine]'\n    EngineExecutionResults: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineExecutionResults.html[EngineExecutionResults]'\n    EngineTestKit: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EngineTestKit.html[EngineTestKit]'\n    Event: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Event.html[Event]'\n    EventConditions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventConditions.html[EventConditions]'\n    Events: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Events.html[Events]'\n    EventStatistics: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventStatistics.html[EventStatistics]'\n    EventType: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/EventType.html[EventType]'\n    Executions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/Executions.html[Executions]'\n    TerminationInfo: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TerminationInfo.html[TerminationInfo]'\n    TestExecutionResultConditions: '{javadoc-root}/org.junit.platform.testkit/org/junit/platform/testkit/engine/TestExecutionResultConditions.html[TestExecutionResultConditions]'\n    # Jupiter Core API\n    api-package: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/package-summary.html[org.junit.jupiter.api]'\n    Assertions: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assertions.html[org.junit.jupiter.api.Assertions]'\n    Assumptions: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Assumptions.html[org.junit.jupiter.api.Assumptions]'\n    AutoClose: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/AutoClose.html[@AutoClose]'\n    ClassOrderer_ClassName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.ClassName.html[ClassOrderer.ClassName]'\n    ClassOrderer_Default: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Default.html[ClassOrderer.Default]'\n    ClassOrderer_DisplayName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.DisplayName.html[ClassOrderer.DisplayName]'\n    ClassOrderer_OrderAnnotation: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.OrderAnnotation.html[ClassOrderer.OrderAnnotation]'\n    ClassOrderer_Random: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.Random.html[ClassOrderer.Random]'\n    ClassOrderer: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassOrderer.html[ClassOrderer]'\n    ClassTemplate: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/ClassTemplate.html[@ClassTemplate]'\n    Disabled: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Disabled.html[@Disabled]'\n    MethodOrderer_Default: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Default.html[MethodOrderer.Default]'\n    MethodOrderer_DisplayName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.DisplayName.html[MethodOrderer.DisplayName]'\n    MethodOrderer_MethodName: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.MethodName.html[MethodOrderer.MethodName]'\n    MethodOrderer_OrderAnnotation: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.OrderAnnotation.html[MethodOrderer.OrderAnnotation]'\n    MethodOrderer_Random: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.Random.html[MethodOrderer.Random]'\n    MethodOrderer: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/MethodOrderer.html[MethodOrderer]'\n    Named: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Named.html[Named]'\n    Order: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/Order.html[@Order]'\n    RepetitionInfo: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/RepetitionInfo.html[RepetitionInfo]'\n    TestInfo: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestInfo.html[TestInfo]'\n    TestClassOrder: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestClassOrder.html[@TestClassOrder]'\n    TestMethodOrder: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestMethodOrder.html[@TestMethodOrder]'\n    TestReporter: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestReporter.html[TestReporter]'\n    TestTemplate: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/TestTemplate.html[@TestTemplate]'\n    # @DefaultLocale and @DefaultTimeZone\n    DefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultLocale.html[@DefaultLocale]'\n    DefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/DefaultTimeZone.html[@DefaultTimeZone]'\n    LocaleProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/LocaleProvider.html[LocaleProvider]'\n    TimeZoneProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/TimeZoneProvider.html[TimeZoneProvider]'\n    ReadsDefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultLocale.html[@ReadsDefaultLocale]'\n    ReadsDefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsDefaultTimeZone.html[@ReadsDefaultTimeZone]'\n    WritesDefaultLocale: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultLocale.html[@WritesDefaultLocale]'\n    WritesDefaultTimeZone: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesDefaultTimeZone.html[@WritesDefaultTimeZone]'\n    # System Property Extensions\n    ClearSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ClearSystemProperty.html[@ClearSystemProperty]'\n    SetSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/SetSystemProperty.html[@SetSystemProperty]'\n    RestoreSystemProperties: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/RestoreSystemProperties.html[@RestoreSystemProperties]'\n    ReadsSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/ReadsSystemProperty.html[@ReadsSystemProperty]'\n    WritesSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/util/WritesSystemProperty.html[@WritesSystemProperty]'\n    # Jupiter Parallel API\n    Execution: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Execution.html[@Execution]'\n    Isolated: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Isolated.html[@Isolated]'\n    ResourceLock: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLock.html[@ResourceLock]'\n    ResourceLockTarget: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLockTarget.html[ResourceLockTarget]'\n    ResourceLocksProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/ResourceLocksProvider.html[ResourceLocksProvider]'\n    Resources: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/parallel/Resources.html[Resources]'\n    # Jupiter Extension APIs\n    extension-api-package: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/package-summary.html[org.junit.jupiter.api.extension]'\n    AfterAllCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterAllCallback.html[AfterAllCallback]'\n    AfterClassTemplateInvocationCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterClassTemplateInvocationCallback.html[AfterClassTemplateInvocationCallback]'\n    AfterEachCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterEachCallback.html[AfterEachCallback]'\n    AfterTestExecutionCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/AfterTestExecutionCallback.html[AfterTestExecutionCallback]'\n    ParameterContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterContext.html[ParameterContext]'\n    BeforeAllCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeAllCallback.html[BeforeAllCallback]'\n    BeforeClassTemplateInvocationCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeClassTemplateInvocationCallback.html[BeforeClassTemplateInvocationCallback]'\n    BeforeEachCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeEachCallback.html[BeforeEachCallback]'\n    BeforeTestExecutionCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.html[BeforeTestExecutionCallback]'\n    ClassTemplateInvocationContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContext.html[ClassTemplateInvocationContext]'\n    ClassTemplateInvocationContextProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ClassTemplateInvocationContextProvider.html[ClassTemplateInvocationContextProvider]'\n    ExecutableInvoker: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutableInvoker.html[ExecutableInvoker]'\n    ExecutionCondition: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExecutionCondition.html[ExecutionCondition]'\n    ExtendWith: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtendWith.html[@ExtendWith]'\n    ExtensionContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.html[ExtensionContext]'\n    ExtensionContext_Store: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.Store.html[Store]'\n    ExtensionContext_StoreScope: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ExtensionContext.StoreScope.html[StoreScope]'\n    InvocationInterceptor: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/InvocationInterceptor.html[InvocationInterceptor]'\n    LifecycleMethodExecutionExceptionHandler: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.html[LifecycleMethodExecutionExceptionHandler]'\n    ParameterResolver: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/ParameterResolver.html[ParameterResolver]'\n    RegisterExtension: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/RegisterExtension.html[@RegisterExtension]'\n    TestExecutionExceptionHandler: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.html[TestExecutionExceptionHandler]'\n    TestInstanceFactory: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstanceFactory.html[TestInstanceFactory]'\n    TestInstancePostProcessor: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePostProcessor.html[TestInstancePostProcessor]'\n    TestInstancePreConstructCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.html[TestInstancePreConstructCallback]'\n    TestInstancePreDestroyCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.html[TestInstancePreDestroyCallback]'\n    TestTemplateInvocationContext: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContext.html[TestTemplateInvocationContext]'\n    TestTemplateInvocationContextProvider: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.html[TestTemplateInvocationContextProvider]'\n    TestWatcher: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/TestWatcher.html[TestWatcher]'\n    PreInterruptCallback: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/extension/PreInterruptCallback.html[PreInterruptCallback]'\n    # Jupiter Conditions\n    DisabledForJreRange: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledForJreRange.html[@DisabledForJreRange]'\n    DisabledIf: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIf.html[@DisabledIf]'\n    DisabledIfEnvironmentVariable: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.html[@DisabledIfEnvironmentVariable]'\n    DisabledIfSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledIfSystemProperty.html[@DisabledIfSystemProperty]'\n    DisabledInNativeImage: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledInNativeImage.html[@DisabledInNativeImage]'\n    DisabledOnJre: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnJre.html[@DisabledOnJre]'\n    DisabledOnOs: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/DisabledOnOs.html[@DisabledOnOs]'\n    EnabledForJreRange: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledForJreRange.html[@EnabledForJreRange]'\n    EnabledIf: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIf.html[@EnabledIf]'\n    EnabledIfEnvironmentVariable: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.html[@EnabledIfEnvironmentVariable]'\n    EnabledIfSystemProperty: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledIfSystemProperty.html[@EnabledIfSystemProperty]'\n    EnabledInNativeImage: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledInNativeImage.html[@EnabledInNativeImage]'\n    EnabledOnJre: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnJre.html[@EnabledOnJre]'\n    EnabledOnOs: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/EnabledOnOs.html[@EnabledOnOs]'\n    JRE: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/condition/JRE.html[JRE]'\n    # Jupiter I/O\n    TempDir: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDir.html[@TempDir]'\n    TempDirDeletionStrategy: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDirDeletionStrategy.html[TempDirDeletionStrategy]'\n    TempDirDeletionStrategyIgnoreFailures: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDirDeletionStrategy.IgnoreFailures.html[IgnoreFailures]'\n    TempDirDeletionStrategyStandard: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDirDeletionStrategy.Standard.html[Standard]'\n    TempDirFactory: '{javadoc-root}/org.junit.jupiter.api/org/junit/jupiter/api/io/TempDirFactory.html[TempDirFactory]'\n    # Jupiter Params\n    params-provider-package: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/package-summary.html[org.junit.jupiter.params.provider]'\n    AfterParameterizedClassInvocation: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/AfterParameterizedClassInvocation.html[@AfterParameterizedClassInvocation]'\n    AnnotationBasedArgumentConverter: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.html[AnnotationBasedArgumentConverter]'\n    AnnotationBasedArgumentsProvider: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.html[AnnotationBasedArgumentsProvider]'\n    AggregateWith: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/AggregateWith.html[@AggregateWith]'\n    Arguments: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/Arguments.html[Arguments]'\n    ArgumentsProvider: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/ArgumentsProvider.html[ArgumentsProvider]'\n    ArgumentsAccessor: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAccessor.html[ArgumentsAccessor]'\n    ArgumentsAggregator: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/aggregator/ArgumentsAggregator.html[ArgumentsAggregator]'\n    ArgumentConverter: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/converter/ArgumentConverter.html[ArgumentConverter]'\n    BeforeParameterizedClassInvocation: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/BeforeParameterizedClassInvocation.html[@BeforeParameterizedClassInvocation]'\n    CsvArgumentsProvider: '{junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java[CsvArgumentsProvider]'\n    EmptySource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/EmptySource.html[@EmptySource]'\n    FieldSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/FieldSource.html[@FieldSource]'\n    MethodSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/MethodSource.html[@MethodSource]'\n    NullAndEmptySource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullAndEmptySource.html[@NullAndEmptySource]'\n    NullSource: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/provider/NullSource.html[@NullSource]'\n    Parameter: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/Parameter.html[@Parameter]'\n    ParameterizedClass: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedClass.html[@ParameterizedClass]'\n    ParameterizedTest: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html[@ParameterizedTest]'\n    ParameterInfo: '{javadoc-root}/org.junit.jupiter.params/org/junit/jupiter/params/ParameterInfo.html[ParameterInfo]'\n    ValueArgumentsProvider: '{junit-framework-repo}/blob/main/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java[ValueArgumentsProvider]'\n    # Jupiter Engine\n    junit-jupiter-engine: '{javadoc-root}/org.junit.jupiter.engine/org/junit/jupiter/engine/package-summary.html[junit-jupiter-engine]'\n    # Jupiter Extension Implementations\n    AutoCloseExtension: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java[AutoCloseExtension]'\n    DisabledCondition: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java[DisabledCondition]'\n    RepetitionExtension: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java[RepetitionExtension]'\n    TempDirectory: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java[TempDirectory]'\n    TestInfoParameterResolver: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java[TestInfoParameterResolver]'\n    TestReporterParameterResolver: '{current-branch}/junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java[TestReporterParameterResolver]'\n    TypeBasedParameterResolver: '{current-branch}/junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java[TypeBasedParameterResolver]'\n    # Jupiter Examples\n    CustomAnnotationParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java[CustomAnnotationParameterResolver]'\n    CustomTypeParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java[CustomTypeParameterResolver]'\n    MapOfListsTypeBasedParameterResolver: '{current-branch}/jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java[MapOfListsTypeBasedParameterResolver]'\n    # Jupiter Migration Support\n    EnableJUnit4MigrationSupport: '{javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.html[@EnableJUnit4MigrationSupport]'\n    EnableRuleMigrationSupport: '{javadoc-root}/org.junit.jupiter.migrationsupport/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.html[@EnableRuleMigrationSupport]'\n    # JUnit Start\n    JUnit: '{javadoc-root}/org.junit.start/org/junit/start/JUnit.html[JUnit]'\n    # Vintage\n    junit-vintage-engine: '{javadoc-root}/org.junit.vintage.engine/org/junit/vintage/engine/package-summary.html[junit-vintage-engine]'\n    # Examples Repository\n    junit-examples-repo: '{junit-team}/junit-examples'\n    junit-jupiter-starter-ant: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-ant[junit-jupiter-starter-ant]'\n    junit-jupiter-starter-bazel: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-bazel[junit-jupiter-starter-bazel]'\n    junit-jupiter-starter-gradle-groovy: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-groovy[junit-jupiter-starter-gradle-groovy]'\n    junit-jupiter-starter-gradle-kotlin: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle-kotlin[junit-jupiter-starter-gradle-kotlin]'\n    junit-jupiter-starter-gradle: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-gradle[junit-jupiter-starter-gradle]'\n    junit-jupiter-starter-maven: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-maven[junit-jupiter-starter-maven]'\n    junit-jupiter-starter-sbt: '{junit-examples-repo}/tree/{release-branch}/junit-jupiter-starter-sbt[junit-jupiter-starter-sbt]'\n    # Third-party Links\n    API: 'https://apiguardian-team.github.io/apiguardian/docs/current/api/[@API]'\n    API_Guardian: 'https://github.com/apiguardian-team/apiguardian[@API Guardian]'\n    AssertJ: 'https://assertj.github.io/doc/[AssertJ]'\n    Checkstyle: 'https://checkstyle.sourceforge.io[Checkstyle]'\n    DiscussionsQA: 'https://github.com/junit-team/junit-framework/discussions/categories/q-a'\n    Hamcrest: 'https://hamcrest.org/JavaHamcrest/[Hamcrest]'\n    Jimfs: 'https://google.github.io/jimfs/[Jimfs]'\n    Log4j: 'https://logging.apache.org/log4j/2.x/[Log4j]'\n    Log4j_JDK_Logging_Adapter: 'https://logging.apache.org/log4j/2.x/log4j-jul/index.html[Log4j JDK Logging Adapter]'\n    Logback: 'https://logback.qos.ch/[Logback]'\n    LogManager: 'https://docs.oracle.com/en/java/javase/17/docs/api/java.logging/java/util/logging/LogManager.html[LogManager]'\n    Maven_Central: 'https://central.sonatype.com/[Maven Central]'\n    MockitoExtension: 'https://github.com/mockito/mockito/blob/release/2.x/subprojects/junit-jupiter/src/main/java/org/mockito/junit/jupiter/MockitoExtension.java[MockitoExtension]'\n    ServiceLoader: '{jdk-javadoc-base-url}/java.base/java/util/ServiceLoader.html[ServiceLoader]'\n    SpringExtension: 'https://github.com/spring-projects/spring-framework/tree/HEAD/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/SpringExtension.java[SpringExtension]'\n    StackOverflow: 'https://stackoverflow.com/questions/tagged/junit5[Stack Overflow]'\n    Truth: 'https://truth.dev/[Truth]'\n    OpenTestReporting: 'https://github.com/ota4j-team/open-test-reporting[Open Test Reporting]'\n    OpenTestReportingCliTool: 'https://github.com/ota4j-team/open-test-reporting#cli-tool-for-validation-and-format-conversion[Open Test Reporting CLI Tool]'\n"
  },
  {
    "path": "documentation/documentation.gradle.kts",
    "content": "import junitbuild.exec.CaptureJavaExecOutput\nimport junitbuild.exec.ClasspathSystemPropertyProvider\nimport junitbuild.exec.GenerateStandaloneConsoleLauncherShadowedArtifactsFile\nimport junitbuild.exec.RunConsoleLauncher\nimport junitbuild.extensions.isSnapshot\nimport junitbuild.extensions.javaModuleName\nimport junitbuild.javadoc.JavadocValuesOption\nimport junitbuild.javadoc.ModuleSpecificJavadocFileOption\nimport junitbuild.javadoc.VersionNumber\nimport org.gradle.api.tasks.PathSensitivity.RELATIVE\nimport java.nio.file.Files\nimport kotlin.io.path.writeLines\n\nplugins {\n\talias(libs.plugins.plantuml)\n\tid(\"junitbuild.antora-conventions\")\n\tid(\"junitbuild.build-parameters\")\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.testing-conventions\")\n}\n\nval mavenizedProjects: List<Project> by rootProject\nval modularProjects: List<Project> by rootProject\n\n// Because we need to set up Javadoc aggregation\nmodularProjects.forEach { evaluationDependsOn(it.path) }\n\njavaLibrary {\n\tmainJavaVersion = JavaVersion.VERSION_17\n\ttestJavaVersion = JavaVersion.VERSION_17\n}\n\nval apiReport = configurations.dependencyScope(\"apiReport\")\nval apiReportClasspath = configurations.resolvable(\"apiReportClasspath\") {\n\textendsFrom(apiReport.get())\n}\nval standaloneConsoleLauncher = configurations.dependencyScope(\"standaloneConsoleLauncher\")\nval standaloneConsoleLauncherClasspath = configurations.resolvable(\"standaloneConsoleLauncherClasspath\") {\n\textendsFrom(standaloneConsoleLauncher.get())\n}\nval attestation = configurations.dependencyScope(\"attestation\")\nval attestationClasspath = configurations.resolvable(\"attestationClasspath\") {\n\textendsFrom(attestation.get())\n\tisTransitive = false\n}\nval allJavadocSinceValues = configurations.dependencyScope(\"allJavadocSinceValues\")\nval allJavadocSinceValuesClasspath = configurations.resolvable(\"allJavadocSinceValuesClasspath\") {\n\textendsFrom(allJavadocSinceValues.get())\n\tattributes {\n\t\tattribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(\"javadoc-since-values\"))\n\t}\n}\nval tools by sourceSets.creating\nval toolsImplementation by configurations.getting\n\ndependencies {\n\timplementation(projects.junitJupiterApi) {\n\t\tbecause(\"Jupiter API is used in src/main/java\")\n\t}\n\n\tcompileOnlyApi(libs.jspecify)\n\n\t// Pull in all \"modular projects\" to ensure that they are included\n\t// in reports generated by the ApiReportGenerator.\n\tmodularProjects.forEach { apiReport(it) }\n\tapiReport(libs.openTestReporting.tooling.spi)\n\n\tmodularProjects.forEach { allJavadocSinceValues(it) }\n\n\t// Pull in all \"mavenized projects\" to ensure that they are included\n\t// in the generation of build provenance attestation.\n\tmavenizedProjects.forEach { attestation(it) }\n\n\ttestImplementation(projects.junitJupiterMigrationsupport)\n\ttestImplementation(projects.junitPlatformConsole)\n\ttestImplementation(projects.junitPlatformSuite)\n\ttestImplementation(projects.junitPlatformTestkit)\n\ttestImplementation(projects.junitVintageEngine)\n\ttestImplementation(kotlin(\"stdlib\"))\n\ttestImplementation(libs.kotlinx.coroutines.test)\n\ttestRuntimeOnly(kotlin(\"reflect\"))\n\n\ttoolsImplementation(projects.junitPlatformCommons)\n\ttoolsImplementation(libs.classgraph)\n\ttoolsImplementation(libs.apiguardian)\n\n\ttestImplementation(libs.jimfs) {\n\t\tbecause(\"Jimfs is used in src/test/java\")\n\t}\n\n\tstandaloneConsoleLauncher(projects.junitPlatformConsoleStandalone)\n}\n\nval buildRevision: String by rootProject.extra\nval snapshot = version.isSnapshot()\nval releaseBranch = if (snapshot) \"HEAD\" else \"r${version}\"\nval replaceCurrentDocs = buildParameters.documentation.replaceCurrentDocs\nval ota4jDocVersion = libs.versions.opentest4j.map { if (it.isSnapshot()) \"snapshot\" else it }.get()\nval apiGuardianDocVersion = libs.versions.apiguardian.map { if (it.isSnapshot()) \"snapshot\" else it }.get()\n\nval generatedAsciiDocPath = layout.buildDirectory.dir(\"generated/asciidoc\")\nval consoleLauncherOptionsFile = generatedAsciiDocPath.map { it.file(\"console-launcher-options.txt\") }\nval consoleLauncherDiscoverOptionsFile = generatedAsciiDocPath.map { it.file(\"console-launcher-discover-options.txt\") }\nval consoleLauncherExecuteOptionsFile = generatedAsciiDocPath.map { it.file(\"console-launcher-execute-options.txt\") }\nval consoleLauncherEnginesOptionsFile = generatedAsciiDocPath.map { it.file(\"console-launcher-engines-options.txt\") }\nval experimentalApisTableFile = generatedAsciiDocPath.map { it.file(\"experimental-apis-table.adoc\") }\nval deprecatedApisTableFile = generatedAsciiDocPath.map { it.file(\"deprecated-apis-table.adoc\") }\nval standaloneConsoleLauncherShadowedArtifactsFile = generatedAsciiDocPath.map { it.file(\"console-launcher-standalone-shadowed-artifacts.adoc\") }\n\nval jdkJavadocBaseUrl = \"https://docs.oracle.com/en/java/javase/${JavaVersion.current().majorVersion}/docs/api\"\nval elementListsDir = layout.buildDirectory.dir(\"elementLists\")\nval externalModulesWithoutModularJavadoc = mapOf(\n\t\t\"org.apiguardian.api\" to \"https://apiguardian-team.github.io/apiguardian/docs/$apiGuardianDocVersion/api/\",\n\t\t\"org.assertj.core\" to \"https://javadoc.io/doc/org.assertj/assertj-core/${libs.versions.assertj.get()}/\",\n\t\t\"org.opentest4j\" to \"https://ota4j-team.github.io/opentest4j/docs/$ota4jDocVersion/api/\",\n\t\t\"org.jspecify\" to \"https://jspecify.dev/docs/api/\",\n)\nrequire(externalModulesWithoutModularJavadoc.values.all { it.endsWith(\"/\") }) {\n\t\"all base URLs must end with a trailing slash: $externalModulesWithoutModularJavadoc\"\n}\n\ntasks {\n\n\tval consoleLauncherTestReportsDir = project.layout.buildDirectory.dir(\"console-launcher-test-results\")\n\tval consoleLauncherTestEventXmlFiles =\n\t\tfiles(consoleLauncherTestReportsDir.map { it.asFileTree.matching { include(\"**/open-test-report.xml\") } })\n\n\tval consoleLauncherTest by registering(RunConsoleLauncher::class) {\n\t\targs.addAll(\"execute\")\n\t\targs.addAll(\"--scan-classpath\")\n\t\targs.addAll(\"--config=junit.platform.reporting.open.xml.enabled=true\")\n\t\targs.addAll(\"--config=junit.platform.reporting.open.xml.git.enabled=true\")\n\t\targs.addAll(\"--config=junit.platform.output.capture.stdout=true\")\n\t\targs.addAll(\"--config=junit.platform.output.capture.stderr=true\")\n\t\targs.addAll(\"--config=junit.platform.discovery.issue.severity.critical=warning\")\n\t\toutputs.dir(consoleLauncherTestReportsDir)\n\t\targumentProviders.add(CommandLineArgumentProvider {\n\t\t\tlistOf(\n\t\t\t\t\"--reports-dir=${consoleLauncherTestReportsDir.get()}\",\n\t\t\t)\n\t\t})\n\t\targs.addAll(\"--include-classname\", \".*Tests\")\n\t\targs.addAll(\"--include-classname\", \".*Demo\")\n\t\targs.addAll(\"--exclude-tag\", \"exclude\")\n\t\targs.addAll(\"--exclude-tag\", \"timeout\")\n\n\t\tdoFirst {\n\t\t\tconsoleLauncherTestReportsDir.get().asFile.deleteRecursively()\n\t\t}\n\n\t\tfinalizedBy(generateOpenTestHtmlReport)\n\t}\n\n\tgenerateOpenTestHtmlReport {\n\t\tmustRunAfter(consoleLauncherTest)\n\t\tinputs.files(consoleLauncherTestEventXmlFiles).withPathSensitivity(RELATIVE).skipWhenEmpty()\n\t\targumentProviders += CommandLineArgumentProvider {\n\t\t\tconsoleLauncherTestEventXmlFiles.files.map { it.absolutePath }.toList()\n\t\t}\n\t}\n\n\tregister<RunConsoleLauncher>(\"consoleLauncher\") {\n\t\thideOutput = false\n\t\toutputs.upToDateWhen { false }\n\t}\n\n\ttest {\n\t\tinclude(\"**/*Demo.class\")\n\t\t(options as JUnitPlatformOptions).apply {\n\t\t\tincludeEngines(\"junit-vintage\")\n\t\t\tincludeTags(\"timeout\")\n\t\t\tsystemProperty(\"junit.platform.discovery.issue.severity.critical\", \"warning\")\n\t\t}\n\t}\n\n\tcheck {\n\t\tdependsOn(consoleLauncherTest)\n\t}\n\n\tnamed<JavaCompile>(tools.compileJavaTaskName) {\n\t\toptions.release.set(25)\n\t}\n\n\tnamed<Checkstyle>(\"checkstyleTools\") {\n\t\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleMain.xml\"))\n\t}\n\n\tval generateConsoleLauncherOptions by registering(CaptureJavaExecOutput::class) {\n\t\tclasspath.from(standaloneConsoleLauncherClasspath)\n\t\tmainClass = \"org.junit.platform.console.ConsoleLauncher\"\n\t\targs.addAll(\"--help\", \"--disable-banner\")\n\t\toutputFile = consoleLauncherOptionsFile\n\t}\n\n\tval generateConsoleLauncherDiscoverOptions by registering(CaptureJavaExecOutput::class) {\n\t\tclasspath.from(standaloneConsoleLauncherClasspath)\n\t\tmainClass = \"org.junit.platform.console.ConsoleLauncher\"\n\t\targs.addAll(\"discover\", \"--help\", \"--disable-banner\")\n\t\toutputFile = consoleLauncherDiscoverOptionsFile\n\t}\n\n\tval generateConsoleLauncherExecuteOptions by registering(CaptureJavaExecOutput::class) {\n\t\tclasspath.from(standaloneConsoleLauncherClasspath)\n\t\tmainClass = \"org.junit.platform.console.ConsoleLauncher\"\n\t\targs.addAll(\"execute\", \"--help\", \"--disable-banner\")\n\t\toutputFile = consoleLauncherExecuteOptionsFile\n\t}\n\n\tval generateConsoleLauncherEnginesOptions by registering(CaptureJavaExecOutput::class) {\n\t\tclasspath.from(standaloneConsoleLauncherClasspath)\n\t\tmainClass = \"org.junit.platform.console.ConsoleLauncher\"\n\t\targs.addAll(\"engines\", \"--help\", \"--disable-banner\")\n\t\toutputFile = consoleLauncherEnginesOptionsFile\n\t}\n\n\tval generateApiTables by registering(JavaExec::class) {\n\t\tclasspath = tools.runtimeClasspath\n\t\tmainClass = \"org.junit.api.tools.ApiReportGenerator\"\n\t\tsystemProperty(\"api.moduleNames\", modularProjects.map { it.javaModuleName }.sorted().joinToString(\",\"))\n\t\tjvmArgumentProviders += ClasspathSystemPropertyProvider(\"api.modulePath\", apiReportClasspath.get())\n\t\targumentProviders += CommandLineArgumentProvider {\n\t\t\tlistOf(\n\t\t\t\t\"DEPRECATED=${deprecatedApisTableFile.get().asFile.absolutePath}\",\n\t\t\t\t\"EXPERIMENTAL=${experimentalApisTableFile.get().asFile.absolutePath}\",\n\t\t\t)\n\t\t}\n\t\toutputs.cacheIf { true }\n\t\toutputs.file(deprecatedApisTableFile)\n\t\toutputs.file(experimentalApisTableFile)\n\t}\n\n\tval generateStandaloneConsoleLauncherShadowedArtifactsFile by registering(GenerateStandaloneConsoleLauncherShadowedArtifactsFile::class) {\n\t\tinputJar.fileProvider(standaloneConsoleLauncherClasspath.flatMap { it.elements.map { it.single().asFile } })\n\t\toutputFile = standaloneConsoleLauncherShadowedArtifactsFile\n\t}\n\n\tplantUml {\n\t\tfileFormat = \"SVG\"\n\t\toutputs.cacheIf { true }\n\t}\n\n\tval plantUmlOutputDirectory = plantUml.flatMap { it.outputDirectory }\n\n\tval generateAsciidocInputs by registering {\n\t\tdependsOn(\n\t\t\tgenerateConsoleLauncherOptions,\n\t\t\tgenerateConsoleLauncherDiscoverOptions,\n\t\t\tgenerateConsoleLauncherExecuteOptions,\n\t\t\tgenerateConsoleLauncherEnginesOptions,\n\t\t\tgenerateApiTables,\n\t\t\tgenerateStandaloneConsoleLauncherShadowedArtifactsFile,\n\t\t\tplantUmlOutputDirectory\n\t\t)\n\t}\n\n\tval downloadJavadocElementLists by registering {\n\t\toutputs.cacheIf { true }\n\t\toutputs.dir(elementListsDir).withPropertyName(\"elementListsDir\")\n\t\tinputs.property(\"externalModulesWithoutModularJavadoc\", externalModulesWithoutModularJavadoc)\n\t\tdoFirst {\n\t\t\texternalModulesWithoutModularJavadoc.forEach { (moduleName, baseUrl) ->\n\t\t\t\tval resource = resources.text.fromUri(\"${baseUrl}element-list\")\n\t\t\t\telementListsDir.get().asFile.resolve(moduleName).apply {\n\t\t\t\t\tmkdir()\n\t\t\t\t\tresolve(\"element-list\").writeText(\"module:$moduleName\\n${resource.asString()}\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tval mergeJavadocSinceValues by registering {\n\t\tinputs.files(allJavadocSinceValuesClasspath).withPathSensitivity(PathSensitivity.NONE)\n\t\tval outputFile = layout.buildDirectory.file(\"docs/aggregated-javadoc-since-values.txt\")\n\t\toutputs.file(outputFile)\n\t\toutputs.cacheIf { true }\n\t\tdoFirst {\n\t\t\tval initialVersions = setOf(\"1.0\", \"4.12\", \"5.0\")\n\t\t\tval noPublicItems = setOf(\"1.3.1\", \"5.0.3\", \"5.4.1\", \"5.11.3\")\n\t\t\tval excludes = initialVersions + noPublicItems\n\t\t\tval values = allJavadocSinceValuesClasspath.get().files.asSequence()\n\t\t\t\t.flatMap { it.readLines().asSequence() }\n\t\t\t\t.filter { it.isNotBlank() && it !in excludes }\n\t\t\t\t.sortedBy { VersionNumber(it) }\n\t\t\t\t.distinct()\n\t\t\twith(outputFile.get().asFile.toPath()) {\n\t\t\t\tFiles.createDirectories(parent)\n\t\t\t\twriteLines(values)\n\t\t\t}\n\t\t}\n\t}\n\n\tval aggregateJavadocs by registering(Javadoc::class) {\n\t\tdependsOn(modularProjects.map { it.tasks.jar })\n\t\tdependsOn(downloadJavadocElementLists)\n\t\tgroup = \"Documentation\"\n\t\tdescription = \"Generates aggregated Javadocs\"\n\n\t\ttitle = \"JUnit $version API\"\n\n\t\tval additionalStylesheetFile = \"src/javadoc/junit-stylesheet.css\"\n\t\tinputs.file(additionalStylesheetFile)\n\t\tval overviewFile = \"src/javadoc/junit-overview.html\"\n\t\tinputs.file(overviewFile)\n\n\t\toptions {\n\n\t\t\tmemberLevel = JavadocMemberLevel.PROTECTED\n\t\t\theader = rootProject.description\n\t\t\tencoding = \"UTF-8\"\n\t\t\tlocale = \"en\"\n\t\t\toverview = overviewFile\n\t\t\tjFlags(\"-Xmx1g\")\n\n\t\t\tthis as StandardJavadocDocletOptions\n\t\t\tsplitIndex(true)\n\t\t\taddBooleanOption(\"Xdoclint:all,-missing\", true)\n\t\t\taddBooleanOption(\"Werror\", true)\n\t\t\taddBooleanOption(\"html5\", true)\n\t\t\taddMultilineStringsOption(\"tag\").value = listOf(\n\t\t\t\t\t\"apiNote:a:API Note:\",\n\t\t\t\t\t\"implNote:a:Implementation Note:\"\n\t\t\t)\n\n\t\t\tlinks(jdkJavadocBaseUrl)\n\t\t\tlinks(\"https://junit.org/junit4/javadoc/${libs.versions.junit4.get()}/\")\n\t\t\texternalModulesWithoutModularJavadoc.forEach { (moduleName, baseUrl) ->\n\t\t\t\tlinksOffline(baseUrl, elementListsDir.get().asFile.resolve(moduleName).absolutePath)\n\t\t\t}\n\n\t\t\tgroups = mapOf(\n\t\t\t\t\t\"Jupiter\" to listOf(\"org.junit.jupiter*\"),\n\t\t\t\t\t\"Vintage\" to listOf(\"org.junit.vintage*\"),\n\t\t\t\t\t\"Platform\" to listOf(\"org.junit.platform*\")\n\t\t\t)\n\t\t\taddStringOption(\"-add-stylesheet\", additionalStylesheetFile)\n\t\t\taddBooleanOption(\"-no-fonts\", true)\n\t\t\tuse(true)\n\t\t\tnoTimestamp(true)\n\n\t\t\taddStringsOption(\"-module\", \",\").value = modularProjects.map { it.javaModuleName }\n\t\t\taddOption(ModuleSpecificJavadocFileOption(\"-module-source-path\", modularProjects.associate { project ->\n\t\t\t\tproject.javaModuleName to provider {\n\t\t\t\t\tfiles(\n\t\t\t\t\t\tproject.sourceSets.named { it.startsWith(\"main\") }.map {\n\t\t\t\t\t\t\tit.allJava.srcDirs.filter { it.exists() }\n\t\t\t\t\t\t}\n\t\t\t\t\t).asPath\n\t\t\t\t}\n\t\t\t}))\n\t\t\taddStringOption(\"-add-modules\", \"info.picocli,org.opentest4j.reporting.events,de.siegmar.fastcsv\")\n\t\t\taddOption(ModuleSpecificJavadocFileOption(\"-add-reads\", mapOf(\n\t\t\t\t\t\"org.junit.platform.console\" to provider { \"info.picocli\" },\n\t\t\t\t\t\"org.junit.platform.reporting\" to provider { \"org.opentest4j.reporting.events\" },\n\t\t\t\t\t\"org.junit.jupiter.params\" to provider { \"de.siegmar.fastcsv\" }\n\t\t\t)))\n\n\t\t\tval javadocSinceValuesFile: Provider<File> = mergeJavadocSinceValues.map { it.outputs.files.singleFile }\n\t\t\tinputs.file(javadocSinceValuesFile)\n\t\t\t\t.withPathSensitivity(PathSensitivity.NONE)\n\t\t\t\t.withPropertyName(\"javadoc-since-values-file\")\n\t\t\taddOption(JavadocValuesOption(\"-since\", javadocSinceValuesFile.map { file ->\n\t\t\t\tfile.readLines().asSequence().filter { it.isNotBlank() }.toList()\n\t\t\t}))\n\t\t}\n\n\t\tsource(modularProjects.map { project ->\n\t\t\tfiles(project.sourceSets.named { it.startsWith(\"main\") }.map { it.allJava })\n\t\t})\n\t\tclasspath = files(modularProjects.map { it.sourceSets.main.get().compileClasspath })\n\n\t\tsetMaxMemory(\"1024m\")\n\t\toptions.destinationDirectory = layout.buildDirectory.dir(\"docs/javadoc\").get().asFile\n\t}\n\n\tval fixJavadoc by registering(Copy::class) {\n\t\tdependsOn(aggregateJavadocs)\n\t\tgroup = \"Documentation\"\n\t\tdescription = \"Fix links to external API specs in the locally aggregated Javadoc HTML files\"\n\n\t\tval inputDir = aggregateJavadocs.map { it.destinationDir!! }\n\t\tinputs.property(\"externalModulesWithoutModularJavadoc\", externalModulesWithoutModularJavadoc)\n\t\tfrom(inputDir.map { File(it, \"element-list\") }) {\n\t\t\t// For compatibility with pre JDK 10 versions of the Javadoc tool\n\t\t\trename { \"package-list\" }\n\t\t}\n\t\tfrom(inputDir) {\n\t\t\tfilesMatching(\"**/*.html\") {\n\t\t\t\tval favicon =\n\t\t\t\t\t\"\"\"\n\t\t\t\t\t\t<link rel=\"icon\" type=\"image/png\" href=\"https://junit.org/assets/img/junit-diamond.png\">\n\t\t\t\t\t\t<link rel=\"icon\" type=\"image/svg+xml\" href=\"https://junit.org/assets/img/junit-diamond-adaptive.svg\" sizes=\"any\">\n\t\t\t\t\t\t\"\"\".trimIndent()\n\n\t\t\t\tval version = project.version.toString().replace(\"-SNAPSHOT\", \"\")\n\t\t\t\tval targetUrl = if (buildParameters.ci)\n\t\t\t\t\t\"https://docs.junit.org/$version\"\n\t\t\t\telse\n\t\t\t\t\tproject.antora.siteDir.get().asFile.toURI().resolve(version).toString()\n\n\t\t\t\tfilter { line ->\n\t\t\t\t\tvar result = if (line.startsWith(\"<head>\")) line.replace(\"<head>\", \"<head>$favicon\") else line\n\t\t\t\t\texternalModulesWithoutModularJavadoc.forEach { (moduleName, baseUrl) ->\n\t\t\t\t\t\tresult = result.replace(\"${baseUrl}$moduleName/\", baseUrl)\n\t\t\t\t\t}\n\n\t\t\t\t\tresult = result.replace(\"https://docs.junit.org/current\", targetUrl)\n\n\t\t\t\t\treturn@filter result\n\t\t\t\t}\n\t\t\t}\n\t\t\tfilesMatching(\"**/stylesheet.css\") {\n\t\t\t\t// Remove invalid import of `dejavu.css` due to `javadoc --no-fonts`\n\t\t\t\tfilter { line -> if (line.startsWith(\"@import url('fonts/\")) null else line }\n\t\t\t}\n\t\t}\n\t\tinto(layout.buildDirectory.dir(\"docs/fixedJavadoc\"))\n\t}\n\n\tval prepareGitHubAttestation by registering(Sync::class) {\n\t\tfrom(attestationClasspath)\n\t\tinto(layout.buildDirectory.dir(\"attestation\"))\n\t\trename(\"(.*)-SNAPSHOT.jar\", \"$1-SNAPSHOT+${buildRevision.substring(0, 7)}.jar\")\n\t}\n\n\tgenerateAntoraYml {\n\t\tasciidocAttributes.putAll(provider {\n\t\t\tmapOf(\n\t\t\t\t\"version\" to project.version,\n\t\t\t\t\"junit4-version\" to libs.versions.junit4.get(),\n\t\t\t\t\"apiguardian-version\" to libs.versions.apiguardian.get(),\n\t\t\t\t\"ota4j-version\" to libs.versions.opentest4j.get(),\n\t\t\t\t\"surefire-version\" to libs.versions.surefire.get(),\n\t\t\t\t\"release-branch\" to releaseBranch,\n\t\t\t\t\"jdk-javadoc-base-url\" to jdkJavadocBaseUrl\n\t\t\t)\n\t\t})\n\t}\n\n\tgenerateAntoraResources {\n\t\tdependsOn(generateAsciidocInputs, fixJavadoc)\n\t}\n}\n"
  },
  {
    "path": "documentation/modules/ROOT/extra-site-nav.adoc",
    "content": "* {javadoc-root}/index.html[Javadoc]\n"
  },
  {
    "path": "documentation/modules/ROOT/images/_source/extensions_BrokenLifecycleMethodConfigDemo.txt",
    "content": "@Starter(Platform)\nResult = JupiterEngine.execute(BrokenLifecycleMethodConfigDemo) {\n\n  Extension1.beforeEach()\n  Extension2.beforeEach()\n  // @BeforeEach\n  BrokenLifecycleMethodConfigDemo.insertTestDataIntoDatabase()\n  // @BeforeEach\n  BrokenLifecycleMethodConfigDemo.connectToDatabase()\n  // @Test\n  BrokenLifecycleMethodConfigDemo.testDatabaseFunctionality()\n  // @AfterEach\n  BrokenLifecycleMethodConfigDemo.disconnectFromDatabase()\n  // @AfterEach\n  BrokenLifecycleMethodConfigDemo.deleteTestDataFromDatabase()\n  Extension2.afterEach()\n  Extension1.afterEach()\n\n}\n"
  },
  {
    "path": "documentation/modules/ROOT/images/_source/extensions_DatabaseTestsDemo.txt",
    "content": "@Starter(Platform)\nResult = JupiterEngine.execute(DatabaseTestsDemo) {\n\n  // @BeforeAll (static invocation)\n  AbstractDatabaseTests.createDatabase()\n  // @BeforeAll (static invocation)\n  DatabaseTestsDemo.beforeAll()\n  Extension1.beforeEach()\n  Extension2.beforeEach()\n  // @BeforeEach inherited from AbstractDatabaseTests\n  DatabaseTestsDemo.connectToDatabase()\n  // @BeforeEach\n  DatabaseTestsDemo.insertTestDataIntoDatabase()\n  // @Test\n  DatabaseTestsDemo.testDatabaseFunctionality()\n  // @AfterEach\n  DatabaseTestsDemo.deleteTestDataFromDatabase()\n  // @AfterEach inherited from AbstractDatabaseTests\n  DatabaseTestsDemo.disconnectFromDatabase()\n  Extension2.afterEach()\n  Extension1.afterEach()\n  // @AfterAll (static invocation)\n  DatabaseTestsDemo.afterAll()\n  // @AfterAll (static invocation)\n  AbstractDatabaseTests.destroyDatabase()\n\n}\n"
  },
  {
    "path": "documentation/modules/ROOT/nav.adoc",
    "content": "* xref:overview.adoc[]\n* xref:writing-tests/intro.adoc[]\n** xref:writing-tests/annotations.adoc[]\n** xref:writing-tests/definitions.adoc[]\n** xref:writing-tests/test-classes-and-methods.adoc[]\n** xref:writing-tests/display-names.adoc[]\n** xref:writing-tests/assertions.adoc[]\n** xref:writing-tests/assumptions.adoc[]\n** xref:writing-tests/exception-handling.adoc[]\n** xref:writing-tests/disabling-tests.adoc[]\n** xref:writing-tests/conditional-test-execution.adoc[]\n** xref:writing-tests/tagging-and-filtering.adoc[]\n** xref:writing-tests/test-execution-order.adoc[]\n** xref:writing-tests/test-instance-lifecycle.adoc[]\n** xref:writing-tests/nested-tests.adoc[]\n** xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[]\n** xref:writing-tests/test-interfaces-and-default-methods.adoc[]\n** xref:writing-tests/repeated-tests.adoc[]\n** xref:writing-tests/parameterized-classes-and-tests.adoc[]\n** xref:writing-tests/class-templates.adoc[]\n** xref:writing-tests/test-templates.adoc[]\n** xref:writing-tests/dynamic-tests.adoc[]\n** xref:writing-tests/timeouts.adoc[]\n** xref:writing-tests/parallel-execution.adoc[]\n** xref:writing-tests/built-in-extensions.adoc[]\n* xref:migrating-from-junit4.adoc[]\n* xref:running-tests/intro.adoc[]\n** xref:running-tests/ide-support.adoc[]\n** xref:running-tests/build-support.adoc[]\n** xref:running-tests/console-launcher.adoc[]\n** xref:running-tests/source-launcher.adoc[]\n** xref:running-tests/discovery-selectors.adoc[]\n** xref:running-tests/configuration-parameters.adoc[]\n** xref:running-tests/tags.adoc[]\n** xref:running-tests/capturing-standard-output-error.adoc[]\n** xref:running-tests/using-listeners-and-interceptors.adoc[]\n** xref:running-tests/stack-trace-pruning.adoc[]\n** xref:running-tests/discovery-issues.adoc[]\n* xref:extensions/overview.adoc[]\n** xref:extensions/registering-extensions.adoc[]\n** xref:extensions/conditional-test-execution.adoc[]\n** xref:extensions/test-instance-pre-construct-callback.adoc[]\n** xref:extensions/test-instance-factories.adoc[]\n** xref:extensions/test-instance-post-processing.adoc[]\n** xref:extensions/test-instance-pre-destroy-callback.adoc[]\n** xref:extensions/parameter-resolution.adoc[]\n** xref:extensions/test-result-processing.adoc[]\n** xref:extensions/test-lifecycle-callbacks.adoc[]\n** xref:extensions/exception-handling.adoc[]\n** xref:extensions/pre-interrupt-callback.adoc[]\n** xref:extensions/intercepting-invocations.adoc[]\n** xref:extensions/providing-invocation-contexts-for-class-templates.adoc[]\n** xref:extensions/providing-invocation-contexts-for-test-templates.adoc[]\n** xref:extensions/keeping-state-in-extensions.adoc[]\n** xref:extensions/supported-utilities-in-extensions.adoc[]\n** xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc[]\n* Advanced Topics\n** xref:advanced-topics/junit-platform-reporting.adoc[]\n** xref:advanced-topics/junit-platform-suite-engine.adoc[]\n** xref:advanced-topics/testkit.adoc[]\n** xref:advanced-topics/launcher-api.adoc[]\n** xref:advanced-topics/engines.adoc[]\n* xref:api-evolution.adoc[]\n* xref:release-notes.adoc[]\n* xref:appendix.adoc[]\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/advanced-topics/engines.adoc",
    "content": "= Test Engines\n\nA `TestEngine` facilitates _discovery_ and _execution_ of tests for a particular\nprogramming model.\n\nFor example, JUnit provides a `TestEngine` that discovers and executes tests written using\nthe JUnit Jupiter programming model (see xref:writing-tests/intro.adoc[] and xref:extensions/overview.adoc[]).\n\n[[junit]]\n== JUnit Test Engines\n\nJUnit provides three `TestEngine` implementations.\n\n* `{junit-jupiter-engine}`: The core of JUnit Jupiter.\n* `{junit-vintage-engine}`: A thin layer on top of JUnit 4 to allow running _vintage_\n  tests (based on JUnit 3.8 and JUnit 4) with the JUnit Platform launcher infrastructure.\n* `{junit-platform-suite-engine}`: Executes declarative suites of tests with the JUnit\n  Platform launcher infrastructure.\n\n[[custom]]\n== Custom Test Engines\n\nYou can contribute your own custom `{TestEngine}` by implementing the interfaces in the\n{junit-platform-engine} module and _registering_ your engine.\n\nEvery `TestEngine` must provide its own _unique ID_, _discover_ tests from an\n`EngineDiscoveryRequest`, and _execute_ those tests according to an `ExecutionRequest`.\n\n[WARNING]\n.The `junit-` unique ID prefix is reserved for TestEngines from the JUnit Team\n====\nThe JUnit Platform `Launcher` enforces that only `TestEngine` implementations published\nby the JUnit Team may use the `junit-` prefix for their `TestEngine` IDs.\n\n* If any third-party `TestEngine` claims to be `junit-jupiter` or `junit-vintage`, an\n  exception will be thrown, immediately halting execution of the JUnit Platform.\n* If any third-party `TestEngine` uses the `junit-` prefix for its ID, a warning message\n  will be logged. Later releases of the JUnit Platform will throw an exception for such\n  violations.\n====\n\nIn order to facilitate test discovery within IDEs and tools prior to launching the JUnit\nPlatform, `TestEngine` implementations are encouraged to make use of the `@Testable`\nannotation. For example, the `@Test` and `@TestFactory` annotations in JUnit Jupiter are\nmeta-annotated with `@Testable`. Consult the Javadoc for `{Testable}` for further details.\n\nIf your custom `TestEngine` needs to be configured, consider allowing users to supply\nconfiguration via xref:running-tests/configuration-parameters.adoc[configuration parameters]. Please note,\nhowever, that you are strongly encouraged to use a unique prefix for all configuration\nparameters supported by your test engine. Doing so will ensure that there are no conflicts\nbetween the names of your configuration parameters and those from other test engines. In\naddition, since configuration parameters may be supplied as JVM system properties, it is\nwise to avoid conflicts with the names of other system properties. For example, JUnit\nJupiter uses `junit.jupiter.` as a prefix of all of its supported configuration\nparameters. Furthermore, as with the warning above regarding the `junit-` prefix for\n`TestEngine` IDs, you should not use `junit.` as a prefix for the names of your own\nconfiguration parameters.\n\nAlthough there is currently no official guide on how to implement a custom `TestEngine`,\nyou can consult the implementation of <<junit>> or the implementation of\nthird-party test engines listed in the\nhttps://github.com/junit-team/junit-framework/wiki/Third-party-Extensions#junit-platform-test-engines[JUnit wiki].\nYou will also find various tutorials and blogs on the Internet that demonstrate how to\nwrite a custom `TestEngine`.\n\nNOTE: `{HierarchicalTestEngine}` is a convenient abstract base implementation of the\n`TestEngine` SPI (used by the `{junit-jupiter-engine}`) that only requires implementors to\nprovide the logic for test discovery. It implements execution of `TestDescriptors` that\nimplement the `Node` interface, including support for parallel execution.\n\n[[registration]]\n== Registering a TestEngine\n\n`TestEngine` registration is supported via Java's `{ServiceLoader}` mechanism.\n\nFor example, the `junit-jupiter-engine` module registers its\n`org.junit.jupiter.engine.JupiterTestEngine` in a file named\n`org.junit.platform.engine.TestEngine` within the `/META-INF/services` folder in the\n`junit-jupiter-engine` JAR.\n\n[[requirements]]\n== Requirements\n\nNOTE: The words \"must\", \"must not\", \"required\", \"shall\", \"shall not\", \"should\", \"should\nnot\", \"recommended\",  \"may\", and \"optional\" in this section are to be interpreted as\ndescribed in https://www.ietf.org/rfc/rfc2119.txt[RFC 2119.]\n\n[[requirements-mandatory]]\n=== Mandatory requirements\n\nFor interoperability with build tools and IDEs, `TestEngine` implementations must adhere\nto the following requirements:\n\n* The `TestDescriptor` returned from `TestEngine.discover()` _must_ be the root of a tree\n  of `TestDescriptor` instances. This implies that there _must not_ be any cycles between\n  a node and its descendants.\n* The hierarchy of test descriptors returned from `TestEngine.discover()` _must_ be\n  mutable, but the test descriptors _must_ otherwise be immutable after discovery.\n* A `TestEngine` _must_ be able to discover `UniqueIdSelectors` for any unique ID that it\n  previously generated and returned from `TestEngine.discover()`. This enables selecting a\n  subset of tests to execute or rerun.\n* The `executionSkipped`, `executionStarted`, and `executionFinished` methods of the\n  `EngineExecutionListener` passed to `TestEngine.execute()` _must_ be called for every\n  `TestDescriptor` node in the tree returned from `TestEngine.discover()` at most\n  once. Parent nodes _must_ be reported as started before their children and as finished\n  after their children. If a node is reported as skipped, there _must not_ be any events\n  reported for its descendants.\n\n[[requirements-enhanced-compatibility]]\n=== Enhanced compatibility\n\nAdhering to the following requirements is optional but recommended for enhanced\ncompatibility with build tools and IDEs:\n\n* Unless to indicate an empty discovery result, the `TestDescriptor` returned from\n  `TestEngine.discover()` _should_ have children rather than being completely dynamic.\n  This allows tools to display the structure of the tests and to select a subset of tests\n  to execute.\n* When resolving `UniqueIdSelectors`, a `TestEngine` _should_ only return `TestDescriptor`\n  instances with matching unique IDs including their ancestors but _may_ return additional\n  siblings or other nodes that are required for the execution of the selected tests.\n* `TestEngines` _should_ support xref:running-tests/tags.adoc[tagging] tests and containers so\n  that tag filters can be applied when discovering tests.\n* [[requirements-cancellation]] A `TestEngine` _should_ cancel its execution\n  when the `{CancellationToken}` it is passed as part of the `ExecutionRequest` indicates\n  that cancellation has been requested. In this case, it _should_ report any remaining\n  `TestDescriptors` as skipped but not report any events for their descendants. It _may_\n  report already started `TestDescriptors` as aborted in case they have not been executed\n  completely. If a `TestEngine` supports cancellation, it should clean up any resources\n  that it has created just like if execution had finished regularly.\n\n[[discovery-issues]]\n== Reporting Discovery Issues\n\nTest engines should report xref:running-tests/discovery-issues.adoc[discovery issues] if they\nencounter any problems or potential misconfigurations during test discovery. This is\nespecially important if the issue could lead to tests not being executed at all or only\npartially.\n\nIn order to report a `{DiscoveryIssue}`, a test engine should call the\n`issueEncountered()` method on the `{EngineDiscoveryListener}` available via the\n`{EngineDiscoveryRequest}` passed to its `discover()` method. Rather than passing the\nlistener around, the `{DiscoveryIssueReporter}` interface should be used. It also provides\na way to create a `Condition` that reports a discovery issue if its check fails and may\nbe used as a `Predicate` or `Consumer`. Please refer to the implementations of the\n<<junit, test engines provided by JUnit>> for examples.\n\nMoreover, xref:advanced-topics/testkit.adoc#engine-discovery[Engine Test Kit] provides a way to write tests for\nreported discovery issues.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/advanced-topics/junit-platform-reporting.adoc",
    "content": "= JUnit Platform Reporting\n\nThe `junit-platform-reporting` artifact contains `{TestExecutionListener}` implementations\nthat generate XML test reports in two flavors:\n<<open-test-reporting, Open Test Reporting>> and\n<<legacy-xml, legacy>>.\n\nNOTE: The module also contains other `TestExecutionListener` implementations that can be\nused to build custom reporting. See xref:running-tests/using-listeners-and-interceptors.adoc[] for details.\n\n[[output-directory]]\n== Output Directory\n\nThe JUnit Platform provides an `{OutputDirectoryCreator}` via `{EngineDiscoveryRequest}`\nand `{TestPlan}` to registered xref:advanced-topics/engines.adoc[test engines] and\nxref:running-tests/using-listeners-and-interceptors.adoc[listeners], respectively. Its root directory can be configured\nvia the following xref:running-tests/configuration-parameters.adoc[configuration parameter]:\n\n`junit.platform.reporting.output.dir=<path>`::\n  Configure the output directory for reporting. By default, `build` is used if a Gradle\n  build script is found, and `target` if a Maven POM is found; otherwise, the current\n  working directory is used.\n\nTo create a unique output directory per test run, you can use the `\\{uniqueNumber}`\nplaceholder in the path. For example, `reports/junit-\\{uniqueNumber}` will create\ndirectories like `reports/junit-8803697269315188212`. This can be useful when using\nGradle's or Maven's parallel execution capabilities which create multiple JVM forks\nthat run concurrently.\n\n[[open-test-reporting]]\n== Open Test Reporting\n\n`{OpenTestReportGeneratingListener}` writes an XML report for the entire execution in the\nevent-based format specified by {OpenTestReporting} which supports all features of the\nJUnit Platform such as hierarchical test structures, display names, tags, etc.\n\nThe listener is auto-registered and can be configured via the following\nxref:running-tests/configuration-parameters.adoc[configuration parameters]:\n\n`junit.platform.reporting.open.xml.enabled=true|false`::\n  Enable/disable writing the report; defaults to `false`.\n`junit.platform.reporting.open.xml.git.enabled=true|false`::\n  Enable/disable including information about the Git repository (see https://github.com/ota4j-team/open-test-reporting#git[Git extension schema] of open-test-reporting); defaults to `false`.\n`junit.platform.reporting.open.xml.socket=<port>`::\n  Optional port number to redirect events to a socket instead of a file. When specified, the\n  listener will connect to `127.0.0.1:<port>` and send the XML events to the socket. The socket\n  connection is automatically closed when the test execution completes.\n\nIf enabled, the listener creates an XML report file named `open-test-report.xml` in the\nconfigured <<output-directory, output directory>>, unless the\n`junit.platform.reporting.open.xml.socket` configuration parameter is set, in which case the\nevents are sent to the specified socket instead.\n\nIf xref:running-tests/capturing-standard-output-error.adoc[output capturing] is enabled, the captured output\nwritten to `System.out` and `System.err` will be included in the report as well.\n\nTIP: The {OpenTestReportingCliTool} can be used to convert from the event-based format to\nthe hierarchical format which is more human-readable.\n\n=== Gradle\n\nFor Gradle, writing Open Test Reporting compatible XML reports can be enabled and\nconfigured via system properties. The following samples configure its output directory to\nbe the same directory Gradle uses for its own XML reports. A `CommandLineArgumentProvider`\nis used to keep the tasks relocatable across different machines which is important when\nusing Gradle's Build Cache.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n.Groovy DSL\n----\ndependencies {\n    testRuntimeOnly(\"org.junit.platform:junit-platform-reporting:{version}\")\n}\ntasks.withType(Test).configureEach {\n    def outputDir = reports.junitXml.outputLocation\n    jvmArgumentProviders << ({\n        [\n            \"-Djunit.platform.reporting.open.xml.enabled=true\",\n            \"-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}\"\n        ]\n    } as CommandLineArgumentProvider)\n}\n----\n\n[source,kotlin,indent=0]\n[subs=attributes+]\n.Kotlin DSL\n----\ndependencies {\n    testRuntimeOnly(\"org.junit.platform:junit-platform-reporting:{version}\")\n}\ntasks.withType<Test>().configureEach {\n    val outputDir = reports.junitXml.outputLocation\n    jvmArgumentProviders += CommandLineArgumentProvider {\n\t\tlistOf(\n\t\t\t\"-Djunit.platform.reporting.open.xml.enabled=true\",\n\t\t\t\"-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}\"\n\t\t)\n\t}\n}\n----\n\n=== Maven\n\nFor Maven Surefire/Failsafe, you can enable Open Test Reporting output and configure the\nresulting XML files to be written to the same directory Surefire/Failsafe uses for its own\nXML reports as follows:\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n<project>\n\t<!-- ... -->\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.junit.platform</groupId>\n\t\t\t<artifactId>junit-platform-reporting</artifactId>\n\t\t\t<version>{version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<properties>\n\t\t\t\t\t\t<configurationParameters>\n\t\t\t\t\t\t\tjunit.platform.reporting.open.xml.enabled = true\n\t\t\t\t\t\t\tjunit.platform.reporting.output.dir = target/surefire-reports\n\t\t\t\t\t\t</configurationParameters>\n\t\t\t\t\t</properties>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n</project>\n----\n\n=== Console Launcher\n\nWhen using the xref:running-tests/console-launcher.adoc[], you can enable Open Test Reporting\noutput by setting the configuration parameters via `--config`:\n\n[source,console,subs=attributes+]\n----\n$ java -jar junit-platform-console-standalone-{version}.jar <OPTIONS> \\\n  --config=junit.platform.reporting.open.xml.enabled=true \\\n  --config=junit.platform.reporting.output.dir=reports\n----\n\nConfiguration parameters can also be set in a custom properties file supplied as a\nclasspath resource via the `--config-resource` option:\n\n[source,console,subs=attributes+]\n----\n$ java -jar junit-platform-console-standalone-{version}.jar <OPTIONS> \\\n  --config-resource=configuration.properties\n----\n\n[[legacy-xml]]\n== Legacy XML format\n\n`{LegacyXmlReportGeneratingListener}` generates a separate XML report for each root in the\n`{TestPlan}`. Note that the generated XML format is compatible with the de facto standard\nfor JUnit 4 based test reports that was made popular by the Ant build system.\n\nThe `LegacyXmlReportGeneratingListener` is used by the xref:running-tests/console-launcher.adoc[]\nas well.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/advanced-topics/junit-platform-suite-engine.adoc",
    "content": "= JUnit Platform Suite Engine\n\nThe Suite Engine supports the declarative selection and execution of tests from _any_ test\nengine on the JUnit Platform using the xref:advanced-topics/launcher-api.adoc[].\n\nimage::junit-platform-suite-engine-diagram.svg[role=text-center]\n\n[[setup]]\n== Setup\n\nIn addition to the `junit-platform-suite-api` and `junit-platform-suite-engine` artifacts,\nyou need _at least one_ other test engine and its dependencies on the classpath. See\nxref:appendix.adoc#dependency-metadata[Dependency Metadata] for details regarding group IDs, artifact IDs, and versions.\n\n[[setup-required-dependencies]]\n=== Required Dependencies\n\n* `junit-platform-suite-api` in _test_ scope: artifact containing annotations needed to\n  configure a test suite\n* `junit-platform-suite-engine` in _test runtime_ scope: implementation of the\n  `TestEngine` API for declarative test suites\n\nNOTE: Both of the required dependencies are aggregated in the `junit-platform-suite`\nartifact which can be declared in _test_ scope instead of declaring explicit dependencies\non `junit-platform-suite-api` and `junit-platform-suite-engine`.\n\n[[setup-transitive-dependencies]]\n=== Transitive Dependencies\n\n* `junit-platform-launcher` in _test_ scope\n* `junit-platform-engine` in _test_ scope\n* `junit-platform-commons` in _test_ scope\n* `opentest4j` in _test_ scope\n\n[[example]]\n== @Suite Example\n\nAnnotate a class with `@Suite` to have it marked as a test suite on the JUnit Platform. As\nseen in the following example, selector and filter annotations can be used to control the\ncontents of the suite.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/SuiteDemo.java[tags=user_guide]\n----\n\n.Additional Configuration Options\nNOTE: There are numerous configuration options for discovering and filtering tests in a\ntest suite. Please consult the Javadoc of the `{suite-api-package}` package for a full\nlist of supported annotations and further details.\n\n== @BeforeSuite and @AfterSuite\n\n`@BeforeSuite` and `@AfterSuite` annotations can be used on methods inside a\n`@Suite`-annotated class. They will be executed before and after all tests of the test\nsuite, respectively.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/BeforeAndAfterSuiteDemo.java[tags=user_guide]\n----\n\n[[duplicate-test-execution]]\n== Duplicate Test Execution\n\nDepending on the declared selectors, different suites may contain the same tests,\npotentially with different configurations. Moreover, tests in a suite are executed in\naddition to the tests executed by every other test engine, which can result in the same\ntests being executed twice.\n\nimage::junit-platform-suite-engine-duplicate-test-execution-diagram.svg[role=text-center]\n\nTo prevent duplicate execution of tests within a suite, configure your build tool to\ninclude only the `junit-platform-suite` engine, or use a custom naming pattern. For\nexample, name all suites `*Suite` and all tests `*Test`, and configure your build tool to\ninclude only the former.\n\nAlternatively, consider xref:running-tests/tags.adoc[using tags] to select specific groups of\ntests.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/advanced-topics/launcher-api.adoc",
    "content": "= JUnit Platform Launcher API\n\nOne of the prominent goals of the JUnit Platform is to make the interface between JUnit\nand its programmatic clients – build tools and IDEs – more powerful and stable. The\npurpose is to decouple the internals of discovering and executing tests from all the\nfiltering and configuration that is necessary from the outside.\n\nJUnit Platform provides a `Launcher` API that can be used to discover, filter, and execute\ntests. Moreover, third party test libraries – like Spock or Cucumber – can plug into the\nJUnit Platform's launching infrastructure by providing a custom\nxref:advanced-topics/engines.adoc[TestEngine].\n\nimage::launcher-api-diagram.svg[role=text-center]\n\nThe launcher API is in the `{junit-platform-launcher}` module.\n\nAn example consumer of the launcher API is the `{ConsoleLauncher}` in the\n`{junit-platform-console}` project.\n\n[[discovery]]\n== Discovering Tests\n\nHaving _test discovery_ as a dedicated feature of the platform frees IDEs and build tools\nfrom most of the difficulties they had to go through to identify test classes and test\nmethods in previous versions of JUnit.\n\nUsage Example:\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherForDiscoveryDemo.java[tags=imports]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherForDiscoveryDemo.java[tags=discovery]\n----\n\nYou can select classes, methods, and all classes in a package or even search for all tests\nin the class-path or module-path. Discovery takes place across all participating test\nengines.\n\nThe resulting `TestPlan` is a hierarchical (and read-only) description of all engines,\nclasses, and test methods that fit the `LauncherDiscoveryRequest`. The client can\ntraverse the tree, retrieve details about a node, and get a link to the original source\n(like class, method, or file position). Every node in the test plan has a _unique ID_\nthat can be used to invoke a particular test or group of tests.\n\nClients can register one or more `{LauncherDiscoveryListener}` implementations via the\n`{LauncherDiscoveryRequestBuilder}` to gain insight into events that occur during test\ndiscovery. By default, the builder registers an \"abort on failure\" listener that aborts\ntest discovery after the first discovery failure is encountered. The default\n`LauncherDiscoveryListener` can be changed via the\n`junit.platform.discovery.listener.default` xref:running-tests/configuration-parameters.adoc[configuration\nparameter].\n\n[[execution]]\n== Executing Tests\n\nTo execute tests, clients can use the same `LauncherDiscoveryRequest` as in the discovery\nphase or create a new request. Test progress and reporting can be achieved by registering\none or more `{TestExecutionListener}` implementations with the `Launcher` as in the\nfollowing example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherDemo.java[tags=execution]\n----\n\nThere is no return value for the `execute()` method, but you can use a\n`TestExecutionListener` to aggregate the results. For examples see the\n`{SummaryGeneratingListener}`, `{LegacyXmlReportGeneratingListener}`, and\n`{UniqueIdTrackingListener}`.\n\nNOTE: All `TestExecutionListener` methods are called sequentially. Methods for start\nevents are called in registration order while methods for finish events are called in\nreverse order.\nTest case execution won't start before all `executionStarted` calls have returned.\n\n[[engines-custom]]\n== Registering a TestEngine\n\nSee the dedicated section on xref:advanced-topics/engines.adoc#registration[TestEngine registration] for\ndetails.\n\n[[post-discovery-filters-custom]]\n== Registering a PostDiscoveryFilter\n\nIn addition to specifying post-discovery filters as part of a `{LauncherDiscoveryRequest}`\npassed to the `{Launcher}` API, `{PostDiscoveryFilter}` implementations will be discovered\nat runtime via Java's `{ServiceLoader}` mechanism and automatically applied by the\n`Launcher` in addition to those that are part of the request.\n\nFor example, an `example.CustomTagFilter` class implementing `PostDiscoveryFilter` and\ndeclared within the `/META-INF/services/org.junit.platform.launcher.PostDiscoveryFilter`\nfile is loaded and applied automatically.\n\n[[launcher-session-listeners-custom]]\n== Registering a LauncherSessionListener\n\nRegistered implementations of `{LauncherSessionListener}` are notified when a\n`{LauncherSession}` is opened (before a `{Launcher}` first discovers and executes tests)\nand closed (when no more tests will be discovered or executed). They can be registered\nprogrammatically via the `{LauncherConfig}` that is passed to the `{LauncherFactory}`, or\nthey can be discovered at runtime via Java's `{ServiceLoader}` mechanism and automatically\nregistered with `LauncherSession` (unless automatic registration is disabled.)\n\n[[launcher-session-listeners-tool-support]]\n=== Tool Support\n\nThe following build tools and IDEs are known to provide full support for `LauncherSession`:\n\n* Gradle 4.6 and later\n* Maven Surefire/Failsafe 3.0.0-M6 and later\n* IntelliJ IDEA 2017.3 and later\n\nOther tools might also work but have not been tested explicitly.\n\n[[launcher-session-listeners-tool-example-usage]]\n=== Example Usage\n\nA `LauncherSessionListener` is well suited for implementing once-per-JVM setup/teardown\nbehavior since it's called before the first and after the last test in a launcher session,\nrespectively. The scope of a launcher session depends on the used IDE or build tool but\nusually corresponds to the lifecycle of the test JVM. A custom listener that starts an\nHTTP server before executing the first test and stops it after the last test has been\nexecuted, could look like this:\n\n[source,java]\n.src/test/java/example/session/GlobalSetupTeardownListener.java\n----\npackage example.session;\n\ninclude::example$java/example/session/GlobalSetupTeardownListener.java[tags=user_guide]\n----\n<1> Get the store from the launcher session\n<2> Lazily create the HTTP server and put it into the store\n<3> Start the HTTP server\n\nIt uses a wrapper class to ensure the server is stopped when the launcher session is\nclosed:\n\n[source,java]\n.src/test/java/example/session/CloseableHttpServer.java\n----\npackage example.session;\n\ninclude::example$java/example/session/CloseableHttpServer.java[tags=user_guide]\n----\n<1> The `close()` method is called when the launcher session is closed\n<2> Stop the HTTP server\n\nThis sample uses the HTTP server implementation from the jdk.httpserver module that comes\nwith the JDK but would work similarly with any other server or resource. In order for the\nlistener to be picked up by JUnit Platform, you need to register it as a service by adding\na resource file with the following name and contents to your test runtime classpath (e.g.\nby adding the file to `src/test/resources`):\n\n[source]\n.src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener\n----\ninclude::example$resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener[]\n----\n\nYou can now use the resource from your test:\n\n[source,java]\n.src/test/java/example/session/HttpTests.java\n----\npackage example.session;\n\ninclude::example$java/example/session/HttpTests.java[tags=user_guide]\n----\n<1> Retrieve the HTTP server instance from the store\n<2> Get the host string directly from the injected HTTP server instance\n<3> Get the port number directly from the injected HTTP server instance\n<4> Send a request to the server\n<5> Check the status code of the response\n\n[[launcher-interceptors-custom]]\n== Registering a LauncherInterceptor\n\nIn order to intercept the creation of instances of `{Launcher}` and\n`{LauncherSessionListener}` and calls to the `discover` and `execute` methods of the\nformer, clients can register custom implementations of `{LauncherInterceptor}` via Java's\n`{ServiceLoader}` mechanism by setting the\n`junit.platform.launcher.interceptors.enabled` xref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`.\n\n[NOTE]\n====\nSince interceptors are registered before the test run starts, the\n`junit.platform.launcher.interceptors.enabled` _configuration parameter_ can only be\nsupplied as a JVM system property or via the JUnit Platform configuration file (see\nxref:running-tests/configuration-parameters.adoc[] for details). This _configuration parameter_ cannot be\nsupplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`.\n====\n\n\nA typical use case is to create a custom interceptor to replace the `ClassLoader` used by\nthe JUnit Platform to load test classes and engine implementations.\n\n[source,java]\n----\ninclude::example$java/example/CustomLauncherInterceptor.java[tags=user_guide]\n----\n\n[[launcher-discovery-listeners-custom]]\n== Registering a LauncherDiscoveryListener\n\nIn addition to specifying discovery listeners as part of a `{LauncherDiscoveryRequest}` or\nregistering them programmatically via the `{Launcher}` API, custom\n`LauncherDiscoveryListener` implementations can be discovered at runtime via Java's\n`{ServiceLoader}` mechanism and automatically registered with the `Launcher` created via\nthe `{LauncherFactory}`.\n\nFor example, an `example.CustomLauncherDiscoveryListener` class implementing\n`LauncherDiscoveryListener` and declared within the\n`/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener` file is loaded\nand registered automatically.\n\n[[listeners-custom]]\n== Registering a TestExecutionListener\n\nIn addition to the public `{Launcher}` API method for registering test execution listeners\nprogrammatically, custom `{TestExecutionListener}` implementations will be discovered at\nruntime via Java's `{ServiceLoader}` mechanism and automatically registered with the\n`Launcher` created via the `{LauncherFactory}`.\n\nFor example, an `example.CustomTestExecutionListener` class implementing\n`TestExecutionListener` and declared within the\n`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file is loaded and\nregistered automatically.\n\n[[listeners-config]]\n== Configuring a TestExecutionListener\n\nWhen a `{TestExecutionListener}` is registered programmatically via the `{Launcher}` API,\nthe listener may provide programmatic ways for it to be configured -- for example, via its\nconstructor, setter methods, etc. However, when a `TestExecutionListener` is registered\nautomatically via Java's `ServiceLoader` mechanism (see\n<<listeners-custom>>), there is no way for the user to directly configure the\nlistener. In such cases, the author of a `TestExecutionListener` may choose to make the\nlistener configurable via xref:running-tests/configuration-parameters.adoc[configuration parameters]. The\nlistener can then access the configuration parameters via the `TestPlan` supplied to the\n`testPlanExecutionStarted(TestPlan)` and `testPlanExecutionFinished(TestPlan)` callback\nmethods. See the `{UniqueIdTrackingListener}` for an example.\n\n[[listeners-custom-deactivation]]\n== Deactivating a TestExecutionListener\n\nSometimes it can be useful to run a test suite _without_ certain execution listeners being\nactive. For example, you might have custom a `{TestExecutionListener}` that sends the test\nresults to an external system for reporting purposes, and while debugging you might not\nwant these _debug_ results to be reported. To do this, provide a pattern for the\n`junit.platform.execution.listeners.deactivate` _configuration parameter_ to specify which\nexecution listeners should be deactivated (i.e. not registered) for the current test run.\n\n[NOTE]\n====\nOnly listeners registered via the `{ServiceLoader}` mechanism within the\n`/META-INF/services/org.junit.platform.launcher.TestExecutionListener` file can be\ndeactivated. In other words, any `TestExecutionListener` registered explicitly via the\n`{LauncherDiscoveryRequest}` cannot be deactivated via the\n`junit.platform.execution.listeners.deactivate` _configuration parameter_.\n\nIn addition, since execution listeners are registered before the test run starts, the\n`junit.platform.execution.listeners.deactivate` _configuration parameter_ can only be\nsupplied as a JVM system property or via the JUnit Platform configuration file (see\nxref:running-tests/configuration-parameters.adoc[] for details). This _configuration parameter_ cannot be\nsupplied in the `LauncherDiscoveryRequest` that is passed to the `{Launcher}`.\n====\n\n[[listeners-custom-deactivation-pattern]]\n=== Pattern Matching Syntax\n\nRefer to xref:running-tests/configuration-parameters.adoc#pattern[Pattern Matching Syntax] for details.\n\n[[launcher-config]]\n== Configuring the Launcher\n\nIf you require fine-grained control over automatic detection and registration of test\nengines and listeners, you may create an instance of `{LauncherConfig}` and supply that to\nthe `{LauncherFactory}`. Typically, an instance of `LauncherConfig` is created via the\nbuilt-in fluent _builder_ API, as demonstrated in the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherDemo.java[tags=launcherConfig]\n----\n\n[[dry-run-mode]]\n== Dry-Run Mode\n\nWhen running tests via the `{Launcher}` API, you can enable _dry-run mode_ by setting the\n`junit.platform.execution.dryRun.enabled` xref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`. In this mode, the `{Launcher}` will not actually\nexecute any tests but will notify registered `{TestExecutionListener}` instances as if all\ntests had been skipped and their containers had been successful. This can be useful to\ntest changes in the configuration of a build or to verify a listener is called as expected\nwithout having to wait for all tests to be executed.\n\n[[managing-state-across-test-engines]]\n== Managing State Across Test Engines\n\nWhen running tests on the JUnit Platform, multiple test engines may need to access shared\nresources. Rather than initializing these resources multiple times, JUnit Platform\nprovides mechanisms to share state across test engines efficiently. Test engines can use\nthe Platform's `{NamespacedHierarchicalStore}` API to lazily initialize and share\nresources, ensuring they are created only once regardless of execution order. Any resource\nthat is put into the store and implements `AutoCloseable` will be closed automatically when\nthe execution is finished.\n\nTIP: The Jupiter engine allows read and write access to such resources via its\n`{ExtensionContext_Store}` API.\n\nThe following example demonstrates two custom test engines sharing a `ServerSocket`\nresource. `FirstCustomEngine` attempts to retrieve an existing `ServerSocket` from the\nglobal store or creates a new one if it doesn't exist:\n\n[source,java]\n----\ninclude::example$java/example/FirstCustomEngine.java[tags=user_guide]\n----\n\n`SecondCustomEngine` follows the same pattern, ensuring that regardless whether it runs\nbefore or after `FirstCustomEngine`, it will use the same socket instance:\n\n[source,java]\n----\ninclude::example$java/example/SecondCustomEngine.java[tags=user_guide]\n----\n\nTIP: In this case, the `ServerSocket` can be stored directly in the global store while\nensuring since it gets closed because it implements `AutoCloseable`. If you need to use a\ntype that does not do so, you can wrap it in a custom class that implements\n`AutoCloseable` and delegates to the original type. This is important to ensure that the\nresource is closed properly when the test run is finished.\n\nFor illustration, the following test verifies that both engines are sharing the same\n`ServerSocket` instance and that it's closed after `Launcher.execute()` returns:\n\n[source,java,indent=0]\n----\ninclude::example$java/example/sharedresources/SharedResourceDemo.java[tags=user_guide]\n----\n\nBy using the Platform's `{NamespacedHierarchicalStore}` API with shared namespaces in this\nway, test engines can coordinate resource creation and sharing without direct dependencies\nbetween them.\n\nAlternatively, it's possible to inject resources into test engines by\n<<launcher-session-listeners-custom, registering a `LauncherSessionListener`>>.\n\n[[launcher-cancellation]]\n== Cancelling a Running Test Execution\n\nThe launcher API provides the ability to cancel a running test execution mid-flight while\nallowing engines to clean up resources. To request an execution to be cancelled, you need\nto call `cancel()` on the `{CancellationToken}` that is passed to `Launcher.execute` as\npart of the `{LauncherExecutionRequest}`.\n\nFor example, implementing a listener that cancels test execution after the first test\nfailed can be achieved as follows.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherDemo.java[tags=cancellation-direct]\n----\n<1> Create a `{CancellationToken}`\n<2> Implement a `{TestExecutionListener}` that calls `cancel()` when a test fails\n<3> Register the cancellation token\n<4> Register the listener\n<5> Pass the `{LauncherExecutionRequest}` to `Launcher.execute`\n\n[NOTE]\n.Test Engine Support for Cancellation\n====\nCancelling tests relies on xref:advanced-topics/engines.adoc[] checking and responding to the\n`{CancellationToken}` appropriately (see\nxref:advanced-topics/engines.adoc#requirements-cancellation[Test Engine Requirements] for details). The\n`Launcher` will also check the token and cancel test execution when multiple test engines\nare present at runtime.\n\nAt the time of writing, the following test engines support cancellation:\n\n* `{junit-jupiter-engine}`\n* `{junit-vintage-engine}`\n* `{junit-platform-suite-engine}`\n* https://github.com/junit-team/testng-engine[`testng-engine`] (version 1.1.0 and later)\n* Any `{TestEngine}` extending `{HierarchicalTestEngine}` such as Spock and Cucumber\n====\n\n[[launcher-cancellation-execution-request-from-discovery-request]]\n=== Building a LauncherExecutionRequest from a LauncherDiscoveryRequest\n\nRather than building a `{LauncherExecutionRequest}` by starting with a\n`{LauncherDiscoveryRequestBuilder}` and calling `forExecution()`, you can create a\n`{LauncherExecutionRequestBuilder}` from a previously built `{LauncherDiscoveryRequest}`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherDemo.java[tags=cancellation-discovery-request]\n----\n<1> Build a `{LauncherDiscoveryRequest}`\n<2> Create a `{LauncherExecutionRequestBuilder}` with it\n<3> Register the cancellation token\n<4> Register the listener\n<5> Pass the `{LauncherExecutionRequest}` to `Launcher.execute`\n\n[[launcher-cancellation-execution-request-from-test-plan]]\n=== Building a LauncherExecutionRequest from a TestPlan\n\nAlternatively, a `{LauncherExecutionRequestBuilder}` can be created from a previously\ndiscovered `{TestPlan}`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/UsingTheLauncherDemo.java[tags=cancellation-test-plan]\n----\n<1> Build a `{LauncherDiscoveryRequest}`\n<2> Discover a `{TestPlan}` via `Launcher.discover`\n<3> Create a `{LauncherExecutionRequestBuilder}` with it\n<4> Register the cancellation token\n<5> Register the listener\n<6> Pass the `{LauncherExecutionRequest}` to `Launcher.execute`\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/advanced-topics/testkit.adoc",
    "content": "= JUnit Platform Test Kit\n\nThe `junit-platform-testkit` artifact provides support for executing a test plan on the\nJUnit Platform and then verifying the expected results. This support is currently limited\nto the execution of a single `TestEngine` (see <<engine>>).\n\n[[engine]]\n== Engine Test Kit\n\nThe `{testkit-engine-package}` package provides support for discovering and executing a\n`{TestPlan}` for a given `{TestEngine}` running on the JUnit Platform and then accessing\nthe results via convenient result objects. For execution, a fluent API may be used to\nverify the expected execution events were received. The key entry point into this API is\nthe `{EngineTestKit}` which provides static factory methods named `engine()`,\n`discover()`, and `execute()`. It is recommended that you select one of the `engine()`\nvariants to benefit from the fluent API for building a `LauncherDiscoveryRequest`.\n\nNOTE: If you prefer to use the `LauncherDiscoveryRequestBuilder` from the `Launcher` API\nto build your `LauncherDiscoveryRequest`, you must use one of the `discover()` or\n`execute()` variants in `EngineTestKit`.\n\nThe following test class written using JUnit Jupiter will be used in subsequent examples.\n\n[[engine-ExampleTestCase]]\n[source,java,indent=0]\n----\ninclude::example$java/example/ExampleTestCase.java[tags=user_guide]\n----\n\nFor the sake of brevity, the following sections demonstrate how to test JUnit's own\n`JupiterTestEngine` whose unique engine ID is `\"junit-jupiter\"`. If you want to test your\nown `TestEngine` implementation, you need to use its unique engine ID. Alternatively, you\nmay test your own `TestEngine` by supplying an instance of it to the\n`EngineTestKit.engine(TestEngine)` static factory method.\n\n[[engine-discovery]]\n== Verifying Test Discovery\n\nThe following test demonstrates how to verify that a `TestPlan` was discovered as expected\nby the JUnit Jupiter `TestEngine`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/testkit/EngineTestKitDiscoveryDemo.java[tags=user_guide]\n----\n<1> Select the JUnit Jupiter `TestEngine`.\n<2> Select the <<engine-ExampleTestCase, `ExampleTestCase`>> test class.\n<3> Discover the `TestPlan`.\n<4> Assert engine root descriptor has expected display name.\n<5> Assert no discovery issues were encountered.\n\n[[engine-statistics]]\n== Asserting Execution Statistics\n\nOne of the most common features of the Test Kit is the ability to assert statistics\nagainst events fired during the execution of a `TestPlan`. The following tests demonstrate\nhow to assert statistics for _containers_ and _tests_ in the JUnit Jupiter `TestEngine`.\nFor details on what statistics are available, consult the Javadoc for `{EventStatistics}`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/testkit/EngineTestKitStatisticsDemo.java[tags=user_guide]\n----\n<1> Select the JUnit Jupiter `TestEngine`.\n<2> Select the <<engine-ExampleTestCase, `ExampleTestCase`>> test class.\n<3> Execute the `TestPlan`.\n<4> Filter by _container_ events.\n<5> Assert statistics for _container_ events.\n<6> Filter by _test_ events.\n<7> Assert statistics for _test_ events.\n\nNOTE: In the `verifyJupiterContainerStats()` test method, the counts for the `started` and\n`succeeded` statistics are `2` since the `JupiterTestEngine` and the\n<<engine-ExampleTestCase, `ExampleTestCase`>> class are both considered containers.\n\n[[engine-events]]\n== Asserting Events\n\nIf you find that <<engine-statistics, asserting statistics>> alone is insufficient\nfor verifying the expected behavior of test execution, you can work directly with the\nrecorded `{Event}` elements and perform assertions against them.\n\nFor example, if you want to verify the reason that the `skippedTest()` method in\n<<engine-ExampleTestCase, `ExampleTestCase`>> was skipped, you can do that as\nfollows.\n\n[TIP]\n====\nThe `assertThatEvents()` method in the following example is a shortcut for\n`org.assertj.core.api.Assertions.assertThat(events.list())` from the {AssertJ} assertion\nlibrary.\n\nFor details on what _conditions_ are available for use with AssertJ assertions against\nevents, consult the Javadoc for `{EventConditions}`.\n====\n\n[source,java,indent=0]\n----\ninclude::example$java/example/testkit/EngineTestKitSkippedMethodDemo.java[tags=user_guide]\n----\n<1> Select the JUnit Jupiter `TestEngine`.\n<2> Select the `skippedTest()` method in the <<engine-ExampleTestCase, `ExampleTestCase`>> test class.\n<3> Execute the `TestPlan`.\n<4> Filter by _test_ events.\n<5> Save the _test_ `Events` to a local variable.\n<6> Optionally assert the expected statistics.\n<7> Assert that the recorded _test_ events contain exactly one skipped test named\n    `skippedTest` with `\"for demonstration purposes\"` as the _reason_.\n\nIf you want to verify the type of exception thrown from the `failingTest()` method in\n<<engine-ExampleTestCase, `ExampleTestCase`>>, you can do that as follows.\n\n[TIP]\n====\nFor details on what _conditions_ are available for use with AssertJ assertions against\nevents and execution results, consult the Javadoc for `{EventConditions}` and\n`{TestExecutionResultConditions}`, respectively.\n====\n\n[source,java,indent=0]\n----\ninclude::example$java/example/testkit/EngineTestKitFailedMethodDemo.java[tags=user_guide]\n----\n<1> Select the JUnit Jupiter `TestEngine`.\n<2> Select the <<engine-ExampleTestCase, `ExampleTestCase`>> test class.\n<3> Execute the `TestPlan`.\n<4> Filter by _test_ events.\n<5> Assert that the recorded _test_ events contain exactly one failing test named\n    `failingTest` with an exception of type `ArithmeticException` and an error message\n    that ends with `\"/ by zero\"`.\n\nAlthough typically unnecessary, there are times when you need to verify **all** of the\nevents fired during the execution of a `TestPlan`. The following test demonstrates how to\nachieve this via the `assertEventsMatchExactly()` method in the `EngineTestKit` API.\n\n[TIP]\n====\nSince `assertEventsMatchExactly()` matches conditions exactly in the order in which the\nevents were fired, <<engine-ExampleTestCase, `ExampleTestCase`>> has been\nannotated with `@TestMethodOrder(OrderAnnotation.class)` and each test method has been\nannotated with `@Order(...)`. This allows us to enforce the order in which the test\nmethods are executed, which in turn allows our `verifyAllJupiterEvents()` test to be\nreliable.\n====\n\nIf you want to do a _partial_ match _with_ or _without_ ordering requirements, you can use\nthe methods `assertEventsMatchLooselyInOrder()` and `assertEventsMatchLoosely()`,\nrespectively.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/testkit/EngineTestKitAllEventsDemo.java[tags=user_guide]\n----\n<1> Select the JUnit Jupiter `TestEngine`.\n<2> Select the <<engine-ExampleTestCase, `ExampleTestCase`>> test class.\n<3> Execute the `TestPlan`.\n<4> Filter by _all_ events.\n<5> Print all events to the supplied `writer` for debugging purposes. Debug information\n    can also be written to an `OutputStream` such as `System.out` or `System.err`.\n<6> Assert _all_ events in exactly the order in which they were fired by the test engine.\n\nThe `debug()` invocation from the preceding example results in output similar to the\nfollowing.\n\n[source,options=\"nowrap\"]\n----\nAll Events:\n\tEvent [type = STARTED, testDescriptor = JupiterEngineDescriptor: [engine:junit-jupiter], timestamp = 2018-12-14T12:45:14.082280Z, payload = null]\n\tEvent [type = STARTED, testDescriptor = ClassTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase], timestamp = 2018-12-14T12:45:14.089339Z, payload = null]\n\tEvent [type = SKIPPED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:skippedTest()], timestamp = 2018-12-14T12:45:14.094314Z, payload = 'for demonstration purposes']\n\tEvent [type = STARTED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:succeedingTest()], timestamp = 2018-12-14T12:45:14.095182Z, payload = null]\n\tEvent [type = FINISHED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:succeedingTest()], timestamp = 2018-12-14T12:45:14.104922Z, payload = TestExecutionResult [status = SUCCESSFUL, throwable = null]]\n\tEvent [type = STARTED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:abortedTest()], timestamp = 2018-12-14T12:45:14.106121Z, payload = null]\n\tEvent [type = FINISHED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:abortedTest()], timestamp = 2018-12-14T12:45:14.109956Z, payload = TestExecutionResult [status = ABORTED, throwable = org.opentest4j.TestAbortedException: Assumption failed: abc does not contain Z]]\n\tEvent [type = STARTED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:failingTest()], timestamp = 2018-12-14T12:45:14.110680Z, payload = null]\n\tEvent [type = FINISHED, testDescriptor = TestMethodTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase]/[method:failingTest()], timestamp = 2018-12-14T12:45:14.111217Z, payload = TestExecutionResult [status = FAILED, throwable = java.lang.ArithmeticException: / by zero]]\n\tEvent [type = FINISHED, testDescriptor = ClassTestDescriptor: [engine:junit-jupiter]/[class:example.ExampleTestCase], timestamp = 2018-12-14T12:45:14.113731Z, payload = TestExecutionResult [status = SUCCESSFUL, throwable = null]]\n\tEvent [type = FINISHED, testDescriptor = JupiterEngineDescriptor: [engine:junit-jupiter], timestamp = 2018-12-14T12:45:14.113806Z, payload = TestExecutionResult [status = SUCCESSFUL, throwable = null]]\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/api-evolution.adoc",
    "content": "= API Evolution\n:page-toclevels: 1\n\nOne of the major goals of the JUnit Platform architecture is to improve maintainers'\ncapabilities to evolve JUnit despite its being used in many projects. With JUnit 4 a lot\nof stuff that was originally added as an internal construct only got used by external\nextension writers and tool builders. That made changing JUnit 4 especially difficult and\nsometimes impossible.\n\nThat's why JUnit now uses a defined lifecycle for all publicly available interfaces,\nclasses, and methods.\n\n[[version-and-status]]\n== API Version and Status\n\nEvery published artifact has a version number `<major>.<minor>.<patch>`, and all publicly\navailable interfaces, classes, and methods are annotated with {API} from the\n{API_Guardian} project. The annotation's `status` attribute can be assigned one of the\nfollowing values.\n\n[cols=\"20,80\"]\n|===\n| Status           | Description\n\n| `INTERNAL`       | Must not be used by any code other than JUnit itself. Might be removed without prior notice.\n| `DEPRECATED`     | Should no longer be used; might disappear in the next minor release.\n| `EXPERIMENTAL`   | Intended for new, experimental features where we are looking for feedback. +\n                     Use this element with caution; it might be promoted to `MAINTAINED` or\n                     `STABLE` in the future, but might also be removed without prior notice, even in a patch.\n| `MAINTAINED`     | Intended for features that will not be changed in a backwards-\n                     incompatible way for *at least* the next minor release of the current\n                     major version. If scheduled for removal, it will be demoted to `DEPRECATED` first.\n| `STABLE`         | Intended for features that will not be changed in a backwards-\n                     incompatible way in the current major version (`6.*`).\n|===\n\nIf the `@API` annotation is present on a type, it is considered to be applicable for all\npublic members of that type as well. A member is allowed to declare a different `status`\nvalue of lower stability.\n\n[[experimental-apis]]\n== Experimental APIs\n\nThe following tables list which APIs are currently designated as _experimental_ via\n`@API(status = EXPERIMENTAL)`. Caution should be taken when relying on such APIs.\n\ninclude::partial$experimental-apis-table.adoc[]\n\n[[deprecated-apis]]\n== Deprecated APIs\n\nThe following tables list which APIs are currently designated as _deprecated_ via\n`@API(status = DEPRECATED)`. You should avoid using deprecated APIs whenever possible,\nsince such APIs will likely be removed in an upcoming release.\n\ninclude::partial$deprecated-apis-table.adoc[]\n\n[[tooling]]\n== @API Tooling Support\n\nThe {API_Guardian} project plans to provide tooling support for publishers and consumers\nof APIs annotated with {API}. For example, the tooling support will likely provide a\nmeans to check if JUnit APIs are being used in accordance with `@API` annotation\ndeclarations.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/appendix.adoc",
    "content": "= Appendix\n\n[[reproducible-builds]]\n== Reproducible Builds\n\nJUnit aims for its non-javadoc JARs to be https://reproducible-builds.org/[reproducible].\n\nUnder identical build conditions, such as Java version, repeated builds should provide the\nsame output byte-for-byte.\n\nThis means that anyone can reproduce the build conditions of the artifacts on Maven\nCentral/Sonatype and produce the same output artifact locally, confirming that the\nartifacts in the repositories were actually generated from this source code.\n\n[[dependency-metadata]]\n== Dependency Metadata\n\nArtifacts for final releases and milestones are deployed to {Maven_Central}. Consult\nhttps://central.sonatype.org/consume/[Sonatype's documentation] for how to consume those\nartifacts with a build tool of your choice.\n\nSnapshot artifacts are deployed to Sonatype's {snapshot-repo}[snapshots repository]\nunder {snapshot-repo}/org/junit/[/org/junit]. Please refer to\nhttps://central.sonatype.org/publish/publish-portal-snapshots/#consuming-snapshot-releases-for-your-project[Sonatype's documentation]\nfor instructions on how to consume them with a build tool of your choice.\n\nThe sections below list all artifacts with their versions for the three groups:\n<<dependency-metadata-junit-platform, Platform>>,\n<<dependency-metadata-junit-jupiter, Jupiter>>, and\n<<dependency-metadata-junit-vintage, Vintage>>.\nThe <<dependency-metadata-junit-bom, Bill of Materials (BOM)>> contains a list of all\nof the above artifacts and their versions.\n\n[TIP]\n.Aligning dependency versions\n====\nTo ensure that all JUnit artifacts are compatible with each other, their versions should\nbe aligned.\nIf you rely on xref:running-tests/build-support.adoc#spring-boot[Spring Boot] for dependency management,\nplease see the corresponding section.\nOtherwise, instead of managing individual versions of the JUnit artifacts, it is\nrecommended to apply the <<dependency-metadata-junit-bom, BOM>> to your project.\nPlease refer to the corresponding sections for xref:running-tests/build-support.adoc#maven-bom[Maven] or\nxref:running-tests/build-support.adoc#gradle-bom[Gradle].\n====\n\n[[dependency-metadata-junit-platform]]\n=== JUnit Platform\n\n* *Group ID*: `org.junit.platform`\n* *Version*: `{version}`\n* *Artifact IDs*:\n  `junit-platform-commons`::\n    Common APIs and support utilities for the JUnit Platform. Any API annotated with\n    `@API(status = INTERNAL)` is intended solely for usage within the JUnit framework\n    itself. _Any usage of internal APIs by external parties is not supported!_\n  `junit-platform-console`::\n    Support for discovering and executing tests on the JUnit Platform from the console.\n    See xref:running-tests/console-launcher.adoc[] for details.\n  `junit-platform-console-standalone`::\n    An executable _Fat JAR_ that contains all dependencies is provided in Maven Central under the\n    https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone]\n    directory. See xref:running-tests/console-launcher.adoc[] for details.\n  `junit-platform-engine`::\n    Public API for test engines. See xref:advanced-topics/launcher-api.adoc#engines-custom[Registering a TestEngine] for details.\n  `junit-platform-launcher`::\n    Public API for configuring and launching test plans -- typically used by IDEs and\n    build tools. See xref:advanced-topics/launcher-api.adoc[] for details.\n  `junit-platform-reporting`::\n    `TestExecutionListener` implementations that generate test reports -- typically used\n    by IDEs and build tools. See xref:advanced-topics/junit-platform-reporting.adoc[] for details.\n  `junit-platform-suite`::\n    JUnit Platform Suite artifact that transitively pulls in dependencies on\n    `junit-platform-suite-api` and `junit-platform-suite-engine` for simplified dependency\n\tmanagement in build tools such as Gradle and Maven.\n  `junit-platform-suite-api`::\n    Annotations for configuring test suites on the JUnit Platform. Supported by the\n    xref:advanced-topics/junit-platform-suite-engine.adoc[JUnit Platform Suite Engine].\n  `junit-platform-suite-engine`::\n    Engine that executes test suites on the JUnit Platform; only required at runtime. See\n    xref:advanced-topics/junit-platform-suite-engine.adoc[JUnit Platform Suite Engine] for details.\n  `junit-platform-testkit`::\n     Provides support for executing a test plan for a given `TestEngine` and then\n     accessing the results via a fluent API to verify the expected results.\n\n[[dependency-metadata-junit-jupiter]]\n=== JUnit Jupiter\n\n* *Group ID*: `org.junit.jupiter`\n* *Version*: `{version}`\n* *Artifact IDs*:\n  `junit-jupiter`::\n    JUnit Jupiter aggregator artifact that transitively pulls in dependencies on\n    `junit-jupiter-api`, `junit-jupiter-params`, and `junit-jupiter-engine` for\n    simplified dependency management in build tools such as Gradle and Maven.\n  `junit-jupiter-api`::\n    JUnit Jupiter API for xref:writing-tests/intro.adoc[writing tests] and xref:extensions/overview.adoc[extensions].\n  `junit-jupiter-engine`::\n    JUnit Jupiter test engine implementation; only required at runtime.\n  `junit-jupiter-params`::\n    Support for xref:writing-tests/parameterized-classes-and-tests.adoc[] in JUnit Jupiter.\n  `junit-jupiter-migrationsupport`::\n    _Deprecated_ support for migrating from JUnit 4 to JUnit Jupiter; only required for\n    support for JUnit 4's `@Ignore` annotation and for running selected JUnit 4 rules.\n\n[[dependency-metadata-junit-vintage]]\n=== JUnit Vintage\n\n* *Group ID*: `org.junit.vintage`\n* *Version*: `{version}`\n* *Artifact ID*:\n  `junit-vintage-engine`::\n    JUnit Vintage test engine implementation that allows one to run _vintage_ JUnit tests\n    on the JUnit Platform. _Vintage_ tests include those written using JUnit 3 or JUnit 4\n    APIs or tests written using testing frameworks built on those APIs.\n\n[[dependency-metadata-junit-bom]]\n=== Bill of Materials (BOM)\n\nThe _Bill of Materials_ POM provided under the following Maven coordinates can be used to\nease dependency management when referencing multiple of the above artifacts using\nxref:running-tests/build-support.adoc#maven-bom[Maven] or\nxref:running-tests/build-support.adoc#gradle-bom[Gradle].\n\n* *Group ID*: `org.junit`\n* *Artifact ID*: `junit-bom`\n* *Version*: `{version}`\n\n[[dependency-metadata-dependencies]]\n=== Dependencies\n\nMost of the above artifacts have a dependency in their published Maven POMs on the\nfollowing _@API Guardian_ JAR.\n\n* *Group ID*: `org.apiguardian`\n* *Artifact ID*: `apiguardian-api`\n* *Version*: `{apiguardian-version}`\n\nIn addition, most of the above artifacts have a direct or transitive dependency on the\nfollowing _OpenTest4J_ JAR.\n\n* *Group ID*: `org.opentest4j`\n* *Artifact ID*: `opentest4j`\n* *Version*: `{ota4j-version}`\n\n[[dependency-diagram]]\n== Dependency Diagram\n\nimage::component-diagram.svg[]\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/conditional-test-execution.adoc",
    "content": "= Conditional Test Execution\n\n`{ExecutionCondition}` defines the `Extension` API for programmatic, _conditional test\nexecution_.\n\nAn `ExecutionCondition` is _evaluated_ for each container (e.g., a test class) to\ndetermine if all the tests it contains should be executed based on the supplied\n`ExtensionContext`. Similarly, an `ExecutionCondition` is _evaluated_ for each test to\ndetermine if a given test method should be executed based on the supplied\n`ExtensionContext`.\n\nWhen multiple `ExecutionCondition` extensions are registered, a container or test is\ndisabled as soon as one of the conditions returns _disabled_. Thus, there is no guarantee\nthat a condition is evaluated because another extension might have already caused a\ncontainer or test to be disabled. In other words, the evaluation works like the\nshort-circuiting boolean OR operator.\n\nSee the source code of `{DisabledCondition}` and `{Disabled}` for concrete examples.\n\n[[deactivation]]\n== Deactivating Conditions\n\nSometimes it can be useful to run a test suite _without_ certain conditions being active.\nFor example, you may wish to run tests even if they are annotated with `@Disabled` in\norder to see if they are still _broken_. To do this, provide a pattern for the\n`junit.jupiter.conditions.deactivate` _configuration parameter_ to specify which\nconditions should be deactivated (i.e., not evaluated) for the current test run. The\npattern can be supplied as a JVM system property, as a _configuration parameter_ in the\n`LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform\nconfiguration file (see xref:running-tests/configuration-parameters.adoc[] for details).\n\nFor example, to deactivate JUnit's `@Disabled` condition, you can start your JVM with the\nfollowing system property.\n\n`-Djunit.jupiter.conditions.deactivate=org.junit.*DisabledCondition`\n\n[[deactivation-patterns]]\n=== Pattern Matching Syntax\n\nRefer to xref:running-tests/configuration-parameters.adoc#pattern[Pattern Matching Syntax] for details.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/exception-handling.adoc",
    "content": "= Exception Handling\n\nExceptions thrown during the test execution may be intercepted and handled accordingly\nbefore propagating further, so that certain actions like error logging or resource releasing\nmay be defined in specialized `Extensions`. JUnit Jupiter offers API for `Extensions` that\nwish to handle exceptions thrown during `@Test` methods via `{TestExecutionExceptionHandler}`\nand for those thrown during one of test lifecycle methods (`@BeforeAll`, `@BeforeEach`,\n`@AfterEach` and `@AfterAll`) via `{LifecycleMethodExecutionExceptionHandler}`.\n\nThe following example shows an extension which will swallow all instances of `IOException`\nbut rethrow any other type of exception.\n\n[source,java,indent=0]\n.An exception handling extension that filters IOExceptions in test execution\n----\ninclude::example$java/example/exception/IgnoreIOExceptionExtension.java[tags=user_guide]\n----\n\nAnother example shows how to record the state of an application under test exactly at\nthe point of unexpected exception being thrown during setup and cleanup. Note that unlike\nrelying on lifecycle callbacks, which may or may not be executed depending on the test\nstatus, this solution guarantees execution immediately after failing `@BeforeAll`,\n`@BeforeEach`, `@AfterEach` or `@AfterAll`.\n\n[source,java,indent=0]\n.An exception handling extension that records application state on error\n----\ninclude::example$java/example/exception/RecordStateOnErrorExtension.java[tags=user_guide]\n----\n\nMultiple execution exception handlers may be invoked for the same lifecycle method in\norder of declaration. If one of the handlers swallows the handled exception, subsequent\nones will not be executed, and no failure will be propagated to JUnit engine, as if the\nexception was never thrown. Handlers may also choose to rethrow the exception or throw\na different one, potentially wrapping the original.\n\nExtensions implementing `{LifecycleMethodExecutionExceptionHandler}` that wish to handle\nexceptions thrown during `@BeforeAll` or `@AfterAll` need to be registered on a class level,\nwhile handlers for `BeforeEach` and `AfterEach` may be also registered for individual\ntest methods.\n\n[source,java,indent=0]\n.Registering multiple exception handling extensions\n----\ninclude::example$java/example/exception/MultipleHandlersTestCase.java[tags=user_guide]\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/intercepting-invocations.adoc",
    "content": "= Intercepting Invocations\n\n`{InvocationInterceptor}` defines the API for `Extensions` that wish to intercept calls to\ntest code.\n\nThe following example shows an extension that executes all test methods in Swing's Event\nDispatch Thread.\n\n[source,java,indent=0]\n.An extension that executes tests in a user-defined thread\n----\ninclude::example$java/example/interceptor/SwingEdtInterceptor.java[tags=user_guide]\n----\n\n[NOTE]\n.Accessing the test-scoped `ExtensionContext`\n====\nYou may override the `getTestInstantiationExtensionContextScope(...)` method to return\n`TEST_METHOD` to make test-specific data available to your extension implementation of\n`interceptTestClassConstructor` or if you want to xref:extensions/keeping-state-in-extensions.adoc[keep state]\non the test method level.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/keeping-state-in-extensions.adoc",
    "content": "= Keeping State in Extensions\n\nUsually, an extension is instantiated only once. So the question becomes relevant: How do\nyou keep the state from one invocation of an extension to the next? The\n`{ExtensionContext}` API provides a `{ExtensionContext_Store}` exactly for this purpose.\nExtensions may put values into a store for later retrieval.\n\nTIP: See the `xref:extensions/test-lifecycle-callbacks.adoc#timing-extension[TimingExtension]` for an\nexample of using the `Store` with a method-level scope.\n\n.The `ExtensionContext` and `Store` hierarchy\nimage::extensions_StoreHierarchy.svg[alt=UML diagram,role=text-center]\n\nAs illustrated by the diagram above, stores are hierarchical in nature. When looking up a\nvalue, if no value is stored in the current `ExtensionContext` for the supplied key, the\nstores of the context's ancestors will be queried for a value with the same key in the\n`Namespace` used to create this store. The root `ExtensionContext` represents the engine\nlevel so its `Store` may be used to store or cache values that are used by multiple test\nclasses or extension. The `{ExtensionContext_StoreScope}` enum allows to go beyond even\nthat and access the stores on the level of the current `{LauncherExecutionRequest}` or\n`{LauncherSession}` which can be used to share data across test engines or inject data\nfrom a registered\nxref:advanced-topics/launcher-api.adoc#launcher-session-listeners-custom[`LauncherSessionListener`],\nrespectively. Please consult the Javadoc of `{ExtensionContext}`,\n`{ExtensionContext_Store}`, and `{ExtensionContext_StoreScope}` for details.\n\n[[support]]\n[NOTE]\n.Resource management via `_AutoCloseable_`\n====\nAn extension context store is bound to its extension context lifecycle. When an extension\ncontext lifecycle ends it closes its associated store.\n\nAll stored values that are instances of `AutoCloseable` are notified by an invocation of\ntheir `close()` method in the inverse order they were added in (unless the\n`junit.jupiter.extensions.store.close.autocloseable.enabled`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] is set to `false`).\n\nVersions prior to 5.13 only supported `CloseableResource`, which is deprecated but still\navailable for backward compatibility.\n====\n\nAn example implementation of `AutoCloseable` is shown below, using an `HttpServer`\nresource.\n\n.`_HttpServer_` resource implementing `_AutoCloseable_`\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/HttpServerResource.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/HttpServerResource.kt[tags=user_guide]\n----\n--\n====\n\nThis resource can then be stored in the desired `ExtensionContext`. It may be stored at\nclass or method level, if desired, but this may add unnecessary overhead for this type of\nresource. For this example it might be prudent to store it at root level and instantiate\nit lazily to ensure it's only created once per test run and reused across different test\nclasses and methods.\n\n.Lazily storing in root context with `_Store.computeIfAbsent_`\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/HttpServerExtension.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/HttpServerExtension.kt[tags=user_guide]\n----\n--\n====\n\n.A test case using the `_HttpServerExtension_`\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/HttpServerDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/HttpServerDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[migration]]\n[TIP]\n.Migration Note for Resource Cleanup\n======\nThe framework automatically closes resources stored in the `ExtensionContext.Store` that\nimplement `AutoCloseable`. In versions prior to 5.13, only resources implementing\n`Store.CloseableResource` were automatically closed.\n\nIf you're developing an extension that needs to support both JUnit Jupiter 5.13+ and\nearlier versions and your extension stores resources that need to be cleaned up, you\nshould implement both interfaces:\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\npublic class MyResource implements Store.CloseableResource, AutoCloseable {\n    @Override\n    public void close() throws Exception {\n        // Resource cleanup code\n    }\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\nclass MyResource : Store.CloseableResource, AutoCloseable {\n    override fun close() {\n        // Resource cleanup code\n    }\n}\n----\n--\n====\n\nThis ensures that your resource will be properly closed regardless of which JUnit Jupiter\nversion is being used.\n======\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/overview.adoc",
    "content": "= Extension Model\n\nIn contrast to the competing `Runner`, `TestRule`, and `MethodRule` extension points in\nJUnit 4, the JUnit Jupiter extension model consists of a single, coherent concept: the\n`Extension` API. Note, however, that `Extension` itself is just a marker interface.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/parameter-resolution.adoc",
    "content": "= Parameter Resolution\n\n`{ParameterResolver}` defines the `Extension` API for dynamically resolving parameters at\nruntime.\n\nIf a _test class_ constructor, _test method_, or _lifecycle method_ (see\nxref:writing-tests/definitions.adoc[]) declares a parameter, the parameter must be _resolved_ at\nruntime by a `ParameterResolver`. A `ParameterResolver` can either be built-in (see\n`{TestInfoParameterResolver}`) or xref:extensions/registering-extensions.adoc[registered by the user].\nGenerally speaking, parameters may be resolved by _name_, _type_, _annotation_, or any\ncombination thereof.\n\nIf you wish to implement a custom `{ParameterResolver}` that resolves parameters based\nsolely on the type of the parameter, you may find it convenient to extend the\n`{TypeBasedParameterResolver}` which serves as a generic adapter for such use cases.\n\nFor concrete examples, consult the source code for `{CustomTypeParameterResolver}`,\n`{CustomAnnotationParameterResolver}`, and `{MapOfListsTypeBasedParameterResolver}`.\n\n[WARNING]\n====\nDue to a bug in the byte code generated by `javac` on JDK versions prior to JDK 9,\nlooking up annotations on parameters directly via the core `java.lang.reflect.Parameter`\nAPI will always fail for _inner class_ constructors (e.g., a constructor in a `@Nested`\ntest class).\n\nThe `{ParameterContext}` API supplied to `ParameterResolver` implementations therefore\nincludes the following convenience methods for correctly looking up annotations on\nparameters. Extension authors are strongly encouraged to use these methods instead of\nthose provided in `java.lang.reflect.Parameter` in order to avoid this bug in the JDK.\n\n* `boolean isAnnotated(Class<? extends Annotation> annotationType)`\n* `Optional<A> findAnnotation(Class<A> annotationType)`\n* `List<A> findRepeatableAnnotations(Class<A> annotationType)`\n====\n\n[NOTE]\n.Accessing the test-scoped `ExtensionContext`\n====\nYou may override the `getTestInstantiationExtensionContextScope(...)` method to return\n`TEST_METHOD` to support injecting test specific data into constructor parameters of the\ntest class instance. Doing so causes a test-specific `{ExtensionContext}` to be used while\nresolving constructor parameters, unless the\nxref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is set to `PER_CLASS`.\n====\n\n[TIP]\n.Parameter resolution for methods called from extensions\n====\nOther extensions can also leverage registered `ParameterResolvers` for method and\nconstructor invocations, using the `{ExecutableInvoker}` available via the\n`getExecutableInvoker()` method in the `ExtensionContext`.\n====\n\n[[conflicts]]\n== Parameter Conflicts\n\nIf multiple implementations of `ParameterResolver` that support the same type are\nregistered for a test, a `ParameterResolutionException` will be thrown, with a\nmessage to indicate that competing resolvers have been discovered. See the following\nexample:\n\n.Conflicting parameter resolution due to multiple resolvers claiming support for integers\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/ParameterResolverConflictDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/ParameterResolverConflictDemo.kt[tags=user_guide]\n----\n--\n====\n\nIf the conflicting `ParameterResolver` implementations are applied to different test\nmethods as shown in the following example, no conflict occurs.\n\n.Fine-grained registration to avoid conflict\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/ParameterResolverNoConflictDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/ParameterResolverNoConflictDemo.kt[tags=user_guide]\n----\n--\n====\n\nIf the conflicting `ParameterResolver` implementations need to be applied to the same test\nmethod, you can implement a custom type or custom annotation as illustrated by\n`{CustomTypeParameterResolver}` and `{CustomAnnotationParameterResolver}`, respectively.\n\n.Custom type to resolve duplicate types\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/ParameterResolverCustomTypeDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/ParameterResolverCustomTypeDemo.kt[tags=user_guide]\n----\n--\n====\n\nA custom annotation makes the duplicate type distinguishable from its counterpart:\n\n.Custom annotation to resolve duplicate types\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/ParameterResolverCustomAnnotationDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/ParameterResolverCustomAnnotationDemo.kt[tags=user_guide]\n----\n--\n====\n\nJUnit includes some built-in parameter resolvers that can cause conflicts if a resolver\nattempts to claim their supported types. For example, `{TestInfo}` provides metadata about\ntests. See xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[] for details. Third-party frameworks such\nas Spring may also define parameter resolvers. Apply one of the techniques in this section\nto resolve any conflicts.\n\nParameterized tests are another potential source of conflict. Ensure that tests annotated\nwith `@ParameterizedTest` are not also annotated with `@Test` and see\nxref:writing-tests/parameterized-classes-and-tests.adoc#consuming-arguments[Consuming Arguments] for more details.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/pre-interrupt-callback.adoc",
    "content": "= Pre-Interrupt Callback\n\n`{PreInterruptCallback}` defines the API for `Extensions` that wish to react on\ntimeouts before the `Thread.interrupt()` is called.\n\nPlease refer to xref:writing-tests/timeouts.adoc#debugging[Debugging Timeouts] for additional information.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-class-templates.adoc",
    "content": "= Providing Invocation Contexts for Class Templates\n\nA `{ClassTemplate}` class can only be executed when at least one\n`{ClassTemplateInvocationContextProvider}` is registered. Each such provider is\nresponsible for providing a `Stream` of `{ClassTemplateInvocationContext}` instances.\nEach context may specify a custom display name and a list of additional extensions that\nwill only be used for the next invocation of the `{ClassTemplate}`.\n\nThe following example shows how to write a class template as well as how to register\nand implement a `{ClassTemplateInvocationContextProvider}`.\n\n[source,java,indent=0]\n.A class template with accompanying extension\n----\ninclude::example$java/example/ClassTemplateDemo.java[tags=user_guide]\n----\n\nIn this example, the class template will be invoked twice, meaning all test methods in\nthe class template will be executed twice. The display names of the invocations will be\n`apple` and `banana` as specified by the invocation context. Each invocation registers a\ncustom `{TestInstancePostProcessor}` which is used to inject a value into a field. The\noutput when using the `ConsoleLauncher` is as follows.\n\n....\n└─ ClassTemplateDemo ✔\n   ├─ apple ✔\n   │  ├─ notNull() ✔\n   │  └─ wellKnown() ✔\n   └─ banana ✔\n      ├─ notNull() ✔\n      └─ wellKnown() ✔\n....\n\nThe `{ClassTemplateInvocationContextProvider}` extension API is primarily intended for\nimplementing different kinds of tests that rely on repetitive invocation of _all_ test\nmethods in a test class albeit in different contexts — for example, with different\nparameters, by preparing the test class instance differently, or multiple times without\nmodifying the context.\nPlease refer to the implementations of\nxref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Classes] which uses this extension\npoint to provide its functionality.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/providing-invocation-contexts-for-test-templates.adoc",
    "content": "= Providing Invocation Contexts for Test Templates\n\nA `{TestTemplate}` method can only be executed when at least one\n`{TestTemplateInvocationContextProvider}` is registered. Each such provider is responsible\nfor providing a `Stream` of `{TestTemplateInvocationContext}` instances. Each context may\nspecify a custom display name and a list of additional extensions that will only be used\nfor the next invocation of the `{TestTemplate}` method.\n\nThe following example shows how to write a test template as well as how to register and\nimplement a `{TestTemplateInvocationContextProvider}`.\n\n[source,java,indent=0]\n.A test template with accompanying extension\n----\ninclude::example$java/example/TestTemplateDemo.java[tags=user_guide]\n----\n\nIn this example, the test template will be invoked twice. The display names of the\ninvocations will be `apple` and `banana` as specified by the invocation context. Each\ninvocation registers a custom `{ParameterResolver}` which is used to resolve the method\nparameter. The output when using the `ConsoleLauncher` is as follows.\n\n....\n└─ testTemplate(String) ✔\n   ├─ apple ✔\n   └─ banana ✔\n....\n\nThe `{TestTemplateInvocationContextProvider}` extension API is primarily intended for\nimplementing different kinds of tests that rely on repetitive invocation of a test-like\nmethod albeit in different contexts — for example, with different parameters, by preparing\nthe test class instance differently, or multiple times without modifying the context.\nPlease refer to the implementations of xref:writing-tests/repeated-tests.adoc[] or\nxref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Tests] which use this extension point\nto provide their functionality.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/registering-extensions.adoc",
    "content": "= Registering Extensions\n\nExtensions can be registered _declaratively_ via\n<<registration-declarative, `@ExtendWith`>>, _programmatically_ via\n<<registration-programmatic, `@RegisterExtension`>>, or _automatically_ via\nJava's <<registration-automatic, `ServiceLoader`>> mechanism.\n\n[[registration-declarative]]\n== Declarative Extension Registration\n\nDevelopers can register one or more extensions _declaratively_ by annotating a test\ninterface, test class, test method, or custom _xref:writing-tests/annotations.adoc#annotations[composed\nannotation]_ with `@ExtendWith(...)` and supplying class references for the extensions to\nregister. `@ExtendWith` may also be declared on fields or on parameters in test class\nconstructors, in test methods, and in `@BeforeAll`, `@AfterAll`, `@BeforeEach`, and\n`@AfterEach` lifecycle methods.\n\nFor example, to register a `WebServerExtension` for a particular test method, you would\nannotate the test method as follows. We assume the `WebServerExtension` starts a local web\nserver and injects the server's URL into parameters annotated with `@WebServerUrl`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@Test\n@ExtendWith(WebServerExtension.class)\nvoid getProductList(@WebServerUrl String serverUrl) {\n\tWebClient webClient = new WebClient();\n\t// Use WebClient to connect to web server using serverUrl and verify response\n\tassertEquals(200, webClient.get(serverUrl + \"/products\").getResponseStatus());\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@Test\n@ExtendWith(WebServerExtension::class)\nfun getProductList(@WebServerUrl serverUrl: String) {\n    val webClient = WebClient()\n    // Use WebClient to connect to web server using serverUrl and verify response\n    assertEquals(200, webClient.get(\"$serverUrl/products\").responseStatus)\n}\n----\n--\n====\n\nTo register the `WebServerExtension` for all tests in a particular class and its\nsubclasses, you would annotate the test class as follows.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@ExtendWith(WebServerExtension.class)\nclass MyTests {\n\t// ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@ExtendWith(WebServerExtension::class)\nclass MyTests {\n    // ...\n}\n----\n--\n====\n\nMultiple extensions can be registered together like this:\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })\nclass MyFirstTests {\n\t// ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@ExtendWith(DatabaseExtension::class, WebServerExtension::class)\nclass MyFirstTests {\n    // ...\n}\n----\n--\n====\n\nAs an alternative, multiple extensions can be registered separately like this:\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@ExtendWith(DatabaseExtension.class)\n@ExtendWith(WebServerExtension.class)\nclass MySecondTests {\n\t// ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@ExtendWith(DatabaseExtension::class)\n@ExtendWith(WebServerExtension::class)\nclass MySecondTests {\n    // ...\n}\n----\n--\n====\n\n[TIP]\n.Extension Registration Order\n====\nExtensions registered declaratively via `@ExtendWith` at the class level, method level, or\nparameter level will be executed in the order in which they are declared in the source\ncode. For example, the execution of tests in both `MyFirstTests` and `MySecondTests` will\nbe extended by the `DatabaseExtension` and `WebServerExtension`, **in exactly that order**.\n====\n\nIf you wish to combine multiple extensions in a reusable way, you can define a custom\n_xref:writing-tests/annotations.adoc#annotations[composed annotation]_ and use `@ExtendWith` as a\n_meta-annotation_ as in the following code listing. Then `@DatabaseAndWebServerExtension`\ncan be used in place of `@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith({ DatabaseExtension.class, WebServerExtension.class })\npublic @interface DatabaseAndWebServerExtension {\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)\n@Retention(AnnotationRetention.RUNTIME)\n@ExtendWith(DatabaseExtension::class, WebServerExtension::class)\nannotation class DatabaseAndWebServerExtension\n----\n--\n====\n\nThe above examples demonstrate how `@ExtendWith` can be applied at the class level or at\nthe method level; however, for certain use cases it makes sense for an extension to be\nregistered declaratively at the field or parameter level. Consider a\n`RandomNumberExtension` which generates random numbers that can be injected into a field or\nvia a parameter in a constructor, test method, or lifecycle method. If the extension\nprovides a `@Random` annotation that is meta-annotated with\n`@ExtendWith(RandomNumberExtension.class)` (see listing below), the extension can be used\ntransparently as in the following `RandomNumberDemo` example.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/Random.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/Random.kt[tags=user_guide]\n----\n--\n====\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/RandomNumberDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/RandomNumberDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[RandomNumberExtension]]\nThe following code listing provides an example of how one might choose to implement such a\n`RandomNumberExtension`. This implementation works for the use cases in\n`RandomNumberDemo`; however, it may not prove robust enough to cover all use cases -- for\nexample, the random number generation support is limited to integers; it uses\n`java.util.Random` instead of `java.security.SecureRandom`; etc. In any case, it is\nimportant to note which extension APIs are implemented and for what reasons.\n\nSpecifically, `RandomNumberExtension` implements the following extension APIs:\n\n- `BeforeAllCallback`: to support static field injection\n- `TestInstancePostProcessor`: to support non-static field injection\n- `ParameterResolver`: to support constructor and method injection\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/extensions/RandomNumberExtension.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/extensions/RandomNumberExtension.kt[tags=user_guide]\n----\n--\n====\n\n[TIP]\n.Extension Registration Order for `@ExtendWith` on Fields\n====\nExtensions registered declaratively via `@ExtendWith` on fields will be ordered relative\nto `@RegisterExtension` fields and other `@ExtendWith` fields using an algorithm that is\ndeterministic but intentionally nonobvious. However, `@ExtendWith` fields can be ordered\nusing the `@Order` annotation. See the <<registration-programmatic-order, Extension Registration Order>> tip for `@RegisterExtension` fields for details.\n====\n\n[TIP]\n.Extension Inheritance\n====\nExtensions registered declaratively via `@ExtendWith` on fields in superclasses will be\ninherited.\n\nSee <<registration-inheritance, Extension Inheritance>> for details.\n====\n\nNOTE: `@ExtendWith` fields may be either `static` or non-static. The documentation on\n<<registration-programmatic-static-fields, Static Fields>> and\n<<registration-programmatic-instance-fields, Instance Fields>> for\n`@RegisterExtension` fields also applies to `@ExtendWith` fields.\n\n[[registration-programmatic]]\n== Programmatic Extension Registration\n\nDevelopers can register extensions _programmatically_ by annotating fields in test classes\nwith `{RegisterExtension}`.\n\nWhen an extension is registered _declaratively_ via\n<<registration-declarative, `@ExtendWith`>>, it can typically only be configured\nvia annotations. In contrast, when an extension is registered via `@RegisterExtension`, it\ncan be configured _programmatically_ -- for example, in order to pass arguments to the\nextension's constructor, a static factory method, or a builder API.\n\n[[registration-programmatic-order]]\n[TIP]\n.Extension Registration Order\n====\nBy default, extensions registered programmatically via `@RegisterExtension` or\ndeclaratively via `@ExtendWith` on fields will be ordered using an algorithm that is\ndeterministic but intentionally nonobvious. This ensures that subsequent runs of a test\nsuite execute extensions in the same order, thereby allowing for repeatable builds.\nHowever, there are times when extensions need to be registered in an explicit order. To\nachieve that, annotate `@RegisterExtension` fields or `@ExtendWith` fields with `{Order}`.\n\nAny `@RegisterExtension` field or `@ExtendWith` field not annotated with `@Order` will be\nordered using the _default_ order which has a value of `Integer.MAX_VALUE / 2`. This\nallows `@Order` annotated extension fields to be explicitly ordered before or after\nnon-annotated extension fields. Extensions with an explicit order value less than the\ndefault order value will be registered before non-annotated extensions. Similarly,\nextensions with an explicit order value greater than the default order value will be\nregistered after non-annotated extensions. For example, assigning an extension an explicit\norder value that is greater than the default order value allows _before_ callback\nextensions to be registered last and _after_ callback extensions to be registered first,\nrelative to other programmatically registered extensions.\n====\n\n[TIP]\n.Extension Inheritance\n====\nExtensions registered via `@RegisterExtension` or `@ExtendWith` on fields in superclasses\nwill be inherited.\n\nSee <<registration-inheritance, Extension Inheritance>> for details.\n====\n\nNOTE: `@RegisterExtension` fields must not be `null` (at evaluation time) but may be\neither `static` or non-static.\n\n[[registration-programmatic-static-fields]]\n=== Static Fields\n\nIf a `@RegisterExtension` field is `static`, the extension will be registered after\nextensions that are registered at the class level via `@ExtendWith`. Such _static\nextensions_ are not limited in which extension APIs they can implement. Extensions\nregistered via static fields may therefore implement class-level and instance-level\nextension APIs such as `BeforeAllCallback`, `AfterAllCallback`,\n`TestInstancePostProcessor`, and `TestInstancePreDestroyCallback` as well as method-level\nextension APIs such as `BeforeEachCallback`, etc.\n\nIn the following example, the `server` field in the test class is initialized\nprogrammatically by using a builder pattern supported by the `WebServerExtension`. The\nconfigured `WebServerExtension` will be automatically registered as an extension at the\nclass level -- for example, in order to start the server before all tests in the class\nand then stop the server after all tests in the class have completed. In addition, static\nlifecycle methods annotated with `@BeforeAll` or `@AfterAll` as well as `@BeforeEach`,\n`@AfterEach`, and `@Test` methods can access the instance of the extension via the\n`server` field if necessary.\n\n[source,java,indent=0]\n.Registering an extension via a static field in Java\n----\ninclude::example$java/example/registration/WebServerDemo.java[tags=user_guide]\n----\n\n[[registration-programmatic-static-fields-kotlin]]\n==== Static Fields in Kotlin\n\nThe Kotlin programming language does not have the concept of a `static` field. However,\nthe compiler can be instructed to generate a `private static` field using the `@JvmStatic`\nannotation in Kotlin. If you want the Kotlin compiler to generate a `public static` field,\nyou can use the `@JvmField` annotation instead.\n\nThe following example is a version of the `WebServerDemo` from the previous section that\nhas been ported to Kotlin.\n\n[source,kotlin,indent=0]\n.Registering an extension via a static field in Kotlin\n----\ninclude::example$kotlin/example/registration/KotlinWebServerDemo.kt[tags=user_guide]\n----\n\n[[registration-programmatic-instance-fields]]\n=== Instance Fields\n\nIf a `@RegisterExtension` field is non-static (i.e., an instance field), the extension\nwill be registered after the test class has been instantiated and after each registered\n`TestInstancePostProcessor` has been given a chance to post-process the test instance\n(potentially injecting the instance of the extension to be used into the annotated\nfield). Thus, if such an _instance extension_ implements class-level or instance-level\nextension APIs such as `BeforeAllCallback`, `AfterAllCallback`, or\n`TestInstancePostProcessor`, those APIs will not be honored. Instance extensions will be\nregistered _before_ extensions that are registered at the method level via `@ExtendWith`.\n\nIn the following example, the `docs` field in the test class is initialized\nprogrammatically by invoking a custom `lookUpDocsDir()` method and supplying the result\nto the static `forPath()` factory method in the `DocumentationExtension`. The configured\n`DocumentationExtension` will be automatically registered as an extension at the method\nlevel. In addition, `@BeforeEach`, `@AfterEach`, and `@Test` methods can access the\ninstance of the extension via the `docs` field if necessary.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n.An extension registered via an instance field\n----\ninclude::example$java/example/registration/DocumentationDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n.An extension registered via an instance field\n----\ninclude::example$kotlin/example/kotlin/registration/DocumentationDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[registration-automatic]]\n== Automatic Extension Registration\n\nIn addition to <<registration-declarative, declarative extension registration>>\nand <<registration-programmatic, programmatic extension registration>> support\nusing annotations, JUnit Jupiter also supports _global extension registration_ via Java's\n`{ServiceLoader}` mechanism, allowing third-party extensions to be auto-detected and\nautomatically registered based on what is available in the classpath.\n\nSpecifically, a custom extension can be registered by supplying its fully qualified class\nname in a file named `org.junit.jupiter.api.extension.Extension` within the\n`/META-INF/services` folder in its enclosing JAR file.\n\n[[registration-automatic-enabling]]\n=== Enabling Automatic Extension Detection\n\nAuto-detection is an advanced feature and is therefore not enabled by default. To enable\nit, set the `junit.jupiter.extensions.autodetection.enabled` _configuration parameter_ to\n`true`. This can be supplied as a JVM system property, as a _configuration parameter_ in\nthe `LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform\nconfiguration file (see xref:running-tests/configuration-parameters.adoc[] for details).\n\nFor example, to enable auto-detection of extensions, you can start your JVM with the\nfollowing system property.\n\n`-Djunit.jupiter.extensions.autodetection.enabled=true`\n\nWhen auto-detection is enabled, extensions discovered via the `{ServiceLoader}` mechanism\nwill be added to the extension registry after JUnit Jupiter's global extensions (e.g.,\nsupport for `TestInfo`, `TestReporter`, etc.).\n\n[[registration-automatic-filtering]]\n=== Filtering Auto-detected Extensions\n\nThe list of auto-detected extensions can be filtered using include and exclude patterns\nvia the following xref:running-tests/configuration-parameters.adoc[configuration parameters]:\n\n`junit.jupiter.extensions.autodetection.include=<patterns>`::\n    Comma-separated list of _include_ patterns for auto-detected extensions.\n`junit.jupiter.extensions.autodetection.exclude=<patterns>`::\n    Comma-separated list of _exclude_ patterns for auto-detected extensions.\n\nInclude patterns are applied _before_ exclude patterns. If both include and exclude\npatterns are provided, only extensions that match at least one include pattern and do not\nmatch any exclude pattern will be auto-detected.\n\nSee xref:running-tests/configuration-parameters.adoc#pattern[Pattern Matching Syntax] for details on the pattern syntax.\n\n[[registration-inheritance]]\n== Extension Inheritance\n\nRegistered extensions are inherited within test class hierarchies with top-down semantics.\nSimilarly, extensions registered at the class-level are inherited at the method-level.\nThis applies to all extensions, independent of how they are registered (declaratively or\nprogrammatically).\n\nThis means that extensions registered declaratively via `@ExtendWith` on a superclass will\nbe registered before extensions registered declaratively via `@ExtendWith` on a subclass.\n\nSimilarly, extensions registered programmatically via `@RegisterExtension` or\n`@ExtendWith` on fields in a superclass will be registered before extensions registered\nprogrammatically via `@RegisterExtension` or `@ExtendWith` on fields in a subclass, unless\n`@Order` is used to alter that behavior (see <<registration-programmatic-order, Extension Registration Order>> for details).\n\nNOTE: A specific extension implementation can only be registered once for a given\nextension context and its parent contexts. Consequently, any attempt to register a\nduplicate extension implementation will be ignored.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/relative-execution-order-of-user-code-and-extensions.adoc",
    "content": "= Relative Execution Order of User Code and Extensions\n\nWhen executing a test class that contains one or more test methods, a number of extension\ncallbacks are called in addition to the user-supplied test and lifecycle methods.\n\nNOTE: See also: xref:writing-tests/test-execution-order.adoc[]\n\n[[overview]]\n== User and Extension Code\n\nThe following diagram illustrates the relative order of user-supplied code and extension\ncode. User-supplied test and lifecycle methods are shown in orange, with callback code\nimplemented by extensions shown in blue. The grey box denotes the execution of a single\ntest method and will be repeated for every test method in the test class.\n\n[[diagram]]\n.User code and extension code\nimage::extensions_lifecycle.png[]\n\nThe following table further explains the sixteen steps in the\n<<diagram>> diagram.\n\n. *interface* `*org.junit.jupiter.api.extension.BeforeAllCallback*` +\nextension code executed before all tests of the container are executed\n. *annotation* `*org.junit.jupiter.api.BeforeAll*` +\nuser code executed before all tests of the container are executed\n. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler\n#handleBeforeAllMethodExecutionException*` +\nextension code for handling exceptions thrown from `@BeforeAll` methods\n. *interface* `*org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback*` +\nextension code executed before each class template invocation is executed (only applicable\nif the test class is a xref:writing-tests/class-templates.adoc[class template])\n. *interface* `*org.junit.jupiter.api.extension.BeforeEachCallback*` +\nextension code executed before each test is executed\n. *annotation* `*org.junit.jupiter.api.BeforeEach*` +\nuser code executed before each test is executed\n. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler\n#handleBeforeEachMethodExecutionException*` +\nextension code for handling exceptions thrown from `@BeforeEach` methods\n. *interface* `*org.junit.jupiter.api.extension.BeforeTestExecutionCallback*` +\nextension code executed immediately before a test is executed\n. *annotation* `*org.junit.jupiter.api.Test*` +\nuser code of the actual test method\n. *interface* `*org.junit.jupiter.api.extension.TestExecutionExceptionHandler*` +\nextension code for handling exceptions thrown during a test\n. *interface* `*org.junit.jupiter.api.extension.AfterTestExecutionCallback*` +\nextension code executed immediately after test execution and its corresponding exception handlers\n. *annotation* `*org.junit.jupiter.api.AfterEach*` +\nuser code executed after each test is executed\n. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler\n#handleAfterEachMethodExecutionException*` +\nextension code for handling exceptions thrown from `@AfterEach` methods\n. *interface* `*org.junit.jupiter.api.extension.AfterEachCallback*` +\nextension code executed after each test is executed\n. *interface* `*org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback*` +\nextension code executed after each class template invocation is executed (only applicable\nif the test class is a xref:writing-tests/class-templates.adoc[class template])\n. *annotation* `*org.junit.jupiter.api.AfterAll*` +\nuser code executed after all tests of the container are executed\n. *interface* `*org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler\n#handleAfterAllMethodExecutionException*` +\nextension code for handling exceptions thrown from `@AfterAll` methods\n. *interface* `*org.junit.jupiter.api.extension.AfterAllCallback*` +\nextension code executed after all tests of the container are executed\n\nIn the simplest case only the actual test method will be executed (step 9); all other\nsteps are optional depending on the presence of user code or extension support for the\ncorresponding lifecycle callback. For further details on the various lifecycle callbacks\nplease consult the respective Javadoc for each annotation and extension.\n\nAll invocations of user code methods in the above table can additionally be intercepted\nby implementing xref:extensions/intercepting-invocations.adoc[`InvocationInterceptor`].\n\n[[wrapping-behavior]]\n== Wrapping Behavior of Callbacks\n\nJUnit Jupiter always guarantees _wrapping_ behavior for multiple registered extensions\nthat implement lifecycle callbacks such as `BeforeAllCallback`, `AfterAllCallback`,\n`BeforeClassTemplateInvocationCallback`, `AfterClassTemplateInvocationCallback`,\n`BeforeEachCallback`, `AfterEachCallback`, `BeforeTestExecutionCallback`, and\n`AfterTestExecutionCallback`.\n\nThat means that, given two extensions `Extension1` and `Extension2` with `Extension1`\nregistered before `Extension2`, any \"before\" callbacks implemented by `Extension1` are\nguaranteed to execute **before** any \"before\" callbacks implemented by `Extension2`.\nSimilarly, given the two same two extensions registered in the same order, any \"after\"\ncallbacks implemented by `Extension1` are guaranteed to execute **after** any \"after\"\ncallbacks implemented by `Extension2`. `Extension1` is therefore said to _wrap_\n`Extension2`.\n\nJUnit Jupiter also guarantees _wrapping_ behavior within class and interface hierarchies\nfor user-supplied _lifecycle methods_ (see xref:writing-tests/definitions.adoc[]).\n\n* `@BeforeAll` methods are inherited from superclasses as long as they are not\n  _overridden_. Furthermore, `@BeforeAll` methods from superclasses will be executed\n  **before** `@BeforeAll` methods in subclasses.\n** Similarly, `@BeforeAll` methods declared in an interface are inherited as long as they\n   are not _overridden_, and `@BeforeAll` methods from an interface will be executed\n   **before** `@BeforeAll` methods in the class that implements the interface.\n* `@AfterAll` methods are inherited from superclasses as long as they are not\n  _overridden_. Furthermore, `@AfterAll` methods from superclasses will be executed\n  **after** `@AfterAll` methods in subclasses.\n** Similarly, `@AfterAll` methods declared in an interface are inherited as long as they\n   are not _overridden_, and `@AfterAll` methods from an interface will be executed\n   **after** `@AfterAll` methods in the class that implements the interface.\n* `@BeforeEach` methods are inherited from superclasses as long as they are not\n  _overridden_. Furthermore, `@BeforeEach` methods from superclasses will be executed\n  **before** `@BeforeEach` methods in subclasses.\n** Similarly, `@BeforeEach` methods declared as interface default methods are inherited as\n   long as they are not _overridden_, and `@BeforeEach` default methods will be executed\n   **before** `@BeforeEach` methods in the class that implements the interface.\n* `@AfterEach` methods are inherited from superclasses as long as they are not\n  _overridden_. Furthermore, `@AfterEach` methods from superclasses will be executed\n  **after** `@AfterEach` methods in subclasses.\n** Similarly, `@AfterEach` methods declared as interface default methods are inherited as\n   long as they are not _overridden_, and `@AfterEach` default methods will be executed\n   **after** `@AfterEach` methods in the class that implements the interface.\n\nThe following examples demonstrate this behavior. Please note that the examples do not\nactually do anything realistic. Instead, they mimic common scenarios for testing\ninteractions with the database. All methods imported statically from the `Logger` class\nlog contextual information in order to help us better understand the execution order of\nuser-supplied callback methods and callback methods in extensions.\n\n[source,java,indent=0]\n.Extension1\n----\ninclude::example$java/example/callbacks/Extension1.java[tags=user_guide]\n----\n\n[source,java,indent=0]\n.Extension2\n----\ninclude::example$java/example/callbacks/Extension2.java[tags=user_guide]\n----\n\n[source,java,indent=0]\n.AbstractDatabaseTests\n----\ninclude::example$java/example/callbacks/AbstractDatabaseTests.java[tags=user_guide]\n----\n\n[source,java,indent=0]\n.DatabaseTestsDemo\n----\ninclude::example$java/example/callbacks/DatabaseTestsDemo.java[tags=user_guide]\n----\n\nWhen the `DatabaseTestsDemo` test class is executed, the following is logged.\n\n----\n@BeforeAll AbstractDatabaseTests.createDatabase()\n@BeforeAll DatabaseTestsDemo.beforeAll()\n  Extension1.beforeEach()\n  Extension2.beforeEach()\n    @BeforeEach AbstractDatabaseTests.connectToDatabase()\n    @BeforeEach DatabaseTestsDemo.insertTestDataIntoDatabase()\n      @Test DatabaseTestsDemo.testDatabaseFunctionality()\n    @AfterEach DatabaseTestsDemo.deleteTestDataFromDatabase()\n    @AfterEach AbstractDatabaseTests.disconnectFromDatabase()\n  Extension2.afterEach()\n  Extension1.afterEach()\n@BeforeAll DatabaseTestsDemo.afterAll()\n@AfterAll AbstractDatabaseTests.destroyDatabase()\n----\n\nThe following sequence diagram helps to shed further light on what actually goes on within\nthe `JupiterTestEngine` when the `DatabaseTestsDemo` test class is executed.\n\n////\nPNG generated using ZenUML: https://app.zenuml.com\n\nSee corresponding *.txt file in images folder for the source.\n////\nimage::extensions_DatabaseTestsDemo.png[caption='',title='DatabaseTestsDemo']\n\nJUnit Jupiter does **not** guarantee the execution order of multiple lifecycle methods\nthat are declared within a _single_ test class or test interface. It may at times appear\nthat JUnit Jupiter invokes such methods in alphabetical order. However, that is not\nprecisely true. The ordering is analogous to the ordering for `@Test` methods within a\nsingle test class.\n\n[NOTE]\n====\nLifecycle methods that are declared within a _single_ test class or test interface will be\nordered using an algorithm that is deterministic but intentionally non-obvious. This\nensures that subsequent runs of a test suite execute lifecycle methods in the same order,\nthereby allowing for repeatable builds.\n====\n\nIn addition, JUnit Jupiter does **not** support _wrapping_ behavior for multiple lifecycle\nmethods declared within a single test class or test interface.\n\nThe following example demonstrates this behavior. Specifically, the lifecycle method\nconfiguration is _broken_ due to the order in which the locally declared lifecycle methods\nare executed.\n\n* Test data is inserted _before_ the database connection has been opened, which results in\n  a failure to connect to the database.\n* The database connection is closed _before_ deleting the test data, which results in a\n  failure to connect to the database.\n\n[source,java,indent=0]\n.BrokenLifecycleMethodConfigDemo\n----\ninclude::example$java/example/callbacks/BrokenLifecycleMethodConfigDemo.java[tags=user_guide]\n----\n\nWhen the `BrokenLifecycleMethodConfigDemo` test class is executed, the following is logged.\n\n----\nExtension1.beforeEach()\nExtension2.beforeEach()\n  @BeforeEach BrokenLifecycleMethodConfigDemo.insertTestDataIntoDatabase()\n  @BeforeEach BrokenLifecycleMethodConfigDemo.connectToDatabase()\n    @Test BrokenLifecycleMethodConfigDemo.testDatabaseFunctionality()\n  @AfterEach BrokenLifecycleMethodConfigDemo.disconnectFromDatabase()\n  @AfterEach BrokenLifecycleMethodConfigDemo.deleteTestDataFromDatabase()\nExtension2.afterEach()\nExtension1.afterEach()\n----\n\nThe following sequence diagram helps to shed further light on what actually goes on within\nthe `JupiterTestEngine` when the `BrokenLifecycleMethodConfigDemo` test class is executed.\n\n////\nPNG generated using ZenUML: https://app.zenuml.com\n\nSee corresponding *.txt file in images folder for the source.\n////\nimage::extensions_BrokenLifecycleMethodConfigDemo.png[caption='',title='BrokenLifecycleMethodConfigDemo']\n\n[TIP]\n====\nDue to the aforementioned behavior, the JUnit Team recommends that developers declare at\nmost one of each type of _lifecycle method_ (see xref:writing-tests/definitions.adoc[]) per test\nclass or test interface unless there are no dependencies between such lifecycle methods.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/supported-utilities-in-extensions.adoc",
    "content": "= Supported Utilities in Extensions\n\nThe `junit-platform-commons` artifact provides _maintained_ utilities for working with\nannotations, classes, reflection, classpath scanning, and conversion tasks. These\nutilities can be found in the `{junit-platform-support-package}` and its subpackages.\n`TestEngine` and `Extension` authors are encouraged to use these supported utilities in\norder to align with the behavior of the JUnit Platform and JUnit Jupiter.\n\n[[annotations]]\n== Annotation Support\n\n`AnnotationSupport` provides static utility methods that operate on annotated elements\n(e.g., packages, annotations, classes, interfaces, constructors, methods, and fields).\nThese include methods to check whether an element is annotated or meta-annotated with a\nparticular annotation, to search for specific annotations, and to find annotated methods\nand fields in a class or interface. Some of these methods search on implemented\ninterfaces and within class hierarchies to find annotations. Consult the Javadoc for\n`{AnnotationSupport}` for further details.\n\nNOTE: The `isAnnotated()` methods do not find repeatable annotations. To check for repeatable annotations,\nuse one of the `findRepeatableAnnotations()` methods and verify that the returned list is not empty.\n\nNOTE: See also: <<search-semantics>>\n\n[[classes]]\n== Class Support\n\n`ClassSupport` provides static utility methods for working with classes (i.e., instances\nof `java.lang.Class`). Consult the Javadoc for `{ClassSupport}` for further details.\n\n[[reflection]]\n== Reflection Support\n\n`ReflectionSupport` provides static utility methods that augment the standard JDK\nreflection and class-loading mechanisms. These include methods to scan the classpath in\nsearch of classes matching specified predicates, to load and create new instances of a\nclass, and to find and invoke methods. Some of these methods traverse class hierarchies\nto locate matching methods. Consult the Javadoc for `{ReflectionSupport}` for further\ndetails.\n\nNOTE: See also: <<search-semantics>>\n\n[[modifier]]\n== Modifier Support\n\n`ModifierSupport` provides static utility methods for working with member and class\nmodifiers -- for example, to determine if a member is declared as `public`, `private`,\n`abstract`, `static`, etc. Consult the Javadoc for `{ModifierSupport}` for further\ndetails.\n\n[[conversion]]\n== Conversion Support\n\n`ConversionSupport` (in the `org.junit.platform.commons.support.conversion` package)\nprovides support for converting from strings to primitive types and their corresponding\nwrapper types, date and time types from the `java.time package`, and some additional\ncommon Java types such as `File`, `BigDecimal`, `BigInteger`, `Currency`, `Locale`, `URI`,\n`URL`, `UUID`, etc. Consult the Javadoc for `{ConversionSupport}` for further details.\n\n[[search-semantics]]\n== Field and Method Search Semantics\n\nVarious methods in `AnnotationSupport` and `ReflectionSupport` use search algorithms that\ntraverse type hierarchies to locate matching fields and methods – for example,\n`AnnotationSupport.findAnnotatedFields(...)`, `ReflectionSupport.findMethods(...)`, etc.\n\nThe field and method search algorithms adhere to standard Java semantics regarding whether\na given field or method is visible or overridden according to the rules of the Java\nlanguage.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-instance-factories.adoc",
    "content": "= Test Instance Factories\n\n`{TestInstanceFactory}` defines the API for `Extensions` that wish to _create_ test class\ninstances.\n\nCommon use cases include acquiring the test instance from a dependency injection\nframework or invoking a static factory method to create the test class instance.\n\nIf no `TestInstanceFactory` is registered, the framework will invoke the _sole_\nconstructor for the test class to instantiate it, potentially resolving constructor\narguments via registered `ParameterResolver` extensions.\n\nExtensions that implement `TestInstanceFactory` can be registered on test interfaces,\ntop-level test classes, or `@Nested` test classes.\n\n[WARNING]\n====\nRegistering multiple extensions that implement `TestInstanceFactory` for any single class\nwill result in an exception being thrown for all tests in that class, in any subclass,\nand in any nested class. Note that any `TestInstanceFactory` registered in a superclass\nor _enclosing_ class (i.e., in the case of a `@Nested` test class) is _inherited_. It is\nthe user's responsibility to ensure that only a single `TestInstanceFactory` is\nregistered for any specific test class.\n====\n\n[NOTE]\n.Accessing the test-scoped `ExtensionContext`\n====\nYou may override the `getTestInstantiationExtensionContextScope(...)` method to return\n`TEST_METHOD` to make test-specific data available to your extension implementation or if\nyou want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-instance-post-processing.adoc",
    "content": "= Test Instance Post-processing\n\n`{TestInstancePostProcessor}` defines the API for `Extensions` that wish to _post\nprocess_ test instances.\n\nCommon use cases include injecting dependencies into the test instance, invoking custom\ninitialization methods on the test instance, etc.\n\nFor a concrete example, consult the source code for the `{MockitoExtension}` and the\n`{SpringExtension}`.\n\n[NOTE]\n.Accessing the test-scoped `ExtensionContext`\n====\nYou may override the `getTestInstantiationExtensionContextScope(...)` method to return\n`TEST_METHOD` to make test-specific data available to your extension implementation or if\nyou want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-instance-pre-construct-callback.adoc",
    "content": "= Test Instance Pre-construct Callback\n\n`{TestInstancePreConstructCallback}` defines the API for `Extensions` that wish to be invoked\n_prior_ to test instances being constructed (by a constructor call or via\n`{TestInstanceFactory}`).\n\nThis extension provides a symmetric call to `{TestInstancePreDestroyCallback}` and is useful\nin combination with other extensions to prepare constructor parameters or keeping track of test\ninstances and their lifecycle.\n\n[NOTE]\n.Accessing the test-scoped `ExtensionContext`\n====\nYou may override the `getTestInstantiationExtensionContextScope(...)` method to return\n`TEST_METHOD` to make test-specific data available to your extension implementation or if\nyou want to xref:extensions/keeping-state-in-extensions.adoc[keep state] on the test method level.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-instance-pre-destroy-callback.adoc",
    "content": "= Test Instance Pre-destroy Callback\n\n`{TestInstancePreDestroyCallback}` defines the API for `Extensions` that wish to process\ntest instances _after_ they have been used in tests and _before_ they are destroyed.\n\nCommon use cases include cleaning dependencies that have been injected into the\ntest instance, invoking custom de-initialization methods on the test instance, etc.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-lifecycle-callbacks.adoc",
    "content": "= Test Lifecycle Callbacks\n\nThe following interfaces define the APIs for extending tests at various points in the\ntest execution lifecycle. Consult the following sections for examples and the Javadoc for\neach of these interfaces in the `{extension-api-package}` package for further details.\n\n* `{BeforeAllCallback}`\n** `{BeforeClassTemplateInvocationCallback}` (only applicable for\n   xref:writing-tests/class-templates.adoc[class templates])\n*** `{BeforeEachCallback}`\n**** `{BeforeTestExecutionCallback}`\n**** `{AfterTestExecutionCallback}`\n*** `{AfterEachCallback}`\n** `{AfterClassTemplateInvocationCallback}` (only applicable for\n   xref:writing-tests/class-templates.adoc[class templates])\n* `{AfterAllCallback}`\n\n.Implementing Multiple Extension APIs\nNOTE: Extension developers may choose to implement any number of these interfaces\nwithin a single extension. Consult the source code of the `{SpringExtension}` for a\nconcrete example.\n\n[[before-after-execution]]\n== Before and After Test Execution Callbacks\n\n`{BeforeTestExecutionCallback}` and `{AfterTestExecutionCallback}` define the APIs for\n`Extensions` that wish to add behavior that will be executed _immediately before_ and\n_immediately after_ a test method is executed, respectively. As such, these callbacks are\nwell suited for timing, tracing, and similar use cases. If you need to implement\ncallbacks that are invoked _around_ `@BeforeEach` and `@AfterEach` methods, implement\n`BeforeEachCallback` and `AfterEachCallback` instead.\n\nThe following example shows how to use these callbacks to calculate and log the execution\ntime of a test method. `TimingExtension` implements both `BeforeTestExecutionCallback`\nand `AfterTestExecutionCallback` in order to time and log the test execution.\n\n[[timing-extension]]\n.An extension that times and logs the execution of test methods\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/timing/TimingExtension.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/timing/TimingExtension.kt[tags=user_guide]\n----\n--\n====\n\nSince the `TimingExtensionTests` class registers the `TimingExtension` via `@ExtendWith`,\nits tests will have this timing applied when they execute.\n\n.A test class that uses the example TimingExtension\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/timing/TimingExtensionTests.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/timing/TimingExtensionTests.kt[tags=user_guide]\n----\n--\n====\n\nThe following is an example of the logging produced when `TimingExtensionTests` is run.\n\n....\nINFO: Method [sleep20ms] took 24 ms.\nINFO: Method [sleep50ms] took 53 ms.\n....\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/extensions/test-result-processing.adoc",
    "content": "= Test Result Processing\n\n`{TestWatcher}` defines the API for extensions that wish to process the results of _test\nmethod_ executions. Specifically, a `TestWatcher` will be invoked with contextual\ninformation for the following events.\n\n* `testDisabled`: invoked after a disabled _test method_ has been skipped\n* `testSuccessful`: invoked after a _test method_ has completed successfully\n* `testAborted`: invoked after a _test method_ has been aborted\n* `testFailed`: invoked after a _test method_ has failed\n\nNOTE: In contrast to the definition of \"test method\" presented in\nxref:writing-tests/definitions.adoc[], in this context _test method_ refers to any `@Test` method\nor `@TestTemplate` method (for example, a `@RepeatedTest` or `@ParameterizedTest`).\n\nExtensions implementing this interface can be registered at the class level, instance\nlevel, or method level. When registered at the class level, a `TestWatcher` will be\ninvoked for any contained _test method_ including those in `@Nested` classes. When\nregistered at the method level, a `TestWatcher` will only be invoked for the _test method_\nfor which it was registered.\n\n[WARNING]\n====\nIf a `TestWatcher` is registered via a non-static (instance) field – for example, using\n`@RegisterExtension` – and the test class is configured with\n`@TestInstance(Lifecycle.PER_METHOD)` semantics (which is the default lifecycle mode), the\n`TestWatcher` will **not** be invoked with events for `@TestTemplate` methods (for\nexample, `@RepeatedTest` or `@ParameterizedTest`).\n\nTo ensure that a `TestWatcher` is invoked for all _test methods_ in a given class, it is\ntherefore recommended that the `TestWatcher` be registered at the class level with\n`@ExtendWith` or via a `static` field with `@RegisterExtension` or `@ExtendWith`.\n====\n\nIf there is a failure at the class level — for example, an exception thrown by a\n`@BeforeAll` method — no test results will be reported. Similarly, if the test class is\ndisabled via an `ExecutionCondition` — for example, `@Disabled` — no test results will be\nreported.\n\nIn contrast to other Extension APIs, a `TestWatcher` is not permitted to adversely\ninfluence the execution of tests. Consequently, any exception thrown by a method in the\n`TestWatcher` API will be logged at `WARNING` level and will not be allowed to propagate\nor fail test execution.\n\n[WARNING]\n====\nAny instances of `ExtensionContext.Store.CloseableResource` stored in the `Store` of the\nprovided `{ExtensionContext}` will be closed _before_ methods in the `TestWatcher` API are\ninvoked (see xref:extensions/keeping-state-in-extensions.adoc[]). You can use the parent context's `Store` to\nwork with such resources.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/migrating-from-junit4.adoc",
    "content": "= Migrating from JUnit 4\n\nAlthough the JUnit Jupiter programming model and extension model do not support JUnit 4\nfeatures such as `Rules` and `Runners` natively, it is not expected that source code\nmaintainers will need to update all of their existing tests, test extensions, and custom\nbuild test infrastructure to migrate to JUnit Jupiter.\n\nInstead, JUnit provides a gentle migration path via a _JUnit Vintage test engine_ which\nallows existing tests based on JUnit 3 and JUnit 4 to be executed using the JUnit Platform\ninfrastructure. Since all classes and annotations specific to JUnit Jupiter reside under\nthe `org.junit.jupiter` base package, having both JUnit 4 and JUnit Jupiter in the\nclasspath does not lead to any conflicts. It is therefore safe to maintain existing JUnit\n4 tests alongside JUnit Jupiter tests and migrate them gradually.\n\n\n[[running]]\n== Running JUnit 4 Tests on the JUnit Platform\n\n[WARNING]\n====\nThe JUnit Vintage engine is deprecated and should only be used temporarily while\nmigrating tests to JUnit Jupiter or another testing framework with native JUnit Platform\nsupport.\n\nBy default, if the JUnit Vintage engine is registered and discovers at least one test\nclass, it reports a xref:running-tests/discovery-issues.adoc[discovery issue] of INFO severity.\nYou can prevent this discovery issue from being reported by setting the\n`junit.vintage.discovery.issue.reporting.enabled`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to `false`.\n====\n\nMake sure that the `junit-vintage-engine` artifact is in your test runtime path. In that\ncase JUnit 3 and JUnit 4 tests will automatically be picked up by the JUnit Platform\nlauncher.\n\nSee the example projects in the {junit-examples-repo}[`junit-examples`] repository to\nfind out how this is done with Gradle and Maven.\n\n[[categories-support]]\n=== Categories Support\n\nFor test classes or methods that are annotated with `@Category`, the _JUnit Vintage test\nengine_ exposes the category's fully qualified class name as a xref:running-tests/tags.adoc[tag]\nfor the corresponding test class or test method. For example, if a test method is\nannotated with `@Category(Example.class)`, it will be tagged with `\"com.acme.Example\"`.\nSimilar to the `Categories` runner in JUnit 4, this information can be used to filter the\ndiscovered tests before executing them (see xref:running-tests/intro.adoc[] for details).\n\n[[parallel-execution]]\n== Parallel Execution\n\nThe JUnit Vintage test engine supports parallel execution of top-level test classes and test methods,\nallowing existing JUnit 3 and JUnit 4 tests to benefit from improved performance through\nconcurrent test execution. It can be enabled and configured using the following\nxref:running-tests/configuration-parameters.adoc[configuration parameters]:\n\n`junit.vintage.execution.parallel.enabled=true|false`::\n  Enable/disable parallel execution (defaults to `false`). Requires opt-in for `classes`\n  or `methods` to be executed in parallel using the configuration parameters below.\n\n`junit.vintage.execution.parallel.classes=true|false`::\n  Enable/disable parallel execution of test classes (defaults to `false`).\n\n`junit.vintage.execution.parallel.methods=true|false`::\n  Enable/disable parallel execution of test methods (defaults to `false`).\n\n`junit.vintage.execution.parallel.pool-size=<number>`::\n  Specifies the size of the thread pool to be used for parallel execution. By default, the\n  number of available processors is used.\n\n[[parallel-execution-class-level]]\n=== Parallelization at Class Level\n\nLet's assume we have two test classes `FooTest` and `BarTest` with each class containing\nthree unit tests. Now, let's enable parallel execution of test classes:\n\n[source,properties]\n----\njunit.vintage.execution.parallel.enabled=true\njunit.vintage.execution.parallel.classes=true\n----\n\nWith this setup, the `VintageTestEngine` will use two different threads,\none for each test class:\n\n[source,plaintext]\n----\nForkJoinPool-1-worker-1 - BarTest::test1\nForkJoinPool-1-worker-2 - FooTest::test1\nForkJoinPool-1-worker-1 - BarTest::test2\nForkJoinPool-1-worker-2 - FooTest::test2\nForkJoinPool-1-worker-1 - BarTest::test3\nForkJoinPool-1-worker-2 - FooTest::test3\n----\n\n[[parallel-execution-method-level]]\n=== Parallelization at Method Level\n\nAlternatively, we can enable parallel test execution at a method level,\nrather than the class level:\n\n[source,properties]\n----\njunit.vintage.execution.parallel.enabled=true\njunit.vintage.execution.parallel.methods=true\n----\n\nTherefore, the test methods within each class will be executed in parallel, while\ndifferent test classes will be executed sequentially:\n\n[source,plaintext]\n----\nForkJoinPool-1-worker-1 - BarTest::test1\nForkJoinPool-1-worker-2 - BarTest::test2\nForkJoinPool-1-worker-3 - BarTest::test3\n\nForkJoinPool-1-worker-3 - FooTest::test1\nForkJoinPool-1-worker-2 - FooTest::test2\nForkJoinPool-1-worker-1 - FooTest::test3\n----\n\n[[parallel-execution-class-and-method-level]]\n=== Full Parallelization\n\nFinally, we can also enable parallelization at both class and method level:\n\n[source,properties]\n----\njunit.vintage.execution.parallel.enabled=true\njunit.vintage.execution.parallel.classes=true\njunit.vintage.execution.parallel.methods=true\n----\n\nWith these properties set, the `VintageTestEngine` will execute all tests classes and\nmethods in parallel, potentially significantly reducing the overall test suite execution time:\n\n[source,plaintext]\n----\nForkJoinPool-1-worker-6 - FooTest::test2\nForkJoinPool-1-worker-7 - BarTest::test3\nForkJoinPool-1-worker-3 - FooTest::test1\nForkJoinPool-1-worker-8 - FooTest::test3\nForkJoinPool-1-worker-5 - BarTest::test2\nForkJoinPool-1-worker-4 - BarTest::test1\n----\n\n[[parallel-execution-pool-size]]\n=== Configuring the Pool Size\n\nThe default thread pool size is equal to the number of available processors. However, we\ncan also configure the pool size explicitly:\n\n[source,properties]\n----\njunit.vintage.execution.parallel.enabled=true\njunit.vintage.execution.parallel.classes=true\njunit.vintage.execution.parallel.methods=true\njunit.vintage.execution.parallel.pool-size=4\n----\n\nFor instance, if we update our previous example that uses full parallelization and\nconfigure the pool size to four, we can expect to see our six test methods executed with\na parallelism of four:\n\n[source,plaintext]\n----\nForkJoinPool-1-worker-2 - FooTest::test1\nForkJoinPool-1-worker-4 - BarTest::test2\nForkJoinPool-1-worker-3 - BarTest::test1\nForkJoinPool-1-worker-4 - BarTest::test3\nForkJoinPool-1-worker-2 - FooTest::test2\nForkJoinPool-1-worker-3 - FooTest::test3\n----\n\nAs we can see, even though we set the thread pool size was four, only three threads were\nused in this case. This happens because the pool adjusts the number of active threads\nbased on workload and system needs.\n\n[[parallel-execution-disabled]]\n=== Sequential Execution\n\nOn the other hand, if we disable parallel execution, the `VintageTestEngine`\nwill execute all tests sequentially, regardless of the other properties:\n\n[source,properties]\n----\njunit.vintage.execution.parallel.enabled=false\njunit.vintage.execution.parallel.classes=true\njunit.vintage.execution.parallel.methods=true\n----\n\nSimilarly, tests will be executed sequentially if you enable parallel execution in general\nbut enable neither class-level nor method-level parallelization.\n\n[[tips]]\n== Migration Tips\n\nThe following are topics that you should be aware of when migrating existing JUnit 4\ntests to JUnit Jupiter.\n\n* Annotations reside in the `org.junit.jupiter.api` package.\n* Assertions reside in `org.junit.jupiter.api.Assertions`.\n  - Note that you may continue to use assertion methods from `org.junit.Assert` or any\n    other assertion library such as {AssertJ}, {Hamcrest}, {Truth}, etc.\n* Assumptions reside in `org.junit.jupiter.api.Assumptions`.\n  - Note that JUnit Jupiter supports methods from JUnit 4's `org.junit.Assume` class for\n    assumptions. Specifically, JUnit Jupiter supports JUnit 4's\n    `AssumptionViolatedException` to signal that a test should be aborted instead of\n    marked as a failure.\n* `@Before` and `@After` no longer exist; use `@BeforeEach` and `@AfterEach` instead.\n* `@BeforeClass` and `@AfterClass` no longer exist; use `@BeforeAll` and `@AfterAll`\n  instead.\n* `@Ignore` no longer exists: use `@Disabled` or one of the other built-in\n  xref:writing-tests/conditional-test-execution.adoc[execution conditions] instead\n  - See also <<ignore-annotation-support>>.\n* `@Category` no longer exists; use `@Tag` instead.\n* `@RunWith` no longer exists; superseded by `@ExtendWith`.\n  - For `@RunWith(Enclosed.class)` use `@Nested`.\n  - For `@RunWith(Parameterized.class)` see <<tips-parameterized>>.\n* `@Rule` and `@ClassRule` no longer exist; superseded by `@ExtendWith` and\n  `@RegisterExtension`.\n  - See also <<rule-support>>.\n* `@Test(expected = ...)` and the `ExpectedException` rule no longer exist; use\n  `Assertions.assertThrows(...)` instead.\n  - See <<rule-support>> if you still need to use\n    `ExpectedException`.\n* Assertions and assumptions in JUnit Jupiter accept the failure message as their last\n  argument instead of the first one.\n  - See <<failure-message-arguments>> for details.\n\n[[tips-parameterized]]\n=== Parameterized test classes\n\nUnless `@UseParametersRunnerFactory` is used, a JUnit 4 parameterized test class can be\nconverted into a JUnit Jupiter\nxref:writing-tests/parameterized-classes-and-tests.adoc[`@ParameterizedClass`] by following these steps:\n\n. Replace `@RunWith(Parameterized.class)` with `@ParameterizedClass`.\n. Add a class-level `@MethodSource(\"methodName\")` annotation where `methodName` is the\n  name of the method annotated with `@Parameters` and remove the `@Parameters` annotation\n  from the method.\n. Replace `@BeforeParam` and `@AfterParam` with `@BeforeParameterizedClassInvocation` and\n  `@AfterParameterizedClassInvocation`, respectively, if there are any methods with such\n  annotations.\n. Change the imports of the `@Test` and `@Parameter` annotations to use the\n  `org.junit.jupiter.params` package.\n. Change assertions etc. to use the `org.junit.jupiter.api` package as usual.\n. Optionally, remove all `public` modifiers from the class and its methods and fields.\n\n====\n[source,java,indent=0]\n.Before\n----\ninclude::example$java/example/ParameterizedMigrationDemo.java[tags=before]\n----\n\n[source,java,indent=0]\n.After\n----\ninclude::example$java/example/ParameterizedMigrationDemo.java[tags=after]\n----\n====\n\n[[rule-support]]\n== Limited JUnit 4 Rule Support\n\nWARNING: _JUnit 4 rule support_ is deprecated for removal since version 6.0.0. Please\nmigrate to the corresponding APIs and extensions provided by JUnit Jupiter.\n\nAs stated above, JUnit Jupiter does not and will not support JUnit 4 rules natively. The\nJUnit team realizes, however, that many organizations, especially large ones, are likely\nto have large JUnit 4 code bases that make use of custom rules. To serve these\norganizations and enable a gradual migration path the JUnit team has decided to support a\nselection of JUnit 4 rules verbatim within JUnit Jupiter. This support is based on\nadapters and is limited to those rules that are semantically compatible to the JUnit\nJupiter extension model, i.e. those that do not completely change the overall execution\nflow of the test.\n\nThe `junit-jupiter-migrationsupport` module from JUnit Jupiter currently supports the\nfollowing three `Rule` types including subclasses of these types:\n\n* `org.junit.rules.ExternalResource` (including `org.junit.rules.TemporaryFolder`)\n* `org.junit.rules.Verifier` (including `org.junit.rules.ErrorCollector`)\n* `org.junit.rules.ExpectedException`\n\nAs in JUnit 4, Rule-annotated fields as well as methods are supported. By using these\nclass-level extensions on a test class such `Rule` implementations in legacy code bases\ncan be _left unchanged_ including the JUnit 4 rule import statements.\n\nThis limited form of `Rule` support can be switched on by the class-level annotation\n`{EnableRuleMigrationSupport}`. This annotation is a _composed annotation_ which enables\nall rule migration support extensions: `VerifierSupport`, `ExternalResourceSupport`, and\n`ExpectedExceptionSupport`. You may alternatively choose to annotate your test class with\n`@EnableJUnit4MigrationSupport` which registers migration support for rules _and_ JUnit\n4's `@Ignore` annotation (see <<ignore-annotation-support>>).\n\nHowever, if you intend to develop a new extension for JUnit Jupiter please use the new\nextension model of JUnit Jupiter instead of the rule-based model of JUnit 4.\n\n\n[[ignore-annotation-support]]\n== JUnit 4 @Ignore Support\n\nWARNING: _JUnit 4 `@Ignore` support_ is deprecated for removal since version 6.0.0. Please\nuse JUnit Jupiter's `@Disabled` annotation instead.\n\nIn order to provide a smooth migration path from JUnit 4 to JUnit Jupiter, the\n`junit-jupiter-migrationsupport` module provides support for JUnit 4's `@Ignore`\nannotation analogous to Jupiter's `{Disabled}` annotation.\n\nTo use `@Ignore` with JUnit Jupiter based tests, configure a _test_ dependency on the\n`junit-jupiter-migrationsupport` module in your build and then annotate your test class\nwith `@ExtendWith(IgnoreCondition.class)` or `{EnableJUnit4MigrationSupport}` (which\nautomatically registers the `IgnoreCondition` along with\n<<rule-support>>). The `IgnoreCondition` is an\n`{ExecutionCondition}` that disables test classes or test methods that are annotated with\n`@Ignore`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/IgnoredTestsDemo.java[tags=user_guide]\n----\n\n\n[[failure-message-arguments]]\n== Failure Message Arguments\n\nThe `Assumptions` and `Assertions` classes in JUnit Jupiter declare arguments in a\ndifferent order than in JUnit 4. In JUnit 4 assertion and assumption methods accept the\nfailure message as the first argument; whereas, in JUnit Jupiter assertion and assumption\nmethods accept the failure message as the last argument.\n\nFor instance, the method `assertEquals` in JUnit 4 is declared as `assertEquals(String\nmessage, Object expected, Object actual)`, but in JUnit Jupiter it is declared as\n`assertEquals(Object expected, Object actual, String message)`. The rationale for this is\nthat a failure message is _optional_, and optional arguments should be declared after\nrequired arguments in a method signature.\n\nThe methods affected by this change are the following:\n\n- Assertions\n  * `assertTrue`\n  * `assertFalse`\n  * `assertNull`\n  * `assertNotNull`\n  * `assertEquals`\n  * `assertNotEquals`\n  * `assertArrayEquals`\n  * `assertSame`\n  * `assertNotSame`\n  * `assertThrows`\n- Assumptions\n  * `assumeTrue`\n  * `assumeFalse`\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/overview.adoc",
    "content": "= Overview\n:page-aliases: index.adoc, user-guide/index.adoc\n\nThe goal of this document is to provide comprehensive reference documentation for\nprogrammers writing tests, extension authors, and engine authors as well as build tool\nand IDE vendors.\n\n[[what-is-junit]]\n== What is JUnit?\n\nJUnit is composed of several different modules from three different sub-projects.\n\n[.text-center]\n**JUnit {version} = _JUnit Platform_ + _JUnit Jupiter_ + _JUnit Vintage_**\n\nThe **JUnit Platform** serves as a foundation for\nxref:advanced-topics/launcher-api.adoc[launching testing frameworks] on the JVM. It also\ndefines the `{TestEngine}` API for developing a testing framework that runs on the\nplatform. Furthermore, the platform provides a\nxref:running-tests/console-launcher.adoc[Console Launcher] to launch the platform from the\ncommand line and the xref:advanced-topics/junit-platform-suite-engine.adoc[] for running a\ncustom test suite using one or more test engines on the platform. First-class support for\nthe JUnit Platform also exists in popular IDEs (see\nxref:running-tests/ide-support.adoc#intellij-idea[IntelliJ IDEA],\nxref:running-tests/ide-support.adoc#eclipse[Eclipse],\nxref:running-tests/ide-support.adoc#netbeans[NetBeans], and\nxref:running-tests/ide-support.adoc#vscode[Visual Studio Code]) and build tools (see\nxref:running-tests/build-support.adoc#gradle[Gradle],\nxref:running-tests/build-support.adoc#maven[Maven],\nxref:running-tests/build-support.adoc#ant[Ant],\nxref:running-tests/build-support.adoc#bazel[Bazel], and\nxref:running-tests/build-support.adoc#sbt[sbt]).\n\n**JUnit Jupiter** is the combination of the xref:writing-tests/intro.adoc[programming model] and\nxref:extensions/overview.adoc[extension model] for writing JUnit tests and extensions. The Jupiter\nsub-project provides a `TestEngine` for running Jupiter based tests on the platform.\n\n**JUnit Vintage** provides a `TestEngine` for running JUnit 3 and JUnit 4 based tests on\nthe platform. It requires JUnit 4.12 or later to be present on the class path or module\npath. Note, however, that the JUnit Vintage engine is deprecated and should only be used\ntemporarily while migrating tests to JUnit Jupiter or another testing framework with\nnative JUnit Platform support.\n\n[[java-versions]]\n== Supported Java Versions\n\nJUnit requires Java 17 (or higher) at runtime. However, you can still test code that\nhas been compiled with previous versions of the JDK.\n\n[[getting-help]]\n== Getting Help\n\nAsk JUnit-related questions on {StackOverflow} or use the {DiscussionsQA}[Q&A category on GitHub Discussions].\n\n[[getting-started]]\n== Getting Started\n\n[[getting-started-junit-artifacts]]\n=== Downloading JUnit Artifacts\n\nTo find out what artifacts are available for download and inclusion in your project, refer\nto xref:appendix.adoc#dependency-metadata[Dependency Metadata]. To set up dependency management for your build, refer to\nxref:running-tests/build-support.adoc[] and the <<getting-started-example-projects>>.\n\n[[getting-started-features]]\n=== JUnit Features\n\nTo find out what features are available in JUnit {version} and how to use them, read the\ncorresponding sections of this User Guide, organized by topic.\n\n* xref:writing-tests/intro.adoc[Writing Tests in JUnit Jupiter]\n* xref:migrating-from-junit4.adoc[Migrating from JUnit 4 to JUnit Jupiter]\n* xref:running-tests/intro.adoc[]\n* xref:extensions/overview.adoc[Extension Model for JUnit Jupiter]\n* Advanced Topics\n  - xref:advanced-topics/launcher-api.adoc[]\n  - xref:advanced-topics/testkit.adoc[]\n\n[[getting-started-example-projects]]\n=== Example Projects\n\nTo see complete, working examples of projects that you can copy and experiment with, the\n{junit-examples-repo}[`junit-examples`] repository is a good place to start. The\n`junit-examples` repository hosts a collection of example projects based on JUnit Jupiter,\nJUnit Vintage, and other testing frameworks. You'll find appropriate build scripts (e.g.,\n`build.gradle`, `pom.xml`, etc.) in the example projects. The links below highlight some\nof the combinations you can choose from.\n\n* For Gradle and Java, check out the `{junit-jupiter-starter-gradle}` project.\n* For Gradle and Kotlin, check out the `{junit-jupiter-starter-gradle-kotlin}` project.\n* For Gradle and Groovy, check out the `{junit-jupiter-starter-gradle-groovy}` project.\n* For Maven, check out the `{junit-jupiter-starter-maven}` project.\n* For Ant, check out the `{junit-jupiter-starter-ant}` project.\n* For Bazel, check out the `{junit-jupiter-starter-bazel}` project.\n* For sbt, check out the `{junit-jupiter-starter-sbt}` project.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/release-notes.adoc",
    "content": "= Release Notes\n:page-aliases: release-notes/index.adoc\n//\n//\n\nThis document contains the change log for all JUnit releases since 6.0 GA.\n\ninclude::partial$release-notes/release-notes-6.1.0.adoc[]\n\ninclude::partial$release-notes/release-notes-6.1.0-RC1.adoc[]\n\ninclude::partial$release-notes/release-notes-6.1.0-M1.adoc[]\n\ninclude::partial$release-notes/release-notes-6.0.3.adoc[]\n\ninclude::partial$release-notes/release-notes-6.0.2.adoc[]\n\ninclude::partial$release-notes/release-notes-6.0.1.adoc[]\n\ninclude::partial$release-notes/release-notes-6.0.0.adoc[]\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/build-support.adoc",
    "content": "= Build Support\n\n[[gradle]]\n== Gradle\n\nStarting with https://docs.gradle.org/4.6/release-notes.html[version 4.6], Gradle provides\nhttps://docs.gradle.org/current/userguide/java_testing.html#using_junit5[native support]\nfor executing tests on the JUnit Platform. To enable it, you need to specify\n`useJUnitPlatform()` within a `test` task declaration in `build.gradle`:\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\ntest {\n\tuseJUnitPlatform()\n}\n----\n\nFiltering by xref:running-tests/tags.adoc[tags],\nxref:running-tests/tags.adoc#expressions[tag expressions], or engines is also supported:\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\ntest {\n\tuseJUnitPlatform {\n\t\tincludeTags(\"fast\", \"smoke & feature-a\")\n\t\t// excludeTags(\"slow\", \"ci\")\n\t\tincludeEngines(\"junit-jupiter\")\n\t\t// excludeEngines(\"junit-vintage\")\n\t}\n}\n----\n\nPlease refer to the\nhttps://docs.gradle.org/current/userguide/java_testing.html[official Gradle documentation]\nfor a comprehensive list of options.\n\n[[gradle-bom]]\n=== Aligning dependency versions\n\nTIP: See <<spring-boot>> for details on how to override the version\nof JUnit used in your Spring Boot application.\n\nUnless you're using Spring Boot which defines its own way of managing dependencies, it is\nrecommended to use the JUnit Platform xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] to align the\nversions of all JUnit artifacts.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n.Explicit platform dependency on the BOM\n----\ndependencies {\n\ttestImplementation(platform(\"org.junit:junit-bom:{version}\"))\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n}\n----\n\nUsing the BOM allows you to omit the version when declaring dependencies on all artifacts\nwith the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs.\n\nSince all JUnit artifacts declare a\nhttps://docs.gradle.org/current/userguide/platforms.html[platform] dependency on the BOM,\nyou usually don't need to declare an explicit dependency on it yourself. Instead, it's\nsufficient to declare _one_ regular dependency that includes a version number. Gradle will\nthen pull in the BOM automatically so you can omit the version for all other JUnit\nartifacts.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n.Implicit platform dependency on the BOM\n----\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:{version}\") // <1>\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\") // <2>\n}\n----\n<1> Dependency declaration with explicit version. Pulls in the `junit-bom` automatically.\n<2> Dependency declaration without version. The version is supplied by the `junit-bom`.\n\n[WARNING]\n.Declaring a dependency on junit-platform-launcher\n====\nEven though pre-8.0 versions of Gradle don't require declaring an explicit\ndependency on `junit-platform-launcher`, it is recommended to do so to ensure the versions\nof JUnit artifacts on the test runtime classpath are aligned.\n\nMoreover, doing so is recommended and in some cases even required when importing the\nproject into an IDE like xref:running-tests/ide-support.adoc#eclipse[Eclipse] or\nxref:running-tests/ide-support.adoc#intellij-idea[IntelliJ IDEA].\n====\n\n[[gradle-engines-configure]]\n=== Configuring Test Engines\n\nIn order to run any tests at all, a `TestEngine` implementation must be on the classpath.\n\nTo configure support for JUnit Jupiter based tests, configure a `testImplementation` dependency\non the dependency-aggregating JUnit Jupiter artifact similar to the following.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:{version}\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n}\n----\n\nAlternatively, you can use Gradle's\nhttps://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html[JVM Test Suite]\nsupport.\n\n[source,kotlin,indent=0]\n[subs=attributes+]\n.Kotlin DSL\n----\ntesting {\n\tsuites {\n\t\tnamed<JvmTestSuite>(\"test\") {\n\t\t\tuseJUnitJupiter(\"{version}\")\n\t\t}\n\t}\n}\n----\n\n[source,groovy,indent=0]\n[subs=attributes+]\n.Groovy DSL\n----\ntesting {\n\tsuites {\n\t\ttest {\n\t\t\tuseJUnitJupiter(\"{version}\")\n\t\t}\n\t}\n}\n----\n\nThe JUnit Platform can run JUnit 4 based tests as long as you configure a `testImplementation`\ndependency on JUnit 4 and a `testRuntimeOnly` dependency on the JUnit Vintage `TestEngine`\nimplementation similar to the following.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\ndependencies {\n\ttestImplementation(\"junit:junit:{junit4-version}\")\n\ttestRuntimeOnly(\"org.junit.vintage:junit-vintage-engine:{version}\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n}\n----\n\n[[gradle-config-params]]\n=== Configuration Parameters\n\nThe standard Gradle `test` task currently does not provide a dedicated DSL to set JUnit\nPlatform xref:running-tests/configuration-parameters.adoc[configuration parameters] to influence test\ndiscovery and execution. However, you can provide configuration parameters within the\nbuild script via system properties (as shown below) or via the\n`junit-platform.properties` file.\n\n[source,groovy,indent=0]\n----\ntest {\n\t// ...\n\tsystemProperty(\"junit.jupiter.conditions.deactivate\", \"*\")\n\tsystemProperty(\"junit.jupiter.extensions.autodetection.enabled\", true)\n\tsystemProperty(\"junit.jupiter.testinstance.lifecycle.default\", \"per_class\")\n\t// ...\n}\n----\n\n[[gradle-logging]]\n=== Configuring Logging (optional)\n\nJUnit uses the Java Logging APIs in the `java.util.logging` package (a.k.a. _JUL_) to\nemit warnings and debug information. Please refer to the official documentation of\n`{LogManager}` for configuration options.\n\nAlternatively, it's possible to redirect log messages to other logging frameworks such as\n{Log4j} or {Logback}. To use a logging framework that provides a custom implementation of\n`{LogManager}`, set the `java.util.logging.manager` system property to the _fully\nqualified class name_ of the `{LogManager}` implementation to use. The example below\ndemonstrates how to configure Log4j{nbsp}2.x (see {Log4j_JDK_Logging_Adapter} for\ndetails).\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\ntest {\n\tsystemProperty(\"java.util.logging.manager\", \"org.apache.logging.log4j.jul.LogManager\")\n\t// Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx)\n\tsystemProperty(\"log4j2.disableJmx\", \"true\")\n}\n----\n\nOther logging frameworks provide different means to redirect messages logged using\n`java.util.logging`. For example, for {Logback} you can use the\nhttps://www.slf4j.org/legacy.html#jul-to-slf4j[JUL to SLF4J Bridge] by adding it as a\ndependency to the test runtime classpath.\n\n[[maven]]\n== Maven\n\nMaven Surefire and Maven Failsafe provide\nhttps://maven.apache.org/surefire/maven-surefire-plugin/examples/junit-platform.html[native support]\nfor executing tests on the JUnit Platform. The `pom.xml` file in the\n`{junit-jupiter-starter-maven}` project demonstrates how to use the Maven Surefire plugin\nand can serve as a starting point for configuring your Maven build.\n\n[WARNING]\n.Minimum required version of Maven Surefire/Failsafe\n====\nAs of JUnit 6.0, the minimum required version of Maven Surefire/Failsafe is 3.0.0.\n====\n\n[[maven-bom]]\n=== Aligning dependency versions\n\nUnless you're using Spring Boot which defines its own way of managing dependencies, it is\nrecommended to use the JUnit Platform xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] to align the\nversions of all JUnit artifacts.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n<dependencyManagement>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.junit</groupId>\n\t\t\t<artifactId>junit-bom</artifactId>\n\t\t\t<version>{version}</version>\n\t\t\t<type>pom</type>\n\t\t\t<scope>import</scope>\n\t\t</dependency>\n\t</dependencies>\n</dependencyManagement>\n----\n\nUsing the BOM allows you to omit the version when declaring dependencies on all artifacts\nwith the `org.junit.platform`, `org.junit.jupiter`, and `org.junit.vintage` group IDs.\n\nTIP: See <<spring-boot>> for details on how to override the version\nof JUnit used in your Spring Boot application.\n\n[[maven-engines-configure]]\n=== Configuring Test Engines\n\nIn order to have Maven Surefire or Maven Failsafe run any tests at all, at least one\n`TestEngine` implementation must be added to the test classpath.\n\nTo configure support for JUnit Jupiter based tests, configure `test` scoped dependencies\non the JUnit Jupiter API and the JUnit Jupiter `TestEngine` implementation similar to the\nfollowing.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n\t<!-- ... -->\n\t<dependencies>\n\t\t<!-- ... -->\n\t\t<dependency>\n\t\t\t<groupId>org.junit.jupiter</groupId>\n\t\t\t<artifactId>junit-jupiter</artifactId>\n\t\t\t<version>{version}</version> <!-- can be omitted when using the BOM -->\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<!-- ... -->\n\t</dependencies>\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-failsafe-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n----\n\nMaven Surefire and Maven Failsafe can run JUnit 4 based tests alongside Jupiter tests as\nlong as you configure `test` scoped dependencies on JUnit 4 and the JUnit Vintage\n`TestEngine` implementation similar to the following.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n\t<!-- ... -->\n\t<dependencies>\n\t\t<!-- ... -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>{junit4-version}</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.junit.vintage</groupId>\n\t\t\t<artifactId>junit-vintage-engine</artifactId>\n\t\t\t<version>{version}</version> <!-- can be omitted when using the BOM -->\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<!-- ... -->\n\t</dependencies>\n\t<!-- ... -->\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-failsafe-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n----\n\n[[maven-filter-test-class-names]]\n=== Filtering by Test Class Names\n\nThe Maven Surefire Plugin will scan for test classes whose fully qualified names match\nthe following patterns.\n\n- `+++**/Test*.java+++`\n- `+++**/*Test.java+++`\n- `+++**/*Tests.java+++`\n- `+++**/*TestCase.java+++`\n\nMoreover, it will exclude all nested classes (including static member classes) by default.\n\nNote, however, that you can override this default behavior by configuring explicit\n`include` and `exclude` rules in your `pom.xml` file. For example, to keep Maven Surefire\nfrom excluding static member classes, you can override its exclude rules as follows.\n\n[source,xml,indent=0]\n[subs=attributes+]\n.Overriding exclude rules of Maven Surefire\n----\n\t<!-- ... -->\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<excludes>\n\t\t\t\t\t\t<exclude/>\n\t\t\t\t\t</excludes>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n----\n\nPlease see the\nhttps://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html[Inclusions and Exclusions of Tests]\ndocumentation for Maven Surefire for details.\n\n[[maven-filter-tags]]\n=== Filtering by Tags\n\nYou can filter tests by xref:running-tests/tags.adoc[tags] or\nxref:running-tests/tags.adoc#expressions[tag expressions] using the following configuration\nproperties.\n\n- to include _tags_ or _tag expressions_, use `groups`.\n- to exclude _tags_ or _tag expressions_, use `excludedGroups`.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n\t<!-- ... -->\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<groups>acceptance | !feature-a</groups>\n\t\t\t\t\t<excludedGroups>integration, regression</excludedGroups>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n----\n\n[[maven-config-params]]\n=== Configuration Parameters\n\nYou can set JUnit Platform xref:running-tests/configuration-parameters.adoc[configuration parameters] to\ninfluence test discovery and execution by declaring the `configurationParameters`\nproperty and providing key-value pairs using the Java `Properties` file syntax (as shown\nbelow) or via the `junit-platform.properties` file.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n\t<!-- ... -->\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>{surefire-version}</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<properties>\n\t\t\t\t\t\t<configurationParameters>\n\t\t\t\t\t\t\tjunit.jupiter.conditions.deactivate = *\n\t\t\t\t\t\t\tjunit.jupiter.extensions.autodetection.enabled = true\n\t\t\t\t\t\t\tjunit.jupiter.testinstance.lifecycle.default = per_class\n\t\t\t\t\t\t</configurationParameters>\n\t\t\t\t\t</properties>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\t<!-- ... -->\n----\n\n[[ant]]\n== Ant\n\nStarting with version `1.10.3`, link:https://ant.apache.org/[Ant] has a\nlink:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher`] task that\nprovides native support for launching tests on the JUnit Platform. The `junitlauncher`\ntask is solely responsible for launching the JUnit Platform and passing it the selected\ncollection of tests. The JUnit Platform then delegates to registered test engines to\ndiscover and execute the tests.\n\nThe `junitlauncher` task attempts to align as closely as possible with native Ant\nconstructs such as\nlink:https://ant.apache.org/manual/Types/resources.html#collection[resource collections]\nfor allowing users to select the tests that they want executed by test engines. This gives\nthe task a consistent and natural feel when compared to many other core Ant tasks.\n\nStarting with version `1.10.6` of Ant, the `junitlauncher` task supports\nlink:https://ant.apache.org/manual/Tasks/junitlauncher.html#fork[forking the tests in a separate JVM].\n\nThe `build.xml` file in the `{junit-jupiter-starter-ant}` project demonstrates how to use\nthe task and can serve as a starting point.\n\n=== Basic Usage\n\nThe following example demonstrates how to configure the `junitlauncher` task to select a\nsingle test class (i.e., `com.example.project.CalculatorTests`).\n\n[source,xml,indent=0]\n----\n\t<path id=\"test.classpath\">\n\t\t<!-- The location where you have your compiled classes -->\n\t\t<pathelement location=\"${build.classes.dir}\" />\n\t</path>\n\n\t<!-- ... -->\n\n\t<junitlauncher>\n\t\t<classpath refid=\"test.classpath\" />\n\t\t<test name=\"com.example.project.CalculatorTests\" />\n\t</junitlauncher>\n----\n\nThe `test` element allows you to specify a single test class that you want to be selected\nand executed. The `classpath` element allows you to specify the classpath to be used to\nlaunch the JUnit Platform. This classpath will also be used to locate test classes that\nare part of the execution.\n\nThe following example demonstrates how to configure the `junitlauncher` task to select\ntest classes from multiple locations.\n\n[source,xml,indent=0]\n----\n\t<path id=\"test.classpath\">\n\t\t<!-- The location where you have your compiled classes -->\n\t\t<pathelement location=\"${build.classes.dir}\" />\n\t</path>\n\t<!-- ... -->\n\t<junitlauncher>\n\t\t<classpath refid=\"test.classpath\" />\n\t\t<testclasses outputdir=\"${output.dir}\">\n\t\t\t<fileset dir=\"${build.classes.dir}\">\n\t\t\t\t<include name=\"org/example/**/demo/**/\" />\n\t\t\t</fileset>\n\t\t\t<fileset dir=\"${some.other.dir}\">\n\t\t\t\t<include name=\"org/myapp/**/\" />\n\t\t\t</fileset>\n\t\t</testclasses>\n\t</junitlauncher>\n----\n\nIn the above example, the `testclasses` element allows you to select multiple test\nclasses that reside in different locations.\n\nFor further details on usage and configuration options please refer to the official Ant\ndocumentation for the\nlink:https://ant.apache.org/manual/Tasks/junitlauncher.html[`junitlauncher` task].\n\n[[bazel]]\n== Bazel\n\nFor Bazel, add dependencies on\nhttps://github.com/bazel-contrib/rules_jvm_external[rules_jvm_external] and\nhttps://github.com/bazel-contrib/rules_jvm[contrib_rules_jvm] to your module's manifest\nand install JUnit's artifacts.\n\n[source,skylark]\n[subs=attributes+]\n.MODULE.bazel\n----\nbazel_dep(name = \"rules_jvm_external\", version = \"6.9\")\nbazel_dep(name = \"contrib_rules_jvm\", version = \"0.31.1\")\n\nmaven = use_extension(\"@rules_jvm_external//:extensions.bzl\", \"maven\")\n\nmaven.install(\n    artifacts = [\n        \"org.junit.jupiter:junit-jupiter-api:{version}\",\n        \"org.junit.jupiter:junit-jupiter-params:{version}\",\n        \"org.junit.jupiter:junit-jupiter-engine:{version}\",\n        \"org.junit.platform:junit-platform-launcher:{version}\",\n        \"org.junit.platform:junit-platform-reporting:{version}\",\n    ],\n    repositories = [\n        \"https://repo1.maven.org/maven2\",\n    ],\n)\n\nuse_repo(maven, \"maven\")\n----\n\nNext, in your `BUILD` file, define a `java_test_suite` for your tests.\n\n[source,skylark]\n[subs=attributes+]\n.BUILD\n----\nload(\"@rules_jvm_external//:defs.bzl\", \"artifact\")\nload(\"@contrib_rules_jvm//java:defs.bzl\", \"java_test_suite\")\n\njava_library(\n    name = \"your-library-name\",\n    srcs = glob([\"src/main/java/**/*.java\"]),\n)\n\njava_test_suite(\n    name = \"your-library-name-tests\",\n    size = \"small\",\n    srcs = glob([\"src/test/java/**/*.java\"]),\n    runner = \"junit5\",\n    test_suffixes = [\"Test.java\", \"Tests.java\"],\n    runtime_deps = runtime_deps = [\n        artifact(\"org.junit.jupiter:junit-jupiter-engine\"),\n        artifact(\"org.junit.platform:junit-platform-launcher\"),\n        artifact(\"org.junit.platform:junit-platform-reporting\"),\n    ],\n    deps = [\n        \":your-library-name\",\n        artifact(\"org.junit.jupiter:junit-jupiter-api\"),\n        artifact(\"org.junit.jupiter:junit-jupiter-params\"),\n    ],\n)\n----\n\nTo execute your tests, run `bazel test //...`. For a full example, please check out the\n`{junit-jupiter-starter-bazel}` example project.\n\n[[sbt]]\n== sbt\n\nFor sbt, you first need to declare a dependency on the\nhttps://github.com/sbt/sbt-jupiter-interface[sbt-jupiter-interface] plugin. Despite its\nname, it supports running any class-based JUnit Platform test engine.\n\n[source,scala]\n.project/plugins.sbt\n----\naddSbtPlugin(\"com.github.sbt.junit\" % \"sbt-jupiter-interface\" % \"0.17.0\")\n----\n\nIn your build definition, add dependencies on the corresponding `jupiter-interface` along\nJUnit's artifacts. Moreover, you should configure the `jupiterTestFramework` and its\nhttps://github.com/sbt/sbt-jupiter-interface#framework-options[options].\n\n[source,scala]\n[subs=attributes+]\n.build.sbt\n----\nlazy val root = project\n  .in(file(\".\"))\n  .settings(\n    name := \"junit-jupiter-starter-sbt\",\n    libraryDependencies ++= Seq(\n      \"com.github.sbt.junit\" % \"jupiter-interface\" % JupiterKeys.jupiterVersion.value % Test,\n      \"org.junit.jupiter\" % \"junit-jupiter\" % \"{version}\" % Test,\n      \"org.junit.platform\" % \"junit-platform-launcher\" % \"{version}\" % Test,\n    ),\n    testOptions += Tests.Argument(jupiterTestFramework, \"--display-mode=tree\")\n  )\n----\n\nFinally, execute your tests by running `sbt test`. For a full example, please check out\nthe `{junit-jupiter-starter-sbt}` example project.\n\n[[spring-boot]]\n== Spring Boot\n\nlink:https://spring.io/projects/spring-boot[Spring Boot] provides automatic support for\nmanaging the version of JUnit used in your project. In addition, the\n`spring-boot-starter-test` artifact automatically includes testing libraries such as JUnit\nJupiter, AssertJ, Mockito, etc.\n\nIf your build relies on dependency management support from Spring Boot, you should not\nimport JUnit's xref:appendix.adoc#dependency-metadata-junit-bom[Bill of Materials (BOM)] in your build script since that would\nresult in duplicate (and potentially conflicting) management of JUnit dependencies.\n\nIf you need to override the version of a dependency used in your Spring Boot application,\nyou have to override the exact name of the\nlink:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#appendix.dependency-versions.properties[version property]\ndefined in the BOM used by the Spring Boot plugin. For example, the name of the JUnit\nJupiter version property in Spring Boot is `junit-jupiter.version`. The mechanism for\nchanging a dependency version is documented for both\nlink:https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#managing-dependencies.dependency-management-plugin.customizing[Gradle]\nand\nlink:https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/htmlsingle/#using.parent-pom[Maven].\n\nWith Gradle you can override the JUnit Jupiter version by including the following in your\n`build.gradle` file.\n\n[source,groovy,indent=0]\n[subs=attributes+]\n----\n\text['junit-jupiter.version'] = '{version}'\n----\n\nWith Maven you can override the JUnit Jupiter version by including the following in your\n`pom.xml` file.\n\n[source,xml,indent=0]\n[subs=attributes+]\n----\n\t<properties>\n\t\t<junit-jupiter.version>{version}</junit-jupiter.version>\n\t</properties>\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/capturing-standard-output-error.adoc",
    "content": "= Capturing Standard Output/Error\n\nThe JUnit Platform provides opt-in support for capturing output printed to `System.out`\nand `System.err`. To enable it, set the `junit.platform.output.capture.stdout` and/or\n`junit.platform.output.capture.stderr` xref:running-tests/configuration-parameters.adoc[configuration\nparameter] to `true`. In addition, you may configure the maximum number of buffered bytes\nto be used per executed test or container using `junit.platform.output.capture.maxBuffer`.\n\nIf enabled, the JUnit Platform captures the corresponding output and publishes it as a\nreport entry using the `stdout` or `stderr` keys to all registered\n`{TestExecutionListener}` instances immediately before reporting the test or container as\nfinished.\n\nPlease note that the captured output will only contain output emitted by the thread that\nwas used to execute a container or test. Any output by other threads will be omitted\nbecause particularly when\nxref:writing-tests/parallel-execution.adoc[executing tests in parallel] it would be impossible\nto attribute it to a specific test or container.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/configuration-parameters.adoc",
    "content": "= Configuration Parameters\n\nIn addition to instructing the platform which test classes and test engines to include,\nwhich packages to scan, etc., it is sometimes necessary to provide additional custom\nconfiguration parameters that are specific to a particular test engine, listener, or\nregistered extension. For example, the JUnit Jupiter `TestEngine` supports _configuration\nparameters_ for the following use cases.\n\n- xref:writing-tests/test-instance-lifecycle.adoc#default[Changing the Default Test Instance Lifecycle]\n- xref:extensions/registering-extensions.adoc#registration-automatic-enabling[Enabling Automatic Extension Detection]\n- xref:extensions/conditional-test-execution.adoc#deactivation[Deactivating Conditions]\n- xref:writing-tests/display-names.adoc#generator-default[Setting the Default Display Name Generator]\n\n_Configuration Parameters_ are text-based key-value pairs that can be supplied to test\nengines running on the JUnit Platform via one of the following mechanisms.\n\n1. The `configurationParameter()` and `configurationParameters()` methods in\n   `LauncherDiscoveryRequestBuilder` which\n   is used to build a request supplied to the xref:advanced-topics/launcher-api.adoc[Launcher API].\n    +\n   When running tests via one of the tools provided by the JUnit Platform you can specify\n   configuration parameters as follows:\n   * xref:running-tests/console-launcher.adoc[Console Launcher]: use the `--config` command-line\n     option.\n   * xref:running-tests/build-support.adoc#gradle-config-params[Gradle]: use the `systemProperty` or\n     `systemProperties` DSL.\n   * xref:running-tests/build-support.adoc#maven-config-params[Maven Surefire provider]: use the\n     `configurationParameters` property.\n2. The `configurationParametersResources()` method in `LauncherDiscoveryRequestBuilder`.\n    +\n   When running tests via the xref:running-tests/console-launcher.adoc[Console Launcher] you can\n   specify custom configuration files using the `--config-resource` command-line option.\n3. JVM system properties.\n4. The JUnit Platform default configuration file: a file named `junit-platform.properties`\n   in the root of the class path that follows the syntax rules for Java `Properties`\n   files.\n\nNOTE: Configuration parameters are looked up in the exact order defined above.\nConsequently, configuration parameters supplied directly to the `Launcher` take\nprecedence over those supplied via custom configuration files, system properties, and the\ndefault configuration file. Similarly, configuration parameters supplied via system\nproperties take precedence over those supplied via the default configuration file.\n\n[[pattern]]\n== Pattern Matching Syntax\n\nThis section describes the pattern matching syntax that is applied to the _configuration\nparameters_ used for the following features.\n\n- xref:extensions/conditional-test-execution.adoc#deactivation[Deactivating Conditions]\n- xref:advanced-topics/launcher-api.adoc#listeners-custom-deactivation[Deactivating a TestExecutionListener]\n- xref:running-tests/stack-trace-pruning.adoc[]\n- xref:extensions/registering-extensions.adoc#registration-automatic-filtering[Filtering Auto-detected Extensions]\n\nIf the value for the given _configuration parameter_ consists solely of an asterisk\n(`+++*+++`), the pattern will match against all candidate classes. Otherwise, the value\nwill be treated as a comma-separated list of patterns where each pattern will be matched\nagainst the fully qualified class name (_FQCN_) of each candidate class. Any dot (`.`) in\na pattern will match against a dot (`.`) or a dollar sign (`$`) in a FQCN. Any asterisk\n(`+++*+++`) will match against one or more characters in a FQCN. All other characters in a\npattern will be matched one-to-one against a FQCN.\n\nExamples:\n\n- `+++*+++`: matches all candidate classes.\n- `+++org.junit.*+++`: matches all candidate classes under the `org.junit` base package and\n  any of its subpackages.\n- `+++*.MyCustomImpl+++`: matches every candidate class whose simple class name is exactly\n  `MyCustomImpl`.\n- `+++*System*+++`: matches every candidate class whose FQCN contains `System`.\n- `+++*System*+++, +++*Unit*+++`: matches every candidate class whose FQCN contains\n  `System` or `Unit`.\n- `org.example.MyCustomImpl`: matches the candidate class whose FQCN is exactly\n  `org.example.MyCustomImpl`.\n- `org.example.MyCustomImpl, org.example.TheirCustomImpl`: matches candidate classes whose\n  FQCN is exactly `org.example.MyCustomImpl` or `org.example.TheirCustomImpl`.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/console-launcher.adoc",
    "content": "= Console Launcher\n\nThe `{ConsoleLauncher}` is a command-line Java application that lets you launch the JUnit\nPlatform from the console. For example, it can be used to run JUnit Vintage and JUnit\nJupiter tests and print test execution results to the console.\n\nAn executable _Fat JAR_ (`junit-platform-console-standalone-{version}.jar`) that\ncontains the contents of all of its dependencies is published in the {Maven_Central}\nrepository under the\nhttps://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone[junit-platform-console-standalone]\ndirectory. It contains the contents of the following artifacts:\n\ninclude::partial$console-launcher-standalone-shadowed-artifacts.adoc[]\n\n[NOTE]\n====\nSince the `junit-platform-console-standalone` JAR contains the contents of all of its\ndependencies, its Maven POM does not declare any dependencies.\n\nFurthermore, it is not very likely that you would need to include a dependency on the\n`junit-platform-console-standalone` artifact in your project's Maven POM or Gradle build\nscript. On the contrary, the executable `junit-platform-console-standalone` JAR is\ntypically invoked directly from the command line or a shell script without a build script.\n\nIf you need to declare dependencies in your build script on some of the artifacts\ncontained in the `junit-platform-console-standalone` artifact, you should declare\ndependencies only on the JUnit artifacts that are used in your project. To simplify\ndependency management of JUnit artifacts in your build, you may wish to use the\n`junit-jupiter` aggregator artifact or `junit-bom`. See xref:appendix.adoc#dependency-metadata[Dependency Metadata] for\ndetails.\n====\n\nYou can https://docs.oracle.com/javase/tutorial/deployment/jar/run.html[run] the\nstandalone `ConsoleLauncher` as shown below.\n\n[source,console,subs=attributes+]\n----\n$ java -jar junit-platform-console-standalone-{version}.jar execute <OPTIONS>\n\n├─ JUnit Vintage\n│  └─ example.JUnit4Tests\n│     └─ standardJUnit4Test ✔\n└─ JUnit Jupiter\n   ├─ StandardTests\n   │  ├─ succeedingTest() ✔\n   │  └─ skippedTest() ↷ for demonstration purposes\n   └─ A special test case\n      ├─ Custom test name containing spaces ✔\n      ├─ ╯°□°)╯ ✔\n      └─ 😱 ✔\n\nTest run finished after 64 ms\n[         5 containers found      ]\n[         0 containers skipped    ]\n[         5 containers started    ]\n[         0 containers aborted    ]\n[         5 containers successful ]\n[         0 containers failed     ]\n[         6 tests found           ]\n[         1 tests skipped         ]\n[         5 tests started         ]\n[         0 tests aborted         ]\n[         5 tests successful      ]\n[         0 tests failed          ]\n----\n\nYou can also run the standalone `ConsoleLauncher` as shown below (for example, to include\nall jars in a directory):\n\n[source,console,subs=attributes+]\n----\n$ java -cp classes:testlib/* org.junit.platform.console.ConsoleLauncher <OPTIONS>\n----\n\n[[options]]\n== Subcommands and Options\n\nThe `{ConsoleLauncher}` provides the following subcommands:\n\n----\ninclude::partial$console-launcher-options.txt[]\n----\n\n[[options-discovering-tests]]\n=== Discovering tests\n\n----\ninclude::partial$console-launcher-discover-options.txt[]\n----\n\n[[options-executing-tests]]\n=== Executing tests\n\n.Exit Code\nNOTE: On successful runs, the `{ConsoleLauncher}` exits with a status code of `0`.\nAll non-zero codes indicate an error of some sort. For example, status code `1`\nis returned if any containers or tests failed. If no tests are discovered and the\n`--fail-if-no-tests` command-line option is supplied, the `ConsoleLauncher` exits\nwith a status code of `2`. Unexpected or invalid user input yields a status code\nof `3`. An exit code of `-1` indicates an unspecified error condition.\n\n----\ninclude::partial$console-launcher-execute-options.txt[]\n----\n\n[[options-listing-test-engines]]\n=== Listing test engines\n\n----\ninclude::partial$console-launcher-engines-options.txt[]\n----\n\n[[argument-files]]\n== Argument Files (@-files)\n\nOn some platforms you may run into system limitations on the length of a command line when\ncreating a command line with lots of options or with long arguments.\n\nThe `ConsoleLauncher` supports _argument files_, also known as _@-files_. Argument files\nare files that themselves contain arguments to be passed to the command. When the\nunderlying https://github.com/remkop/picocli[picocli] command line parser encounters an\nargument beginning with the character `@`, it expands the contents of that file into the\nargument list.\n\nThe arguments within a file can be separated by spaces or newlines. If an argument\ncontains embedded whitespace, the whole argument should be wrapped in double or single\nquotes -- for example, `\"-f=My Files/Stuff.java\"`.\n\nIf the argument file does not exist or cannot be read, the argument will be treated\nliterally and will not be removed. This will likely result in an \"unmatched argument\"\nerror message. You can troubleshoot such errors by executing the command with the\n`picocli.trace` system property set to `DEBUG`.\n\nMultiple _@-files_ may be specified on the command line. The specified path may be\nrelative to the current directory or absolute.\n\nYou can pass a real parameter with an initial `@` character by escaping it with an\nadditional `@` symbol. For example, `@@somearg` will become `@somearg` and will not be\nsubject to expansion.\n\n[[redirecting-stdout-and-stderr]]\n== Redirecting Standard Output/Error to Files\n\nYou can redirect the `System.out` (stdout) and `System.err` (stderr) output streams to\nfiles using the `--redirect-stdout` and `--redirect-stderr` options:\n\n[source,console,subs=attributes+]\n----\n$ java -jar junit-platform-console-standalone-{version}.jar <OPTIONS> \\\n  --redirect-stdout=stdout.txt \\\n  --redirect-stderr=stderr.txt\n----\n\n[NOTE]\n====\nIf the `--redirect-stdout` and `--redirect-stderr` arguments point to the same file, both\noutput streams will be redirected to that file.\n\nThe default charset is used for writing to the files.\n====\n\n[[color-customization]]\n== Color Customization\n\nThe colors used in the output of the `{ConsoleLauncher}` can be customized.\nThe option `--single-color` will apply a built-in monochrome style, while\n`--color-palette` will accept a properties file to override the\nhttps://en.wikipedia.org/wiki/ANSI_escape_code#Colors[ANSI SGR] color styling.\nThe properties file below demonstrates the default style:\n\n[source,properties,indent=0]\n----\nSUCCESSFUL = 32\nABORTED = 33\nFAILED = 31\nSKIPPED = 35\nCONTAINER = 35\nTEST = 34\nDYNAMIC = 35\nREPORTED = 37\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/discovery-issues.adoc",
    "content": "= Discovery Issues\n\nTest engines may encounter issues during test discovery. For example, the declaration of a\ntest class or method may be invalid. To avoid such issues from going unnoticed, the JUnit\nPlatform provides a xref:advanced-topics/engines.adoc#discovery-issues[mechanism for test engines] to\nreport them with different severity levels:\n\nINFO::\nIndicates that the engine encountered something that could be potentially problematic, but\ncould also happen due to a valid setup or configuration.\n\nWARNING::\nIndicates that the engine encountered something that is problematic and might lead to\nunexpected behavior or will be removed or changed in a future release.\n\nERROR::\nIndicates that the engine encountered something that is definitely problematic and will\nlead to unexpected behavior.\n\nIf an engine reports an issue with a severity equal to or higher than a configurable\n_critical_ severity, its tests will not be executed. Instead, the engine will be reported\nas failed during execution with a `{DiscoveryIssueException}` listing all critical issues.\nNon-critical issues will be logged but will not prevent the engine from executing its\ntests. The `junit.platform.discovery.issue.severity.critical`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] can be used to set the critical\nseverity level. Currently, the default value is `ERROR` but it may be changed in a future\nrelease.\n\nTIP: To surface all discovery issues in your project, it is recommended to set the\n`junit.platform.discovery.issue.severity.critical` configuration parameter to `INFO`.\n\nIn addition, registered `{LauncherDiscoveryListener}` implementations can receive\ndiscovery issues via the `issueEncountered()` method. This allows IDEs and build tools to\nreport issues to the user in a more user-friendly way. For example, IDEs may choose to\ndisplay all issues in a list or table.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/discovery-selectors.adoc",
    "content": "= Discovery Selectors\n\nThe JUnit Platform provides a rich set of discovery selectors that can be used to specify\nwhich tests should be discovered or executed.\n\nDiscovery selectors can be created programmatically using the factory methods in the\n`{DiscoverySelectors}` class, specified declaratively via annotations when using the\nxref:advanced-topics/junit-platform-suite-engine.adoc[], via options of the xref:running-tests/console-launcher.adoc[], or\ngenerically as strings via their identifiers.\n\nThe following discovery selectors are provided out of the box:\n\n|===\n| Java Type                     | API                                            | Annotation                  | Console Launcher                                 | Identifier\n\n| `{ClasspathResourceSelector}` | `{DiscoverySelectors_selectClasspathResource}` | `{SelectClasspathResource}` | `--select-resource /foo.csv`                     | `resource:/foo.csv`\n| `{ClasspathRootSelector}`     | `{DiscoverySelectors_selectClasspathRoots}`    | --                          | `--scan-classpath bin`                           | `classpath-root:bin`\n| `{ClassSelector}`             | `{DiscoverySelectors_selectClass}`             | `{SelectClasses}`           | `--select-class com.acme.Foo`                    | `class:com.acme.Foo`\n| `{DirectorySelector}`         | `{DiscoverySelectors_selectDirectory}`         | `{SelectDirectories}`       | `--select-directory foo/bar`                     | `directory:foo/bar`\n| `{FileSelector}`              | `{DiscoverySelectors_selectFile}`              | `{SelectFile}`              | `--select-file dir/foo.txt`                      | `file:dir/foo.txt`\n| `{IterationSelector}`         | `{DiscoverySelectors_selectIteration}`         | `{Select}(\"<identifier>\")`  | `--select-iteration method=com.acme.Foo#m[1..2]` | `iteration:method:com.acme.Foo#m[1..2]`\n| `{MethodSelector}`            | `{DiscoverySelectors_selectMethod}`            | `{SelectMethod}`            | `--select-method com.acme.Foo#m`                 | `method:com.acme.Foo#m`\n| `{ModuleSelector}`            | `{DiscoverySelectors_selectModule}`            | `{SelectModules}`           | `--select-module com.acme`                       | `module:com.acme`\n| `{NestedClassSelector}`       | `{DiscoverySelectors_selectNestedClass}`       | `{Select}(\"<identifier>\")`  | `--select <identifier>`                          | `nested-class:com.acme.Foo/Bar`\n| `{NestedMethodSelector}`      | `{DiscoverySelectors_selectNestedMethod}`      | `{Select}(\"<identifier>\")`  | `--select <identifier>`                          | `nested-method:com.acme.Foo/Bar#m`\n| `{PackageSelector}`           | `{DiscoverySelectors_selectPackage}`           | `{SelectPackages}`          | `--select-package com.acme.foo`                  | `package:com.acme.foo`\n| `{UniqueIdSelector}`          | `{DiscoverySelectors_selectUniqueId}`          | `{Select}(\"<identifier>\")`  | `--select-unique-id <identifier>`                | `uid:[engine:Foo]/[segment:Bar]`\n| `{UriSelector}`               | `{DiscoverySelectors_selectUri}`               | `{SelectUris}`              | `--select-uri \\file:///foo.txt`                  | `uri:file:///foo.txt`\n|===\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/ide-support.adoc",
    "content": "= IDE Support\n\n[[intellij-idea]]\n== IntelliJ IDEA\n\nIntelliJ IDEA supports running tests on the JUnit Platform since version 2016.2. For more\ninformation, please consult this https://jb.gg/junit-idea/[IntelliJ IDEA resource]. Note,\nhowever, that it is recommended to use IDEA 2017.3 or newer since more recent versions of\nIDEA download the following JARs automatically based on the API version used in the\nproject: `junit-platform-launcher`, `junit-jupiter-engine`, and `junit-vintage-engine`.\n\nIn order to use a different JUnit version (e.g., {version}), you may need to\ninclude the corresponding versions of the `junit-platform-launcher`,\n`junit-jupiter-engine`, and `junit-vintage-engine` JARs in the classpath.\n\n.Additional Gradle Dependencies\n[source,groovy]\n[subs=attributes+]\n----\ntestImplementation(platform(\"org.junit:junit-bom:{version}\"))\ntestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\ntestRuntimeOnly(\"org.junit.jupiter:junit-jupiter-engine\")\ntestRuntimeOnly(\"org.junit.vintage:junit-vintage-engine\")\n----\n\n.Additional Maven Dependencies\n[source,xml]\n[subs=attributes+]\n----\n<!-- ... -->\n<dependencies>\n\t<dependency>\n\t\t<groupId>org.junit.platform</groupId>\n\t\t<artifactId>junit-platform-launcher</artifactId>\n\t\t<scope>test</scope>\n\t</dependency>\n\t<dependency>\n\t\t<groupId>org.junit.jupiter</groupId>\n\t\t<artifactId>junit-jupiter-engine</artifactId>\n\t\t<scope>test</scope>\n\t</dependency>\n\t<dependency>\n\t\t<groupId>org.junit.vintage</groupId>\n\t\t<artifactId>junit-vintage-engine</artifactId>\n\t\t<scope>test</scope>\n\t</dependency>\n</dependencies>\n<dependencyManagement>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.junit</groupId>\n\t\t\t<artifactId>junit-bom</artifactId>\n\t\t\t<version>{version}</version>\n\t\t\t<type>pom</type>\n\t\t\t<scope>import</scope>\n\t\t</dependency>\n\t</dependencies>\n</dependencyManagement>\n----\n\n[[eclipse]]\n== Eclipse\n\nEclipse IDE offers support for the JUnit Platform since the Eclipse Oxygen.1a (4.7.1a)\nrelease.\n\nFor more information on using JUnit Platform in Eclipse consult the official _Eclipse\nsupport for JUnit 5_ section of the\nhttps://www.eclipse.org/eclipse/news/4.7.1a/#junit-5-support[Eclipse Project Oxygen.1a\n(4.7.1a) - New and Noteworthy] documentation.\n\n[[netbeans]]\n== NetBeans\n\nNetBeans offers support for JUnit Jupiter and the JUnit Platform since the\nhttps://netbeans.apache.org/download/nb100/nb100.html[Apache NetBeans 10.0 release].\n\nFor more information consult the JUnit 5 section of the\nhttps://netbeans.apache.org/download/nb100/index.html#_junit_5[Apache NetBeans 10.0\nrelease notes].\n\n[[vscode]]\n== Visual Studio Code\n\nhttps://code.visualstudio.com/[Visual Studio Code] supports JUnit Jupiter and the JUnit\nPlatform via the\nhttps://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-test[Java Test\nRunner] extension which is installed by default as part of the\nhttps://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack[Java\nExtension Pack].\n\nFor more information consult the _Testing_ section of the\nhttps://code.visualstudio.com/docs/languages/java#_testing[Java in Visual Studio Code]\ndocumentation.\n\n[[other]]\n== Other IDEs\n\nIf you are using an editor or IDE other than one of those listed in the previous sections,\nand it doesn't support running tests on the JUnit Platform, you can use the\nxref:running-tests/console-launcher.adoc[] to run them from the command line.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/intro.adoc",
    "content": "= Running Tests\n\nThis section explains how to run tests from IDEs and build tools.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/source-launcher.adoc",
    "content": "= Source Launcher\n\nStarting with Java 25 it is possible to write minimal source code test programs\nusing the `org.junit.start` module.\n\nFor example, create a `HelloTests.java` file with the following content:\n\n[source,java]\n----\nimport module org.junit.start;\n\nvoid main() {\n  JUnit.run();\n}\n\n@Test\nvoid stringLength() {\n  Assertions.assertEquals(11, \"Hello JUnit\".length());\n}\n----\n\nWith all required modular JAR files available in a local `lib/` directory, the\nfollowing Java 25+ command will discover and execute tests using the JUnit Platform.\nIt will also print the result tree to the console.\n\n[source,shell]\n----\njava --module-path lib --add-modules org.junit.start HelloTests.java\n╷\n└─ JUnit Jupiter ✔\n   └─ HelloTests ✔\n      └─ stringLength() ✔\n----\n\nFor details, please refer to the Javadoc of the `{JUnit}` class.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/stack-trace-pruning.adoc",
    "content": "= Stack Trace Pruning\n\nThe JUnit Platform provides built-in support for pruning stack traces produced by failing\ntests. This feature is enabled by default but can be disabled by setting the\n`junit.platform.stacktrace.pruning.enabled` _configuration parameter_ to `false`.\n\nWhen enabled, all calls from the `org.junit`, `jdk.internal.reflect`, and `sun.reflect`\npackages are removed from the stack trace, unless the calls occur after the test itself\nor any of its ancestors. For that reason, calls to `{Assertions}` or `{Assumptions}` will\nnever be excluded.\n\nIn addition, all elements prior to and including the first call from the JUnit Platform\n`Launcher` will be removed.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/tags.adoc",
    "content": "= Tags\n\nTags are a JUnit Platform concept for marking and filtering tests. The programming model\nfor adding tags to containers and tests is defined by the testing framework. For example,\nin JUnit Jupiter based tests, the `@Tag` annotation (see\nxref:writing-tests/tagging-and-filtering.adoc[]) should be used. For JUnit 4 based tests, the\nVintage engine maps `@Category` annotations to tags (see\nxref:migrating-from-junit4.adoc#categories-support[Categories Support]). Other testing frameworks may define their\nown annotation or other means for users to specify tags.\n\n[[syntax-rules]]\n== Syntax Rules for Tags\n\nRegardless how a tag is specified, the JUnit Platform enforces the following rules:\n\n* A tag must not be `null` or _blank_.\n* A _stripped_ tag must not contain whitespace.\n* A _stripped_ tag must not contain ISO control characters.\n* A _stripped_ tag must not contain any of the following _reserved characters_.\n- `,`: _comma_\n- `(`: _left parenthesis_\n- `)`: _right parenthesis_\n- `&`: _ampersand_\n- `|`: _vertical bar_\n- `!`: _exclamation point_\n\nNOTE: In the above context, \"stripped\" means that leading and trailing whitespace\ncharacters have been removed using `java.lang.String.strip()`.\n\n[[expressions]]\n== Tag Expressions\n\nTag expressions are boolean expressions with the operators `!`, `&` and `|`. In addition,\n`(` and `)` can be used to adjust for operator precedence.\n\nTwo special expressions are supported, `any()` and `none()`, which select all tests _with_\nany tags at all, and all tests _without_ any tags, respectively.\nThese special expressions may be combined with other expressions just like normal tags.\n\n.Operators (in descending order of precedence)\n|===\n| Operator | Meaning | Associativity\n\n| `!`      | not     | right\n| `&`      | and     | left\n| `\\|`     | or      | left\n|===\n\nIf you are tagging your tests across multiple dimensions, tag expressions help you to\nselect which tests to execute. When tagging by test type (e.g., _micro_, _integration_,\n_end-to-end_) and feature (e.g., *product*, *catalog*, *shipping*), the following tag\nexpressions can be useful.\n\n[%header,cols=\"40,60\"]\n|===\n| Tag Expression\n| Selection\n\n| `+++product+++`\n| all tests for *product*\n\n| `+++catalog \\| shipping+++`\n| all tests for *catalog* plus all tests for *shipping*\n\n| `+++catalog &amp; shipping+++`\n| all tests for the intersection between *catalog* and *shipping*\n\n| `+++product &amp; !end-to-end+++`\n| all tests for *product*, but not the _end-to-end_ tests\n\n| `+++(micro \\| integration) &amp; (product \\| shipping)+++`\n| all _micro_ or _integration_ tests for *product* or *shipping*\n|===\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/running-tests/using-listeners-and-interceptors.adoc",
    "content": "= Using Listeners and Interceptors\n\nThe JUnit Platform provides the following listener APIs that allow JUnit, third parties,\nand custom user code to react to events fired at various points during the discovery and\nexecution of a `TestPlan`.\n\n* `{LauncherSessionListener}`: receives events when a `{LauncherSession}` is opened and\n  closed.\n* `{LauncherInterceptor}`: intercepts test discovery and execution in the context of a\n  `LauncherSession`.\n* `{LauncherDiscoveryListener}`: receives events that occur during test discovery.\n* `{TestExecutionListener}`: receives events that occur during test execution.\n\nThe `LauncherSessionListener` API is typically implemented by build tools or IDEs and\nregistered automatically for you in order to support some feature of the build tool or IDE.\n\nThe `LauncherDiscoveryListener` and `TestExecutionListener` APIs are often implemented in\norder to produce some form of report or to display a graphical representation of the test\nplan in an IDE. Such listeners may be implemented and automatically registered by a build\ntool or IDE, or they may be included in a third-party library – potentially registered\nfor you automatically. You can also implement and register your own listeners.\n\nFor details on registering and configuring listeners, see the following sections of this\nguide.\n\n* xref:advanced-topics/launcher-api.adoc#launcher-session-listeners-custom[Registering a LauncherSessionListener]\n* xref:advanced-topics/launcher-api.adoc#launcher-interceptors-custom[Registering a LauncherInterceptor]\n* xref:advanced-topics/launcher-api.adoc#launcher-discovery-listeners-custom[Registering a LauncherDiscoveryListener]\n* xref:advanced-topics/launcher-api.adoc#listeners-custom[Registering a TestExecutionListener]\n* xref:advanced-topics/launcher-api.adoc#listeners-config[Configuring a TestExecutionListener]\n* xref:advanced-topics/launcher-api.adoc#listeners-custom-deactivation[Deactivating a TestExecutionListener]\n\nThe JUnit Platform provides the following listeners which you may wish to use with your\ntest suite.\n\nxref:advanced-topics/junit-platform-reporting.adoc[] ::\n  `{LegacyXmlReportGeneratingListener}` can be used via the\n  xref:running-tests/console-launcher.adoc[] or registered manually to generate XML reports\n  compatible with the de facto standard for JUnit 4 based test reports.\n+\n`{OpenTestReportGeneratingListener}` generates an XML report in the event-based format\nspecified by {OpenTestReporting}. It is auto-registered and can be enabled and\nconfigured via xref:running-tests/configuration-parameters.adoc[].\n+\nSee xref:advanced-topics/junit-platform-reporting.adoc[] for details.\n\n<<recorder>> ::\n  `FlightRecordingExecutionListener` and `FlightRecordingDiscoveryListener` that generate\n  Java Flight Recorder events during test discovery and execution.\n\n`{LoggingListener}` ::\n  `TestExecutionListener` for logging informational messages for all events via a\n  `BiConsumer` that consumes `Throwable` and `Supplier<String>`.\n\n`{SummaryGeneratingListener}` ::\n  `TestExecutionListener` that generates a summary of the test execution which can be\n  printed via a `PrintWriter`.\n\n`{UniqueIdTrackingListener}` ::\n  `TestExecutionListener` that that tracks the unique IDs of all tests that were skipped\n  or executed during the execution of the `TestPlan` and generates a file containing the\n  unique IDs once execution of the `TestPlan` has finished.\n\n[[recorder]]\n== Flight Recorder Support\n\nThe JUnit Platform provides opt-in support for generating Flight Recorder events.\nhttps://openjdk.java.net/jeps/328[JEP 328] describes the Java Flight Recorder (JFR) as\nfollows.\n\n> Flight Recorder records events originating from applications, the JVM, and the OS.\nEvents are stored in a single file that can be attached to bug reports and examined by\nsupport engineers, allowing after-the-fact analysis of issues in the period leading up\nto a problem.\n\nIn order to record Flight Recorder events generated while running tests, you need to\nstart flight recording when launching a test suite via the following java command line\noption.\n\n   -XX:StartFlightRecording:filename=...\n\nPlease consult the manual of your build tool for the appropriate commands.\n\nTo analyze the recorded events, use the\nhttps://docs.oracle.com/en/java/javase/17/docs/specs/man/jfr.html[jfr]\ncommand line tool shipped with recent JDKs or open the recording file with\nhttps://jdk.java.net/jmc/[JDK Mission Control].\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/annotations.adoc",
    "content": "= Annotations\n\nJUnit Jupiter supports the following annotations for configuring tests and extending the\nframework.\n\nUnless otherwise stated, all core annotations are located in the `{api-package}` package\nin the `junit-jupiter-api` module.\n\n`*@Test*`:: Denotes that a method is a test method. Unlike JUnit 4's `@Test` annotation,\nthis annotation does not declare any attributes, since test extensions in JUnit Jupiter\noperate based on their own dedicated annotations. Such methods are inherited unless they\nare overridden.\n\n`*@ParameterizedTest*`:: Denotes that a method is a\nxref:writing-tests/parameterized-classes-and-tests.adoc[parameterized test]. Such methods are inherited\nunless they are overridden.\n\n`*@RepeatedTest*`:: Denotes that a method is a test template for a\nxref:writing-tests/repeated-tests.adoc[repeated test]. Such methods are inherited unless they\nare overridden.\n\n`*@TestFactory*`:: Denotes that a method is a test factory for\nxref:writing-tests/dynamic-tests.adoc[dynamic tests]. Such methods are inherited unless they are\noverridden.\n\n`*@TestTemplate*`:: Denotes that a method is a\nxref:writing-tests/test-templates.adoc[template for a test case] designed to be invoked multiple\ntimes depending on the number of invocation contexts returned by the registered\nxref:extensions/providing-invocation-contexts-for-test-templates.adoc[providers]. Such methods are inherited unless they are\noverridden.\n\n`*@TestClassOrder*`:: Used to configure the\nxref:writing-tests/test-execution-order.adoc#classes[test class execution order] for `@Nested`\ntest classes in the annotated test class. Such annotations are inherited.\n\n`*@TestMethodOrder*`:: Used to configure the\nxref:writing-tests/test-execution-order.adoc#methods[test method execution order] for the\nannotated test class; similar to JUnit 4's `@FixMethodOrder`. Such annotations are\ninherited.\n\n`*@TestInstance*`:: Used to configure the\nxref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] for the annotated test\nclass. Such annotations are inherited.\n\n`*@DisplayName*`:: Declares a custom xref:writing-tests/display-names.adoc[display name] for the\ntest class or test method. Such annotations are not inherited.\n\n`*@DisplayNameGeneration*`:: Declares a custom\nxref:writing-tests/display-names.adoc#generator[display name generator] for the test class. Such\nannotations are inherited.\n\n`*@BeforeEach*`:: Denotes that the annotated method should be executed _before_ *each*\n`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current\nclass; analogous to JUnit 4's `@Before`. Such methods are inherited unless they are\noverridden.\n\n`*@AfterEach*`:: Denotes that the annotated method should be executed _after_ *each*\n`@Test`, `@RepeatedTest`, `@ParameterizedTest`, or `@TestFactory` method in the current\nclass; analogous to JUnit 4's `@After`. Such methods are inherited unless they are\noverridden.\n\n`*@BeforeAll*`:: Denotes that the annotated method should be executed _before_ *all*\n`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current\ntop-level or `@Nested` test class; analogous to JUnit 4's `@BeforeClass`. Such methods are\ninherited unless they are overridden and must be `static` unless the \"per-class\"\nxref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is used.\n\n`*@AfterAll*`:: Denotes that the annotated method should be executed _after_ *all*\n`@Test`, `@RepeatedTest`, `@ParameterizedTest`, and `@TestFactory` methods in the current\ntop-level or `@Nested` test class; analogous to JUnit 4's `@AfterClass`. Such methods are\ninherited unless they are overridden and must be `static` unless the \"per-class\"\nxref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] is used.\n\n`*@ParameterizedClass*`:: Denotes that the annotated class is a\nxref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such annotations are\ninherited.\n\n`*@BeforeParameterizedClassInvocation*`:: Denotes that the annotated method should be\nexecuted once _before_ each invocation of a\nxref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such methods are inherited\nunless they are overridden.\n\n`*@AfterParameterizedClassInvocation*`:: Denotes that the annotated method should be\nexecuted once _after_ each invocation of a\nxref:writing-tests/parameterized-classes-and-tests.adoc[parameterized class]. Such methods are inherited\nunless they are overridden.\n\n`*@ClassTemplate*`:: Denotes that the annotated class is a\nxref:writing-tests/class-templates.adoc[template for a test class] designed to be executed\nmultiple times depending on the number of invocation contexts returned by the registered\nxref:extensions/providing-invocation-contexts-for-class-templates.adoc[providers]. Such annotations are inherited.\n\n`*@Nested*`:: Denotes that the annotated class is a non-static\nxref:writing-tests/nested-tests.adoc[nested test class]. Such annotations are not inherited.\n\n`*@Tag*`:: Used to declare\nxref:writing-tests/tagging-and-filtering.adoc[tags for filtering tests], either at the class or\nmethod level; analogous to test groups in TestNG or Categories in JUnit 4. Such\nannotations are inherited at the class level but not at the method level.\n\n`*@Disabled*`:: Used to xref:writing-tests/disabling-tests.adoc[disable] a test class or test method;\nanalogous to JUnit 4's `@Ignore`. Such annotations are not inherited.\n\n`*@AutoClose*`:: Denotes that the annotated field represents a resource that will be\nxref:writing-tests/built-in-extensions.adoc#AutoClose[automatically closed] after test\nexecution. Such fields are inherited.\n\n`*@Timeout*`:: Used to fail a test, test factory, test template, or lifecycle method if\nits execution exceeds a given duration. Such annotations are inherited.\n\n`*@TempDir*`:: Used to supply a\nxref:writing-tests/built-in-extensions.adoc#TempDirectory[temporary directory] via field\ninjection or parameter injection in a test class constructor, lifecycle method, or test\nmethod; located in the `org.junit.jupiter.api.io` package. Such fields are inherited.\n\n`*@ExtendWith*`:: Used to\nxref:extensions/registering-extensions.adoc#registration-declarative[register extensions declaratively]. Such\nannotations are inherited.\n\n`*@RegisterExtension*`:: Used to\nxref:extensions/registering-extensions.adoc#registration-programmatic[register extensions programmatically] via fields.\nSuch fields are inherited.\n\nWARNING: Some annotations may currently be _experimental_. Consult the table in\nxref:api-evolution.adoc#experimental-apis[Experimental APIs] for details.\n\n[[annotations]]\n== Meta-Annotations and Composed Annotations\n\nJUnit Jupiter annotations can be used as _meta-annotations_. That means that you can\ndefine your own _composed annotation_ that will automatically _inherit_ the semantics of\nits meta-annotations.\n\nFor example, instead of copying and pasting `@Tag(\"fast\")` throughout your code base (see\nxref:writing-tests/tagging-and-filtering.adoc[]), you can create a custom _composed annotation_\nnamed `@Fast` as follows. `@Fast` can then be used as a drop-in replacement for\n`@Tag(\"fast\")`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/Fast.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/Fast.kt[tags=user_guide]\n----\n--\n====\n\nThe following `@Test` method demonstrates usage of the `@Fast` annotation.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@Fast\n@Test\nvoid myFastTest() {\n    // ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@Fast\n@Test\nfun myFastTest() {\n    // ...\n}\n----\n--\n====\n\nYou can even take that one step further by introducing a custom `@FastTest` annotation\nthat can be used as a drop-in replacement for `@Tag(\"fast\")` _and_ `@Test`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/FastTest.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/FastTest.kt[tags=user_guide]\n----\n--\n====\n\nJUnit automatically recognizes the following as a `@Test` method that is tagged with\n\"fast\".\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\n@FastTest\nvoid myFastTest() {\n    // ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@FastTest\nfun myFastTest() {\n    // ...\n}\n----\n--\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/assertions.adoc",
    "content": "= Assertions\n\nJUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few\nthat lend themselves well to being used with Java lambdas. All JUnit Jupiter assertions\nare `static` methods in the `{Assertions}` class.\n\nAssertion methods optionally accept the assertion message as their third parameter, which\ncan be either a `String` or a `Supplier<String>`.\n\nWhen using a `Supplier<String>` (e.g., a lambda expression), the message is evaluated\nlazily. This can provide a performance benefit, especially if message construction is\ncomplex or time-consuming, as it is only evaluated when the assertion fails.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/AssertionsDemo.java[tags=user_guide]\n----\n\n[[preemptive-timeouts]]\n[WARNING]\n.Preemptive Timeouts with `assertTimeoutPreemptively()`\n====\nThe various `assertTimeoutPreemptively()` methods in the `Assertions` class execute\nthe provided `executable` or `supplier` in a different thread than that of the calling\ncode. This behavior can lead to undesirable side effects if the code that is executed\nwithin the `executable` or `supplier` relies on `java.lang.ThreadLocal` storage.\n\nOne common example of this is the transactional testing support in the Spring Framework.\nSpecifically, Spring's testing support binds transaction state to the current thread (via\na `ThreadLocal`) before a test method is invoked. Consequently, if an `executable` or\n`supplier` provided to `assertTimeoutPreemptively()` invokes Spring-managed components\nthat participate in transactions, any actions taken by those components will not be rolled\nback with the test-managed transaction. On the contrary, such actions will be committed to\nthe persistent store (e.g., relational database) even though the test-managed transaction\nis rolled back.\n\nSimilar side effects may be encountered with other frameworks that rely on\n`ThreadLocal` storage.\n====\n\n[[kotlin]]\n== Kotlin Assertion Support\n\nJUnit Jupiter also comes with a few assertion methods that lend themselves well to being\nused in https://kotlinlang.org/[Kotlin]. All JUnit Jupiter Kotlin assertions are top-level\nfunctions in the `org.junit.jupiter.api` package.\n\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/KotlinAssertionsDemo.kt[tags=user_guide]\n----\n\n[[third-party]]\n== Third-party Assertion Libraries\n\nEven though the assertion facilities provided by JUnit Jupiter are sufficient for many\ntesting scenarios, there are times when more power and additional functionality are\ndesired or required. In such cases, the JUnit team recommends the use of third-party\nassertion libraries such as {AssertJ}, {Hamcrest}, {Truth}, etc. Developers are therefore\nfree to use the assertion library of their choice.\n\nFor example, the following demonstrates how to use the `assertThat()` support from AssertJ\nin a JUnit Jupiter test. As long as the AssertJ library has been added to the classpath,\nyou can statically import methods such as `assertThat()`, `assertThatException()`, etc.\nfrom `org.assertj.core.api.Assertions` and then use them in tests like in the\n`assertWithAssertJ()` method below.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/AssertJAssertionsDemo.java[tags=user_guide]\n----\n\n[TIP]\n.Excluding Jupiter’s Assertions From a Project’s Classpath\n====\nIf you would like to enforce that all your tests use a certain third-party assertion\nlibrary instead of Jupiter's, you can set up a rule using {Checkstyle} or another static\nanalysis tool that fails the build if Jupiter's `Assertions` class is used.\n\n[source,xml]\n----\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\" \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n\t<property name=\"severity\" value=\"error\" />\n\t<module name=\"TreeWalker\">\n    <module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"jupiterAssertions\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"org\\.junit\\.jupiter\\.api\\.(Assertions|Assumptions)\\.\"/>\n\t\t\t<property name=\"message\" value=\"Jupiter Assertions/Assumptions should not be used in this project. Please use ... instead.\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t</module>\n</module>\n----\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/assumptions.adoc",
    "content": "= Assumptions\n\nAssumptions are typically used whenever it does not make sense to continue execution of a\ngiven test — for example, if the test depends on something that does not exist in the\ncurrent runtime environment.\n\n* When an assumption is valid, the assumption method does not throw an exception, and\n  execution of the test continues as usual.\n* When an assumption is invalid, the assumption method throws an exception of type\n  `org.opentest4j.TestAbortedException` to signal that the test should be aborted instead\n  of marked as a failure.\n\nJUnit Jupiter comes with a subset of the _assumption_ methods that JUnit 4 provides and\nadds a few that lend themselves well to being used with Java lambda expressions and method\nreferences.\n\nAll JUnit Jupiter assumptions are static methods in the `{Assumptions}` class.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/AssumptionsDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/AssumptionsDemo.kt[tags=user_guide]\n----\n--\n====\n\nNOTE: It is also possible to use methods from JUnit 4's `org.junit.Assume` class for\nassumptions. Specifically, JUnit Jupiter supports JUnit 4's `AssumptionViolatedException`\nto signal that a test should be aborted instead of marked as a failure.\n\nTIP: If you use AssertJ for assertions, you may also wish to use AssertJ for assumptions.\nTo do so, you can statically import the `assumeThat()` method from\n`org.assertj.core.api.Assumptions` and then use AssertJ's fluent API to specify your\nassumptions.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/built-in-extensions.adoc",
    "content": "= Built-in Extensions\n\nWhile the JUnit team encourages reusable extensions to be packaged and maintained in\nseparate libraries, JUnit Jupiter includes a few user-facing extension implementations\nthat are considered so generally useful that users shouldn't have to add another\ndependency.\n\n[[TempDirectory]]\n== The @TempDir Extension\n\nThe `{TempDirectory}` extension is used to create and clean up a temporary\ndirectory for an individual test or all tests in a test class. It is registered by\ndefault. To use it, annotate a non-final, unassigned field of type `java.nio.file.Path` or\n`java.io.File` with `{TempDir}` or add a parameter of type `java.nio.file.Path` or\n`java.io.File` annotated with `@TempDir` to a test class constructor, lifecycle method, or\ntest method.\n\nFor example, the following test declares a parameter annotated with `@TempDir` for a\nsingle test method, creates and writes to a file in the temporary directory, and checks\nits content.\n\n.A test method that requires a temporary directory\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_parameter_injection]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_parameter_injection]\n----\n--\n====\n\nYou can inject multiple temporary directories by specifying multiple annotated parameters.\n\n.A test method that requires multiple temporary directories\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_multiple_directories]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_multiple_directories]\n----\n--\n====\n\nThe following example stores a _shared_ temporary directory in a `static` field. This\nallows the same `sharedTempDir` to be used in all lifecycle methods and test methods of\nthe test class. For better isolation, you should use an instance field or constructor\ninjection so that each test method uses a separate directory.\n\n.A test class that shares a temporary directory across test methods\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_field_injection]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_field_injection]\n----\n--\n====\n\nThe `@TempDir` annotation has an optional `cleanup` attribute that can be set to either\n`NEVER`, `ON_SUCCESS`, or `ALWAYS`. If the cleanup mode is set to `NEVER`, the temporary\ndirectory will not be deleted after the test completes. If it is set to `ON_SUCCESS`, the\ntemporary directory will only be deleted after the test if the test completed successfully.\n\nThe default cleanup mode is `ALWAYS`. You can use the\n`junit.jupiter.tempdir.cleanup.mode.default`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to override this default.\n\n.A test class with a temporary directory that doesn't get cleaned up\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_cleanup_mode]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_cleanup_mode]\n----\n--\n====\n\n[[TempDirFactory]]\n=== Factories\n\n`@TempDir` supports the programmatic creation of temporary directories via the optional\n`factory` attribute. This is typically used to gain control over the temporary directory\ncreation, like defining the parent directory or the file system that should be used.\n\nFactories can be created by implementing `{TempDirFactory}`. Implementations must provide a\nno-args constructor and should not make any assumptions regarding when and how many times\nthey are instantiated, but they can assume that their `createTempDirectory(...)` and\n`close()` methods will both be called once per instance, in this order, and from the same\nthread.\n\nThe default implementation available in Jupiter delegates directory creation to\n`java.nio.file.Files::createTempDirectory` which uses the default file system and the\nsystem's temporary directory as the parent directory. It passes `junit-` as the prefix\nstring of the generated directory name to help identify it as a created by JUnit.\n\nThe following example defines a factory that uses the test name as the directory name\nprefix instead of the `junit` constant value.\n\n.A test class with a temporary directory having the test name as the directory name prefix\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_name_prefix]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_factory_name_prefix]\n----\n--\n====\n\nIt is also possible to use an in-memory file system like `{Jimfs}` for the creation of the\ntemporary directory. The following example demonstrates how to achieve that.\n\n.A test class with a temporary directory created with the Jimfs in-memory file system\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_factory_jimfs]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_factory_jimfs]\n----\n--\n====\n\n`@TempDir` can also be used as a xref:writing-tests/annotations.adoc#annotations[meta-annotation] to\nreduce repetition. The following code listing shows how to create a custom `@JimfsTempDir`\nannotation that can be used as a drop-in replacement for\n`@TempDir(factory = JimfsTempDirFactory.class)`.\n\n.A custom annotation meta-annotated with `@TempDir`\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_composed_annotation]\n----\n--\n====\n\nThe following example demonstrates how to use the custom `@JimfsTempDir` annotation.\n\n.A test class using the custom annotation\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_composed_annotation_usage]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_composed_annotation_usage]\n----\n--\n====\n\nMeta-annotations or additional annotations on the field or parameter the `TempDir`\nannotation is declared on might expose additional attributes to configure the factory.\nSuch annotations and related attributes can be accessed via the `AnnotatedElementContext`\nparameter of the `createTempDirectory(...)` method.\n\nYou can use the `junit.jupiter.tempdir.factory.default`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the\nfully qualified class name of the `{TempDirFactory}` you would like to use by default. Just\nlike for factories configured via the `factory` attribute of the `@TempDir` annotation,\nthe supplied class has to implement the `{TempDirFactory}` interface. The default factory\nwill be used for all `@TempDir` annotations unless the `factory` attribute of the\nannotation specifies a different factory.\n\nIn summary, the factory for a temporary directory is determined according to the following\nprecedence rules:\n\n1. The `factory` attribute of the `@TempDir` annotation, if present\n2. The default `{TempDirFactory}` configured via the configuration\nparameter, if present\n3. Otherwise, `org.junit.jupiter.api.io.TempDirFactory$Standard` will be used.\n\n[[TempDirDeletionStrategy]]\n=== Deletion\n\n`@TempDir` supports the programmatic deletion of temporary directories via the optional\n`deletionStrategy` attribute. This is typically used to gain control over what happens\nwhen deletion of a file or directory fails.\n\nDeletion strategies can be created by implementing `{TempDirDeletionStrategy}`.\nImplementations must provide a no-args constructor.\n\nJupiter ships with two built-in deletion strategies:\n\n* `{TempDirDeletionStrategyStandard}` (the default): attempts to delete all files and\n  directories recursively, retrying with permission resets on failure. Paths that still\n  cannot be deleted are scheduled for deletion on JVM exit, if possible. Additionally,\n  the test is failed.\n* `{TempDirDeletionStrategyIgnoreFailures}`: delegates to `{TempDirDeletionStrategyStandard}`\n  but suppresses deletion failures by logging a warning instead of failing the test.\n\nThe following example uses `{TempDirDeletionStrategyIgnoreFailures}` so that any deletion\nfailures are only logged.\n\n.A test class with a temporary directory that ignores deletion failures\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TempDirectoryDemo.java[tags=user_guide_deletion_strategy]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TempDirectoryDemo.kt[tags=user_guide_deletion_strategy]\n----\n--\n====\n\nYou can use the `junit.jupiter.tempdir.deletion.strategy.default`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the\nfully qualified class name of the `{TempDirDeletionStrategy}` you would like to use by\ndefault. Just like for strategies configured via the `deletionStrategy` attribute of the\n`@TempDir` annotation, the supplied class has to implement the `{TempDirDeletionStrategy}`\ninterface. The default strategy will be used for all `@TempDir` annotations unless the\n`deletionStrategy` attribute of the annotation specifies a different strategy.\n\nIn summary, the deletion strategy for a temporary directory is determined according to the\nfollowing precedence rules:\n\n1. The `deletionStrategy` attribute of the `@TempDir` annotation, if present\n2. The default `{TempDirDeletionStrategy}` configured via the configuration parameter,\nif present\n3. Otherwise, `org.junit.jupiter.api.io.TempDirDeletionStrategy$Standard` will be used.\n\n[[AutoClose]]\n== The @AutoClose Extension\n\nThe `{AutoCloseExtension}` automatically closes resources associated with fields.\nIt is registered by default. To use it, annotate a field in a test class with\n`{AutoClose}`.\n\n`@AutoClose` fields may be either `static` or non-static. If the value of an `@AutoClose`\nfield is `null` when it is evaluated the field will be ignored, but a warning message will\nbe logged to inform you.\n\nBy default, `@AutoClose` expects the value of the annotated field to implement a `close()`\nmethod that will be invoked to close the resource. However, developers can customize the\nname of the close method via the `value` attribute. For example, `@AutoClose(\"shutdown\")`\ninstructs JUnit to look for a `shutdown()` method to close the resource.\n\n`@AutoClose` fields are inherited from superclasses. Furthermore, `@AutoClose` fields from\nsubclasses will be closed before `@AutoClose` fields in superclasses.\n\nWhen multiple `@AutoClose` fields exist within a given test class, the order in which the\nresources are closed depends on an algorithm that is deterministic but intentionally\nnonobvious. This ensures that subsequent runs of a test suite close resources in the same\norder, thereby allowing for repeatable builds.\n\nThe `AutoCloseExtension` implements the `AfterAllCallback` and\n`TestInstancePreDestroyCallback` extension APIs. Consequently, a `static` `@AutoClose`\nfield will be closed after all tests in the current test class have completed, effectively\nafter `@AfterAll` methods have executed for the test class. A non-static `@AutoClose`\nfield will be closed before the current test class instance is destroyed. Specifically, if\nthe test class is configured with `@TestInstance(Lifecycle.PER_METHOD)` semantics, a\nnon-static `@AutoClose` field will be closed after the execution of each test method, test\nfactory method, or test template method. However, if the test class is configured with\n`@TestInstance(Lifecycle.PER_CLASS)` semantics, a non-static `@AutoClose` field will not\nbe closed until the current test class instance is no longer needed, which means after\n`@AfterAll` methods and after all `static` `@AutoClose` fields have been closed.\n\nThe following example demonstrates how to annotate an instance field with `@AutoClose` so\nthat the resource is automatically closed after test execution. In this example, we assume\nthat the default `@TestInstance(Lifecycle.PER_METHOD)` semantics apply.\n\n.A test class using `@AutoClose` to close a resource\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/AutoCloseDemo.java[tags=user_guide_example]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/AutoCloseDemo.kt[tags=user_guide_example]\n----\n--\n====\n<1> Annotate an instance field with `@AutoClose`.\n<2> `WebClient` implements `java.lang.AutoCloseable` which defines a `close()` method that\n    will be invoked after each `@Test` method.\n\n[[DefaultLocaleAndTimeZone]]\n== The @DefaultLocale and @DefaultTimeZone Extensions\n\nThe `{DefaultLocale}` and `{DefaultTimeZone}` annotations can be used to change the values\nreturned from `Locale.getDefault()` and `TimeZone.getDefault()`, respectively, which are\noften used implicitly when no specific locale or time zone is chosen. Both annotations\nwork on the test class level and on the test method level, and are inherited from\nhigher-level containers. After the annotated element has been executed, the initial\ndefault value is restored.\n\n[[DefaultLocale]]\n=== @DefaultLocale\n\nThe default `Locale` can be specified using an\n{jdk-javadoc-base-url}/java.base/java/util/Locale.html#forLanguageTag-java.lang.String-[IETF BCP 47 language tag string].\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tags=default_locale_language]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tags=default_locale_language]\n----\n--\n====\n\nAlternatively, the default `Locale` can be created using the following attributes from\nwhich a {jdk-javadoc-base-url}/java.base/java/util/Locale.Builder.html[`Locale.Builder`]\ncan create an instance:\n\n* `language`\n* `language` and `country`\n* `language`, `country`, and `variant`\n\nNOTE: The variant needs to be a string which follows the\nhttps://www.rfc-editor.org/rfc/rfc5646.html[IETF BCP 47 / RFC 5646] syntax.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_language_alternatives]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_language_alternatives]\n----\n--\n====\n\nMixing language tag configuration (via the annotation's `value` attribute) and\nattribute-based configuration will cause an exception to be thrown. Furthermore, a\n`variant` can only be specified if `country` is also specified. Otherwise, an exception\nwill be thrown.\n\nMethod-level `@DefaultLocale` configuration overrides class-level configuration.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_class_level]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_class_level]\n----\n--\n====\n\nNOTE: With class-level configuration, the specified locale is set before and reset after\neach individual test in the annotated class.\n\nIf your use case is not covered, you can implement the `{LocaleProvider}` interface.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_locale_with_provider]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_locale_with_provider]\n----\n--\n====\n\nNOTE: The provider implementation must have a no-args (or default) constructor.\n\n[[DefaultTimeZone]]\n=== @DefaultTimeZone\n\nThe default `TimeZone` is specified according to the\n{jdk-javadoc-base-url}/java.base/java/util/TimeZone.html#getTimeZone(java.lang.String)[TimeZone.getTimeZone(String)]\nmethod.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_zone]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_timezone_zone]\n----\n--\n====\n\nMethod-level `@DefaultTimeZone` configuration overrides class-level configuration.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_timezone_class_level]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_timezone_class_level]\n----\n--\n====\n\nNOTE: With class-level configuration, the specified time zone is set before and reset\nafter each individual test in the annotated class.\n\nIf your use case is not covered, you can implement the `{TimeZoneProvider}` interface.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DefaultLocaleTimezoneExtensionDemo.java[tag=default_time_zone_with_provider]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt[tag=default_time_zone_with_provider]\n----\n--\n====\n\nNOTE: The provider implementation must have a no-args (or default) constructor.\n\n=== Thread Safety\n\nSince the default locale and time zone are global state, reading and writing them during\nxref:writing-tests/parallel-execution.adoc[parallel test execution] can lead to unpredictable\nresults and flaky tests. The `@DefaultLocale` and `@DefaultTimeZone` extensions are\nprepared for that and tests annotated with them will never execute in parallel (thanks to\n`{ResourceLock}`) to guarantee correct test results.\n\nHowever, this does not cover all possible cases. Tested code that reads or writes the\ndefault locale or time zone _independently_ of the extensions can still run in parallel\nand may thus behave erratically when, for example, such code unexpectedly reads a locale\nset by the extension in another thread. Consequently, tests that cover code that reads or\nwrites the default locale or time zone need to be annotated with one of the following\nrespective annotations.\n\n* `{ReadsDefaultLocale}`\n* `{ReadsDefaultTimeZone}`\n* `{WritesDefaultLocale}`\n* `{WritesDefaultTimeZone}`\n\nTests annotated with one of the above annotations will never execute in parallel with\ntests annotated with `@DefaultLocale` or `@DefaultTimeZone`.\n\n[[system-properties]]\n== The System Properties Extension\n\nThe system properties extension supports a set of annotations that work together to clear,\nset, and restore JVM system properties.\n\n[[system-properties-clear-and-set]]\n=== @ClearSystemProperty and @SetSystemProperty\n\nThe `{ClearSystemProperty}` and `{SetSystemProperty}` annotations can be used to clear and\nset, respectively, the values of JVM system properties for test execution. Both\nannotations work on the test method and class level and are repeatable, combinable, and\ninherited from higher-level containers. After the annotated method has been executed, the\nproperties configured in the annotation will be restored to their original value or the\nvalue of the higher-level container, or will be cleared if they did not previously have a\nvalue. Other system properties that are changed during the test are _not_ restored (unless\nrestoration is <<system-properties-restore, explicitly enabled>> via\n`{RestoreSystemProperties}`).\n\nFor example, clearing a system property for test execution can be done as follows.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_clear_simple]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_clear_simple]\n----\n--\n====\n\nThe following demonstrates how to set a system property for test execution.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_set_simple]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_set_simple]\n----\n--\n====\n\nAs mentioned before, both annotations are repeatable, and they can also be combined.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_using_set_and_clear]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_using_set_and_clear]\n----\n--\n====\n\nNote that class-level configuration is overridden by method-level configuration.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_using_at_class_level]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_using_at_class_level]\n----\n--\n====\n\n[NOTE]\n====\nMethod-level configuration is visible in both `@BeforeEach` methods and `@AfterEach`\nmethods (see\nxref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#overview[user\ncode and extension code execution order]).\n\nWith class-level configuration, the specified system properties are cleared or set before\nand reset after each individual test in the annotated class.\n====\n\n[[system-properties-restore]]\n=== @RestoreSystemProperties\n\nThe `{RestoreSystemProperties}` annotation can be used to restore changes to system\nproperties made directly in the test or in the code being tested. Although\n`@ClearSystemProperty` and `@SetSystemProperty` clear or set properties and values that\nare statically declared, they do not allow property values to be calculated dynamically.\nThus, there are times you may want to directly set properties in your test code.\n`@RestoreSystemProperties` can be placed on test methods or test classes and will\ncompletely restore all system properties to their original state after the test or test\nclass has finished.\n\n[NOTE]\n====\nDuring the execution of the annotated scope, the JVM system properties are set to a clone\nof the original `Properties` object. However, the clone does not include\n{jdk-javadoc-base-url}/java.base/java/util/Properties.html#defaults[the defaults] from the\noriginal.\n\nConsequently, the extension will perform a best effort attempt to detect default properties\nand fail if any were detected. For classes that extend `Properties` it is assumed that\n`clone()` is implemented with sufficient fidelity.\n====\n\nIn the following example, `@RestoreSystemProperties` is used on a test method, ensuring\nany changes made in that method are restored.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_restore_test]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_restore_test]\n----\n--\n====\n\nWhen `@RestoreSystemProperties` is used on a test class, any changes to system properties\nduring the entire lifecycle of the test class, including test methods, `@BeforeAll`,\n`@BeforeEach`, and 'after' methods, are restored after the lifecycle of the test class is\ncomplete. In addition, the annotation is inherited by each test method just as if each one\nwere annotated with `@RestoreSystemProperties`.\n\nIn the following example, both test methods see the system property changes made in\n`@BeforeAll` and `@BeforeEach`; however, the test methods are isolated from each other\n(`isolatedTest2` does not _see_ changes made in `isolatedTest1`). As shown in the second\nexample below, the class-level `@RestoreSystemProperties` annotation ensures that system\nproperty changes made within the annotated class are completely restored after the class's\nlifecycle, ensuring that changes are not visible to `SomeOtherTestClass`. Note that\n`SomeOtherTestClass` uses the `@ReadsSystemProperty` annotation, which ensures that JUnit\ndoes not schedule the class to run during any test known to modify system properties (see\n<<system-properties-thread-safety>>).\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_class_restore_setup]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_class_restore_setup]\n----\n--\n====\n\nSome other test class, running later:\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_class_restore_isolated_class]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_class_restore_isolated_class]\n----\n--\n====\n\n[[system-properties-combined-usage]]\n=== Combining @ClearSystemProperty, @SetSystemProperty, and @RestoreSystemProperties\n\nThe three system property annotations can be combined, which can be useful when some\nsystem properties are set dynamically in code and others are not. For instance, imagine\nyou need to test an image generation utility that takes configuration from system\nproperties. Basic configuration can be specified declaratively using the `Clear` and `Set`\nannotations, and the image size could be set programmatically.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/SystemPropertyExtensionDemo.java[tag=systemproperty_method_combine_all_test]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/SystemPropertyExtensionDemo.kt[tag=systemproperty_method_combine_all_test]\n----\n--\n====\n\n[NOTE]\n====\nUsing `@RestoreSystemProperties` is not necessary to restore system properties modified\nvia `@ClearSystemProperty` or `@SetSystemProperty` since they both automatically restore\nthe referenced properties. `@RestoreSystemProperties` is only needed if system properties\nare modified during a test in some way _other than_ via the `Clear` and `Set` annotations.\n====\n\n[[system-properties-thread-safety]]\n=== Thread Safety\n\nSince system properties are global state, reading and writing them during\nxref:writing-tests/parallel-execution.adoc[parallel execution] can lead to unpredictable\nresults and flaky tests. The system property extension is prepared for that and tests\nannotated with `@ClearSystemProperty`, `@SetSystemProperty`, or `@RestoreSystemProperties`\nwill never execute in parallel (thanks to\nxref:writing-tests/parallel-execution.adoc#synchronization[resource locks]) to guarantee\ncorrect test results.\n\nHowever, this does not cover all possible cases. Tested code that reads or writes system\nproperties _independently_ of the extension can still run in parallel to it and may thus\nbehave erratically when, for example, it unexpectedly reads a property set by the\nextension in another thread. Tests that cover code that reads or writes system properties\nneed to be annotated with the respective annotation:\n\n* `{ReadsSystemProperty}`\n* `{WritesSystemProperty}` (though consider using `@RestoreSystemProperties` instead)\n\nTests annotated in this way will never execute in parallel with tests annotated with\n`@ClearSystemProperty`, `@SetSystemProperty`, or `@RestoreSystemProperties`.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/class-templates.adoc",
    "content": "= Class Templates\n\nA `{ClassTemplate}` is not a regular test class but rather a template for the contained\ntest cases. As such, it is designed to be invoked multiple times depending on invocation\ncontexts returned by the registered providers. Thus, it must be used in conjunction with a\nregistered `{ClassTemplateInvocationContextProvider}` extension.\nEach invocation of a class template behaves like the execution of a regular test class\nwith full support for the same lifecycle callbacks and extensions. Please refer to\nxref:extensions/providing-invocation-contexts-for-class-templates.adoc[] for usage examples.\n\nNOTE: xref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Classes] are a built-in\nspecialization of class templates.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/conditional-test-execution.adoc",
    "content": "= Conditional Test Execution\n\nThe xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`] extension API in JUnit Jupiter allows\ndevelopers to either _enable_ or _disable_ a test class or test method based on certain\nconditions _programmatically_. The simplest example of such a condition is the built-in\n`{DisabledCondition}` which supports the `{Disabled}` annotation (see\nxref:writing-tests/disabling-tests.adoc[]).\n\nIn addition to `@Disabled`, JUnit Jupiter also supports several other annotation-based\nconditions in the `org.junit.jupiter.api.condition` package that allow developers to\nenable or disable test classes and test methods _declaratively_. If you wish to provide\ndetails about why they might be disabled, every annotation associated with these built-in\nconditions has a `disabledReason` attribute available for that purpose.\n\nWhen multiple `ExecutionCondition` extensions are registered, a test class or test method\nis disabled as soon as one of the conditions returns _disabled_. If a test class is\ndisabled, all test methods within that class are automatically disabled as well. If a test\nmethod is disabled, that prevents execution of the test method and method-level lifecycle\ncallbacks such as `@BeforeEach` methods, `@AfterEach` methods, and corresponding extension\nAPIs. However, that does not prevent the test class from being instantiated, and it does\nnot prevent the execution of class-level lifecycle callbacks such as `@BeforeAll` methods,\n`@AfterAll` methods, and corresponding extension APIs.\n\nSee xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`] and the following sections for\ndetails.\n\n[TIP]\n.Composed Annotations\n====\nNote that any of the _conditional_ annotations listed in the following sections may also\nbe used as a meta-annotation in order to create a custom _composed annotation_. For\nexample, the `@TestOnMac` annotation in the\n<<os-demo, @EnabledOnOs demo>> shows how you can\ncombine `@Test` and `@EnabledOnOs` in a single, reusable annotation.\n====\n\n[NOTE]\n====\n_Conditional_ annotations in JUnit Jupiter are not `@Inherited`. Consequently, if you wish\nto apply the same semantics to subclasses, each conditional annotation must be redeclared\non each subclass.\n====\n\n[WARNING]\n====\nUnless otherwise stated, each of the _conditional_ annotations listed in the following\nsections can only be declared once on a given test interface, test class, or test method.\nIf a conditional annotation is directly present, indirectly present, or meta-present\nmultiple times on a given element, only the first such annotation discovered by JUnit will\nbe used; any additional declarations will be silently ignored. Note, however, that each\nconditional annotation may be used in conjunction with other conditional annotations in\nthe `org.junit.jupiter.api.condition` package.\n====\n\n[[os]]\n== Operating System and Architecture Conditions\n\nA container or test may be enabled or disabled on a particular operating system,\narchitecture, or combination of both via the `{EnabledOnOs}` and `{DisabledOnOs}`\nannotations.\n\n[[os-demo]]\n.Conditional execution based on operating system\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_os]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_os]\n----\n--\n====\n\n[[architectures-demo]]\n.Conditional execution based on architecture\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_architecture]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_architecture]\n----\n--\n====\n\n[[jre]]\n== Java Runtime Environment Conditions\n\nA container or test may be enabled or disabled on particular versions of the Java Runtime\nEnvironment (JRE) via the `{EnabledOnJre}` and `{DisabledOnJre}` annotations or on a\nparticular range of versions of the JRE via the `{EnabledForJreRange}` and\n`{DisabledForJreRange}` annotations. The range effectively defaults to `JRE.JAVA_8` as the\nlower bound and `JRE.OTHER` as the upper bound, which allows usage of half open ranges.\n\nThe following listing demonstrates the use of these annotations with predefined {JRE} enum\nconstants.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_jre]\n----\n--\n====\n\nSince the enum constants defined in {JRE} are static for any given JUnit release, you\nmight find that you need to configure a Java version that is not supported by the `JRE`\nenum. For example, when JUnit Jupiter 5.12 was released the `JRE` enum defined `JAVA_25`\nas the highest supported Java version. However, you may wish to run your tests against\nlater versions of Java. To support such use cases, you can specify arbitrary Java versions\nvia the `versions` attributes in `@EnabledOnJre` and `@DisabledOnJre` and via the\n`minVersion` and `maxVersion` attributes in `@EnabledForJreRange` and\n`@DisabledForJreRange`.\n\nThe following listing demonstrates the use of these annotations with arbitrary Java\nversions.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_jre_arbitrary_versions]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_jre_arbitrary_versions]\n----\n--\n====\n\n[[native]]\n== Native Image Conditions\n\nA container or test may be enabled or disabled within a\nhttps://www.graalvm.org/reference-manual/native-image/[GraalVM native image] via the\n`{EnabledInNativeImage}` and `{DisabledInNativeImage}` annotations. These annotations are\ntypically used when running tests within a native image using the Gradle and Maven\nplug-ins from the GraalVM https://graalvm.github.io/native-build-tools/latest/[Native\nBuild Tools] project.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_native]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_native]\n----\n--\n====\n\n[[system-properties]]\n== System Property Conditions\n\nA container or test may be enabled or disabled based on the value of the `named` JVM\nsystem property via the `{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}`\nannotations. The value supplied via the `matches` attribute will be interpreted as a\nregular expression.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_system_property]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_system_property]\n----\n--\n====\n\n[TIP]\n====\n`{EnabledIfSystemProperty}` and `{DisabledIfSystemProperty}` are _repeatable annotations_.\nConsequently, these annotations may be declared multiple times on a test interface, test\nclass, or test method. Specifically, these annotations will be found if they are directly\npresent, indirectly present, or meta-present on a given element.\n====\n\n[[environment-variables]]\n== Environment Variable Conditions\n\nA container or test may be enabled or disabled based on the value of the `named`\nenvironment variable from the underlying operating system via the\n`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` annotations. The\nvalue supplied via the `matches` attribute will be interpreted as a regular expression.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_environment_variable]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_environment_variable]\n----\n--\n====\n\n[TIP]\n====\n`{EnabledIfEnvironmentVariable}` and `{DisabledIfEnvironmentVariable}` are _repeatable\nannotations_. Consequently, these annotations may be declared multiple times on a test\ninterface, test class, or test method. Specifically, these annotations will be found if\nthey are directly present, indirectly present, or meta-present on a given element.\n====\n\n[[custom]]\n== Custom Conditions\n\nAs an alternative to implementing an xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`], a\ncontainer or test may be enabled or disabled based on a _condition method_ configured via\nthe `{EnabledIf}` and `{DisabledIf}` annotations. A condition method must have a `boolean`\nreturn type and may accept either no arguments or a single `ExtensionContext` argument.\n\nThe following test class demonstrates how to configure a local method named\n`customCondition` via `@EnabledIf` and `@DisabledIf`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ConditionalTestExecutionDemo.java[tags=user_guide_custom]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ConditionalTestExecutionDemo.kt[tags=user_guide_custom]\n----\n--\n====\n\nAlternatively, the condition method can be located outside the test class. In this case,\nit must be referenced by its _fully qualified name_ as demonstrated in the following\nexample.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\npackage example;\n\ninclude::example$java/example/ExternalCustomConditionDemo.java[tags=user_guide_external_custom_condition]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\npackage example.kotlin\n\ninclude::example$kotlin/example/kotlin/ExternalCustomConditionDemo.kt[tags=user_guide_external_custom_condition]\n----\n--\n====\n\n[NOTE]\n====\nThere are several cases where a condition method would need to be `static`:\n\n- when `@EnabledIf` or `@DisabledIf` is used at class level\n- when `@EnabledIf` or `@DisabledIf` is used on a `@ParameterizedTest` or a\n  `@TestTemplate` method\n- when the condition method is located in an external class\n\nIn any other case, you can use either static methods or instance methods as condition\nmethods.\n====\n\n[TIP]\n====\nIt is often the case that you can use an existing static method in a utility class as a\ncustom condition.\n\nFor example, `java.awt.GraphicsEnvironment` provides a `public static boolean isHeadless()`\nmethod that can be used to determine if the current environment does not support a\ngraphical display. Thus, if you have a test that depends on graphical support you can\ndisable it when such support is unavailable as follows.\n\n[tabs]\n======\nJava::\n+\n--\n[source,java,indent=0]\n----\n@DisabledIf(value = \"java.awt.GraphicsEnvironment#isHeadless\",\n\tdisabledReason = \"headless environment\")\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\n@DisabledIf(value = \"java.awt.GraphicsEnvironment#isHeadless\",\n    disabledReason = \"headless environment\")\n----\n--\n======\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/definitions.adoc",
    "content": "= Definitions\n\n== Platform Concepts\n\nContainer::\na node in the test tree that contains other containers or tests as its children (e.g. a _test class_).\n\nTest::\na node in the test tree that verifies expected behavior when executed (e.g. a `@Test` method).\n\n== Jupiter Concepts\n\nLifecycle Method::\nany method that is directly annotated or meta-annotated with\n`@BeforeAll`, `@AfterAll`, `@BeforeEach`, or `@AfterEach`.\n\nTest Class::\nany top-level class, `static` member class, or xref:writing-tests/nested-tests.adoc[`@Nested` class] that contains at least one _test method_, i.e. a _container_.\nTest classes must not be `abstract` and must have a single constructor.\nJava `record` classes are supported as well.\n\nTest Method::\nany instance method that is directly annotated or meta-annotated with\n`@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`, or `@TestTemplate`.\nWith the exception of `@Test`, these create a _container_ in the test tree that groups\n_tests_ or, potentially (for `@TestFactory`), other _containers_.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/dependency-injection-for-constructors-and-methods.adoc",
    "content": "= Dependency Injection for Constructors and Methods\n\nIn JUnit Jupiter, both test constructors and methods are permitted to have parameters.\nThis allows for greater flexibility and enables _Dependency Injection_ for constructors\nand methods.\n\n`{ParameterResolver}` defines the API for test extensions that wish to _dynamically_\nresolve parameters at runtime. If a _test class_ constructor, a _test method_, or a\n_lifecycle method_ (see xref:writing-tests/definitions.adoc[]) accepts a parameter, the parameter\nmust be resolved at runtime by a registered `ParameterResolver`.\n\n[[built-in-parameter-resolvers]]\n== Built-In Parameter Resolvers\n\nThere are currently three built-in resolvers that are registered automatically.\n\n[[test-info]]\n=== TestInfo\n\nIf a constructor or method parameter is of type `{TestInfo}`, the\n`{TestInfoParameterResolver}` will supply an instance of `TestInfo` corresponding to the\ncurrent container or test as the value for the parameter. The `TestInfo` can then be used\nto retrieve information about the current container or test such as the display name, the\ntest class, the test method, and associated tags. The display name is either a technical\nname, such as the name of the test class or test method, or a custom name configured via\n`@DisplayName`.\n\n`{TestInfo}` acts as a drop-in replacement for the `TestName` rule from JUnit 4. The\nfollowing demonstrates how to have `TestInfo` injected into a `@BeforeAll` method, test\nclass constructor, `@BeforeEach` method, and `@Test` method.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TestInfoDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TestInfoDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[repetition-info]]\n=== RepetitionInfo\n\nIf a method parameter in a `@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method is of\ntype `{RepetitionInfo}`, the `{RepetitionExtension}` will supply an instance of\n`RepetitionInfo`. `RepetitionInfo` can then be used to retrieve information about the\ncurrent repetition, the total number of repetitions, the number of repetitions that have\nfailed, and the failure threshold for the corresponding `@RepeatedTest`. Note, however,\nthat `RepetitionExtension` is not registered outside the context of a `@RepeatedTest`. See\nxref:writing-tests/repeated-tests.adoc#examples[Repeated Test Examples].\n\n[[test-reporter]]\n=== TestReporter\n\nIf a constructor or method parameter is of type `{TestReporter}`, the\n`{TestReporterParameterResolver}` will supply an instance of `TestReporter`. The\n`TestReporter` can be used to publish additional data about the current test run or attach\nfiles to it. The data can be consumed in a `{TestExecutionListener}` via the\n`reportingEntryPublished()` or `fileEntryPublished()` method, respectively. This allows\nthem to be viewed in IDEs or included in reports.\n\nIn JUnit Jupiter you should use `TestReporter` where you used to print information to\n`stdout` or `stderr` in JUnit 4. Some IDEs print report entries to `stdout` or display\nthem in the user interface for test results.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TestReporterDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TestReporterDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[custom-parameter-resolvers]]\n== Custom Parameter Resolvers\n\nNOTE: Custom parameter resolvers must be explicitly enabled by\nxref:extensions/registering-extensions.adoc[registering] appropriate\nxref:extensions/overview.adoc[extensions].\n\nCheck out the `xref:extensions/registering-extensions.adoc#RandomNumberExtension[RandomNumberExtension]`\nfor an example of a custom `{ParameterResolver}`. While not intended to be production-ready,\nit demonstrates the simplicity and expressiveness of both the extension model and the\nparameter resolution process. `MyRandomParametersTest` demonstrates how to inject random\nvalues into a constructor and a `@Test` method.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/MyRandomParametersTest.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/MyRandomParametersTest.kt[tags=user_guide]\n----\n--\n====\n\nFor real-world use cases, check out the source code for the `{MockitoExtension}` and the\n`{SpringExtension}`.\n\nWhen the type of the parameter to inject is the only condition for your\n`{ParameterResolver}`, you can use the generic `{TypeBasedParameterResolver}` base class.\nThe `supportsParameters` method is implemented behind the scenes and supports\nparameterized types.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/disabling-tests.adoc",
    "content": "= Disabling Tests\n\nEntire test classes or individual test methods may be _disabled_ via the `{Disabled}`\nannotation, via one of the annotations discussed in\nxref:writing-tests/conditional-test-execution.adoc[], or via a custom xref:extensions/conditional-test-execution.adoc[`ExecutionCondition`].\n\nWhen `@Disabled` is applied at the class level, all test methods within that class are\nautomatically disabled as well.\n\nIf a test method is disabled via `@Disabled`, that prevents execution of the test method\nand method-level lifecycle callbacks such as `@BeforeEach` methods, `@AfterEach` methods,\nand corresponding extension APIs. However, that does not prevent the test class from being\ninstantiated, and it does not prevent the execution of class-level lifecycle callbacks\nsuch as `@BeforeAll` methods, `@AfterAll` methods, and corresponding extension APIs.\n\nHere's a `@Disabled` test class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/DisabledClassDemo.java[tags=user_guide]\n----\n\nAnd here's a test class that contains a `@Disabled` test method.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/DisabledTestsDemo.java[tags=user_guide]\n----\n\n[TIP]\n====\n`@Disabled` may be declared without providing a _reason_; however, the JUnit team\nrecommends that developers provide a short explanation for why a test class or test\nmethod has been disabled. Consequently, the above examples both show the use of a reason\n-- for example, `@Disabled(\"Disabled until bug #42 has been resolved\")`. Some development\nteams even require the presence of issue tracking numbers in the _reason_ for automated\ntraceability, etc.\n====\n\n[NOTE]\n====\n`@Disabled` is not `@Inherited`. Consequently, if you wish to disable a class whose\nsuperclass is `@Disabled`, you must redeclare `@Disabled` on the subclass.\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/display-names.adoc",
    "content": "= Display Names\n\nTest classes and test methods can declare custom display names via `@DisplayName` -- with\nspaces, special characters, and even emojis -- that will be displayed in test reports and\nby test runners and IDEs.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DisplayNameDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DisplayNameDemo.kt[tags=user_guide]\n----\n--\n====\n\n[TIP]\n====\nIn Kotlin, you can use backtick-enclosed function names as an alternative to\n`@DisplayName` for simple display names:\n\n[source,kotlin,indent=0]\n----\n@Test\nfun `custom test name containing spaces`() {\n    // will be displayed as \"custom test name containing spaces\"\n}\n----\n====\n\n[NOTE]\n====\nControl characters in text-based arguments in display names for parameterized tests are\nescaped by default. See xref:writing-tests/parameterized-classes-and-tests.adoc#display-names-quoted-text[Quoted Text-based Arguments]\nfor details.\n\nAny remaining ISO control characters in a display name will be replaced as follows.\n\n[cols=\"25%,15%,60%\"]\n|===\n| Original | Replacement | Description\n\n| ```\\r```\n| ```<CR>```\n| Textual representation of a carriage return\n\n| ```\\n```\n| ```<LF>```\n| Textual representation of a line feed\n\n| Other control character\n| ```�```\n| Unicode replacement character (U+FFFD)\n|===\n====\n\n[[generator]]\n== Display Name Generators\n\nJUnit Jupiter supports custom display name generators that can be configured via the\n`@DisplayNameGeneration` annotation.\n\nGenerators can be created by implementing the `DisplayNameGenerator` API. The following\ntable lists the default display name generators available in Jupiter.\n\n[cols=\"20,80\"]\n|===\n| DisplayNameGenerator   | Behavior\n\n| `Standard`            | Matches the standard display name generation behavior in place since JUnit Jupiter was introduced.\n| `Simple`              | Extends the functionality of `Standard` by removing trailing parentheses for methods with no parameters.\n| `ReplaceUnderscores`  | Replaces underscores with spaces.\n| `IndicativeSentences` | Generates complete sentences by concatenating the names of the test and the enclosing classes.\n|===\n\nNOTE: Values provided via `@DisplayName` annotations always take precedence over display\nnames generated by a `DisplayNameGenerator`.\n\n======\nThe following example demonstrates the use of the `ReplaceUnderscores` display name\ngenerator.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_replace_underscores]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DisplayNameGeneratorDemo.kt[tags=user_guide_replace_underscores]\n----\n--\n====\n\nRunning the above test class results in the following display names.\n\n```\nA year is not supported ✔\n├─ if it is zero ✔\n└─ A negative value for year is not supported by the leap year computation. ✔\n   ├─ For example, year -1 is not supported. ✔\n   └─ For example, year -4 is not supported. ✔\n```\n======\n\n======\nWith the `IndicativeSentences` display name generator, you can customize the separator and\nthe underlying generator by using `@IndicativeSentencesGeneration` as shown in the\nfollowing example.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_indicative_sentences]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DisplayNameGeneratorDemo.kt[tags=user_guide_indicative_sentences]\n----\n--\n====\n\nRunning the above test class results in the following display names.\n\n```\nA year is a leap year ✔\n├─ A year is a leap year -> if it is divisible by 4 but not by 100 ✔\n└─ A year is a leap year -> if it is one of the following years ✔\n   ├─ Year 2016 is a leap year. ✔\n   ├─ Year 2020 is a leap year. ✔\n   └─ Year 2048 is a leap year. ✔\n```\n======\n\n======\nWith `IndicativeSentences`, you can optionally specify custom sentence fragments via the\n`@SentenceFragment` annotation as demonstrated in the following example.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DisplayNameGeneratorDemo.java[tags=user_guide_custom_sentence_fragments]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DisplayNameGeneratorDemo.kt[tags=user_guide_custom_sentence_fragments]\n----\n--\n====\n\nRunning the above test class results in the following display names.\n\n```\nA year is a leap year ✔\n├─ A year is a leap year, if it is divisible by 4 but not by 100 ✔\n└─ A year is a leap year, if it is one of the following years ✔\n   ├─ 2016 ✔\n   ├─ 2020 ✔\n   └─ 2048 ✔\n```\n======\n\n\n[[generator-default]]\n== Setting the Default Display Name Generator\n\nYou can use the `junit.jupiter.displayname.generator.default`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the fully qualified\nclass name of the `DisplayNameGenerator` you would like to use by default. Just like for\ndisplay name generators configured via the `@DisplayNameGeneration` annotation, the\nsupplied class has to implement the `DisplayNameGenerator` interface. The default display\nname generator will be used for all tests unless the `@DisplayNameGeneration` annotation\nis present on an enclosing test class or test interface. Values provided via\n`@DisplayName` annotations always take precedence over display names generated by a\n`DisplayNameGenerator`.\n\nFor example, to use the `ReplaceUnderscores` display name generator by default, you should\nset the configuration parameter to the corresponding fully qualified class name (e.g., in\n`src/test/resources/junit-platform.properties`):\n\n[source,properties,indent=0]\n----\njunit.jupiter.displayname.generator.default = \\\n    org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores\n----\n\nSimilarly, you can specify the fully qualified name of any custom class that implements\n`DisplayNameGenerator`.\n\n[[generator-precedence-rules]]\nIn summary, the display name for a test class or method is determined according to the\nfollowing precedence rules:\n\n1. value of the `@DisplayName` annotation, if present\n2. by calling the `DisplayNameGenerator` specified in the `@DisplayNameGeneration`\n   annotation, if present\n3. by calling the default `DisplayNameGenerator` configured via the configuration\n   parameter, if present\n4. by calling `org.junit.jupiter.api.DisplayNameGenerator.Standard`\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/dynamic-tests.adoc",
    "content": "= Dynamic Tests\n\nThe standard `@Test` annotation in JUnit Jupiter described in\nxref:writing-tests/annotations.adoc[] is very similar to the `@Test` annotation in JUnit 4. Both\ndescribe methods that implement test cases. These test cases are static in the sense that\nthey are fully specified at compile time, and their behavior cannot be changed by\nanything happening at runtime. _Assumptions provide a basic form of dynamic behavior but\nare intentionally rather limited in their expressiveness._\n\nIn addition to these standard tests a completely new kind of test programming model has\nbeen introduced in JUnit Jupiter. This new kind of test is a _dynamic test_ which is\ngenerated at runtime by a factory method that is annotated with `@TestFactory`.\n\nIn contrast to `@Test` methods, a `@TestFactory` method is not itself a test case but\nrather a factory for test cases. Thus, a dynamic test is the product of a factory.\nTechnically speaking, a `@TestFactory` method must return a single `DynamicNode` or a\n_stream_ of `DynamicNode` instances or any of its subclasses. In this context, a \"stream\"\nis anything that JUnit can reliably convert into a `Stream`, such as `Stream`,\n`Collection`, `Iterator`, `Iterable`, an array of objects, or any type that provides an\n`iterator(): Iterator` method (such as, for example, a `kotlin.sequences.Sequence`).\n\nInstantiable subclasses of `DynamicNode` are `DynamicContainer` and `DynamicTest`.\n`DynamicContainer` instances are composed of a _display name_ and a list of dynamic child\nnodes, enabling the creation of arbitrarily nested hierarchies of dynamic nodes.\n`DynamicTest` instances will be executed lazily, enabling dynamic and even\nnon-deterministic generation of test cases.\n\nAny `Stream` returned by a `@TestFactory` will be properly closed by calling\n`stream.close()`, making it safe to use a resource such as `Files.lines()`.\n\nAs with `@Test` methods, `@TestFactory` methods must not be `private` or `static` and may\noptionally declare parameters to be resolved by `ParameterResolvers`.\n\nA `DynamicTest` is a test case generated at runtime. It is composed of a _display name_\nand an `Executable`. `Executable` is a `@FunctionalInterface` which means that the\nimplementations of dynamic tests can be provided as _lambda expressions_ or _method\nreferences_.\n\n.Dynamic Test Lifecycle\nWARNING: The execution lifecycle of a dynamic test is quite different than it is for a\nstandard `@Test` case. Specifically, there are no lifecycle callbacks for individual\ndynamic tests. This means that `@BeforeEach` and `@AfterEach` methods and their\ncorresponding extension callbacks are executed for the `@TestFactory` method but not for\neach _dynamic test_. In other words, if you access fields from the test instance within a\nlambda expression for a dynamic test, those fields will not be reset by callback methods\nor extensions between the execution of individual dynamic tests generated by the same\n`@TestFactory` method.\n\n[[examples]]\n== Dynamic Test Examples\n\nThe following `DynamicTestsDemo` class demonstrates several examples of test factories\nand dynamic tests.\n\nThe first method returns an invalid return type and will cause a warning to be reported by\nJUnit during test discovery. Such methods are not executed.\n\nThe next six methods demonstrate the generation of a `Collection`, `Iterable`, `Iterator`,\narray, or `Stream` of `DynamicTest` instances. Most of these examples do not really\nexhibit dynamic behavior but merely demonstrate the supported return types in principle.\nHowever, `dynamicTestsFromStream()` and `dynamicTestsFromIntStream()` demonstrate how to\ngenerate dynamic tests for a given set of strings or a range of input numbers.\n\nThe next method is truly dynamic in nature. `generateRandomNumberOfTests()` implements an\n`Iterator` that generates random numbers, a display name generator, and a test executor\nand then provides all three to `DynamicTest.stream()`. Although the non-deterministic\nbehavior of `generateRandomNumberOfTests()` is of course in conflict with test\nrepeatability and should thus be used with care, it serves to demonstrate the\nexpressiveness and power of dynamic tests.\n\nThe next method is similar to `generateRandomNumberOfTests()` in terms of flexibility;\nhowever, `dynamicTestsFromStreamFactoryMethod()` generates a stream of dynamic tests from\nan existing `Stream` via the `DynamicTest.stream()` factory method.\n\nFor demonstration purposes, the `dynamicNodeSingleTest()` method generates a single\n`DynamicTest` instead of a stream, and the `dynamicNodeSingleContainer()` method generates\na nested hierarchy of dynamic tests utilizing `DynamicContainer`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DynamicTestsDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DynamicTestsDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[named-support]]\n== Dynamic Tests and Named\n\nIn some cases, it can be more natural to specify inputs together with a descriptive name\nusing the {Named} API and the corresponding `stream()` factory methods on `DynamicTest` as\nshown in the first example below. The second example takes it one step further and allows\nto provide the code block that should be executed by implementing the `Executable`\ninterface along with `Named` via the `NamedExecutable` base class.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DynamicTestsNamedDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DynamicTestsNamedDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[uri-test-source]]\n== URI Test Sources for Dynamic Tests\n\nThe JUnit Platform provides `TestSource`, a representation of the source of a test or\ncontainer used to navigate to its location by IDEs and build tools.\n\nThe `TestSource` for a dynamic test or dynamic container can be constructed from a\n`java.net.URI` which can be supplied via the `DynamicTest.dynamicTest(String, URI,\nExecutable)` or `DynamicContainer.dynamicContainer(String, URI, Stream)` factory method,\nrespectively. The `URI` will be converted to one of the following `TestSource`\nimplementations.\n\n`ClasspathResourceSource` ::\n  If the `URI` contains the `classpath` scheme -- for example,\n  `classpath:/test/foo.xml?line=20,column=2`.\n\n`DirectorySource` ::\n  If the `URI` represents a directory present in the file system.\n\n`FileSource` ::\n  If the `URI` represents a file present in the file system.\n\n`MethodSource` ::\n  If the `URI` contains the `method` scheme and the fully qualified method name (FQMN) --\n  for example, `method:org.junit.Foo#bar(java.lang.String, java.lang.String[])`. Please\n  refer to the Javadoc for `{DiscoverySelectors}.{DiscoverySelectors_selectMethod}` for the\n  supported formats for a FQMN.\n\n`ClassSource` ::\n  If the `URI` contains the `class` scheme and the fully qualified class name --\n  for example, `class:org.junit.Foo?line=42`.\n\n`UriSource` ::\n  If none of the above `TestSource` implementations are applicable.\n\n[[parallel-execution]]\n== Parallel Execution\n\nDynamic tests and containers support\nxref:writing-tests/parallel-execution.adoc[parallel execution]. You can configure their\n`ExecutionMode` by using the `dynamicTest(Consumer)` and `dynamicContainer(Consumer)`\nfactory methods as illustrated by the following example.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/DynamicTestsDemo.java[tags=execution_mode]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/DynamicTestsDemo.kt[tags=execution_mode]\n----\n--\n====\n\nExecuting the above test factory method results in the following test tree and execution\nmodes:\n\n* dynamicTestsWithConfiguredExecutionMode() -- `CONCURRENT` (from `@Execution` annotation)\n** Container A -- `CONCURRENT` (from `@Execution` annotation)\n*** not null -- `SAME_THREAD` (from `executionMode(...)` call)\n*** properties -- `CONCURRENT` (from `@Execution` annotation)\n**** length > 0 -- `CONCURRENT` (from `executionMode(...)` call)\n**** not empty -- `SAME_THREAD` (from `childExecutionMode(...)` call)\n** ... (same for \"Container B\" and \"Container C\")\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/exception-handling.adoc",
    "content": "= Exception Handling\n\nJUnit Jupiter provides robust support for handling test exceptions. This includes the\nbuilt-in mechanisms for managing test failures due to exceptions, the role of exceptions\nin implementing assertions and assumptions, and how to specifically assert non-throwing\nconditions in code.\n\n[[uncaught]]\n== Uncaught Exceptions\n\nIn JUnit Jupiter, if an exception is thrown from a test method, a lifecycle method, or an\nextension and not caught within that test method, lifecycle method, or extension, the\nframework will mark the test or test class as failed.\n\n[TIP]\n====\nFailed assumptions deviate from this general rule.\n\nIn contrast to failed assertions, failed assumptions do not result in a test failure;\nrather, a failed assumption results in a test being aborted.\n\nSee xref:writing-tests/assumptions.adoc[] for further details and examples.\n====\n\nIn the following example, the `failsDueToUncaughtException()` method throws an\n`ArithmeticException`. Since the exception is not caught within the test method, JUnit\nJupiter will mark the test as failed.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/exception/UncaughtExceptionHandlingDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/exception/UncaughtExceptionHandlingDemo.kt[tags=user_guide]\n----\n--\n====\n\nNOTE: It's important to note that specifying a `throws` clause in the test method has\nno effect on the outcome of the test. JUnit Jupiter does not interpret a `throws` clause\nas an expectation or assertion about what exceptions the test method should throw. A test\nfails only if an exception is thrown unexpectedly or if an assertion fails.\n\n[[failed-assertions]]\n== Failed Assertions\n\nAssertions in JUnit Jupiter are implemented using exceptions. The framework provides a set\nof assertion methods in the `org.junit.jupiter.api.Assertions` class, which throw\n`AssertionError` when an assertion fails. This mechanism is a core aspect of how JUnit\nhandles assertion failures as exceptions. See the xref:writing-tests/assertions.adoc[] section for\nfurther information about JUnit Jupiter's assertion support.\n\nNOTE: Third-party assertion libraries may choose to throw an `AssertionError` to signal a\nfailed assertion; however, they may also choose to throw different types of exceptions to\nsignal failures. See also: xref:writing-tests/assertions.adoc#third-party[Third-party Assertion Libraries].\n\nTIP: JUnit Jupiter itself does not differentiate between failed assertions\n(`AssertionError`) and other types of exceptions. All uncaught exceptions lead to a test\nfailure. However, Integrated Development Environments (IDEs) and other tools may\ndistinguish between these two types of failures by checking whether the thrown exception\nis an instance of `AssertionError`.\n\nIn the following example, the `failsDueToUncaughtAssertionError()` method throws an\n`AssertionError`. Since the exception is not caught within the test method, JUnit Jupiter\nwill mark the test as failed.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/exception/FailedAssertionDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/exception/FailedAssertionDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[expected]]\n== Asserting Expected Exceptions\n\nJUnit Jupiter offers specialized assertions for testing that specific exceptions are\nthrown under expected conditions. The `assertThrows()` and `assertThrowsExactly()`\nassertions are critical tools for validating that your code responds correctly to error\nconditions by throwing the appropriate exceptions.\n\n[[expected-assertThrows]]\n=== Using `assertThrows()`\n\nThe `assertThrows()` method is used to verify that a particular type of exception is\nthrown during the execution of a provided executable block. It not only checks for the\ntype of the thrown exception but also its subclasses, making it suitable for more\ngeneralized exception handling tests. The `assertThrows()` assertion method returns the\nthrown exception object to allow performing additional assertions on it.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/exception/ExceptionAssertionDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/exception/ExceptionAssertionDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[expected-assertThrowsExactly]]\n=== Using `assertThrowsExactly()`\n\nThe `assertThrowsExactly()` method is used when you need to assert that the exception\nthrown is exactly of a specific type, not allowing for subclasses of the expected\nexception type. This is useful when precise exception handling behavior needs to be\nvalidated. Similar to `assertThrows()`, the `assertThrowsExactly()` assertion method also\nreturns the thrown exception object to allow performing additional assertions on it.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/exception/ExceptionAssertionExactDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/exception/ExceptionAssertionExactDemo.kt[tags=user_guide]\n----\n--\n====\n\n[[not-expected]]\n== Asserting That no Exception is Expected\n\nAlthough any exception thrown from a test method will cause the test to fail, there are\ncertain use cases where it can be beneficial to explicitly assert that an exception is\n_not_ thrown for a given code block within a test method. The `assertDoesNotThrow()`\nassertion can be used when you want to verify that a particular piece of code does not\nthrow any exceptions.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/exception/AssertDoesNotThrowExceptionDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/exception/AssertDoesNotThrowExceptionDemo.kt[tags=user_guide]\n----\n--\n====\n\nNOTE: Third-party assertion libraries often provide similar support. For example, AssertJ\nhas `assertThatNoException().isThrownBy(() -> ...)`. See also:\nxref:writing-tests/assertions.adoc#third-party[Third-party Assertion Libraries].\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/intro.adoc",
    "content": "= Writing Tests\n\nThe following example provides a glimpse at the minimum requirements for writing a test in\nJUnit Jupiter. Subsequent sections of this chapter will provide further details on all\navailable features.\n\n.A first test case\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/MyFirstJUnitJupiterTests.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/MyFirstJUnitJupiterTests.kt[tags=user_guide]\n----\n--\n====\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/nested-tests.adoc",
    "content": "= Nested Tests\n\n`@Nested` tests give the test writer more capabilities to express the relationship among\nseveral groups of tests. Such nested tests make use of Java's nested classes and\nfacilitate hierarchical thinking about the test structure. Here's an elaborate example,\nboth as source code and as a screenshot of the execution within an IDE.\n\n.Nested test suite for testing a stack\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/TestingAStackDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/TestingAStackDemo.kt[tags=user_guide]\n----\n--\n====\n\nWhen executing this example in an IDE, the test execution tree in the GUI will look\nsimilar to the following image.\n\nimage::writing-tests_nested_test_ide.png[caption='',title='Executing a nested test in an IDE']\n\nIn this example, preconditions from outer tests are used in inner tests by defining\nhierarchical lifecycle methods for the setup code. For example, `createNewStack()` is a\n`@BeforeEach` lifecycle method that is used in the test class in which it is defined and\nin all levels in the nesting tree below the class in which it is defined.\n\nThe fact that setup code from outer tests is run before inner tests are executed gives you\nthe ability to run all tests independently. You can even run inner tests alone without\nrunning the outer tests, because the setup code from the outer tests is always executed.\n\nNOTE: _Only non-static nested classes_ (i.e. _inner classes_) can serve as `@Nested` test\nclasses. Nesting can be arbitrarily deep, and those inner classes are subject to full\nlifecycle support, including `@BeforeAll` and `@AfterAll` methods on each level.\n\n[[interoperability]]\n== Interoperability\n\n`@Nested` may be combined with\nxref:writing-tests/parameterized-classes-and-tests.adoc[`@ParameterizedClass`] in which case the nested test\nclass is parameterized.\n\nThe following example illustrates how to combine `@Nested` with `@ParameterizedClass` and\n`@ParameterizedTest`.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedClassDemo.java[tags=nested]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/ParameterizedClassDemo.kt[tags=nested]\n----\n--\n====\n\nExecuting the above test class yields the following output:\n\n....\nFruitTests ✔\n├─ [1] fruit = \"apple\" ✔\n│  └─ QuantityTests ✔\n│     ├─ [1] quantity = 23 ✔\n│     │  └─ test(Duration) ✔\n│     │     ├─ [1] duration = \"PT1H\" ✔\n│     │     └─ [2] duration = \"PT2H\" ✔\n│     └─ [2] quantity = 42 ✔\n│        └─ test(Duration) ✔\n│           ├─ [1] duration = \"PT1H\" ✔\n│           └─ [2] duration = \"PT2H\" ✔\n└─ [2] fruit = \"banana\" ✔\n   └─ QuantityTests ✔\n      ├─ [1] quantity = 23 ✔\n      │  └─ test(Duration) ✔\n      │     ├─ [1] duration = \"PT1H\" ✔\n      │     └─ [2] duration = \"PT2H\" ✔\n      └─ [2] quantity = 42 ✔\n         └─ test(Duration) ✔\n            ├─ [1] duration = \"PT1H\" ✔\n            └─ [2] duration = \"PT2H\" ✔\n....\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/parallel-execution.adoc",
    "content": "= Parallel Execution\n\nBy default, JUnit Jupiter tests are run sequentially in a single thread; however, running\ntests in parallel -- for example, to speed up execution -- is available as an opt-in\nfeature. To enable parallel execution, set the `junit.jupiter.execution.parallel.enabled`\nconfiguration parameter to `true` -- for example, in `junit-platform.properties` (see\nxref:running-tests/configuration-parameters.adoc[] for other options).\n\nPlease note that enabling this property is only the first step required to execute tests\nin parallel. If enabled, test classes and methods will still be executed sequentially by\ndefault. Whether or not a node in the test tree is executed concurrently is controlled by\nits execution mode. The following two modes are available.\n\n`SAME_THREAD`::\n  Force execution in the same thread used by the parent. For example, when used on a test\n  method, the test method will be executed in the same thread as any `@BeforeAll` or\n  `@AfterAll` methods of the containing test class.\n\n`CONCURRENT`::\n  Execute concurrently unless a resource lock forces execution in the same thread.\n\nBy default, nodes in the test tree use the `SAME_THREAD` execution mode. You can change\nthe default by setting the `junit.jupiter.execution.parallel.mode.default` configuration\nparameter. Alternatively, you can use the `{Execution}` annotation to change the\nexecution mode for the annotated element and its subelements (if any) which allows you to\nactivate parallel execution for individual test classes, one by one.\n\n[source,properties]\n.Configuration parameters to execute all tests in parallel\n----\njunit.jupiter.execution.parallel.enabled = true\njunit.jupiter.execution.parallel.mode.default = concurrent\n----\n\nThe default execution mode is applied to all nodes of the test tree with a few notable\nexceptions, namely test classes that use the `Lifecycle.PER_CLASS` mode or a\n`{MethodOrderer}`. In the former case, test authors have to ensure that the test class is\nthread-safe; in the latter, concurrent execution might conflict with the configured\nexecution order. Thus, in both cases, test methods in such test classes are only executed\nconcurrently if the `@Execution(CONCURRENT)` annotation is present on the test class or\nmethod.\n\nYou can use the `@Execution` annotation to explicitly configure the execution mode for a\ntest class or method:\n\n[source,java]\n----\ninclude::example$java/example/ExplicitExecutionModeDemo.java[tags=user_guide]\n----\n\nThis allows test classes or methods to opt in or out of concurrent execution regardless of\nthe globally configured default.\n\nWhen parallel execution is enabled and a default `{ClassOrderer}` is registered (see\nxref:writing-tests/test-execution-order.adoc#classes[Class Order] for details), top-level test classes will\ninitially be sorted accordingly and scheduled in that order. However, they are not\nguaranteed to be started in exactly that order since the threads they are executed on are\nnot controlled directly by JUnit.\n\nAll nodes of the test tree that are configured with the `CONCURRENT` execution mode will\nbe executed fully in parallel according to the provided\n<<config, configuration>> while observing the\ndeclarative <<synchronization, synchronization>>\nmechanism. Please note that xref:running-tests/capturing-standard-output-error.adoc[] needs to be enabled\nseparately.\n\nIn addition, you can configure the default execution mode for top-level classes by setting\nthe `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter. By\ncombining both configuration parameters, you can configure classes to run in parallel but\ntheir methods in the same thread:\n\n[source,properties]\n.Configuration parameters to execute top-level classes in parallel but methods in same thread\n----\njunit.jupiter.execution.parallel.enabled = true\njunit.jupiter.execution.parallel.mode.default = same_thread\njunit.jupiter.execution.parallel.mode.classes.default = concurrent\n----\n\nThe opposite combination will run all methods within one class in parallel, but top-level\nclasses will run sequentially:\n\n[source,properties]\n.Configuration parameters to execute top-level classes sequentially but their methods in parallel\n----\njunit.jupiter.execution.parallel.enabled = true\njunit.jupiter.execution.parallel.mode.default = concurrent\njunit.jupiter.execution.parallel.mode.classes.default = same_thread\n----\n\nThe following diagram illustrates how the execution of two top-level test classes `A` and\n`B` with two test methods per class behaves for all four combinations of\n`junit.jupiter.execution.parallel.mode.default` and\n`junit.jupiter.execution.parallel.mode.classes.default` (see labels in first column).\n\n////\nSource: https://mermaid-js.github.io/mermaid-live-editor/edit#pako:eNqFlE1u2zAQha9CEChio7IQKfVGXfUH_QEatICyKAIBwYQaW0QkUiDHhV3X2x4gvWFPUlKUbTmpEq2kN2-GHx403HKhS-QZn81mhSqlbWvYXDopY0I3LQgqVFcq1BIUuS_mnhIIP2jTALHvQYG1tL3ywgaJpLj7rAjND6hZsteoRvb39x9GlUEoLfvltMZL9_4M77EoSGrFJhYavAm-iA0-psH3Jia0lEymLANrk4idR_tjQintS2nEYOE4WLClwfP22H7b6QeP818MPWnvOcwJ_ldPAwutxMoYVPQ_XjHOKwa8YoT3tP0EUwww-_YHmEey52IV47EKH8dDhEAnBmmKR4mnvScdeNLnMJ8MU4yHKcQ45XiGgy4e8Qbdby1LtyNbby04VdhgwTP3qnBFBuqCR6EUdsSVtmFqwWtc0DcoS6mWXk_TebQv3YL5CK1Xk_ODuDSy_CIV5gRm2DiwuL5PKJdVd9DFUV9oRbn82aElc6_uogHxuzwP0DGBvbvCtcs17tO-6vZyy_yI2QIaWW8ydva1RcVyUPbsdahYNz1L5u2a7VjsSVnst5yRG-a6--sjU1rhqSNTVM1EJetykqqXyfSRueCF2rmwYUU63yjBMzIrjPiq9XfNewlLAw3PFlBbp2IpSZvLcHN1F1jEW1DXWu89u3-YPX1X\n\n---\ndisplayMode: compact\n---\n\ngantt\n    dateFormat X\n    axisFormat %s\n    tickInterval 1\n    title ↓ threads | time →\n\n    section (same_thread, same_thread)\n    A.test1() :ass1, 0, 1\n    A.test2() :ass2, after ass1, 2\n    B.test1() :bss1, after ass2, 3\n    B.test2() :bss2, after bss1, 4\n\n    section (same_thread, concurrent)\n    A.test1() :asc1, 0, 1\n    A.test2() :asc2, after asc1, 2\n    B.test1() :bsc1, 0, 1\n    B.test2() :bsc2, after bsc1, 2\n\n    section (concurrent, same_thread)\n    A.test1() :acs1, 0, 1\n    A.test2() :acs2, 0, 1\n    B.test1() :bcs1, after acs1, 2\n    B.test2() :bcs2, after acs2, 2\n\n    section (concurrent, concurrent)\n    A.test1() :acc1, 0, 1\n    A.test2() :acc2, 0, 1\n    B.test1() :bcc1, 0, 1\n    B.test2() :bcc2, 0, 1\n\n////\nimage::writing-tests_execution_mode.svg[caption='',title='Default execution mode configuration combinations']\n\nIf the `junit.jupiter.execution.parallel.mode.classes.default` configuration parameter is\nnot explicitly set, the value for `junit.jupiter.execution.parallel.mode.default` will be\nused instead.\n\n[[config]]\n== Configuration\n\n[[config-executor-service]]\n=== Executor Service\n\nIf parallel execution is enabled, a thread pool is used behind the scenes to execute tests\nconcurrently. You can configure which implementation of `HierarchicalTestExecutorService`\nis used be setting the `junit.jupiter.execution.parallel.config.executor-service`\nconfiguration parameter to one of the following options:\n\n`fork_join_pool` (default)::\nUse an executor service that is backed by a `ForkJoinPool` from the JDK. This will cause\ntests to be executed in a `ForkJoinWorkerThread`. In some cases, usages of\n`ForkJoinPool` in test or production code or calls to blocking JDK APIs may cause the\nnumber of concurrently executing tests to increase. To avoid this situation, please use\n`worker_thread_pool`.\n\n`worker_thread_pool` (experimental)::\nUse an executor service that is backed by a regular thread pool and does not create\nadditional threads if test or production code uses `ForkJoinPool` or calls a blocking\nAPI in the JDK.\n\nWARNING: Using `worker_thread_pool` is currently an _experimental_ feature. You're invited\nto give it a try and provide feedback to the JUnit team so they can improve and eventually\nxref:api-evolution.adoc[promote] this feature.\n\n[[config-strategies]]\n=== Strategies\n\nProperties such as the desired parallelism and the maximum pool size can be configured\nusing a `{ParallelExecutionConfigurationStrategy}`. The JUnit Platform provides two\nimplementations out of the box: `dynamic` and `fixed`. Alternatively, you may implement a\n`custom` strategy.\n\nTo select a strategy, set the `junit.jupiter.execution.parallel.config.strategy`\nconfiguration parameter to one of the following options.\n\n`dynamic`::\n  Computes the desired parallelism based on the number of available processors/cores\n  multiplied by the `junit.jupiter.execution.parallel.config.dynamic.factor`\n  configuration parameter (defaults to `1`).\n  The optional `junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor`\n  configuration parameter can be used to limit the maximum number of threads.\n\n`fixed`::\n  Uses the mandatory `junit.jupiter.execution.parallel.config.fixed.parallelism`\n  configuration parameter as the desired parallelism.\n  The optional `junit.jupiter.execution.parallel.config.fixed.max-pool-size`\n  configuration parameter can be used to limit the maximum number of threads.\n\n`custom`::\n  Allows you to specify a custom `{ParallelExecutionConfigurationStrategy}`\n  implementation via the mandatory `junit.jupiter.execution.parallel.config.custom.class`\n  configuration parameter to determine the desired configuration.\n\nIf no configuration strategy is set, JUnit Jupiter uses the `dynamic` configuration\nstrategy with a factor of `1`. Consequently, the desired parallelism will be equal to the\nnumber of available processors/cores.\n\n.Parallelism alone does not imply maximum number of concurrent threads\nNOTE: By default, JUnit Jupiter does not guarantee that the number of threads used to\nexecute test will not exceed the configured parallelism. For example, when using one\nof the synchronization mechanisms described in the next section, the executor service\nimplementation may spawn additional threads to ensure execution continues with sufficient\nparallelism. If you require such guarantees, it is possible to limit the maximum number of\nthreads by configuring the maximum pool size of the `dynamic`, `fixed` and `custom`\nstrategies.\n\n[[config-properties]]\n=== Relevant properties\n\nThe following table lists relevant properties for configuring parallel execution. See\nxref:running-tests/configuration-parameters.adoc[] for details on how to set such properties.\n\n==== General\n\n`junit.jupiter.execution.parallel.enabled=true|false`::\n  Enable/disable parallel test execution (defaults to `false`).\n\n`junit.jupiter.execution.parallel.mode.default=concurrent|same_thread`::\n  Default execution mode of nodes in the test tree (defaults to `same_thread`).\n\n`junit.jupiter.execution.parallel.mode.classes.default=concurrent|same_thread`::\n  Default execution mode of top-level classes (defaults to `same_thread`).\n\n`junit.jupiter.execution.parallel.config.executor-service=fork_join_pool|worker_thread_pool`::\n  Type of `HierarchicalTestExecutorService` to use for parallel execution (defaults to\n  `fork_join_pool`).\n\n`junit.jupiter.execution.parallel.config.strategy=dynamic|fixed|custom`::\n  Execution strategy for desired parallelism, maximum pool size, etc. (defaults to `dynamic`).\n\n==== Dynamic strategy\n\n`junit.jupiter.execution.parallel.config.dynamic.factor=decimal`::\n  Factor to be multiplied by the number of available processors/cores to determine the\n  desired parallelism for the ```dynamic``` configuration strategy.\n  Must be a positive decimal number (defaults to `1.0`).\n\n`junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor=decimal`::\n  Factor to be multiplied by the number of available processors/cores and the value of\n  `junit.jupiter.execution.parallel.config.dynamic.factor` to determine the desired\n  parallelism for the ```dynamic``` configuration strategy.\n  Must be a positive decimal number greater than or equal to `1.0` (defaults to 256 plus\n  the value of `junit.jupiter.execution.parallel.config.dynamic.factor` multiplied by the\n  number of available processors/cores)\n\n`junit.jupiter.execution.parallel.config.dynamic.saturate=true|false`::\n  Enable/disable saturation of the underlying `ForkJoinPool` for the ```dynamic```\n  configuration strategy (defaults to `true`). Only used if\n  `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`.\n\n==== Fixed strategy\n\n`junit.jupiter.execution.parallel.config.fixed.parallelism=integer`::\n  Desired parallelism for the ```fixed``` configuration strategy (no default value). Must\n  be a positive integer.\n\n`junit.jupiter.execution.parallel.config.fixed.max-pool-size=integer`::\n  Desired maximum pool size of the underlying fork-join pool for the ```fixed```\n  configuration strategy. Must be a positive integer greater than or equal to\n  `junit.jupiter.execution.parallel.config.fixed.parallelism` (defaults to 256 plus the\n  value of `junit.jupiter.execution.parallel.config.fixed.parallelism`).\n\n`junit.jupiter.execution.parallel.config.fixed.saturate=true|false`::\n  Enable/disable saturation of the underlying `ForkJoinPool` for the ```fixed```\n  configuration strategy (defaults to `true`). Only used if\n  `junit.jupiter.execution.parallel.config.executor-service` is set to `fork_join_pool`.\n\n==== Custom strategy\n\n`junit.jupiter.execution.parallel.config.custom.class=classname`::\n  Fully qualified class name of the `ParallelExecutionConfigurationStrategy` to be used\n  for the ```custom``` configuration strategy (no default value).\n\n[[synchronization]]\n== Synchronization\n\nIn addition to controlling the execution mode using the `{Execution}` annotation, JUnit\nJupiter provides another annotation-based declarative synchronization mechanism. The\n`{ResourceLock}` annotation allows you to declare that a test class or method uses a\nspecific shared resource that requires synchronized access to ensure reliable test\nexecution. The shared resource is identified by a unique name which is a `String`. The\nname can be user-defined or one of the predefined constants in `{Resources}`:\n`SYSTEM_PROPERTIES`, `SYSTEM_OUT`, `SYSTEM_ERR`, `LOCALE`, or `TIME_ZONE`.\n\nIn addition to declaring these shared resources statically, the `{ResourceLock}`\nannotation has a `providers` attribute that allows registering implementations of the\n`{ResourceLocksProvider}` interface that can add shared resources dynamically at runtime.\nNote that resources declared statically with `{ResourceLock}` annotation are combined with\nresources added dynamically by `{ResourceLocksProvider}` implementations.\n\nIf the tests in the following example were run in parallel _without_ the use of\n`{ResourceLock}`, they would be _flaky_. Sometimes they would pass, and at other times they\nwould fail due to the inherent race condition of writing and then reading the same JVM\nSystem Property.\n\nWhen access to shared resources is declared using the `{ResourceLock}` annotation, the\nJUnit Jupiter engine uses this information to ensure that no conflicting tests are run in\nparallel. This guarantee extends to lifecycle methods of a test class or method. For\nexample, if a test method is annotated with a `{ResourceLock}` annotation, the \"lock\" will\nbe acquired before any `@BeforeEach` methods are executed and released after all\n`@AfterEach` methods have been executed.\n\n[NOTE]\n.Running tests in isolation\n====\nIf most of your test classes can be run in parallel without any synchronization but you\nhave some test classes that need to run in isolation, you can mark the latter with the\n`{Isolated}` annotation. Tests in such classes are executed sequentially without any other\ntests running at the same time.\n====\n\nIn addition to the `String` that uniquely identifies the shared resource, you may specify\nan access mode. Two tests that require `READ` access to a shared resource may run in\nparallel with each other but not while any other test that requires `READ_WRITE` access\nto the same shared resource is running.\n\n[source,java]\n.Declaring shared resources \"statically\" with `{ResourceLock}` annotation\n----\ninclude::example$java/example/sharedresources/StaticSharedResourcesDemo.java[tags=user_guide]\n----\n\n[source,java]\n.Adding shared resources \"dynamically\" with `{ResourceLocksProvider}` implementation\n----\ninclude::example$java/example/sharedresources/DynamicSharedResourcesDemo.java[tags=user_guide]\n----\n\nAlso, \"static\" shared resources can be declared for _direct_ child nodes via the `target`\nattribute in the `{ResourceLock}` annotation, the attribute accepts a value from\nthe `{ResourceLockTarget}` enum.\n\nSpecifying `target = CHILDREN` in a class-level `{ResourceLock}` annotation\nhas the same semantics as adding an annotation with the same `value` and `mode`\nto each test method and nested test class declared in this class.\n\nThis may improve parallelization when a test class declares a `READ` lock,\nbut only a few methods hold a `READ_WRITE` lock.\n\nTests in the following example would run in the `SAME_THREAD` if the `{ResourceLock}`\ndidn't have `target = CHILDREN`. This is because the test class declares a `READ`\nshared resource, but one test method holds a `READ_WRITE` lock,\nwhich would force the `SAME_THREAD` execution mode for all the test methods.\n\n[source,java]\n.Declaring shared resources for child nodes with `target` attribute\n----\ninclude::example$java/example/sharedresources/ChildrenSharedResourcesDemo.java[tags=user_guide]\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc",
    "content": "= Parameterized Classes and Tests\n\n_Parameterized tests_ make it possible to run a test method multiple times with different\narguments. They are declared just like regular `@Test` methods but use the\n`{ParameterizedTest}` annotation instead.\n\n_Parameterized classes_ make it possible to run _all_ tests in a test class, including\nxref:writing-tests/nested-tests.adoc[], multiple times with different arguments. They are declared just\nlike regular test classes and may contain any supported test method type (including\n`@ParameterizedTest`) but annotated with the `{ParameterizedClass}` annotation.\n\nWARNING: _Parameterized classes_ are currently an _experimental_ feature. You're invited\nto give it a try and provide feedback to the JUnit team so they can improve and eventually\nxref:api-evolution.adoc[promote] this feature.\n\nRegardless of whether you are parameterizing a test method or a test class, you must\ndeclare at least one <<sources, source>> that will\nprovide the arguments for each invocation and then\n<<consuming-arguments, consume>> the arguments in the\nparameterized method or class, respectively.\n\nThe following example demonstrates a parameterized test that uses the `@ValueSource`\nannotation to specify a `String` array as the source of arguments.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=first_example]\n----\n\nWhen executing the above parameterized test method, each invocation will be reported\nseparately. For instance, the `ConsoleLauncher` will print output similar to the\nfollowing.\n\n....\npalindromes(String) ✔\n├─ [1] candidate = \"racecar\" ✔\n├─ [2] candidate = \"radar\" ✔\n└─ [3] candidate = \"able was I ere I saw elba\" ✔\n....\n\nThe same `@ValueSource` annotation can be used to specify the source of arguments for a\n`@ParameterizedClass`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedClassDemo.java[tags=first_example]\n----\n\nWhen executing the above parameterized test class, each invocation will be reported\nseparately. For instance, the `ConsoleLauncher` will print output similar to the\nfollowing.\n\n....\nPalindromeTests ✔\n├─ [1] candidate = \"racecar\" ✔\n│  ├─ palindrome() ✔\n│  └─ reversePalindrome() ✔\n├─ [2] candidate = \"radar\" ✔\n│  ├─ palindrome() ✔\n│  └─ reversePalindrome() ✔\n└─ [3] candidate = \"able was I ere I saw elba\" ✔\n   ├─ palindrome() ✔\n   └─ reversePalindrome() ✔\n....\n\n[[setup]]\n== Required Setup\n\nIn order to use parameterized classes or tests you need to add a dependency on the\n`junit-jupiter-params` artifact. Please refer to xref:appendix.adoc#dependency-metadata[Dependency Metadata] for details.\n\n[[consuming-arguments]]\n== Consuming Arguments\n\n[[consuming-arguments-methods]]\n=== Parameterized Tests\n\nParameterized test methods _consume_ arguments directly from the configured source (see\n<<sources>>) following a one-to-one correlation between argument source index and method\nparameter index (see examples in <<sources-CsvSource>>). However, a parameterized test\nmethod may also choose to _aggregate_ arguments from the source into a single object\npassed to the method (see <<argument-aggregation>>). Additional arguments may also be\nprovided by a `ParameterResolver` — for example, to obtain an instance of `TestInfo`,\n`TestReporter`, and so forth. Specifically, a parameterized test method must declare\nformal parameters according to the following rules.\n\n* Zero or more _indexed parameters_ must be declared first.\n* Zero or more _aggregators_ must be declared next.\n* Zero or more arguments supplied by a `ParameterResolver` must be declared last.\n\nIn this context, an _indexed parameter_ is an argument for a given index in the\n`{Arguments}` provided by an `{ArgumentsProvider}` that is passed as an argument to the\nparameterized method at the same index in the method's formal parameter list. An\n_aggregator_ is any parameter of type `{ArgumentsAccessor}` or any parameter annotated\nwith `{AggregateWith}`.\n\n[[consuming-arguments-classes]]\n=== Parameterized Classes\n\nParameterized classes _consume_ arguments directly from the configured source (see\n<<sources>>); either via their unique constructor or via\nfield injection. If a `{Parameter}`-annotated field is declared in the parameterized class\nor one of its superclasses, field injection will be used. Otherwise, constructor injection\nwill be used.\n\n[[consuming-arguments-constructor-injection]]\n==== Constructor Injection\n\nWARNING: Constructor injection can only be used with the (default) `PER_METHOD`\nxref:writing-tests/test-instance-lifecycle.adoc[test instance lifecycle] mode. Please use\n<<consuming-arguments-field-injection, field injection>>\nwith the `PER_CLASS` mode instead.\n\nFor constructor injection, the same rules apply as defined for\n<<consuming-arguments-methods, parameterized tests>>\nabove. In the following example, two arguments are injected into the constructor of the\ntest class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedClassDemo.java[tags=constructor_injection]\n----\n\nYou may use _records_ to implement parameterized classes that avoid the boilerplate code\nof declaring a test class constructor.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedRecordDemo.java[tags=example]\n----\n\n[[consuming-arguments-field-injection]]\n==== Field Injection\n\nFor field injection, the following rules apply for fields annotated with `@Parameter`.\n\n* Zero or more _indexed parameters_ may be declared; each must have a unique index\n  specified in its `@Parameter(index)` annotation. The index may be omitted if there is\n  only one indexed parameter. If there are at least two indexed parameter declarations,\n  there must be declarations for all indexes from 0 to the largest declared index.\n* Zero or more _aggregators_ may be declared; each without specifying an index in its\n  `@Parameter` annotation.\n* Zero or more other fields may be declared as usual as long as they're not annotated with\n  `@Parameter`.\n\nIn this context, an _indexed parameter_ is an argument for a given index in the\n`{Arguments}` provided by an `{ArgumentsProvider}` that is injected into a field annotated\nwith `@Parameter(index)`. An _aggregator_ is any `@Parameter`-annotated field of type\n{ArgumentsAccessor} or any field annotated with {AggregateWith}.\n\nThe following example demonstrates how to use field injection to consume multiple\narguments in a parameterized class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedClassDemo.java[tags=field_injection]\n----\n\nIf field injection is used, no constructor parameters will be resolved with arguments from\nthe source. Other xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[`ParameterResolver` extensions]\nmay resolve constructor parameters as usual, though.\n\n[[consuming-arguments-lifecycle-method]]\n==== Lifecycle Methods\n\n`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` can also\nbe used to consume arguments if their `injectArguments` attribute is set to `true` (the\ndefault). If so, their method signatures must follow the same rules apply as defined for\n<<consuming-arguments-methods, parameterized tests>> and\nadditionally use the same parameter types as the _indexed parameters_ of the parameterized\ntest class. Please refer to the Javadoc of `{BeforeParameterizedClassInvocation}` and\n`{AfterParameterizedClassInvocation}` for details and to the\n<<lifecycle-interop-classes, Lifecycle>> section for an\nexample.\n\n[NOTE]\n.AutoCloseable arguments\n====\nArguments that implement `java.lang.AutoCloseable` (or `java.io.Closeable` which extends\n`java.lang.AutoCloseable`) will be automatically closed after the parameterized class or\ntest invocation.\n\nTo prevent this from happening, set the `autoCloseArguments` attribute in\n`@ParameterizedTest` to `false`. Specifically, if an argument that implements\n`AutoCloseable` is reused for multiple invocations of the same parameterized class or test\nmethod, you must specify the `autoCloseArguments = false` on the `{ParameterizedClass}` or\n`{ParameterizedTest}` annotation to ensure that the argument is not closed between\ninvocations.\n====\n\n[[consuming-arguments-other-extensions]]\n=== Other Extensions\n\nOther extensions can access the parameters and resolved arguments of a parameterized test\nor class by retrieving a `{ParameterInfo}` object from the `{ExtensionContext_Store}`.\nPlease refer to the Javadoc of `{ParameterInfo}` for details.\n\n[[sources]]\n== Sources of Arguments\n\nOut of the box, JUnit Jupiter provides quite a few _source_ annotations. Each of the\nfollowing subsections provides a brief overview and an example for each of them. Please\nrefer to the Javadoc in the `{params-provider-package}` package for additional\ninformation.\n\nTIP: All source annotations in this section are applicable to both `{ParameterizedClass}`\nand `{ParameterizedTest}`. For the sake of brevity, the examples in this section will only\nshow how to use them with `{ParameterizedTest}` methods.\n\n[[sources-ValueSource]]\n=== @ValueSource\n\n`@ValueSource` is one of the simplest possible sources. It lets you specify a single\narray of literal values and can only be used for providing a single argument per\nparameterized test invocation.\n\nThe following types of literal values are supported by `@ValueSource`.\n\n- `short`\n- `byte`\n- `int`\n- `long`\n- `float`\n- `double`\n- `char`\n- `boolean`\n- `java.lang.String`\n- `java.lang.Class`\n\nFor example, the following `@ParameterizedTest` method will be invoked three times, with\nthe values `1`, `2`, and `3` respectively.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ValueSource_example]\n----\n\n[[sources-null-and-empty]]\n=== Null and Empty Sources\n\nIn order to check corner cases and verify proper behavior of our software when it is\nsupplied _bad input_, it can be useful to have `null` and _empty_ values supplied to our\nparameterized tests. The following annotations serve as sources of `null` and empty values\nfor parameterized tests that accept a single argument.\n\n* `{NullSource}`: provides a single `null` argument to the annotated `@ParameterizedClass`\n   or `@ParameterizedTest`.\n   - `@NullSource` cannot be used for a parameter that has a primitive type.\n* `{EmptySource}`: provides a single _empty_ argument to the annotated\n  `@ParameterizedClass` or `@ParameterizedTest` for parameters of the following types:\n  `java.lang.String`, `java.lang.Iterable`, `java.util.Iterator`, `java.util.ListIterator`\n  `java.util.Collection` (and concrete subtypes with a `public` no-arg constructor),\n  `java.util.List`, `java.util.Set`, `java.util.SortedSet`, `java.util.NavigableSet`,\n  `java.util.Map` (and concrete subtypes with a `public` no-arg constructor),\n  `java.util.SortedMap`, `java.util.NavigableMap`, primitive arrays (for example, `int[]`,\n  `char[][]`, etc.), object arrays (for example, `String[]`, `Integer[][]`, etc.).\n* `{NullAndEmptySource}`: a _composed annotation_ that combines the functionality of\n  `@NullSource` and `@EmptySource`.\n\nIf you need to supply multiple varying types of _blank_ strings to a parameterized\nclass or test, you can achieve that using\n<<sources-ValueSource>> -- for example,\n`@ValueSource(strings = {\"{nbsp}\", \"{nbsp}{nbsp}{nbsp}\", \"\\t\", \"\\n\"})`.\n\nYou can also combine `@NullSource`, `@EmptySource`, and `@ValueSource` to test a wider\nrange of `null`, _empty_, and _blank_ input. The following example demonstrates how to\nachieve this for strings.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example1]\n----\n\nMaking use of the composed `@NullAndEmptySource` annotation simplifies the above as\nfollows.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example2]\n----\n\nNOTE: Both variants of the `nullEmptyAndBlankStrings(String)` parameterized test method\nresult in six invocations: 1 for `null`, 1 for the empty string, and 4 for the explicit\nblank strings supplied via `@ValueSource`.\n\n[[sources-EnumSource]]\n=== @EnumSource\n\n`@EnumSource` provides a convenient way to use `Enum` constants.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example]\n----\n\nThe annotation's `value` attribute is optional. When omitted, the declared type of the\nfirst parameter is used. The test will fail if it does not reference an enum type.\nThus, the `value` attribute is required in the above example because the method parameter\nis declared as `TemporalUnit`, i.e. the interface implemented by `ChronoUnit`, which isn't\nan enum type. Changing the method parameter type to `ChronoUnit` allows you to omit the\nexplicit enum type from the annotation as follows.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example_autodetection]\n----\n\nThe annotation provides an optional `names` attribute that lets you specify which\nconstants shall be used, like in the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_include_example]\n----\n\nIn addition to `names`, you can use the `from` and `to` attributes to specify a range of\nconstants. The range starts from the constant specified in the `from` attribute and\nincludes all subsequent constants up to and including the one specified in the `to`\nattribute, based on the natural order of the enum constants.\n\nIf `from` and `to` attributes are omitted, they default to the first and last constants\nin the enum type, respectively. If all `names`, `from`, and `to` attributes are omitted,\nall constants will be used. The following example demonstrates how to specify a range of\nconstants.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_example]\n----\n\nThe `@EnumSource` annotation also provides an optional `mode` attribute that enables\nfine-grained control over which constants are passed to the test method. For example, you\ncan exclude names from the enum constant pool or specify regular expressions as in the\nfollowing examples.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_exclude_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_regex_example]\n----\n\nYou can also combine `mode` with the `from`, `to` and `names` attributes to define a\nrange of constants while excluding specific values from that range as shown below.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_exclude_example]\n----\n\n[[sources-MethodSource]]\n=== @MethodSource\n\n`{MethodSource}` allows you to refer to one or more _factory_ methods of the test class\nor external classes.\n\nFactory methods within the test class must be `static` unless the test class is annotated\nwith `@TestInstance(Lifecycle.PER_CLASS)`; whereas, factory methods in external classes\nmust always be `static`.\n\nEach factory method must generate a _stream_ of _arguments_, and each set of arguments\nwithin the stream will be provided as the physical arguments for individual invocations of\nthe annotated `@ParameterizedClass` or `@ParameterizedTest`. Generally speaking this\ntranslates to a `Stream` of `Arguments` (i.e., `Stream<Arguments>`); however, the actual\nconcrete return type can take on many forms. In this context, a \"stream\" is anything that\nJUnit can reliably convert into a `Stream`, such as `Stream`, `DoubleStream`,\n`LongStream`, `IntStream`, `Collection`, `Iterator`, `Iterable`, an array of objects or\nprimitives, or any type that provides an `iterator(): Iterator` method (such as, for\nexample, a `kotlin.sequences.Sequence`). The \"arguments\" within the stream can be supplied\nas an instance of `Arguments`, an array of objects (for example, `Object[]`), or a single\nvalue if the parameterized class or test method accepts a single argument.\n\nIf the return type is `Stream` or one of the primitive streams,\nJUnit will properly close it by calling `BaseStream.close()`,\nmaking it safe to use a resource such as `Files.lines()`.\n\nIf you only need a single parameter, you can return a `Stream` of instances of the\nparameter type as demonstrated in the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_example]\n----\n\nFor a `@ParameterizedClass`, providing a factory method name via `@MethodSource` is\nmandatory. For a `@ParameterizedTest`, if you do not explicitly provide a factory method\nname, JUnit Jupiter will search for a _factory_ method with the same name as the current\n`@ParameterizedTest` method by convention. This is demonstrated in the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_without_value_example]\n----\n\nStreams for primitive types (`DoubleStream`, `IntStream`, and `LongStream`) are also\nsupported as demonstrated by the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=primitive_MethodSource_example]\n----\n\nIf a parameterized class or test method declares multiple parameters, you need to return a\ncollection, stream, or array of `Arguments` instances or object arrays as shown below (see\nthe Javadoc for `{MethodSource}` for further details on supported return types). Note that\n`arguments(Object...)` is a static factory method defined in the `Arguments` interface. In\naddition, `Arguments.of(Object...)` may be used as an alternative to\n`arguments(Object...)`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSource_example]\n----\n\nAn external, `static` _factory_ method can be referenced by providing its _fully qualified\nmethod name_ as demonstrated in the following example.\n\n[source,java,indent=0]\n----\npackage example;\n\ninclude::example$java/example/ExternalMethodSourceDemo.java[tags=external_MethodSource_example]\n----\n\nFactory methods can declare parameters, which will be provided by registered\nimplementations of the `ParameterResolver` extension API. In the following example, the\nfactory method is referenced by its name since there is only one such method in the test\nclass. If there are several local methods with the same name, parameters can also be\nprovided to differentiate them – for example, `@MethodSource(\"factoryMethod()\")` or\n`@MethodSource(\"factoryMethod(java.lang.String)\")`. Alternatively, the factory method\ncan be referenced by its fully qualified method name — for example,\n`@MethodSource(\"example.MyTests#factoryMethod(java.lang.String)\")`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example]\n----\n\n[[sources-FieldSource]]\n=== @FieldSource\n\n`{FieldSource}` allows you to refer to one or more fields of the test class or external\nclasses.\n\nFields within the test class must be `static` unless the test class is annotated with\n`@TestInstance(Lifecycle.PER_CLASS)`; whereas, fields in external classes must always be\n`static`.\n\nEach field must be able to supply a _stream_ of arguments, and each set of \"arguments\"\nwithin the \"stream\" will be provided as the physical arguments for individual invocations\nof the annotated `@ParameterizedClass` or `@ParameterizedTest`.\n\nIn this context, a \"stream\" is anything that JUnit can reliably convert to a `Stream`;\nhowever, the actual concrete field type can take on many forms. Generally speaking this\ntranslates to a `Collection`, an `Iterable`, a `Supplier` of a stream (`Stream`,\n`DoubleStream`, `LongStream`, or `IntStream`), a `Supplier` of an `Iterator`, an array of\nobjects or primitives, or any type that provides an `iterator(): Iterator` method (such\nas, for example, a `kotlin.sequences.Sequence`). Each set of \"arguments\" within the\n\"stream\" can be supplied as an instance of `Arguments`, an array of objects (for example,\n`Object[]`, `String[]`, etc.), or a single value if the parameterized class or test method accepts\na single argument.\n\n[WARNING]\n====\nIn contrast to the supported return types for\n<<sources-MethodSource, `@MethodSource`>> factory\nmethods, the value of a `@FieldSource` field cannot be an instance of `Stream`,\n`DoubleStream`, `LongStream`, `IntStream`, or `Iterator`, since the values of such types\nare _consumed_ the first time they are processed. However, if you wish to use one of\nthese types, you can wrap it in a `Supplier` — for example, `Supplier<IntStream>`.\n====\n\nIf the `Supplier` return type is `Stream` or one of the primitive streams,\nJUnit will properly close it by calling `BaseStream.close()`,\nmaking it safe to use a resource such as `Files.lines()`.\n\nPlease note that a one-dimensional array of objects supplied as a set of \"arguments\" will\nbe handled differently than other types of arguments. Specifically, all the elements of a\none-dimensional array of objects will be passed as individual physical arguments to the\n`@ParameterizedClass` or `@ParameterizedTest`. See the Javadoc for `{FieldSource}` for\nfurther details.\n\nFor a `@ParameterizedClass`, providing a field name via `@FieldSource` is mandatory. For a\n`@ParameterizedTest`, if you do not explicitly provide a field name, JUnit Jupiter will\nsearch in the test class for a field that has the same name as the current\n`@ParameterizedTest` method by convention. This is demonstrated in the following example.\nThis parameterized test method will be invoked twice: with the values `\"apple\"` and\n`\"banana\"`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=default_field_FieldSource_example]\n----\n\nThe following example demonstrates how to provide a single explicit field name via\n`@FieldSource`. This parameterized test method will be invoked twice: with the values\n`\"apple\"` and `\"banana\"`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=explicit_field_FieldSource_example]\n----\n\nThe following example demonstrates how to provide multiple explicit field names via\n`@FieldSource`. This example uses the `listOfFruits` field from the previous example as\nwell as the `additionalFruits` field. Consequently, this parameterized test method will\nbe invoked four times: with the values `\"apple\"`, `\"banana\"`, `\"cherry\"`, and\n`\"dewberry\"`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=multiple_fields_FieldSource_example]\n----\n\nIt is also possible to provide a `Stream`, `DoubleStream`, `IntStream`, `LongStream`, or\n`Iterator` as the source of arguments via a `@FieldSource` field as long as the stream or\niterator is wrapped in a `java.util.function.Supplier`. The following example demonstrates\nhow to provide a `Supplier` of a `Stream` of named arguments. This parameterized test\nmethod will be invoked twice: with the values `\"apple\"` and `\"banana\"` and with display\nnames `\"Apple\"` and `\"Banana\"`, respectively.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=named_arguments_FieldSource_example]\n----\n\n[NOTE]\n====\nNote that `arguments(Object...)` is a static factory method defined in the\n`org.junit.jupiter.params.provider.Arguments` interface.\n\nSimilarly, `named(String, Object)` is a static factory method defined in the\n`org.junit.jupiter.api.Named` interface.\n====\n\nIf a parameterized class or test method declares multiple parameters, the corresponding\n`@FieldSource` field must be able to provide a collection, stream supplier, or array of\n`Arguments` instances or object arrays as shown below (see the Javadoc for `{FieldSource}`\nfor further details on supported types).\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSource_example]\n----\n\n[NOTE]\n====\nNote that `arguments(Object...)` is a static factory method defined in the\n`org.junit.jupiter.params.provider.Arguments` interface.\n====\n\nAn external, `static` `@FieldSource` field can be referenced by providing its\n_fully qualified field name_ as demonstrated in the following example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ExternalFieldSourceDemo.java[tags=external_field_FieldSource_example]\n----\n\n[[sources-CsvSource]]\n=== @CsvSource\n\n`@CsvSource` allows you to express argument lists as comma-separated values (i.e., CSV\n`String` literals). Each string provided via the `value` attribute in `@CsvSource`\nrepresents a CSV record and results in one invocation of the parameterized class or\ntest. The first record may optionally be used to supply CSV headers (see the Javadoc for\nthe `useHeadersInDisplayName` attribute for details and an example).\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=CsvSource_example]\n----\n\nThe default delimiter is a comma (`,`), but you can use another character by setting the\n`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a\n`String` delimiter instead of a single character. However, both delimiter attributes\ncannot be set simultaneously.\n\nBy default, `@CsvSource` uses a single quote (`'`) as its quote character, but this can be\nchanged via the `quoteCharacter` attribute. See the `'lemon, lime'` value in the example\nabove and in the table below. An empty, quoted value (`''`) results in an empty `String`\nunless the `emptyValue` attribute is set; whereas, an entirely _empty_ value is\ninterpreted as a `null` reference. By specifying one or more `nullValues`, a custom value\ncan be interpreted as a `null` reference (see the `NIL` example in the table below). An\n`ArgumentConversionException` is thrown if the target type of a `null` reference is a\nprimitive type.\n\nNOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless\nof any custom values configured via the `nullValues` attribute.\n\nExcept within a quoted string, leading and trailing whitespace in a CSV column is trimmed\nby default. This behavior can be changed by setting the\n`ignoreLeadingAndTrailingWhitespace` attribute to `true`.\n\n[cols=\"50,50\"]\n|===\n| Example Input                                                                           | Resulting Argument List\n\n| `@CsvSource({ \"apple, banana\" })`                                                       | `\"apple\"`, `\"banana\"`\n| `@CsvSource({ \"apple, 'lemon, lime'\" })`                                                | `\"apple\"`, `\"lemon, lime\"`\n| `@CsvSource({ \"apple, ''\" })`                                                           | `\"apple\"`, `\"\"`\n| `@CsvSource({ \"apple, \" })`                                                             | `\"apple\"`, `null`\n| `@CsvSource(value = { \"apple, banana, NIL\" }, nullValues = \"NIL\")`                      | `\"apple\"`, `\"banana\"`, `null`\n| `@CsvSource(value = { \" apple , banana\" }, ignoreLeadingAndTrailingWhitespace = false)` | `\" apple \"`, `\" banana\"`\n|===\n\nIf the programming language you are using supports Java _text blocks_  or equivalent\nmulti-line string literals, you can alternatively use the `textBlock` attribute of\n`@CsvSource`. Each record within a text block represents a CSV record and results in one\ninvocation of the parameterized class or test. The first record may optionally be used to\nsupply CSV headers by setting the `useHeadersInDisplayName` attribute to `true` as in the\nexample below.\n\nUsing a text block, the previous example can be implemented as follows.\n\n[source,java,indent=0]\n----\n@ParameterizedTest\n@CsvSource(useHeadersInDisplayName = true, textBlock = \"\"\"\n\tFRUIT,         RANK\n\tapple,         1\n\tbanana,        2\n\t'lemon, lime', 0xF1\n\tstrawberry,    700_000\n\t\"\"\")\nvoid testWithCsvSource(String fruit, int rank) {\n\t// ...\n}\n----\n\nThe generated display names for the previous example include the CSV header names.\n\n----\n[1] FRUIT = \"apple\", RANK = \"1\"\n[2] FRUIT = \"banana\", RANK = \"2\"\n[3] FRUIT = \"lemon, lime\", RANK = \"0xF1\"\n[4] FRUIT = \"strawberry\", RANK = \"700_000\"\n----\n\nIn contrast to CSV records supplied via the `value` attribute, a text block can contain\ncomments. Any line beginning with the value of the `commentCharacter` attribute (`+++#+++`\nby default) will be treated as a comment and ignored. Note that there is one exception\nto this rule: if the comment character appears within a quoted field, it loses\nits special meaning.\n\nThe comment character must be the first character on the line without any leading\nwhitespace. It is therefore recommended that the closing text block delimiter (`\"\"\"`)\nbe placed either at the end of the last line of input or on the following line,\nleft aligned with the rest of the input (as can be seen in the example below which\ndemonstrates formatting similar to a table).\n\n[source,java,indent=0]\n----\n@ParameterizedTest\n@CsvSource(delimiter = '|', quoteCharacter = '\"', textBlock = \"\"\"\n\t#-----------------------------\n\t#    FRUIT     |     RANK\n\t#-----------------------------\n\t     apple     |      1\n\t#-----------------------------\n\t     banana    |      2\n\t#-----------------------------\n\t  \"lemon lime\" |     0xF1\n\t#-----------------------------\n\t   strawberry  |    700_000\n\t#-----------------------------\n\t\"\"\")\nvoid testWithCsvSource(String fruit, int rank) {\n\t// ...\n}\n----\n\n[NOTE]\n====\nJava's https://docs.oracle.com/en/java/javase/17/text-blocks/index.html[text block]\nfeature automatically removes _incidental whitespace_ when the code is compiled.\nHowever other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a\nprogramming language other than Java and your text block contains comments or new lines\nwithin quoted strings, you will need to ensure that there is no leading whitespace within\nyour text block.\n====\n\n[[sources-CsvFileSource]]\n=== @CsvFileSource\n\n`@CsvFileSource` lets you use comma-separated value (CSV) files from the classpath or the\nlocal file system. Each record from a CSV file results in one invocation of the\nparameterized class or test. The first record may optionally be used to supply CSV\nheaders. You can instruct JUnit to ignore the headers via the `numLinesToSkip` attribute.\nIf you would like for the headers to be used in the display names, you can set the\n`useHeadersInDisplayName` attribute to `true`. The examples below demonstrate the use of\n`numLinesToSkip` and `useHeadersInDisplayName`.\n\nThe default delimiter is a comma (`,`), but you can use another character by setting the\n`delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a\n`String` delimiter instead of a single character. However, both delimiter attributes\ncannot be set simultaneously.\n\n.Comments in CSV files\nNOTE: Any line beginning with the value of the `commentCharacter` attribute (`+++#+++`\nby default) will be interpreted as a comment and will be ignored.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=CsvFileSource_example]\n----\n\n[source,csv,indent=0]\n.two-column.csv\n----\ninclude::example$resources/two-column.csv[]\n----\n\nThe following listing shows the generated display names for the first two parameterized\ntest methods above.\n\n----\n[1] country = \"Sweden\", reference = \"1\"\n[2] country = \"Poland\", reference = \"2\"\n[3] country = \"United States of America\", reference = \"3\"\n[4] country = \"France\", reference = \"700_000\"\n----\n\nThe following listing shows the generated display names for the last parameterized test\nmethod above that uses CSV header names.\n\n----\n[1] COUNTRY = \"Sweden\", REFERENCE = \"1\"\n[2] COUNTRY = \"Poland\", REFERENCE = \"2\"\n[3] COUNTRY = \"United States of America\", REFERENCE = \"3\"\n[4] COUNTRY = \"France\", REFERENCE = \"700_000\"\n----\n\nIn contrast to the default syntax used in `@CsvSource`, `@CsvFileSource` uses a double\nquote (`+++\"+++`) as the quote character by default, but this can be changed via the\n`quoteCharacter` attribute. See the `\"United States of America\"` value in the example\nabove. An empty, quoted value (`+++\"\"+++`) results in an empty `String` unless the\n`emptyValue` attribute is set; whereas, an entirely _empty_ value is interpreted as a\n`null` reference. By specifying one or more `nullValues`, a custom value can be\ninterpreted as a `null` reference. An `ArgumentConversionException` is thrown if the\ntarget type of a `null` reference is a primitive type.\n\nNOTE: An _unquoted_ empty value will always be converted to a `null` reference regardless\nof any custom values configured via the `nullValues` attribute.\n\nExcept within a quoted string, leading and trailing whitespace in a CSV column is trimmed\nby default. This behavior can be changed by setting the\n`ignoreLeadingAndTrailingWhitespace` attribute to `true`.\n\n[[sources-ArgumentsSource]]\n=== @ArgumentsSource\n\n`@ArgumentsSource` can be used to specify a custom, reusable `ArgumentsProvider`. Note\nthat an implementation of `ArgumentsProvider` must be declared as either a top-level\nclass or as a `static` nested class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsSource_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProvider_example]\n----\n\nIf you wish to implement a custom `ArgumentsProvider` that also consumes an annotation\n(like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`),\nyou have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class.\n\nMoreover, `ArgumentsProvider` implementations may declare constructor parameters in case\nthey need to be resolved by a registered `ParameterResolver` as demonstrated in the\nfollowing example.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProviderWithConstructorInjection_example]\n----\n\n[[repeatable-sources]]\n=== Multiple sources using repeatable annotations\n\nRepeatable annotations provide a convenient way to specify multiple sources from\ndifferent providers.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=repeatable_annotations]\n----\n\nFollowing the above parameterized test, a test case will run for each argument:\n\n----\n[1] foo\n[2] bar\n----\n\nThe following annotations are repeatable:\n\n* `@ValueSource`\n* `@EnumSource`\n* `@MethodSource`\n* `@FieldSource`\n* `@CsvSource`\n* `@CsvFileSource`\n* `@ArgumentsSource`\n\n[[argument-count-validation]]\n== Argument Count Validation\n\nBy default, when an arguments source provides more arguments than the test method needs,\nthose additional arguments are ignored and the test executes as usual.\nThis can lead to bugs where arguments are never passed to the parameterized class or\nmethod.\n\nTo prevent this, you can set argument count validation to 'strict'.\nThen, any additional arguments will cause an error instead.\n\nTo change this behavior for all tests, set the\n`junit.jupiter.params.argumentCountValidation`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to `strict`.\nTo change this behavior for a single parameterized class or test method,\nuse the `argumentCountValidation` attribute of the `@ParameterizedClass` or\n`@ParameterizedTest` annotation:\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=argument_count_validation]\n----\n\n[[argument-conversion]]\n== Argument Conversion\n\n[[argument-conversion-widening]]\n=== Widening Conversion\n\nJUnit Jupiter supports\nhttps://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2[Widening Primitive\nConversion] for arguments supplied to a `@ParameterizedClass` or `@ParameterizedTest`.\nFor example, a parameterized class or test method annotated with\n`@ValueSource(ints = { 1, 2, 3 })` can be declared to accept not only an argument of type\n`int` but also an argument of type `long`, `float`, or `double`.\n\n[[argument-conversion-implicit]]\n=== Implicit Conversion\n\nTo support use cases like `@CsvSource`, JUnit Jupiter provides a number of built-in\nimplicit type converters. The conversion process depends on the declared type of each\nmethod parameter.\n\nFor example, if a `@ParameterizedClass` or `@ParameterizedTest` declares a parameter\nof type `TimeUnit` and the actual type supplied by the declared source is a `String`, the\nstring will be automatically converted into the corresponding `TimeUnit` enum constant.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=implicit_conversion_example]\n----\n\n`String` instances are implicitly converted to the following target types.\n\nNOTE: Decimal, hexadecimal, and octal `String` literals will be converted to their\nintegral types: `byte`, `short`, `int`, `long`, and their boxed counterparts.\n\n[[argument-conversion-implicit-table]]\n[cols=\"10,90\"]\n|===\n| Target Type | Example\n\n| `boolean`/`Boolean`        | `\"true\"`                                 -> `true` _(only accepts values 'true' or 'false', case-insensitive)_\n| `byte`/`Byte`              | `\"15\"`, `\"0xF\"`, or `\"017\"`              -> `(byte) 15`\n| `char`/`Character`         | `\"o\"`                                    -> `'o'`\n| `short`/`Short`            | `\"15\"`, `\"0xF\"`, or `\"017\"`              -> `(short) 15`\n| `int`/`Integer`            | `\"15\"`, `\"0xF\"`, or `\"017\"`              -> `15`\n| `long`/`Long`              | `\"15\"`, `\"0xF\"`, or `\"017\"`              -> `15L`\n| `float`/`Float`            | `\"1.0\"`                                  -> `1.0f`\n| `double`/`Double`          | `\"1.0\"`                                  -> `1.0d`\n| `Enum` subclass            | `\"SECONDS\"`                              -> `TimeUnit.SECONDS`\n| `java.io.File`             | `\"/path/to/file\"`                        -> `new File(\"/path/to/file\")`\n| `java.lang.Class`          | `\"java.lang.Integer\"`                    -> `java.lang.Integer.class` _(use `$` for nested classes: `\"java.lang.Thread$State\"`)_\n| `java.lang.Class`          | `\"byte\"`                                 -> `byte.class` _(primitive types are supported)_\n| `java.lang.Class`          | `\"char[]\"`                               -> `char[].class` _(array types are supported)_\n| `java.math.BigDecimal`     | `\"123.456e789\"`                          -> `new BigDecimal(\"123.456e789\")`\n| `java.math.BigInteger`     | `\"1234567890123456789\"`                  -> `new BigInteger(\"1234567890123456789\")`\n| `java.net.URI`             | `\"https://junit.org/\"`                   -> `URI.create(\"https://junit.org/\")`\n| `java.net.URL`             | `\"https://junit.org/\"`                   -> `URI.create(\"https://junit.org/\").toURL()`\n| `java.nio.charset.Charset` | `\"UTF-8\"`                                -> `Charset.forName(\"UTF-8\")`\n| `java.nio.file.Path`       | `\"/path/to/file\"`                        -> `Path.of(\"/path/to/file\")`\n| `java.time.Duration`       | `\"PT3S\"`                                 -> `Duration.ofSeconds(3)`\n| `java.time.Instant`        | `\"1970-01-01T00:00:00Z\"`                 -> `Instant.ofEpochMilli(0)`\n| `java.time.LocalDateTime`  | `\"2017-03-14T12:34:56.789\"`              -> `LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)`\n| `java.time.LocalDate`      | `\"2017-03-14\"`                           -> `LocalDate.of(2017, 3, 14)`\n| `java.time.LocalTime`      | `\"12:34:56.789\"`                         -> `LocalTime.of(12, 34, 56, 789_000_000)`\n| `java.time.MonthDay`       | `\"--03-14\"`                              -> `MonthDay.of(3, 14)`\n| `java.time.OffsetDateTime` | `\"2017-03-14T12:34:56.789Z\"`             -> `OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)`\n| `java.time.OffsetTime`     | `\"12:34:56.789Z\"`                        -> `OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)`\n| `java.time.Period`         | `\"P2M6D\"`                                -> `Period.of(0, 2, 6)`\n| `java.time.YearMonth`      | `\"2017-03\"`                              -> `YearMonth.of(2017, 3)`\n| `java.time.Year`           | `\"2017\"`                                 -> `Year.of(2017)`\n| `java.time.ZonedDateTime`  | `\"2017-03-14T12:34:56.789Z\"`             -> `ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)`\n| `java.time.ZoneId`         | `\"Europe/Berlin\"`                        -> `ZoneId.of(\"Europe/Berlin\")`\n| `java.time.ZoneOffset`     | `\"+02:30\"`                               -> `ZoneOffset.ofHoursMinutes(2, 30)`\n| `java.util.Currency`       | `\"JPY\"`                                  -> `Currency.getInstance(\"JPY\")`\n| `java.util.Locale`         | `\"en-US\"`                                -> `Locale.forLanguageTag(\"en-US\")`\n| `java.util.UUID`           | `\"d043e930-7b3b-48e3-bdbe-5a3ccfb833db\"` -> `UUID.fromString(\"d043e930-7b3b-48e3-bdbe-5a3ccfb833db\")`\n|===\n\n[[argument-conversion-implicit-fallback]]\n==== Fallback String-to-Object Conversion\n\nIn addition to implicit conversion from strings to the target types listed in the above\ntable, JUnit Jupiter also provides a fallback mechanism for automatic conversion from a\n`String` to a given target type if the target type declares a suitable _factory method_\nor _factory constructor_ as defined below.\n\n- __factory method__: a non-private, `static` method declared in the target type that\n  accepts either a single `String` argument or a single `CharSequence` argument and\n  returns an instance of the target type. The name of the method can be arbitrary and need\n  not follow any particular convention.\n- __factory constructor__: a non-private constructor in the target type that accepts a\n  either a single `String` argument or a single `CharSequence` argument. Note that the\n  target type must be declared as either a top-level class or as a `static` nested class.\n\nIf there are multiple _factory methods_ or _factory constructors_, matching proceeds in the\nfollowing order:\n\n1. A single _factory method_ accepting a `String` argument.\n2. A single _factory constructor_ accepting a `String` argument.\n3. A single _factory method_ accepting a `CharSequence` argument.\n4. A single _factory constructor_ accepting a `CharSequence` argument.\n5. A single _factory method_ accepting a `String` argument once all `@Deprecated` factory\nmethods have been removed from the set of methods being considered.\n6. A single _factory method_ accepting a `CharSequence` argument once all `@Deprecated` factory\nmethods have been removed from the set of methods being considered.\n\nFor example, in the following `@ParameterizedTest` method, the `Book` argument will be\ncreated by invoking the `Book.fromTitle(String)` factory method and passing `\"42 Cats\"`\nas the title of the book.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example_Book]\n----\n\n[[argument-conversion-explicit]]\n=== Explicit Conversion\n\nInstead of relying on implicit argument conversion, you may explicitly specify an\n`{ArgumentConverter}` to use for a certain parameter using the `@ConvertWith` annotation\nlike in the following example. Note that an implementation of `ArgumentConverter` must be\ndeclared as either a top-level class or as a `static` nested class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_ToStringArgumentConverter]\n----\n\nIf the converter is only meant to convert one type to another, you can extend\n`TypedArgumentConverter` to avoid boilerplate type checks.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_TypedArgumentConverter]\n----\n\nExplicit argument converters are meant to be implemented by test and extension authors.\nThus, `junit-jupiter-params` only provides a single explicit argument converter that may\nalso serve as a reference implementation: `JavaTimeArgumentConverter`. It is used via the\ncomposed annotation `JavaTimeConversionPattern`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=explicit_java_time_converter]\n----\n\nIf you wish to implement a custom `{ArgumentConverter}` that also consumes an annotation\n(like `JavaTimeArgumentConverter`), you have the possibility to extend the\n`{AnnotationBasedArgumentConverter}` class.\n\n[[argument-aggregation]]\n== Argument Aggregation\n\nBy default, each _argument_ provided to a `@ParameterizedClass` or `@ParameterizedTest`\ncorresponds to a single method parameter. Consequently, argument sources which are\nexpected to supply a large number of arguments can lead to large constructor or method\nsignatures, respectively.\n\nIn such cases, an `{ArgumentsAccessor}` can be used instead of multiple parameters. Using\nthis API, you can access the provided arguments through a single argument passed to your\ntest method. In addition, type conversion is supported as discussed in\n<<argument-conversion-implicit>>.\n\nBesides, you can retrieve the current test invocation index with\n`ArgumentsAccessor.getInvocationIndex()`.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAccessor_example]\n----\n\n_An instance of `ArgumentsAccessor` is automatically injected into any parameter of type\n`ArgumentsAccessor`._\n\n[[argument-aggregation-custom]]\n=== Custom Aggregators\n\nApart from direct access to the arguments of a `@ParameterizedClass` or\n`@ParameterizedTest` using an `ArgumentsAccessor`, JUnit Jupiter also supports the usage\nof custom, reusable _aggregators_.\n\nTo use a custom aggregator, implement the `{ArgumentsAggregator}` interface and register\nit via the `@AggregateWith` annotation on a compatible parameter of the\n`@ParameterizedClass` or `@ParameterizedTest`. The result of the aggregation will then be\nprovided as an argument for the corresponding parameter when the parameterized test is\ninvoked. Note that an implementation of `ArgumentsAggregator` must be declared as either a\ntop-level class or as a `static` nested class.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example_PersonAggregator]\n----\n\nIf you find yourself repeatedly declaring `@AggregateWith(MyTypeAggregator.class)` for\nmultiple parameterized classes or methods across your codebase, you may wish to create a\ncustom _composed annotation_ such as `@CsvToMyType` that is meta-annotated with\n`@AggregateWith(MyTypeAggregator.class)`. The following example demonstrates this in\naction with a custom `@CsvToPerson` annotation.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example]\n----\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example_CsvToPerson]\n----\n\n\n[[display-names]]\n== Customizing Display Names\n\nBy default, the display name of a parameterized class or test invocation contains the\ninvocation index and a comma-separated list of the `String` representations of all\narguments for that specific invocation. If parameter names are present in the bytecode,\neach argument will be preceded by its parameter name and an equals sign (unless the\nargument is only available via an `ArgumentsAccessor` or `ArgumentAggregator`) – for\nexample, `firstName = \"Jane\"`.\n\n[TIP]\n====\nTo ensure that parameter names are present in the bytecode, test code must be compiled\nwith the `-parameters` compiler flag for Java or with the `-java-parameters` compiler flag\nfor Kotlin.\n====\n\nHowever, you can customize invocation display names via the `name` attribute of the\n`@ParameterizedClass` or `@ParameterizedTest` annotation as in the following example.\n\n======\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=custom_display_names]\n----\n\nWhen executing the above method using the `ConsoleLauncher` you will see output similar to\nthe following.\n\n....\nDisplay name of container ✔\n├─ 1 ==> the rank of \"apple\" is \"1\" ✔\n├─ 2 ==> the rank of \"banana\" is \"2\" ✔\n└─ 3 ==> the rank of \"lemon, lime\" is \"3\" ✔\n....\n======\n\n[NOTE]\n====\nPlease note that `name` is a `MessageFormat` pattern. Thus, a single quote (`'`) needs to\nbe represented as a doubled single quote (`''`) in order to be displayed.\n====\n\nThe following placeholders are supported within custom display names.\n\n[cols=\"20,80\"]\n|===\n| Placeholder                              | Description\n\n| `\\{displayName}`                         | the display name of the method\n| `\\{index}`                               | the current invocation index (1-based)\n| `\\{arguments}`                           | the complete, comma-separated arguments list\n| `\\{argumentsWithNames}`                  | the complete, comma-separated arguments list with parameter names\n| `\\{argumentSetName}`                     | the name of the argument set\n| `\\{argumentSetNameOrArgumentsWithNames}` | `\\{argumentSetName}` or `\\{argumentsWithNames}`, depending on how the arguments are supplied\n| `\\{0}`, `\\{1}`, ...                      | an individual argument\n|===\n\nNOTE: When including arguments in display names, their string representations are truncated\nif they exceed the configured maximum length. The limit is configurable via the\n`junit.jupiter.params.displayname.argument.maxlength` configuration parameter and defaults\nto 512 characters.\n\nWhen using `@MethodSource`, `@FieldSource`, or `@ArgumentsSource`, you can provide custom\nnames for individual arguments or custom names for entire sets of arguments.\n\nUse the `{Named}` API to provide a custom name for an individual argument, and the custom\nname will be used if the argument is included in the invocation display name, like in the\nexample below.\n\n======\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=named_arguments]\n----\n\nWhen executing the above method using the `ConsoleLauncher` you will see output similar to\nthe following.\n\n....\nA parameterized test with named arguments ✔\n├─ 1: An important file ✔\n└─ 2: Another file ✔\n....\n======\n\n[NOTE]\n====\nNote that `arguments(Object...)` is a static factory method defined in the\n`org.junit.jupiter.params.provider.Arguments` interface.\n\nSimilarly, `named(String, Object)` is a static factory method defined in the\n`org.junit.jupiter.api.Named` interface.\n====\n\nUse the `ArgumentSet` API to provide a custom name for the entire set of arguments, and\nthe custom name will be used as the display name, like in the example below.\n\n======\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=named_argument_set]\n----\n\nWhen executing the above method using the `ConsoleLauncher` you will see output similar to\nthe following.\n\n....\nA parameterized test with named argument sets ✔\n├─ [1] Important files ✔\n└─ [2] Other files ✔\n....\n======\n\n[NOTE]\n====\nNote that `argumentSet(String, Object...)` is a static factory method defined in the\n`org.junit.jupiter.params.provider.Arguments` interface.\n====\n\n[[display-names-quoted-text]]\n=== Quoted Text-based Arguments\n\nAs of JUnit Jupiter 6.0, text-based arguments in display names for parameterized tests are\nquoted by default. In this context, any `CharSequence` (such as a `String`) or `Character`\nis considered text. A `CharSequence` is wrapped in double quotes (`\"`), and a `Character`\nis wrapped in single quotes (`'`).\n\nSpecial characters will be escaped in the quoted text. For example, carriage returns and\nline feeds will be escaped as `\\\\r` and `\\\\n`, respectively.\n\n[TIP]\n====\nThis feature can be disabled by setting the `quoteTextArguments` attributes in\n`@ParameterizedClass` and `@ParameterizedTest` to `false`.\n====\n\nFor example, given a string argument `\"line 1\\nline 2\"`, the physical representation in\nthe display name will be `\"\\\"line 1\\\\nline 2\\\"\"` which is printed as `\"line 1\\nline 2\"`.\nSimilarly, given a string argument `\"\\t\"`, the physical representation in the display name\nwill be `\"\\\"\\\\t\\\"\"` which is printed as `\"\\t\"` instead of a blank string or invisible tab\ncharacter. The same applies for a character argument `'\\t'`, whose physical representation\nin the display name would be `\"'\\\\t'\"` which is printed as `'\\t'`.\n\nFor a concrete example, if you run the first `nullValuesAndBlankStrings(String text)`\nparameterized test method from the <<sources-null-and-empty>> section above, the following\ndisplay names are generated.\n\n----\n[1] text = null\n[2] text = \"\"\n[3] text = \" \"\n[4] text = \"   \"\n[5] text = \"\\t\"\n[6] text = \"\\n\"\n----\n\nIf you run the first `testWithCsvSource(String fruit, int rank)` parameterized test method\nfrom the <<sources-CsvSource>> section above, the following display names are generated.\n\n----\n[1] fruit = \"apple\", rank = \"1\"\n[2] fruit = \"banana\", rank = \"2\"\n[3] fruit = \"lemon, lime\", rank = \"0xF1\"\n[4] fruit = \"strawberry\", rank = \"700_000\"\n----\n\n[NOTE]\n====\nThe original source arguments are quoted when generating a display name, and this occurs\nbefore any implicit or explicit argument conversion is performed.\n\nFor example, if a parameterized test accepts `3.14` as a `float` argument that was\nconverted from `\"3.14\"` as an input string, `\"3.14\"` will be present in the display name\ninstead of `3.14`. You can see the effect of this with the `rank` values in the above\nexample.\n====\n\n[[display-names-default-pattern]]\n=== Default Display Name Pattern\n\nIf you'd like to set a default name pattern for all parameterized classes and tests in\nyour project, you can declare the `junit.jupiter.params.displayname.default` configuration\nparameter in the `junit-platform.properties` file as demonstrated in the following example (see\nxref:running-tests/configuration-parameters.adoc[] for other options).\n\n[source,properties,indent=0]\n----\njunit.jupiter.params.displayname.default = {index}\n----\n\n[[display-names-precedence-rules]]\n=== Precedence Rules\n\nThe display name for a parameterized class or test is determined according to the\nfollowing precedence rules:\n\n1. `name` attribute in `@ParameterizedClass` or `@ParameterizedTest`, if present\n2. value of the `junit.jupiter.params.displayname.default` configuration parameter, if present\n3. `DEFAULT_DISPLAY_NAME` constant defined in\n   `org.junit.jupiter.params.ParameterizedInvocationConstants`\n\n[[lifecycle-interop]]\n== Lifecycle and Interoperability\n\n[[lifecycle-interop-methods]]\n=== Parameterized Tests\n\nEach invocation of a parameterized test has the same lifecycle as a regular `@Test`\nmethod. For example, `@BeforeEach` methods will be executed before each invocation.\nSimilar to xref:writing-tests/dynamic-tests.adoc[], invocations will appear one by one in the\ntest tree of an IDE. You may at will mix regular `@Test` methods and `@ParameterizedTest`\nmethods within the same test class.\n\nYou may use `ParameterResolver` extensions with `@ParameterizedTest` methods. However,\nmethod parameters that are resolved by argument sources need to come first in the\nparameter list. Since a test class may contain regular tests as well as parameterized\ntests with different parameter lists, values from argument sources are not resolved for\nlifecycle methods (for example, `@BeforeEach`) and test class constructors.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/ParameterizedTestDemo.java[tags=ParameterResolver_example]\n----\n\n[[lifecycle-interop-classes]]\n=== Parameterized Classes\n\nEach invocation of a parameterized class has the same lifecycle as a regular test class.\nFor example, `@BeforeAll` methods will be executed _once_ before all invocations and\n`@BeforeEach` methods will be executed before each _test method_ invocation. Similar to\nxref:writing-tests/dynamic-tests.adoc[], invocations will appear one by one in the test tree of an\nIDE.\n\nYou may use `ParameterResolver` extensions with `@ParameterizedClass` constructors.\nHowever, if constructor injection is used, constructor parameters that are resolved by\nargument sources need to come first in the parameter list. Values from argument sources\nare not resolved for regular lifecycle methods (for example, `@BeforeEach`).\n\nIn addition to regular lifecycle methods, parameterized classes may declare\n`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` lifecycle\nmethods that are called once before/after each invocation of the parameterized class.\nThese methods must be `static` unless the parameterized class is configured to use\n`@TestInstance(Lifecycle.PER_CLASS)` (see xref:writing-tests/test-instance-lifecycle.adoc[]).\n\nThese lifecycle methods may optionally declare parameters that are resolved depending on\nthe setting of the `injectArguments` annotation attribute. If it is set to `false`, the\nparameters must be resolved by other registered {ParameterResolver} extensions. If the\nattribute is set to `true` (the default), the method may declare parameters that match the\narguments of the parameterized class (see the Javadoc of\n`{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` for\ndetails). This may, for example, be used to initialize the used arguments as demonstrated\nby the following example.\n\n[source,java,indent=0]\n.Using parameterized class lifecycle methods\n----\ninclude::example$java/example/ParameterizedLifecycleDemo.java[tags=example]\n----\n<1> Initialization of the argument _before_ each invocation of the parameterized class\n<2> Usage of the previously initialized argument in a test method\n<3> Validation and cleanup of the argument _after_ each invocation of the parameterized\n    class\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/repeated-tests.adoc",
    "content": "= Repeated Tests\n\nJUnit Jupiter provides the ability to repeat a test a specified number of times by\nannotating a method with `@RepeatedTest` and specifying the total number of repetitions\ndesired. Each invocation of a repeated test behaves like the execution of a regular\n`@Test` method with full support for the same lifecycle callbacks and extensions.\n\nThe following example demonstrates how to declare a test named `repeatedTest()` that\nwill be automatically repeated 10 times.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java]\n----\n@RepeatedTest(10)\nvoid repeatedTest() {\n\t// ...\n}\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin]\n----\n@RepeatedTest(10)\nfun repeatedTest() {\n    // ...\n}\n----\n--\n====\n\n`@RepeatedTest` can be configured with a failure threshold which signifies the number of\nfailures after which remaining repetitions will be automatically skipped. Set the\n`failureThreshold` attribute to a positive number less than the total number of\nrepetitions in order to skip the invocations of remaining repetitions after the specified\nnumber of failures has been encountered.\n\nFor example, if you are using `@RepeatedTest` to repeatedly invoke a test that you suspect\nto be _flaky_, a single failure is sufficient to demonstrate that the test is flaky, and\nthere is no need to invoke the remaining repetitions. To support that specific use case,\nset `failureThreshold = 1`. You can alternatively set the threshold to a number greater\nthan 1 depending on your use case.\n\nBy default, the `failureThreshold` attribute is set to `Integer.MAX_VALUE`, signaling that\nno failure threshold will be applied, which effectively means that the specified number of\nrepetitions will be invoked regardless of whether any repetitions fail.\n\nWARNING: If the repetitions of a `@RepeatedTest` method are executed in parallel, no\nguarantees can be made regarding the failure threshold. It is therefore recommended that a\n`@RepeatedTest` method be annotated with `@Execution(SAME_THREAD)` when parallel execution\nis configured. See xref:writing-tests/parallel-execution.adoc[] for further details.\n\nIn addition to specifying the number of repetitions and failure threshold, a custom\ndisplay name can be configured for each repetition via the `name` attribute of the\n`@RepeatedTest` annotation. Furthermore, the display name can be a pattern composed of a\ncombination of static text and dynamic placeholders. The following placeholders are\ncurrently supported.\n\n- `+{displayName}+`: display name of the `@RepeatedTest` method\n- `+{currentRepetition}+`: the current repetition count\n- `+{totalRepetitions}+`: the total number of repetitions\n\nThe default display name for a given repetition is generated based on the following\npattern: `\"repetition +{currentRepetition}+ of +{totalRepetitions}+\"`.Thus, the display\nnames for individual repetitions of the previous `repeatedTest()` example would be:\n`repetition 1 of 10`, `repetition 2 of 10`, etc.If you would like the display name of\nthe `@RepeatedTest` method included in the name of each repetition, you can define your\nown custom pattern or use the predefined `RepeatedTest.LONG_DISPLAY_NAME` pattern.The\nlatter is equal to `\"+{displayName}+ :: repetition +{currentRepetition}+ of\n+{totalRepetitions}+\"` which results in display names for individual repetitions like\n`repeatedTest() :: repetition 1 of 10`, `repeatedTest() :: repetition 2 of 10`, etc.\n\nIn order to retrieve information about the current repetition, the total number of\nrepetitions, the number of repetitions that have failed, and the failure threshold, a\ndeveloper can choose to have an instance of `{RepetitionInfo}` injected into a\n`@RepeatedTest`, `@BeforeEach`, or `@AfterEach` method.\n\n[[examples]]\n== Repeated Test Examples\n\nThe `RepeatedTestsDemo` class at the end of this section demonstrates several examples of\nrepeated tests.\n\nThe `repeatedTest()` method is identical to the example from the previous section; whereas,\n`repeatedTestWithRepetitionInfo()` demonstrates how to have an instance of\n`RepetitionInfo` injected into a test to access the total number of repetitions for the\ncurrent repeated test.\n\n`repeatedTestWithFailureThreshold()` demonstrates how to set a failure threshold and\nsimulates an unexpected failure for every second repetition.The resulting behavior can be\nviewed in the `ConsoleLauncher` output at the end of this section.\n\nThe next two methods demonstrate how to include a custom `@DisplayName` for the\n`@RepeatedTest` method in the display name of each repetition. `customDisplayName()`\ncombines a custom display name with a custom pattern and then uses `TestInfo` to verify\nthe format of the generated display name. `Repeat!` is the `+{displayName}+` which comes\nfrom the `@DisplayName` declaration, and `1/1` comes from\n`+{currentRepetition}+/+{totalRepetitions}+`.In contrast,\n`customDisplayNameWithLongPattern()` uses the aforementioned predefined\n`RepeatedTest.LONG_DISPLAY_NAME` pattern.\n\n`repeatedTestInGerman()` demonstrates the ability to translate display names of repeated\ntests into foreign languages -- in this case German, resulting in names for individual\nrepetitions such as: `Wiederholung 1 von 5`, `Wiederholung 2 von 5`, etc.\n\nSince the `beforeEach()` method is annotated with `@BeforeEach` it will get executed\nbefore each repetition of each repeated test. By having the `TestInfo` and\n`RepetitionInfo` injected into the method, we see that it's possible to obtain\ninformation about the currently executing repeated test. Executing `RepeatedTestsDemo`\nwith the `INFO` log level enabled results in the following output.\n\n....\nINFO: About to execute repetition 1 of 10 for repeatedTest\nINFO: About to execute repetition 2 of 10 for repeatedTest\nINFO: About to execute repetition 3 of 10 for repeatedTest\nINFO: About to execute repetition 4 of 10 for repeatedTest\nINFO: About to execute repetition 5 of 10 for repeatedTest\nINFO: About to execute repetition 6 of 10 for repeatedTest\nINFO: About to execute repetition 7 of 10 for repeatedTest\nINFO: About to execute repetition 8 of 10 for repeatedTest\nINFO: About to execute repetition 9 of 10 for repeatedTest\nINFO: About to execute repetition 10 of 10 for repeatedTest\nINFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo\nINFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo\nINFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo\nINFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo\nINFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo\nINFO: About to execute repetition 1 of 8 for repeatedTestWithFailureThreshold\nINFO: About to execute repetition 2 of 8 for repeatedTestWithFailureThreshold\nINFO: About to execute repetition 3 of 8 for repeatedTestWithFailureThreshold\nINFO: About to execute repetition 4 of 8 for repeatedTestWithFailureThreshold\nINFO: About to execute repetition 1 of 1 for customDisplayName\nINFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern\nINFO: About to execute repetition 1 of 5 for repeatedTestInGerman\nINFO: About to execute repetition 2 of 5 for repeatedTestInGerman\nINFO: About to execute repetition 3 of 5 for repeatedTestInGerman\nINFO: About to execute repetition 4 of 5 for repeatedTestInGerman\nINFO: About to execute repetition 5 of 5 for repeatedTestInGerman\n....\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/RepeatedTestsDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/RepeatedTestsDemo.kt[tags=user_guide]\n----\n--\n====\n\nWhen using the `ConsoleLauncher` with the unicode theme enabled, execution of\n`RepeatedTestsDemo` results in the following output to the console.\n\n....\n├─ RepeatedTestsDemo ✔\n│  ├─ repeatedTest() ✔\n│  │  ├─ repetition 1 of 10 ✔\n│  │  ├─ repetition 2 of 10 ✔\n│  │  ├─ repetition 3 of 10 ✔\n│  │  ├─ repetition 4 of 10 ✔\n│  │  ├─ repetition 5 of 10 ✔\n│  │  ├─ repetition 6 of 10 ✔\n│  │  ├─ repetition 7 of 10 ✔\n│  │  ├─ repetition 8 of 10 ✔\n│  │  ├─ repetition 9 of 10 ✔\n│  │  └─ repetition 10 of 10 ✔\n│  ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔\n│  │  ├─ repetition 1 of 5 ✔\n│  │  ├─ repetition 2 of 5 ✔\n│  │  ├─ repetition 3 of 5 ✔\n│  │  ├─ repetition 4 of 5 ✔\n│  │  └─ repetition 5 of 5 ✔\n│  ├─ repeatedTestWithFailureThreshold(RepetitionInfo) ✔\n│  │  ├─ repetition 1 of 8 ✔\n│  │  ├─ repetition 2 of 8 ✘ Boom!\n│  │  ├─ repetition 3 of 8 ✔\n│  │  ├─ repetition 4 of 8 ✘ Boom!\n│  │  ├─ repetition 5 of 8 ↷ Failure threshold [2] exceeded\n│  │  ├─ repetition 6 of 8 ↷ Failure threshold [2] exceeded\n│  │  ├─ repetition 7 of 8 ↷ Failure threshold [2] exceeded\n│  │  └─ repetition 8 of 8 ↷ Failure threshold [2] exceeded\n│  ├─ Repeat! ✔\n│  │  └─ Repeat! 1/1 ✔\n│  ├─ Details... ✔\n│  │  └─ Details... :: repetition 1 of 1 ✔\n│  └─ repeatedTestInGerman() ✔\n│     ├─ Wiederholung 1 von 5 ✔\n│     ├─ Wiederholung 2 von 5 ✔\n│     ├─ Wiederholung 3 von 5 ✔\n│     ├─ Wiederholung 4 von 5 ✔\n│     └─ Wiederholung 5 von 5 ✔\n....\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc",
    "content": "= Tagging and Filtering\n\nTest classes and methods can be tagged via the `@Tag` annotation. Those tags can later be\nused to filter xref:running-tests/intro.adoc[test discovery and execution]. Please refer to the\nxref:running-tests/tags.adoc[] section for more information about tag support in the JUnit\nPlatform.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/TaggingDemo.java[tags=user_guide]\n----\n\nTIP: See xref:writing-tests/annotations.adoc#annotations[Meta-Annotations and Composed Annotations] for examples demonstrating how to create\ncustom annotations for tags.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/test-classes-and-methods.adoc",
    "content": "= Test Classes and Methods\n\nTest methods and lifecycle methods may be declared locally within the current test class,\ninherited from superclasses, or inherited from interfaces (see\nxref:writing-tests/test-interfaces-and-default-methods.adoc[]). In addition, test methods and\nlifecycle methods must not be `abstract` and must not return a value (except `@TestFactory`\nmethods which are required to return a value).\n\n[NOTE]\n.Class and method visibility\n====\nTest classes, test methods, and lifecycle methods are not required to be `public`, but\nthey must _not_ be `private`.\n\nIt is generally recommended to omit the `public` modifier for test classes, test methods,\nand lifecycle methods unless there is a technical reason for doing so – for example, when\na test class is extended by a test class in another package. Another technical reason for\nmaking classes and methods `public` is to simplify testing on the module path when using\nthe Java Module System.\n====\n\n[NOTE]\n.Field and method inheritance\n====\nFields in test classes are inherited. For example, a `@TempDir` field from a superclass\nwill always be applied in a subclass.\n\nTest methods and lifecycle methods are inherited unless they are overridden according to\nthe visibility rules of the Java language. For example, a `@Test` method from a superclass\nwill always be applied in a subclass unless the subclass explicitly overrides the method.\nSimilarly, if a package-private `@Test` method is declared in a superclass that resides in\na different package than the subclass, that `@Test` method will always be applied in the\nsubclass since the subclass cannot override a package-private method from a superclass in\na different package.\n\nSee also: xref:extensions/supported-utilities-in-extensions.adoc#search-semantics[Field and Method Search Semantics]\n====\n\nThe following test class demonstrates the use of `@Test` methods and all supported\nlifecycle methods. For further information on runtime semantics, see\nxref:writing-tests/test-execution-order.adoc[] and\nxref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#wrapping-behavior[Wrapping Behavior of Callbacks].\n\n.A standard test class\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/StandardTests.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/StandardTests.kt[tags=user_guide]\n----\n--\n====\n\nIt is also possible to use Java `record` classes as test classes as illustrated by the\nfollowing example.\n\n[source,java,indent=0]\n.A test class written as a Java record\n----\ninclude::example$java/example/MyFirstJUnitJupiterRecordTests.java[tags=user_guide]\n----\n\n[[kotlin-coroutines]]\n== Kotlin Coroutines\n\nTest methods and lifecycle methods may be written in Kotlin and may optionally use the `suspend`\nkeyword for testing code using coroutines.\n\n[source,kotlin]\n.A test class written in Kotlin\n----\ninclude::example$kotlin/example/KotlinCoroutinesDemo.kt[tags=user_guide]\n----\n\nNOTE: Using suspending functions as test or lifecycle methods requires\nhttps://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-stdlib[`kotlin-stdlib`],\nhttps://central.sonatype.com/artifact/org.jetbrains.kotlin/kotlin-reflect[`kotlin-reflect`],\nand\nhttps://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core[`kotlinx-coroutines-core`]\nto be present on the classpath or module path.\n\nWhen using `suspend` test or lifecycle methods, JUnit internally uses `runBlocking` to execute them.\nThis is sufficient for simple coroutine-based tests.\n\nHowever, `runBlocking` does not support skipping calls to `delay` and does not provide control over\nvirtual time or dispatchers.\n\nAs an alternative, tests can be written as regular `@Test` methods and wrapped in `runTest` from\nhttps://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-test[`kotlinx-coroutines-test`]\nto support skipping delays during tests and gain control over virtual time and dispatchers.\n\n[source,kotlin]\n----\ninclude::example$kotlin/example/KotlinCoroutinesRunTestDemo.kt[tags=user_guide]\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc",
    "content": "= Test Execution Order\n\nBy default, test classes and methods will be ordered using an algorithm that is\ndeterministic but intentionally nonobvious. This ensures that subsequent runs of a test\nsuite execute test classes and test methods in the same order, thereby allowing for\nrepeatable builds.\n\nNOTE: See xref:writing-tests/definitions.adoc[] for a definition of _test method_ and _test class_.\n\n[[methods]]\n== Method Order\n\nAlthough true _unit tests_ typically should not rely on the order in which they are\nexecuted, there are times when it is necessary to enforce a specific test method execution\norder -- for example, when writing _integration tests_ or _functional tests_ where the\nsequence of the tests is important, especially in conjunction with\n`@TestInstance(Lifecycle.PER_CLASS)`.\n\nTo control the order in which test methods are executed, annotate your test class or test\ninterface with `{TestMethodOrder}` and specify the desired `{MethodOrderer}`\nimplementation. You can implement your own custom `MethodOrderer` or use one of the\nfollowing built-in `MethodOrderer` implementations.\n\n* `{MethodOrderer_DisplayName}`: sorts test methods _alphanumerically_ based on their\n  display names (see xref:writing-tests/display-names.adoc#generator-precedence-rules[display name\n  generation precedence rules])\n* `{MethodOrderer_MethodName}`: sorts test methods _alphanumerically_ based on their names\n  and formal parameter lists\n* `{MethodOrderer_OrderAnnotation}`: sorts test methods _numerically_ based on values\n  specified via the `{Order}` annotation\n* `{MethodOrderer_Random}`: orders test methods _pseudo-randomly_ and supports\n  configuration of a custom _seed_\n\nThe `MethodOrderer` configured on a test class is inherited by the `@Nested` test classes\nit contains, recursively. If you want to avoid that a `@Nested` test class uses the same\n`MethodOrderer` as its enclosing class, you can specify `{MethodOrderer_Default}` together\nwith `{TestMethodOrder}`.\n\nNOTE: See also: xref:extensions/relative-execution-order-of-user-code-and-extensions.adoc#wrapping-behavior[Wrapping Behavior of Callbacks]\n\nThe following example demonstrates how to guarantee that test methods are executed in the\norder specified via the `@Order` annotation.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/OrderedTestsDemo.java[tags=user_guide]\n----\n\n[[methods-default]]\n=== Setting the Default Method Orderer\n\nYou can use the `junit.jupiter.testmethod.order.default` xref:running-tests/configuration-parameters.adoc[configuration parameter] to specify the fully qualified class name of the\n`{MethodOrderer}` you would like to use by default. Just like for the orderer configured\nvia the `{TestMethodOrder}` annotation, the supplied class has to implement the\n`MethodOrderer` interface. The default orderer will be used for all tests unless the\n`@TestMethodOrder` annotation is present on an enclosing test class or test interface.\n\nFor example, to use the `{MethodOrderer_OrderAnnotation}` method orderer by default, you\nshould set the configuration parameter to the corresponding fully qualified class name\n(e.g., in `src/test/resources/junit-platform.properties`):\n\n[source,properties,indent=0]\n----\njunit.jupiter.testmethod.order.default = \\\n    org.junit.jupiter.api.MethodOrderer$OrderAnnotation\n----\n\nSimilarly, you can specify the fully qualified name of any custom class that implements\n`MethodOrderer`.\n\n[[classes]]\n== Class Order\n\nAlthough test classes typically should not rely on the order in which they are executed,\nthere are times when it is desirable to enforce a specific test class execution order. You\nmay wish to execute test classes in a random order to ensure there are no accidental\ndependencies between test classes, or you may wish to order test classes to optimize build\ntime as outlined in the following scenarios.\n\n* Run previously failing tests and faster tests first: \"fail fast\" mode\n* With parallel execution enabled, schedule longer tests first: \"shortest test plan\n  execution duration\" mode\n* Various other use cases\n\nTo configure test class execution order _globally_ for the entire test suite, use the\n`junit.jupiter.testclass.order.default` xref:running-tests/configuration-parameters.adoc[configuration\nparameter] to specify the fully qualified class name of the `{ClassOrderer}` you would\nlike to use. The supplied class must implement the `ClassOrderer` interface.\n\nYou can implement your own custom `ClassOrderer` or use one of the following built-in\n`ClassOrderer` implementations.\n\n* `{ClassOrderer_ClassName}`: sorts test classes _alphanumerically_ based on their fully\n  qualified class names\n* `{ClassOrderer_DisplayName}`: sorts test classes _alphanumerically_ based on their\n  display names (see xref:writing-tests/display-names.adoc#generator-precedence-rules[display name\n  generation precedence rules])\n* `{ClassOrderer_OrderAnnotation}`: sorts test classes _numerically_ based on values\n  specified via the `{Order}` annotation\n* `{ClassOrderer_Random}`: orders test classes _pseudo-randomly_ and supports\n  configuration of a custom _seed_\n\nFor example, for the `@Order` annotation to be honored on _test classes_, you should\nconfigure the `{ClassOrderer_OrderAnnotation}` class orderer using the configuration\nparameter with the corresponding fully qualified class name (e.g., in\n`src/test/resources/junit-platform.properties`):\n\n[source,properties,indent=0]\n----\njunit.jupiter.testclass.order.default = \\\n    org.junit.jupiter.api.ClassOrderer$OrderAnnotation\n----\n\nThe configured `ClassOrderer` will be applied to all top-level test classes (including\n`static` nested test classes) and `@Nested` test classes.\n\nNOTE: Top-level test classes will be ordered relative to each other; whereas, `@Nested`\ntest classes will be ordered relative to other `@Nested` test classes sharing the same\n_enclosing class_.\n\nTo configure test class execution order _locally_ for `@Nested` test classes, declare the\n`{TestClassOrder}` annotation on the enclosing class for the `@Nested` test classes you\nwant to order, and supply a class reference to the `ClassOrderer` implementation you would\nlike to use directly in the `@TestClassOrder` annotation. The configured `ClassOrderer`\nwill be applied recursively to `@Nested` test classes and their `@Nested` test classes.\nIf you want to avoid that a `@Nested` test class uses the same `ClassOrderer` as its\nenclosing class, you can specify `{ClassOrderer_Default}` together with `@TestClassOrder`.\nNote that a local `@TestClassOrder` declaration always overrides an inherited\n`@TestClassOrder` declaration or a `ClassOrderer` configured globally via the\n`junit.jupiter.testclass.order.default` configuration parameter.\n\nThe following example demonstrates how to guarantee that `@Nested` test classes are\nexecuted in the order specified via the `@Order` annotation.\n\n[source,java,indent=0]\n----\ninclude::example$java/example/OrderedNestedTestClassesDemo.java[tags=user_guide]\n----\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/test-instance-lifecycle.adoc",
    "content": "= Test Instance Lifecycle\n\nIn order to allow individual test methods to be executed in isolation and to avoid\nunexpected side effects due to mutable test instance state, JUnit creates a new instance\nof each test class before executing each _test method_ (see\nxref:writing-tests/definitions.adoc[]). This \"per-method\" test instance lifecycle is the default\nbehavior in JUnit Jupiter and is analogous to all previous versions of JUnit.\n\nNOTE: Please note that the test class will still be instantiated if a given _test method_\nis _disabled_ via a xref:writing-tests/conditional-test-execution.adoc[condition] (e.g., `@Disabled`,\n`@DisabledOnOs`, etc.) even when the \"per-method\" test instance lifecycle mode is active.\n\nIf you would prefer that JUnit Jupiter execute all test methods on the same test\ninstance, annotate your test class with `@TestInstance(Lifecycle.PER_CLASS)`. When using\nthis mode, a new test instance will be created once per test class. Thus, if your test\nmethods rely on state stored in instance variables, you may need to reset that state in\n`@BeforeEach` or `@AfterEach` methods.\n\nThe \"per-class\" mode has some additional benefits over the default \"per-method\" mode.\nSpecifically, with the \"per-class\" mode it becomes possible to declare `@BeforeAll` and\n`@AfterAll` on non-static methods as well as on interface `default` methods.\n\nIf you are authoring tests using the Kotlin programming language, you may also find it\neasier to implement non-static `@BeforeAll` and `@AfterAll` lifecycle methods as well as\n`@MethodSource` factory methods by switching to the \"per-class\" test instance lifecycle\nmode.\n\n[[default]]\n== Changing the Default Test Instance Lifecycle\n\nIf a test class or test interface is not annotated with `@TestInstance`, JUnit Jupiter\nwill use a _default_ lifecycle mode. The standard _default_ mode is `PER_METHOD`;\nhowever, it is possible to change the _default_ for the execution of an entire test plan.\nTo change the default test instance lifecycle mode, set the\n`junit.jupiter.testinstance.lifecycle.default` _configuration parameter_ to the name of\nan enum constant defined in `TestInstance.Lifecycle`, ignoring case. This can be supplied\nas a JVM system property, as a _configuration parameter_ in the\n`LauncherDiscoveryRequest` that is passed to the `Launcher`, or via the JUnit Platform\nconfiguration file (see xref:running-tests/configuration-parameters.adoc[] for details).\n\nFor example, to set the default test instance lifecycle mode to `Lifecycle.PER_CLASS`,\nyou can start your JVM with the following system property.\n\n`-Djunit.jupiter.testinstance.lifecycle.default=per_class`\n\nNote, however, that setting the default test instance lifecycle mode via the JUnit\nPlatform configuration file is a more robust solution since the configuration file can be\nchecked into a version control system along with your project and can therefore be used\nwithin IDEs and your build software.\n\nTo set the default test instance lifecycle mode to `Lifecycle.PER_CLASS` via the JUnit\nPlatform configuration file, create a file named `junit-platform.properties` in the root\nof the class path (e.g., `src/test/resources`) with the following content.\n\n`junit.jupiter.testinstance.lifecycle.default = per_class`\n\nWARNING: Changing the _default_ test instance lifecycle mode can lead to unpredictable\nresults and fragile builds if not applied consistently. For example, if the build\nconfigures \"per-class\" semantics as the default but tests in the IDE are executed using\n\"per-method\" semantics, that can make it difficult to debug errors that occur on the\nbuild server. It is therefore recommended to change the default in the JUnit Platform\nconfiguration file instead of via a JVM system property.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/test-interfaces-and-default-methods.adoc",
    "content": "= Test Interfaces and Default Methods\n\nJUnit Jupiter allows `@Test`, `@RepeatedTest`, `@ParameterizedTest`, `@TestFactory`,\n`@TestTemplate`, `@BeforeEach`, and `@AfterEach` to be declared on interface `default`\nmethods. `@BeforeAll` and `@AfterAll` can either be declared on `static` methods in a\ntest interface or on interface `default` methods _if_ the test interface or test class is\nannotated with `@TestInstance(Lifecycle.PER_CLASS)` (see\nxref:writing-tests/test-instance-lifecycle.adoc[]). Here are some examples.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/testinterface/TestLifecycleLogger.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/testinterface/TestLifecycleLogger.kt[tags=user_guide]\n----\n--\n====\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/testinterface/TestInterfaceDynamicTestsDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/testinterface/TestInterfaceDynamicTestsDemo.kt[tags=user_guide]\n----\n--\n====\n\n`@ExtendWith` and `@Tag` can be declared on a test interface so that classes that\nimplement the interface automatically inherit its tags and extensions. See\nxref:extensions/test-lifecycle-callbacks.adoc#before-after-execution[Before and After Test Execution Callbacks] for the source code of the\nxref:extensions/test-lifecycle-callbacks.adoc#timing-extension[TimingExtension].\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/testinterface/TimeExecutionLogger.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/testinterface/TimeExecutionLogger.kt[tags=user_guide]\n----\n--\n====\n\nIn your test class you can then implement these test interfaces to have them applied.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/testinterface/TestInterfaceDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/testinterface/TestInterfaceDemo.kt[tags=user_guide]\n----\n--\n====\n\nRunning the `TestInterfaceDemo` results in output similar to the following:\n\n....\nINFO  example.TestLifecycleLogger - Before all tests\nINFO  example.TestLifecycleLogger - About to execute [dynamicTestsForPalindromes()]\nINFO  example.TimingExtension - Method [dynamicTestsForPalindromes] took 19 ms.\nINFO  example.TestLifecycleLogger - Finished executing [dynamicTestsForPalindromes()]\nINFO  example.TestLifecycleLogger - About to execute [isEqualValue()]\nINFO  example.TimingExtension - Method [isEqualValue] took 1 ms.\nINFO  example.TestLifecycleLogger - Finished executing [isEqualValue()]\nINFO  example.TestLifecycleLogger - After all tests\n....\n\nAnother possible application of this feature is to write tests for interface contracts.\nFor example, you can write tests for how implementations of `Object.equals` or\n`Comparable.compareTo` should behave as follows.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/defaultmethods/Testable.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/defaultmethods/Testable.kt[tags=user_guide]\n----\n--\n====\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/defaultmethods/EqualsContract.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/defaultmethods/EqualsContract.kt[tags=user_guide]\n----\n--\n====\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/defaultmethods/ComparableContract.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/defaultmethods/ComparableContract.kt[tags=user_guide]\n----\n--\n====\n\nIn your test class you can then implement both contract interfaces thereby inheriting the\ncorresponding tests. Of course you'll have to implement the abstract methods.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java,indent=0]\n----\ninclude::example$java/example/defaultmethods/StringTests.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin,indent=0]\n----\ninclude::example$kotlin/example/kotlin/defaultmethods/StringTests.kt[tags=user_guide]\n----\n--\n====\n\nNOTE: The above tests are merely meant as examples and therefore not complete.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/test-templates.adoc",
    "content": "= Test Templates\n\nA `{TestTemplate}` method is not a regular test case but rather a template for a test\ncase. As such, it is designed to be invoked multiple times depending on the number of\ninvocation contexts returned by the registered providers. Thus, it must be used in\nconjunction with a registered `{TestTemplateInvocationContextProvider}` extension. Each\ninvocation of a test template method behaves like the execution of a regular `@Test`\nmethod with full support for the same lifecycle callbacks and extensions. Please refer to\nxref:extensions/providing-invocation-contexts-for-test-templates.adoc[] for usage examples.\n\nNOTE: xref:writing-tests/repeated-tests.adoc[] and\nxref:writing-tests/parameterized-classes-and-tests.adoc[Parameterized Tests] are built-in specializations of\ntest templates.\n"
  },
  {
    "path": "documentation/modules/ROOT/pages/writing-tests/timeouts.adoc",
    "content": "= Timeouts\n\nThe `@Timeout` annotation allows one to declare that a test, test factory, test template,\nor lifecycle method should fail if its execution time exceeds a given duration. The time\nunit for the duration defaults to seconds but is configurable.\n\nThe following example shows how `@Timeout` is applied to lifecycle and test methods.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java]\n----\ninclude::example$java/example/TimeoutDemo.java[tags=user_guide]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin]\n----\ninclude::example$kotlin/example/kotlin/TimeoutDemo.kt[tags=user_guide]\n----\n--\n====\n\nTo apply the same timeout to all test methods within a test class and all of its `@Nested`\nclasses, you can declare the `@Timeout` annotation at the class level. It will then be\napplied to all test, test factory, and test template methods within that class and its\n`@Nested` classes unless overridden by a `@Timeout` annotation on a specific method or\n`@Nested` class. Please note that `@Timeout` annotations declared at the class level are\nnot applied to lifecycle methods.\n\nDeclaring `@Timeout` on a `@TestFactory` method checks that the factory method returns\nwithin the specified duration but does not verify the execution time of each individual\n`DynamicTest` generated by the factory. Please use\n`assertTimeout()` or `assertTimeoutPreemptively()` for that purpose.\n\nIf `@Timeout` is present on a `@TestTemplate` method — for example, a `@RepeatedTest` or\n`@ParameterizedTest` — each invocation will have the given timeout applied to it.\n\n[[thread-mode]]\n== Thread mode\n\nThe timeout can be applied using one of the following three thread modes: `SAME_THREAD`,\n`SEPARATE_THREAD`, or `INFERRED`.\n\nWhen `SAME_THREAD` is used, the execution of the annotated method proceeds in the main\nthread of the test. If the timeout is exceeded, the main thread is interrupted from\nanother thread. This is done to ensure interoperability with frameworks such as Spring\nthat make use of mechanisms that are sensitive to the currently running thread — for\nexample, `ThreadLocal` transaction management.\n\nOn the contrary when `SEPARATE_THREAD` is used, like the `assertTimeoutPreemptively()`\nassertion, the execution of the annotated method proceeds in a separate thread, this\ncan lead to undesirable side effects, see xref:writing-tests/assertions.adoc#preemptive-timeouts[Preemptive Timeouts with `assertTimeoutPreemptively()`].\n\nWhen `INFERRED` (default) thread mode is used, the thread mode is resolved via the\n`junit.jupiter.execution.timeout.thread.mode.default` configuration parameter. If the\nprovided configuration parameter is invalid or not present then `SAME_THREAD` is used as\nfallback.\n\n[[default-timeouts]]\n== Default Timeouts\n\nThe following xref:running-tests/configuration-parameters.adoc[configuration parameters] can be used to\nspecify default timeouts for all methods of a certain category unless they or an enclosing\ntest class is annotated with `@Timeout`:\n\n`junit.jupiter.execution.timeout.default`::\n    Default timeout for all testable and lifecycle methods\n`junit.jupiter.execution.timeout.testable.method.default`::\n    Default timeout for all testable methods\n`junit.jupiter.execution.timeout.test.method.default`::\n    Default timeout for `@Test` methods\n`junit.jupiter.execution.timeout.testtemplate.method.default`::\n    Default timeout for `@TestTemplate` methods\n`junit.jupiter.execution.timeout.testfactory.method.default`::\n    Default timeout for `@TestFactory` methods\n`junit.jupiter.execution.timeout.lifecycle.method.default`::\n    Default timeout for all lifecycle methods\n`junit.jupiter.execution.timeout.beforeall.method.default`::\n    Default timeout for `@BeforeAll` methods\n`junit.jupiter.execution.timeout.beforeeach.method.default`::\n    Default timeout for `@BeforeEach` methods\n`junit.jupiter.execution.timeout.aftereach.method.default`::\n    Default timeout for `@AfterEach` methods\n`junit.jupiter.execution.timeout.afterall.method.default`::\n    Default timeout for `@AfterAll` methods\n\nMore specific configuration parameters override less specific ones. For example,\n`junit.jupiter.execution.timeout.test.method.default` overrides\n`junit.jupiter.execution.timeout.testable.method.default` which overrides\n`junit.jupiter.execution.timeout.default`.\n\nThe values of such configuration parameters must be in the following, case-insensitive\nformat: `<number> [ns|μs|ms|s|m|h|d]`. The space between the number and the unit may be\nomitted. Specifying no unit is equivalent to using seconds.\n\n.Example timeout configuration parameter values\n[cols=\"20,80\"]\n|===\n| Parameter value | Equivalent annotation\n\n| `42`            | `@Timeout(42)`\n| `42 ns`         | `@Timeout(value = 42, unit = NANOSECONDS)`\n| `42 μs`         | `@Timeout(value = 42, unit = MICROSECONDS)`\n| `42 ms`         | `@Timeout(value = 42, unit = MILLISECONDS)`\n| `42 s`          | `@Timeout(value = 42, unit = SECONDS)`\n| `42 m`          | `@Timeout(value = 42, unit = MINUTES)`\n| `42 h`          | `@Timeout(value = 42, unit = HOURS)`\n| `42 d`          | `@Timeout(value = 42, unit = DAYS)`\n|===\n\n\n[[polling]]\n== Using @Timeout for Polling Tests\n\nWhen dealing with asynchronous code, it is common to write tests that poll while waiting\nfor something to happen before performing any assertions. In some cases you can rewrite\nthe logic to use a `CountDownLatch` or another synchronization mechanism, but sometimes\nthat is not possible — for example, if the subject under test sends a message to a channel\nin an external message broker and assertions cannot be performed until the message has\nbeen successfully sent through the channel. Asynchronous tests like these require some\nform of timeout to ensure they don't hang the test suite by executing indefinitely, as\nwould be the case if an asynchronous message never gets successfully delivered.\n\nBy configuring a timeout for an asynchronous test that polls, you can ensure that the test\ndoes not execute indefinitely. The following example demonstrates how to achieve this with\nJUnit Jupiter's `@Timeout` annotation. This technique can be used to implement \"poll\nuntil\" logic very easily.\n\n[tabs]\n====\nJava::\n+\n--\n[source,java]\n----\ninclude::example$java/example/PollingTimeoutDemo.java[tags=user_guide,indent=0]\n----\n--\n\nKotlin::\n+\n--\n[source,kotlin]\n----\ninclude::example$kotlin/example/kotlin/PollingTimeoutDemo.kt[tags=user_guide,indent=0]\n----\n--\n====\n\nNOTE: If you need more control over polling intervals and greater flexibility with\nasynchronous tests, consider using a dedicated library such as\nlink:https://github.com/awaitility/awaitility[Awaitility].\n\n\n[[debugging]]\n== Debugging Timeouts\n\nRegistered xref:extensions/pre-interrupt-callback.adoc[] extensions are called prior to invoking\n`Thread.interrupt()` on the thread that is executing the timed out method. This allows to\ninspect the application state and output additional information that might be helpful for\ndiagnosing the cause of a timeout.\n\n\n[[debugging-thread-dump]]\n=== Thread Dump on Timeout\n\nJUnit registers a default implementation of the xref:extensions/pre-interrupt-callback.adoc[]\nextension point that dumps the stacks of all threads to `System.out` if enabled by setting\nthe `junit.jupiter.execution.timeout.threaddump.enabled`\nxref:running-tests/configuration-parameters.adoc[configuration parameter] to `true`.\n\n\n[[mode]]\n== Disable @Timeout Globally\n\nWhen stepping through your code in a debug session, a fixed timeout limit may influence\nthe result of the test, e.g. mark the test as failed although all assertions were met.\n\nJUnit Jupiter supports the `junit.jupiter.execution.timeout.mode` configuration parameter\nto configure when timeouts are applied. There are three modes: `enabled`, `disabled`,\nand `disabled_on_debug`. The default mode is `enabled`.\nA VM runtime is considered to run in debug mode when one of its input parameters starts\nwith `-agentlib:jdwp` or `-Xrunjdwp`.\nThis heuristic is queried by the `disabled_on_debug` mode.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.0.0.adoc",
    "content": "[[v6.0.0]]\n== 6.0.0\n\n*Date of Release:* September 30, 2025\n\n*Scope:*\n\n* Java 17 and Kotlin 2.1 baseline\n* Single version number for Platform, Jupiter, and Vintage\n* Use of JSpecify annotations to express nullability\n* Integration of JFR functionality in `junit-platform-launcher`\n* Removal of `junit-platform-runner` and `junit-platform-jfr`\n* Deterministic order of `@Nested` classes\n* `MethodOrderer.Default` and `ClassOrderer.Default` for `@Nested` classes\n* Inheritance of `@TestMethodOrder` by enclosed `@Nested` classes\n* Switch to FastCSV library for `@CsvSource` and `@CsvFileSource`\n* Support for using Kotlin `suspend` functions as test methods\n* New `--fail-fast` mode for ConsoleLauncher\n* Support for cancelling test execution via `CancellationToken`\n* Removal of various deprecated behaviors and APIs\n\nFor complete details consult the\nhttps://docs.junit.org/6.0.0/release-notes.html[6.0.0 Release Notes] online.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.0.1.adoc",
    "content": "[[v6.0.1]]\n== 6.0.1\n\n*Date of Release:* October 31, 2025\n\n*Scope:* Bug fixes and enhancements since 6.0.0\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/110?closed=1+[6.0.1] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.0.1-junit-platform]]\n=== JUnit Platform\n\n[[v6.0.1-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* The `jdk.jfr` package is now an optional import when using the `junit-platform-launcher`\n  as an OSGi bundle.\n\n[[v6.0.1-junit-platform-new-features-and-improvements]]\n==== New Features and Improvements\n\n* Legacy documentation regarding Java 8 compatibility has been removed from the User Guide.\n\n\n[[v6.0.1-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.0.1-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* A regression introduced in version 6.0.0 caused an exception when using `@CsvSource` or\n  `@CsvFileSource` if the `delimiter` or `delimiterString` attribute was set to `+++#+++`.\n  This occurred because `+++#+++` was used as the default comment character without an\n  option to change it. To resolve this, a new `commentCharacter` attribute has been added\n  to both annotations. Its default value remains `+++#+++`, but it can now be customized\n  to avoid conflicts with other control characters.\n* Fix `IllegalAccessError` thrown when using the Kotlin-specific `assertDoesNotThrow`\n  assertion.\n* Stop reporting discovery issues for synthetic methods, particularly in conjunction with\n  Kotlin suspend functions.\n* Fix support for test methods with the same signature as package-private methods declared\n  in super classes in different packages.\n\n[[v6.0.1-junit-jupiter-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* The `org.junit.jupiter.migrationsupport` module descriptor has been marked as deprecated\n  for removal.\n\n[[v6.0.1-junit-jupiter-new-features-and-improvements]]\n==== New Features and Improvements\n\n* The `@CsvSource` and `@CsvFileSource` annotations now allow specifying a custom comment\n  character using the new `commentCharacter` attribute.\n* Improve error message when `@ParameterizedClass` is used with field injection without\n  providing enough arguments.\n* Allow calling the `TypedArgumentConverter` constructor for `@Nullable T` target types\n  without having to cast class literals to `Class<@Nullable T>`.\n\n\n[[v6.0.1-junit-vintage]]\n=== JUnit Vintage\n\n[[v6.0.1-junit-vintage-new-features-and-improvements]]\n==== New Features and Improvements\n\n* Allow disabling the reporting of discovery issues by the JUnit Vintage engine (including\n  the one reported for its deprecation) by setting the new\n  `junit.vintage.discovery.issue.reporting.enabled` configuration parameter to `false`.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.0.2.adoc",
    "content": "[[v6.0.2]]\n== 6.0.2\n\n*Date of Release:* January 6, 2026\n\n*Scope:* Bug fixes and enhancements since 6.0.1\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/113?closed=1+[6.0.2] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.0.2-junit-platform]]\n=== JUnit Platform\n\n[[v6.0.2-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* Make `ConsoleLauncher` compatible with JDK 26 by avoiding final field mutations.\n* Enable recursive updates when using `NamespacedHierarchicalStore.computeIfAbsent(N, K, Function)`.\n  This provides parity with the deprecated `NamespacedHierarchicalStore.getOrComputeIfAbsent(N, K, Function)`\n\n\n[[v6.0.2-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.0.2-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* Allow using `@ResourceLock` on classes annotated with `@ClassTemplate` (or\n  `@ParameterizedClass`).\n* Change API status of recommended method in `ArgumentsProvider` as well as\n  `ParameterDeclaration(s)` as \"maintained\" rather than \"experimental\".\n\n\n[[v6.0.2-junit-vintage]]\n=== JUnit Vintage\n\nNo changes.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.0.3.adoc",
    "content": "[[v6.0.3]]\n== 6.0.3\n\n*Date of Release:* February 15, 2026\n\n*Scope:* Bug fixes and enhancements since 6.0.2\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/116?closed=1+[6.0.3] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.0.3-junit-platform]]\n=== JUnit Platform\n\n[[v6.0.3-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* A deadlock issue in `NamespacedHierarchicalStore.computeIfAbsent(N, K, Function)` has\n  been fixed.\n\n\n[[v6.0.3-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.0.3-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* `@EnabledOnJre` and `@DisabledOnJre` once again work reliably when used with `JRE.OTHER`\n  in a test running on a Java runtime whose version is higher than the version of the last\n  `JAVA_*` constant in the `JRE` enum.\n\n\n[[v6.0.3-junit-vintage]]\n=== JUnit Vintage\n\nNo changes.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-M1.adoc",
    "content": "[[v6.1.0-M1]]\n== 6.1.0-M1\n\n*Date of Release:* November 17, 2025\n\n*Scope:*\n\n* New `org.junit.start` module for usage in compact source files\n* Execution mode configuration support for dynamic tests and containers\n* New parallel test executor implementation\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/104?closed=1+[6.1.0-M1] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.1.0-M1-junit-platform]]\n=== JUnit Platform\n\n[[v6.1.0-M1-junit-platform-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* Deprecate constructors for `ForkJoinPoolHierarchicalTestExecutorService` in favor of the\n  new `ParallelHierarchicalTestExecutorServiceFactory` that also supports\n  `WorkerThreadPoolHierarchicalTestExecutorService`.\n\n[[v6.1.0-M1-junit-platform-new-features-and-improvements]]\n==== New Features and Improvements\n\n* Support for creating a `ModuleSelector` from a `java.lang.Module` and using\n  its classloader for test discovery.\n* New `WorkerThreadPoolHierarchicalTestExecutorService` implementation used for parallel\n  test execution that is backed by a regular thread pool rather than a `ForkJoinPool`.\n  Engine authors should switch to use `ParallelHierarchicalTestExecutorServiceFactory`\n  rather than instantiating a concrete `HierarchicalTestExecutorService` implementation\n  for parallel execution directly.\n* `OpenTestReportGeneratingListener` now supports redirecting XML events to a socket via\n  the new `junit.platform.reporting.open.xml.socket` configuration parameter. When set to a\n  port number, events are sent to `127.0.0.1:<port>` instead of being written to a file.\n* Allow implementations of `HierarchicalTestEngine` to specify which nodes require the\n  global read lock by overriding the `Node.isGlobalReadLockRequired()` method to return\n  `false`.\n\n\n[[v6.1.0-M1-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.1.0-M1-junit-jupiter-new-features-and-improvements]]\n==== New Features and Improvements\n\n* Introduce new module `org.junit.start` for writing and running tests. It simplifies\n  using JUnit in compact source files together with a single `module import` statement.\n  Find an example at the xref:running-tests/source-launcher.adoc[User Guide].\n* Introduce new `dynamicTest(Consumer<? super Configuration>)` factory method for dynamic\n  tests. It allows configuring the `ExecutionMode` of the dynamic test in addition to its\n  display name, test source URI, and executable.\n* Introduce new `dynamicContainer(Consumer<? super Configuration>)` factory method for\n  dynamic containers. It allows configuring the `ExecutionMode` of the dynamic container\n  and/or its children in addition to its display name, test source URI, and children.\n* Enrich `assertInstanceOf` failure using the test subject `Throwable` as cause. It\n  results in the stack trace of the test subject `Throwable` to get reported along with\n  the failure.\n* Make implementation of `HierarchicalTestExecutorService` used for parallel test\n  execution configurable via the new\n  `junit.jupiter.execution.parallel.config.executor-service` configuration parameter to\n  in order to add support for `WorkerThreadPoolHierarchicalTestExecutorService`. Please\n  refer to the\n  xref:writing-tests/parallel-execution.adoc#config-executor-service[User Guide]\n  for details.\n\n[[v6.1.0-M1-junit-vintage]]\n=== JUnit Vintage\n\nNo changes.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0-RC1.adoc",
    "content": "[[v6.1.0-RC1]]\n== 6.1.0-RC1\n\n*Date of Release:* April 25, 2026\n\n*Scope:*\n\n* New `@DefaultLocale` and `@DefaultTimeZone` built-in extensions\n* New built-in extension for clearing/setting/restoring system properties\n* Configurable deletion strategy for `@TempDir` that allows ignoring failures\n* Improved stack trace pruning for assertion failures\n* New `org.junit.jupiter.api.Constants` class for referencing configuration parameters\n* Improvements to legacy XML reports for parameterized test classes\n* New experimental memory cleanup mode for large test suites\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/112?closed=1+[6.1.0-RC1] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.1.0-RC1-junit-platform]]\n=== JUnit Platform\n\n[[v6.1.0-RC1-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* A deadlock issue in `NamespacedHierarchicalStore.computeIfAbsent(N, K, Function)` has\n  been fixed.\n* `WorkerThreadPoolHierarchicalTestExecutorService` now rechecks the _done_ condition\n  before rejecting an extraneous worker.\n* Missing precondition checks have been added to `Launcher` implementations.\n* Failures to resolve selectors are now propagated by the Suite Engine.\n\n[[v6.1.0-RC1-junit-platform-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* In the `EngineTestKit`, `Executions.started()` has been deprecated in favor of\n  `Executions.finished()`, since started executions are always finished.\n\n[[v6.1.0-RC1-junit-platform-new-features-and-improvements]]\n==== New Features and Improvements\n\n* `TestDescriptor` implementation requirements have now been clarified in the\n  corresponding Javadoc.\n* The `UniqueId.uniqueIdFormat` field has been removed, reducing the size of `UniqueId`\n  objects.\n* New `selectClasspathResources(String...)` and `selectClasspathResources(List<String)`\n  methods in `DiscoverySelectors`.\n* New, experimental memory cleanup mode that is useful when executing a large\n  number of tests. It can be enabled via the new\n  `junit.platform.execution.memory.cleanup.enabled` configuration parameter.\n\n\n[[v6.1.0-RC1-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.1.0-RC1-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* Recursive updates are now supported when using `computeIfAbsent(K, Function, Class)` in\n  the `ExtensionContext.Store`, providing parity with the deprecated\n  `getOrComputeIfAbsent(K, Function, Class)` method.\n* Legacy XML reports now include the index of `@ClassTemplate`/`@ParameterizedClass`\n  invocations in test names to make them unique.\n* Legacy XML reports now include parent display names to make it easier to distinguish\n  between invocations for different parameters.\n\n[[v6.1.0-RC1-junit-jupiter-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* The `OTHER` constant of `{JRE}` has been deprecated in favor of the `int`/`int[]`\n  annotation attributes of `{EnabledOnJre}`, `{DisabledOnJre}`, `{EnabledForJreRange}`,\n  and `{DisabledForJreRange}` which allow referencing JRE versions later than those\n  supported by the `JRE` enum.\n* The `org.junit.jupiter.engine.Constants` class and its constants are now deprecated in\n  favor of `org.junit.jupiter.api.Constants`.\n\n[[v6.1.0-RC1-junit-jupiter-new-features-and-improvements]]\n==== New Features and Improvements\n\n* `JAVA_27` has been added to the `JRE` enum for use with `JRE`-based execution conditions.\n* https://www.junit-pioneer.org/[JUnit Pioneer]'s `DefaultLocaleExtension` and\n  `DefaultTimeZoneExtension` are now part of JUnit Jupiter. Find examples in the\n  xref:writing-tests/built-in-extensions.adoc#DefaultLocaleAndTimeZone[User Guide].\n* JUnit Pioneer's `SystemPropertyExtension` is now part of JUnit Jupiter. Find examples in\n  the xref:writing-tests/built-in-extensions.adoc#system-properties[User Guide]. For\n  details regarding implementation differences between JUnit Pioneer and Jupiter, see the\n  corresponding https://github.com/junit-team/junit-framework/pull/5258[pull request].\n* `@TempDir` now allows configuring a deletion strategy for temporary directories. This is\n  typically used to gain control over what happens when deletion of a file or directory\n  fails (see\n  xref:writing-tests/built-in-extensions.adoc#TempDirDeletionStrategy[User Guide] for\n  details).\n* `Arguments` may now be created from instances of `Iterable` via the following new\n  methods which make it easier to dynamically build arguments from collections when using\n  `@ParameterizedTest`.\n** `Arguments.of(Iterable<?>)`\n** `Arguments.argumentsFrom(Iterable<?>)` (alias for `of(Iterable<?>)`)\n** `Arguments.argumentSetFrom(String, Iterable<?>)`\n** `Arguments.toList()` — returns a mutable `List<@Nullable Object>`\n* `@EmptySource` now supports `Iterable`, `Iterator`, and `ListIterator`.\n* `@Deprecated` factory methods are now excluded in the\n  xref:writing-tests/parameterized-classes-and-tests.adoc#argument-conversion-implicit-fallback[fallback String-to-Object]\n  conversion algorithm.\n* Internal stack frames are now removed from `AssertionFailedError` stack traces.\n* New `trimStacktrace(Class<?>)` and `retainStackTraceElements(int)` methods in\n  `AssertionFailureBuilder` which allow user-defined assertions to trim their stack traces.\n* Generic inline value classes (such as `kotlin.Result<T>`) can now be used as parameters\n  in `@ParameterizedTest` methods when `kotlin-reflect` is on the classpath. Note,\n  however, that primitive-wrapper inline value classes (such as `UInt` or custom value\n  classes wrapping primitives) are not yet supported.\n* Kotlin-specific assertions now include a variant of `assertThrowsExactly` with reified\n  generics.\n* The new `org.junit.jupiter.api.Constants` class provides constants for\n  xref:running-tests/configuration-parameters.adoc[] specific to the Jupiter engine.\n* The `{EmptySource}` annotation now provides a `type` attribute that allows configuring\n  the type of the empty argument explicitly. This is intended to be used in conjunction\n  with an `{ArgumentConverter}` that supports the specified type.\n\n\n[[v6.1.0-RC1-junit-vintage]]\n=== JUnit Vintage\n\nNo changes.\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-6.1.0.adoc",
    "content": "[[v6.1.0]]\n== 6.1.0\n\n*Date of Release:* ❓\n\n*Scope:* ❓\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/119?closed=1+[6.1.0] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[v6.1.0-junit-platform]]\n=== JUnit Platform\n\n[[v6.1.0-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* `AbstractTestDescriptor.getChildren()` now returns immutable set of children rather than a\n  merely unmodifiable set.\n\n[[v6.1.0-junit-platform-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[v6.1.0-junit-platform-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n\n\n[[v6.1.0-junit-jupiter]]\n=== JUnit Jupiter\n\n[[v6.1.0-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* ❓\n\n[[v6.1.0-junit-jupiter-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[v6.1.0-junit-jupiter-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n\n\n[[v6.1.0-junit-vintage]]\n=== JUnit Vintage\n\n[[v6.1.0-junit-vintage-bug-fixes]]\n==== Bug Fixes\n\n* ❓\n\n[[v6.1.0-junit-vintage-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[v6.1.0-junit-vintage-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n"
  },
  {
    "path": "documentation/modules/ROOT/partials/release-notes/release-notes-TEMPLATE.adoc",
    "content": "// TODO:\n//\n// 1) Make a copy of this template file, replacing TEMPLATE in the file name with the\n//    current version (for example, 5.10.0-M1, 5.10.0-RC1, 5.10.0).\n// 2) Open the new file for editing.\n// 3) Replace all occurrences of VERSION with the current version (for example, 5.10.0-M1,\n//    5.10.0-RC1, 5.10.0). The same version must be used in the file name and within the\n//    file.\n// 4) Replace MILESTONE_NUMBER with the appropriate milestone number. This is an integer\n//    which you can determine via https://github.com/junit-team/junit-framework/milestones/.\n//    If a GitHub milestone does not yet exist for the given VERSION, you will need to\n//    create a new GitHub milestone or ask a member of the JUnit team to create it for you.\n// 5) 'include:' this new file in ../../pages/release-notes.adoc.\n// 6) Delete this entire comment block.\n//\n[[vVERSION]]\n== VERSION\n\n*Date of Release:* ❓\n\n*Scope:* ❓\n\nFor a complete list of all _closed_ issues and pull requests for this release, consult the\nlink:{junit-framework-repo}+/milestone/MILESTONE_NUMBER?closed=1+[VERSION] milestone page in the JUnit\nrepository on GitHub.\n\n\n[[vVERSION-junit-platform]]\n=== JUnit Platform\n\n[[vVERSION-junit-platform-bug-fixes]]\n==== Bug Fixes\n\n* ❓\n\n[[vVERSION-junit-platform-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[vVERSION-junit-platform-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n\n\n[[vVERSION-junit-jupiter]]\n=== JUnit Jupiter\n\n[[vVERSION-junit-jupiter-bug-fixes]]\n==== Bug Fixes\n\n* ❓\n\n[[vVERSION-junit-jupiter-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[vVERSION-junit-jupiter-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n\n\n[[vVERSION-junit-vintage]]\n=== JUnit Vintage\n\n[[vVERSION-junit-vintage-bug-fixes]]\n==== Bug Fixes\n\n* ❓\n\n[[vVERSION-junit-vintage-deprecations-and-breaking-changes]]\n==== Deprecations and Breaking Changes\n\n* ❓\n\n[[vVERSION-junit-vintage-new-features-and-improvements]]\n==== New Features and Improvements\n\n* ❓\n"
  },
  {
    "path": "documentation/package.json",
    "content": "{\n  \"devDependencies\": {\n    \"antora\": \"3.2.0-alpha.11\"\n  },\n  \"dependencies\": {\n    \"@antora/collector-extension\": \"1.0.3\",\n    \"@antora/lunr-extension\": \"1.0.0-alpha.13\",\n    \"@asciidoctor/tabs\": \"^1.0.0-beta.6\",\n    \"@springio/antora-extensions\": \"1.14.11\",\n    \"@springio/antora-xref-extension\": \"^1.0.0-alpha.5\",\n    \"highlight.js\": \"11.11.1\"\n  }\n}\n"
  },
  {
    "path": "documentation/src/javadoc/junit-overview.html",
    "content": "<body>\n\n<p>This document consists of four sections:</p>\n\n<dl>\n    <dt>Platform</dt>\n    <dd>The JUnit Platform serves as a foundation for launching testing frameworks on the JVM.\n        It also defines the TestEngine API for developing a testing framework that runs on the\n        platform. Furthermore, the platform provides a Console Launcher to launch the platform\n        from the command line and the Suite Engine for running a custom test suite using one\n        or more test engines on the platform\n    </dd>\n    <dt>Jupiter</dt>\n    <dd>JUnit Jupiter is the combination of the programming model and extension model for\n        writing JUnit tests and extensions. The Jupiter subproject provides a TestEngine\n        for running Jupiter based tests on the platform.\n    </dd>\n    <dt>Vintage</dt>\n    <dd>JUnit Vintage provides a TestEngine for running JUnit 3 and JUnit 4 based tests on the\n        platform.\n    </dd>\n    <dt>Other Modules</dt>\n    <dd>This section lists all modules that are not part of a dedicated section.\n    </dd>\n</dl>\n\n<p>Already consulted the <a href=\"../overview.html\">JUnit User Guide</a>?</p>\n\n</body>\n"
  },
  {
    "path": "documentation/src/javadoc/junit-stylesheet.css",
    "content": "/*\n * CSS customizations for JUnit\n */\n\n@import url('https://assets.junit.org/fonts/index.css');\n\n:root {\n    /* body, block and code fonts */\n    --body-font-family: \"Inter Variable\", sans-serif;\n    --block-font-family: \"Inter Variable\", sans-serif;\n    --code-font-family: \"JetBrains Mono Variable\", monospace;\n    /* Text colors for body and block elements */\n    --body-text-color: #333;\n    --block-text-color: #333;\n    /* Colors for navigation bar and table captions */\n    --navbar-background-color: #25a162;\n    /* Background color for subnavigation and various headers */\n    --subnav-background-color: #e8e8e8;\n    --subnav-link-color: var(--link-color);\n    /* Background and text colors for selected tabs and navigation items */\n    --selected-background-color: #dc524a;\n    --selected-text-color: #fff;\n    --selected-link-color: #651410;\n    /* Background colors for generated tables */\n    --table-header-color: #eee;\n    --even-row-color: #fff;\n    --odd-row-color: #f6f6f6;\n    /* Text color for page title */\n    --title-color: #444;\n    /* Text colors for links */\n    --link-color: #dc524a;\n    --link-color-active: #b62b23;\n    /* Table of contents */\n    --toc-background-color: #f8f8f8;\n    --toc-hover-color: #eee;\n    /* Snippet colors */\n    --snippet-background-color: #ebecee;\n    --snippet-text-color: var(--block-text-color);\n    --snippet-highlight-color: #fcdbd9;\n    /* Border colors for structural elements and user defined tables */\n    --border-color: #eee;\n    --table-border-color: #eee;\n    /* Highlight color for active search tag target */\n    --search-tag-highlight-color: #ffff00;\n    /* Copy button colors and filters */\n    --button-border-color: #b0b8c8;\n}\n\nh1, h2 {\n    font-family: \"Blinker\", sans-serif;\n    font-style: normal !important;\n    font-weight: 600;\n}\n\ndl.notes > dt {\n    font-size: var(--body-font-size);\n}\n\n@media screen and (min-width: 1024px) {\n    .title {\n        font-size: 2rem;\n    }\n}\n\n.top-nav a:hover, .bottom-nav a:hover {\n    text-decoration:underline;\n    color:inherit;\n}\n\n.nav-bar-cell1-rev {\n    background-color:#fff;\n    color:#dc524a;\n    border-radius: 6px;\n    font-weight: bold;\n}\n\nhr {\n    color: transparent;\n    border-top: 1px solid var(--border-color);\n}\n\ndt {\n    font-weight: bold;\n}\n\ntt,\ncode,\npre,\n.module-signature,\n.package-signature,\n.type-signature,\n.member-signature {\n    font-variant-ligatures: none;\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/domain/Person.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.domain;\n\nimport java.time.LocalDate;\n\npublic final class Person {\n\n\tpublic enum Gender {\n\t\tF, M\n\t}\n\n\tprivate String firstName;\n\tprivate String lastName;\n\tprivate Gender gender;\n\tprivate LocalDate dateOfBirth;\n\n\tpublic Person(String firstName, String lastName) {\n\t\tthis.firstName = firstName;\n\t\tthis.lastName = lastName;\n\t}\n\n\tpublic Person(String firstName, String lastName, Gender gender, LocalDate dateOfBirth) {\n\t\tthis(firstName, lastName);\n\t\tthis.gender = gender;\n\t\tthis.dateOfBirth = dateOfBirth;\n\t}\n\n\tpublic String getFirstName() {\n\t\treturn firstName;\n\t}\n\n\tpublic String getLastName() {\n\t\treturn lastName;\n\t}\n\n\tpublic Gender getGender() {\n\t\treturn gender;\n\t}\n\n\tpublic LocalDate getDateOfBirth() {\n\t\treturn dateOfBirth;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tfinal int prime = 31;\n\t\tint result = 1;\n\t\tresult = prime * result + ((firstName == null) ? 0 : firstName.hashCode());\n\t\tresult = prime * result + ((lastName == null) ? 0 : lastName.hashCode());\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tPerson other = (Person) obj;\n\t\tif (firstName == null) {\n\t\t\tif (other.firstName != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (!firstName.equals(other.firstName)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (lastName == null) {\n\t\t\tif (other.lastName != null) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\telse if (!lastName.equals(other.lastName)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"Person [firstName=\" + firstName + \", lastName=\" + lastName + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/domain/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Demo domain model.\n */\n\npackage example.domain;\n"
  },
  {
    "path": "documentation/src/main/java/example/registration/WebClient.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration;\n\npublic class WebClient implements AutoCloseable {\n\n\tpublic WebResponse get(String string) {\n\t\treturn new WebResponse();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\t/* no-op for demo */\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/registration/WebResponse.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration;\n\npublic class WebResponse {\n\n\tpublic int getResponseStatus() {\n\t\treturn 200;\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/registration/WebServerExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration;\n\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\npublic class WebServerExtension implements BeforeAllCallback {\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\t/* no-op for demo */\n\t}\n\n\tpublic String getServerUrl() {\n\t\treturn \"https://example.org:8181\";\n\t}\n\n\tpublic static Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\tpublic static class Builder {\n\n\t\tpublic Builder enableSecurity(boolean b) {\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic WebServerExtension build() {\n\t\t\treturn new WebServerExtension();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/registration/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Demo code for a WebServer extension.\n */\n\npackage example.registration;\n"
  },
  {
    "path": "documentation/src/main/java/example/util/Calculator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.util;\n\npublic class Calculator {\n\n\tpublic int add(int a, int b) {\n\t\treturn a + b;\n\t}\n\n\tpublic int subtract(int a, int b) {\n\t\treturn a - b;\n\t}\n\n\tpublic int multiply(int a, int b) {\n\t\treturn a * b;\n\t}\n\n\tpublic int divide(int a, int b) {\n\t\treturn a / b;\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/util/ListWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.util;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\npublic class ListWriter {\n\n\tprivate final Path file;\n\n\tpublic ListWriter(Path file) {\n\t\tthis.file = file;\n\t}\n\n\tpublic void write(String... items) throws IOException {\n\t\tFiles.write(file, List.of(String.join(\",\", items)));\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/util/StringUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.util;\n\nimport static java.util.Objects.requireNonNull;\n\nimport org.jspecify.annotations.Nullable;\n\npublic class StringUtils {\n\n\tpublic static boolean isPalindrome(@Nullable String candidate) {\n\t\tint length = requireNonNull(candidate).length();\n\t\tfor (int i = 0; i < length / 2; i++) {\n\t\t\tif (candidate.charAt(i) != candidate.charAt(length - (i + 1))) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate StringUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/main/java/example/util/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Demo utilities.\n */\n\npackage example.util;\n"
  },
  {
    "path": "documentation/src/plantuml/component-diagram.puml",
    "content": "@startuml\n\nskinparam {\n    defaultFontName sans-serif\n}\n\npackage org.junit.jupiter {\n    [junit-jupiter] as jupiter\n    [junit-jupiter-api] as jupiter_api\n    [junit-jupiter-engine] as jupiter_engine\n    [junit-jupiter-params] as jupiter_params\n    [junit-jupiter-migrationsupport] as jupiter_migration_support\n}\n\npackage org.junit.vintage {\n    [junit-vintage-engine] as vintage_engine\n}\n\npackage org.junit.platform {\n    [junit-platform-commons] as commons\n    [junit-platform-console] as console\n    [junit-platform-engine] as engine\n    [junit-platform-launcher] as launcher\n    [junit-platform-reporting] as reporting\n    [junit-platform-suite] as suite\n    [junit-platform-suite-api] as suite_api\n    [junit-platform-suite-engine] as suite_engine\n    [junit-platform-testkit] as testkit\n}\n\npackage \"JUnit 4\" {\n    [junit:junit] as junit4\n}\n\npackage org.opentest4j {\n    [opentest4j]\n}\n\npackage org.opentest4j.reporting {\n    [open-test-reporting-tooling-spi] as otr_tooling_spi\n}\n\npackage org.apiguardian {\n    [apiguardian-api] as apiguardian\n    note bottom of apiguardian #white\n        All artifacts except\n        opentest4j and junit:junit\n        have a dependency on this\n        artifact. The edges have\n        been omitted from this\n        diagram for the sake of\n        readability.\n    endnote\n}\n\njupiter ..> jupiter_api\njupiter ..> jupiter_params\njupiter ..> jupiter_engine\n\njupiter_api ....> opentest4j\njupiter_api ...> commons\n\njupiter_engine ...> engine\njupiter_engine ..> jupiter_api\n\njupiter_params ..> jupiter_api\njupiter_migration_support ..> jupiter_api\njupiter_migration_support ...> junit4\n\nconsole ..> launcher\nconsole ..> reporting\n\nlauncher ..> engine\n\nengine ....> opentest4j\nengine ..> commons\n\nreporting ..> launcher\nreporting ......> otr_tooling_spi\n\nsuite ..> suite_api\nsuite ..> suite_engine\n\nsuite_engine ..> launcher\nsuite_engine ..> suite_api\n\ntestkit ....> opentest4j\ntestkit ..> launcher\n\nvintage_engine ...> engine\nvintage_engine ..> junit4\n\n@enduml\n"
  },
  {
    "path": "documentation/src/plantuml/junit-platform-suite-engine-diagram.puml",
    "content": "@startuml\nobject \"IDEs\"\nobject \"Build Tools\"\nobject \"Console Launcher\"\n\nobject \"JUnit Platform\"\nobject \"Suite Test Engine\"\n\nobject \"@Suite annotated class A\"\nobject \"@Suite annotated class B\"\nobject \"JUnit Platform (A)\"\nobject \"JUnit Platform (B)\"\nobject \"Jupiter Test Engine (A)\"\nobject \"Jupiter Test Engine (B)\"\nobject \"Tests in package A\"\nobject \"Tests in package B\"\n\n\"IDEs\" --> \"JUnit Platform\"\n\"Build Tools\" --> \"JUnit Platform\"\n\"Console Launcher\" --> \"JUnit Platform\" : requests discovery and execution\n\"JUnit Platform\" --> \"Suite Test Engine\": forwards request\n\n\"Suite Test Engine\" --> \"@Suite annotated class A\"\n\"@Suite annotated class A\" --> \"JUnit Platform (A)\" \n\"JUnit Platform (A)\" --> \"Jupiter Test Engine (A)\"\n\"Jupiter Test Engine (A)\"  --> \"Tests in package A\"\n\n\"Suite Test Engine\" --> \"@Suite annotated class B\" : discovers and executes\n\"@Suite annotated class B\" --> \"JUnit Platform (B)\" : requests discovery and execution\n\"JUnit Platform (B)\" --> \"Jupiter Test Engine (B)\" : forwards request\n\"Jupiter Test Engine (B)\" --> \"Tests in package B\" : discovers and executes\n@enduml\n"
  },
  {
    "path": "documentation/src/plantuml/junit-platform-suite-engine-duplicate-test-execution-diagram.puml",
    "content": "@startuml\nobject \"IDEs\"\nobject \"Build Tools\"\nobject \"Console Launcher\"\nobject \"JUnit Platform\"\ntogether {\n    object \"Suite Test Engine\"\n    object \"Jupiter Test Engine\"\n}\nobject \"@Suite annotated class\"\nobject \"JUnit Platform (@Suite)\"\nobject \"Jupiter Test Engine (@Suite)\"\ntogether {\n    object \"Example Test A\"\n    object \"Example Test A (@Suite)\"\n}\n\n\"IDEs\" --> \"JUnit Platform\"\n\"Build Tools\" --> \"JUnit Platform\"\n\"Console Launcher\" --> \"JUnit Platform\" : requests discovery and execution\n\n\"JUnit Platform\" --> \"Suite Test Engine\"\n\"Suite Test Engine\" --> \"@Suite annotated class\" : discovers and executes\n\"@Suite annotated class\" --> \"JUnit Platform (@Suite)\" : requests discovery and execution\n\"JUnit Platform (@Suite)\" --> \"Jupiter Test Engine (@Suite)\" : forwards request\n\"Jupiter Test Engine (@Suite)\"  --> \"Example Test A (@Suite)\" : discovers and executes\n\n\"JUnit Platform\" --> \"Jupiter Test Engine\": forwards request\n\"Jupiter Test Engine\" --> \"Example Test A\" #line:red;line.bold;text:red : also discovers and executes!\n@enduml\n"
  },
  {
    "path": "documentation/src/plantuml/launcher-api-diagram.puml",
    "content": "@startuml\nobject \"IDEs\"\nobject \"Build Tools\"\nobject \"Console Launcher\"\nobject \"JUnit Platform\"\nobject \"Jupiter Test Engine\"\nobject \"Cucumber Test Engine\"\nobject \"Spock Test Engine\"\nobject \"Test Classes\"\nobject \"Feature Files\"\nobject \"Specifications\"\n\n\"IDEs\" --> \"JUnit Platform\"\n\"Build Tools\" --> \"JUnit Platform\"\n\"Console Launcher\" --> \"JUnit Platform\" : requests discovery and execution\n\"JUnit Platform\" --> \"Jupiter Test Engine\"\n\"Jupiter Test Engine\" --> \"Test Classes\"\n\n\"JUnit Platform\" --> \"Cucumber Test Engine\"\n\"Cucumber Test Engine\" --> \"Feature Files\"\n\n\"JUnit Platform\" --> \"Spock Test Engine\": forwards request\n\"Spock Test Engine\" --> \"Specifications\" : discovers and executes\n@enduml\n"
  },
  {
    "path": "documentation/src/test/java/example/AssertJAssertionsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Test;\n\nclass AssertJAssertionsDemo {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\t@Test\n\tvoid assertWithAssertJ() {\n\t\tassertThat(calculator.subtract(4, 1)).isEqualTo(3);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/AssertionsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// @formatter:off\n// tag::user_guide[]\nimport static java.time.Duration.ofMillis;\nimport static java.time.Duration.ofMinutes;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTimeout;\nimport static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport example.domain.Person;\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\n\nclass AssertionsDemo {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\tprivate final Person person = new Person(\"Jane\", \"Doe\");\n\n\t@Test\n\tvoid standardAssertions() {\n\t\tassertEquals(2, calculator.add(1, 1));\n\t\tassertEquals(4, calculator.multiply(2, 2),\n\t\t\t\t\"The optional failure message is now the last parameter\");\n\n\t\t// Lazily evaluates generateFailureMessage('a','b').\n\t\tassertTrue('a' < 'b', () -> generateFailureMessage('a','b'));\n\t}\n\n\t@Test\n\tvoid groupedAssertions() {\n\t\t// In a grouped assertion all assertions are executed, and all\n\t\t// failures will be reported together.\n\t\tassertAll(\"person\",\n\t\t\t() -> assertEquals(\"Jane\", person.getFirstName()),\n\t\t\t() -> assertEquals(\"Doe\", person.getLastName())\n\t\t);\n\t}\n\n\t@Test\n\tvoid dependentAssertions() {\n\t\t// Within a code block, if an assertion fails the\n\t\t// subsequent code in the same block will be skipped.\n\t\tassertAll(\"properties\",\n\t\t\t() -> {\n\t\t\t\tString firstName = person.getFirstName();\n\t\t\t\tassertNotNull(firstName);\n\n\t\t\t\t// Executed only if the previous assertion is valid.\n\t\t\t\tassertAll(\"first name\",\n\t\t\t\t\t() -> assertTrue(firstName.startsWith(\"J\")),\n\t\t\t\t\t() -> assertTrue(firstName.endsWith(\"e\"))\n\t\t\t\t);\n\t\t\t},\n\t\t\t() -> {\n\t\t\t\t// Grouped assertion, so processed independently\n\t\t\t\t// of results of first name assertions.\n\t\t\t\tString lastName = person.getLastName();\n\t\t\t\tassertNotNull(lastName);\n\n\t\t\t\t// Executed only if the previous assertion is valid.\n\t\t\t\tassertAll(\"last name\",\n\t\t\t\t\t() -> assertTrue(lastName.startsWith(\"D\")),\n\t\t\t\t\t() -> assertTrue(lastName.endsWith(\"e\"))\n\t\t\t\t);\n\t\t\t}\n\t\t);\n\t}\n\n\t// end::user_guide[]\n\t@extensions.DisabledOnOpenJ9\n\t// tag::user_guide[]\n\t@Test\n\tvoid exceptionTesting() {\n\t\tException exception = assertThrows(ArithmeticException.class, () ->\n\t\t\tcalculator.divide(1, 0));\n\t\tassertEquals(\"/ by zero\", exception.getMessage());\n\t}\n\n\t// end::user_guide[]\n\t@Tag(\"timeout\")\n\t// tag::user_guide[]\n\t@Test\n\tvoid timeoutNotExceeded() {\n\t\t// The following assertion succeeds.\n\t\tassertTimeout(ofMinutes(2), () -> {\n\t\t\t// Perform task that takes less than 2 minutes.\n\t\t});\n\t}\n\n\t// end::user_guide[]\n\t@Tag(\"timeout\")\n\t// tag::user_guide[]\n\t@Test\n\tvoid timeoutNotExceededWithResult() {\n\t\t// The following assertion succeeds, and returns the supplied object.\n\t\tString actualResult = assertTimeout(ofMinutes(2), () -> {\n\t\t\treturn \"a result\";\n\t\t});\n\t\tassertEquals(\"a result\", actualResult);\n\t}\n\n\t// end::user_guide[]\n\t@Tag(\"timeout\")\n\t// tag::user_guide[]\n\t@Test\n\tvoid timeoutNotExceededWithMethod() {\n\t\t// The following assertion invokes a method reference and returns an object.\n\t\tString actualGreeting = assertTimeout(ofMinutes(2), AssertionsDemo::greeting);\n\t\tassertEquals(\"Hello, World!\", actualGreeting);\n\t}\n\n\t// end::user_guide[]\n\t@Tag(\"timeout\")\n\t@extensions.ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\tvoid timeoutExceeded() {\n\t\t// The following assertion fails with an error message similar to:\n\t\t// execution exceeded timeout of 10 ms by 91 ms\n\t\tassertTimeout(ofMillis(10), () -> {\n\t\t\t// Simulate task that takes more than 10 ms.\n\t\t\tThread.sleep(100);\n\t\t});\n\t}\n\n\t// end::user_guide[]\n\t@Tag(\"timeout\")\n\t@extensions.ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\tvoid timeoutExceededWithPreemptiveTermination() {\n\t\t// The following assertion fails with an error message similar to:\n\t\t// execution timed out after 10 ms\n\t\tassertTimeoutPreemptively(ofMillis(10), () -> {\n\t\t\t// Simulate task that takes more than 10 ms.\n\t\t\tnew CountDownLatch(1).await();\n\t\t});\n\t}\n\n\tprivate static String greeting() {\n\t\treturn \"Hello, World!\";\n\t}\n\n\tprivate static String generateFailureMessage(char a, char b) {\n\t\treturn \"Assertion messages can be lazily evaluated -- \"\n\t\t\t\t+ \"to avoid constructing complex messages unnecessarily.\" + (a < b);\n\t}\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/AssumptionsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// @formatter:off\n// tag::user_guide[]\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.jupiter.api.Assumptions.assumingThat;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Test;\n\nclass AssumptionsDemo {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\t@Test\n\tvoid testOnlyOnCiServer() {\n\t\tassumeTrue(\"CI\".equals(System.getenv(\"ENV\")));\n\t\t// remainder of test\n\t}\n\n\t@Test\n\tvoid testOnlyOnDeveloperWorkstation() {\n\t\tassumeTrue(\"DEV\".equals(System.getenv(\"ENV\")),\n\t\t\t() -> \"Aborting test: not on developer workstation\");\n\t\t// remainder of test\n\t}\n\n\t@Test\n\tvoid testInAllEnvironments() {\n\t\tassumingThat(\"CI\".equals(System.getenv(\"ENV\")),\n\t\t\t() -> {\n\t\t\t\t// perform these assertions only on the CI server\n\t\t\t\tassertEquals(2, calculator.divide(4, 2));\n\t\t\t});\n\n\t\t// perform these assertions in all environments\n\t\tassertEquals(42, calculator.multiply(6, 7));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/AutoCloseDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport example.registration.WebClient;\n\nimport org.junit.jupiter.api.AutoClose;\nimport org.junit.jupiter.api.Test;\n\n// tag::user_guide_example[]\nclass AutoCloseDemo {\n\n\t@AutoClose // <1>\n\tWebClient webClient = new WebClient(); // <2>\n\n\tString serverUrl = // specify server URL ...\n\t\t// end::user_guide_example[]\n\t\t\"https://localhost\";\n\t// tag::user_guide_example[]\n\n\t@Test\n\tvoid getProductList() {\n\t\t// Use WebClient to connect to web server and verify response\n\t\tassertEquals(200, webClient.get(serverUrl + \"/products\").getResponseStatus());\n\t}\n\n}\n// end::user_guide_example[]\n"
  },
  {
    "path": "documentation/src/test/java/example/BeforeAndAfterSuiteDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.platform.suite.api.AfterSuite;\nimport org.junit.platform.suite.api.BeforeSuite;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n//tag::user_guide[]\n@Suite\n@SelectPackages(\"example\")\nclass BeforeAndAfterSuiteDemo {\n\n\t@BeforeSuite\n\tstatic void beforeSuite() {\n\t\t// executes before the test suite\n\t}\n\n\t@AfterSuite\n\tstatic void afterSuite() {\n\t\t// executes after the test suite\n\t}\n\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ClassTemplateDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\n\n// tag::user_guide[]\n@ClassTemplate\n@ExtendWith(ClassTemplateDemo.MyClassTemplateInvocationContextProvider.class)\nclass ClassTemplateDemo {\n\n\tstatic final List<String> WELL_KNOWN_FRUITS\n\t// tag::custom_line_break[]\n\t\t= List.of(\"apple\", \"banana\", \"lemon\");\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tprivate String fruit;\n\n\t@Test\n\tvoid notNull() {\n\t\tassertNotNull(fruit);\n\t}\n\n\t@Test\n\tvoid wellKnown() {\n\t\tassertTrue(WELL_KNOWN_FRUITS.contains(fruit));\n\t}\n\n\t// end::user_guide[]\n\tstatic\n\t// tag::user_guide[]\n\tpublic class MyClassTemplateInvocationContextProvider\n\t\t\t// tag::custom_line_break[]\n\t\t\timplements ClassTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<ClassTemplateInvocationContext>\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\tprovideClassTemplateInvocationContexts(ExtensionContext context) {\n\n\t\t\treturn Stream.of(invocationContext(\"apple\"), invocationContext(\"banana\"));\n\t\t}\n\n\t\tprivate ClassTemplateInvocationContext invocationContext(String parameter) {\n\t\t\treturn new ClassTemplateInvocationContext() {\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn parameter;\n\t\t\t\t}\n\n\t\t\t\t// end::user_guide[]\n\t\t\t\t@SuppressWarnings(\"Convert2Lambda\")\n\t\t\t\t// tag::user_guide[]\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of(new TestInstancePostProcessor() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void postProcessTestInstance(\n\t\t\t\t\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t\t\t\t\tObject testInstance, ExtensionContext context) {\n\t\t\t\t\t\t\t((ClassTemplateDemo) testInstance).fruit = parameter;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ConditionalTestExecutionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.condition.JRE.JAVA_17;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_18;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_19;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_21;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_25;\nimport static org.junit.jupiter.api.condition.OS.LINUX;\nimport static org.junit.jupiter.api.condition.OS.MAC;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.DisabledForJreRange;\nimport org.junit.jupiter.api.condition.DisabledIf;\nimport org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;\nimport org.junit.jupiter.api.condition.DisabledIfSystemProperty;\nimport org.junit.jupiter.api.condition.DisabledInNativeImage;\nimport org.junit.jupiter.api.condition.DisabledOnJre;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.condition.EnabledForJreRange;\nimport org.junit.jupiter.api.condition.EnabledIf;\nimport org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;\nimport org.junit.jupiter.api.condition.EnabledIfSystemProperty;\nimport org.junit.jupiter.api.condition.EnabledInNativeImage;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.EnabledOnOs;\n\nclass ConditionalTestExecutionDemo {\n\n\t// tag::user_guide_os[]\n\t@Test\n\t@EnabledOnOs(MAC)\n\tvoid onlyOnMacOs() {\n\t\t// ...\n\t}\n\n\t@TestOnMac\n\tvoid testOnMac() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledOnOs({ LINUX, MAC })\n\tvoid onLinuxOrMac() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnOs(WINDOWS)\n\tvoid notOnWindows() {\n\t\t// ...\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Test\n\t@EnabledOnOs(MAC)\n\t@interface TestOnMac {\n\t}\n\t// end::user_guide_os[]\n\n\t// tag::user_guide_architecture[]\n\t@Test\n\t@EnabledOnOs(architectures = \"aarch64\")\n\tvoid onAarch64() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnOs(architectures = \"x86_64\")\n\tvoid notOnX86_64() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = MAC, architectures = \"aarch64\")\n\tvoid onNewMacs() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = MAC, architectures = \"aarch64\")\n\tvoid notOnNewMacs() {\n\t\t// ...\n\t}\n\t// end::user_guide_architecture[]\n\n\t// tag::user_guide_jre[]\n\t@Test\n\t@EnabledOnJre(JAVA_17)\n\tvoid onlyOnJava17() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledOnJre({ JAVA_17, JAVA_21 })\n\tvoid onJava17And21() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_21, max = JAVA_25)\n\tvoid fromJava21To25() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_21)\n\tvoid onJava21ndHigher() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledForJreRange(max = JAVA_18)\n\tvoid fromJava17To18() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnJre(JAVA_19)\n\tvoid notOnJava19() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledForJreRange(min = JAVA_17, max = JAVA_17)\n\tvoid notFromJava17To19() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledForJreRange(min = JAVA_19)\n\tvoid notOnJava19AndHigher() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledForJreRange(max = JAVA_18)\n\tvoid notFromJava17To18() {\n\t\t// ...\n\t}\n\t// end::user_guide_jre[]\n\n\t// tag::user_guide_jre_arbitrary_versions[]\n\t@Test\n\t@EnabledOnJre(versions = 26)\n\tvoid onlyOnJava26() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = { 25, 26 })\n\t// Can also be expressed as follows.\n\t// @EnabledOnJre(value = JAVA_25, versions = 26)\n\tvoid onJava25And26() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 26)\n\tvoid onJava26AndHigher() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 25, maxVersion = 27)\n\t// Can also be expressed as follows.\n\t// @EnabledForJreRange(min = JAVA_25, maxVersion = 27)\n\tvoid fromJava25To27() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnJre(versions = 26)\n\tvoid notOnJava26() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledOnJre(versions = { 25, 26 })\n\t// Can also be expressed as follows.\n\t// @DisabledOnJre(value = JAVA_25, versions = 26)\n\tvoid notOnJava25And26() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = 26)\n\tvoid notOnJava26AndHigher() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = 25, maxVersion = 27)\n\t// Can also be expressed as follows.\n\t// @DisabledForJreRange(min = JAVA_25, maxVersion = 27)\n\tvoid notFromJava25To27() {\n\t\t// ...\n\t}\n\t// end::user_guide_jre_arbitrary_versions[]\n\n\t// tag::user_guide_native[]\n\t@Test\n\t@EnabledInNativeImage\n\tvoid onlyWithinNativeImage() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledInNativeImage\n\tvoid neverWithinNativeImage() {\n\t\t// ...\n\t}\n\t// end::user_guide_native[]\n\n\t// tag::user_guide_system_property[]\n\t@Test\n\t@EnabledIfSystemProperty(named = \"os.arch\", matches = \".*64.*\")\n\tvoid onlyOn64BitArchitectures() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = \"ci-server\", matches = \"true\")\n\tvoid notOnCiServer() {\n\t\t// ...\n\t}\n\t// end::user_guide_system_property[]\n\n\t// tag::user_guide_environment_variable[]\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = \"ENV\", matches = \"staging-server\")\n\tvoid onlyOnStagingServer() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = \"ENV\", matches = \".*development.*\")\n\tvoid notOnDeveloperWorkstation() {\n\t\t// ...\n\t}\n\t// end::user_guide_environment_variable[]\n\n\t// tag::user_guide_custom[]\n\t@Test\n\t@EnabledIf(\"customCondition\")\n\tvoid enabled() {\n\t\t// ...\n\t}\n\n\t@Test\n\t@DisabledIf(\"customCondition\")\n\tvoid disabled() {\n\t\t// ...\n\t}\n\n\tboolean customCondition() {\n\t\treturn true;\n\t}\n\t// end::user_guide_custom[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/CustomLauncherInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\nimport org.junit.platform.launcher.LauncherInterceptor;\n\npublic class CustomLauncherInterceptor implements LauncherInterceptor {\n\n\tprivate final URLClassLoader customClassLoader;\n\n\tpublic CustomLauncherInterceptor() throws Exception {\n\t\tClassLoader parent = Thread.currentThread().getContextClassLoader();\n\t\tcustomClassLoader = new URLClassLoader(new URL[] { URI.create(\"some.jar\").toURL() }, parent);\n\t}\n\n\t@Override\n\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\tThread currentThread = Thread.currentThread();\n\t\tClassLoader originalClassLoader = currentThread.getContextClassLoader();\n\t\tcurrentThread.setContextClassLoader(customClassLoader);\n\t\ttry {\n\t\t\treturn invocation.proceed();\n\t\t}\n\t\tfinally {\n\t\t\tcurrentThread.setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tcustomClassLoader.close();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to close custom class loader\", e);\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/CustomTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * This is a no-op {@link TestEngine} that is only\n * used to make examples compile.\n */\nclass CustomTestEngine implements TestEngine {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"custom-test-engine\";\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\treturn new EngineDescriptor(UniqueId.forEngine(getId()), \"Custom Test Engine\");\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/DefaultLocaleTimezoneExtensionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.time.ZoneOffset;\nimport java.util.Locale;\nimport java.util.TimeZone;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.util.DefaultLocale;\nimport org.junit.jupiter.api.util.DefaultTimeZone;\nimport org.junit.jupiter.api.util.LocaleProvider;\nimport org.junit.jupiter.api.util.TimeZoneProvider;\n\npublic class DefaultLocaleTimezoneExtensionDemo {\n\n\t// tag::default_locale_language[]\n\t@Test\n\t@DefaultLocale(\"zh-Hant-TW\")\n\tvoid test_with_language() {\n\t\tassertThat(Locale.getDefault()).isEqualTo(Locale.forLanguageTag(\"zh-Hant-TW\"));\n\t}\n\t// end::default_locale_language[]\n\n\t// tag::default_locale_language_alternatives[]\n\t@Test\n\t@DefaultLocale(language = \"en\")\n\tvoid test_with_language_only() {\n\t\tassertThat(Locale.getDefault()).isEqualTo(new Locale.Builder().setLanguage(\"en\").build());\n\t}\n\n\t@Test\n\t@DefaultLocale(language = \"en\", country = \"EN\")\n\tvoid test_with_language_and_country() {\n\t\tassertThat(Locale.getDefault()).isEqualTo(new Locale.Builder().setLanguage(\"en\").setRegion(\"EN\").build());\n\t}\n\n\t@Test\n\t@DefaultLocale(language = \"ja\", country = \"JP\", variant = \"japanese\")\n\tvoid test_with_language_and_country_and_vairant() {\n\t\tassertThat(Locale.getDefault()).isEqualTo(\n\t\t\tnew Locale.Builder().setLanguage(\"ja\").setRegion(\"JP\").setVariant(\"japanese\").build());\n\t}\n\t// end::default_locale_language_alternatives[]\n\n\t@Nested\n\t// tag::default_locale_class_level[]\n\t@DefaultLocale(language = \"fr\")\n\tclass MyLocaleTests {\n\n\t\t@Test\n\t\tvoid test_with_class_level_configuration() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(new Locale.Builder().setLanguage(\"fr\").build());\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"en\")\n\t\tvoid test_with_method_level_configuration() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(new Locale.Builder().setLanguage(\"en\").build());\n\t\t}\n\n\t}\n\t// end::default_locale_class_level[]\n\n\t// tag::default_locale_with_provider[]\n\t@Test\n\t@DefaultLocale(localeProvider = EnglishProvider.class)\n\tvoid test_with_locale_provider() {\n\t\tassertThat(Locale.getDefault()).isEqualTo(new Locale.Builder().setLanguage(\"en\").build());\n\t}\n\n\tstatic class EnglishProvider implements LocaleProvider {\n\t\t@Override\n\t\tpublic Locale get() {\n\t\t\treturn Locale.ENGLISH;\n\t\t}\n\t}\n\t// end::default_locale_with_provider[]\n\n\t// tag::default_timezone_zone[]\n\t@Test\n\t@DefaultTimeZone(\"CET\")\n\tvoid test_with_short_zone_id() {\n\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"CET\"));\n\t}\n\n\t@Test\n\t@DefaultTimeZone(\"Africa/Juba\")\n\tvoid test_with_long_zone_id() {\n\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"Africa/Juba\"));\n\t}\n\t// end::default_timezone_zone[]\n\n\t@Nested\n\t// tag::default_timezone_class_level[]\n\t@DefaultTimeZone(\"CET\")\n\tclass MyTimeZoneTests {\n\n\t\t@Test\n\t\tvoid test_with_class_level_configuration() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"CET\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"Africa/Juba\")\n\t\tvoid test_with_method_level_configuration() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"Africa/Juba\"));\n\t\t}\n\n\t}\n\t// end::default_timezone_class_level[]\n\n\t// tag::default_time_zone_with_provider[]\n\t@Test\n\t@DefaultTimeZone(timeZoneProvider = UtcTimeZoneProvider.class)\n\tvoid test_with_time_zone_provider() {\n\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"UTC\"));\n\t}\n\n\tstatic class UtcTimeZoneProvider implements TimeZoneProvider {\n\t\t@Override\n\t\tpublic TimeZone get() {\n\t\t\treturn TimeZone.getTimeZone(ZoneOffset.UTC);\n\t\t}\n\t}\n\t// end::default_time_zone_with_provider[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/DisabledClassDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n@Disabled(\"Disabled until bug #99 has been fixed\")\nclass DisabledClassDemo {\n\n\t@Test\n\tvoid testWillBeSkipped() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/DisabledTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nclass DisabledTestsDemo {\n\n\t@Disabled(\"Disabled until bug #42 has been resolved\")\n\t@Test\n\tvoid testWillBeSkipped() {\n\t}\n\n\t@Test\n\tvoid testWillBeExecuted() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/DisplayNameDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\n\n@DisplayName(\"A special test case\")\nclass DisplayNameDemo {\n\n\t@Test\n\t@DisplayName(\"Custom test name containing spaces\")\n\tvoid testWithDisplayNameContainingSpaces() {\n\t}\n\n\t@Test\n\t@DisplayName(\"╯°□°）╯\")\n\tvoid testWithDisplayNameContainingSpecialCharacters() {\n\t}\n\n\t@Test\n\t@DisplayName(\"😱\")\n\tvoid testWithDisplayNameContainingEmoji() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/DisplayNameGeneratorDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DisplayNameGeneration;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences.SentenceFragment;\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\nimport org.junit.jupiter.api.IndicativeSentencesGeneration;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nclass DisplayNameGeneratorDemo {\n\n\t@Nested\n\t// tag::user_guide_replace_underscores[]\n\t@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)\n\tclass A_year_is_not_supported {\n\n\t\t@Test\n\t\tvoid if_it_is_zero() {\n\t\t}\n\n\t\t@DisplayName(\"A negative value for year is not supported by the leap year computation.\")\n\t\t@ParameterizedTest(name = \"For example, year {0} is not supported.\")\n\t\t@ValueSource(ints = { -1, -4 })\n\t\tvoid if_it_is_negative(int year) {\n\t\t}\n\n\t}\n\t// end::user_guide_replace_underscores[]\n\n\t@Nested\n\t// tag::user_guide_indicative_sentences[]\n\t@IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores.class)\n\tclass A_year_is_a_leap_year {\n\n\t\t@Test\n\t\tvoid if_it_is_divisible_by_4_but_not_by_100() {\n\t\t}\n\n\t\t@ParameterizedTest(name = \"Year {0} is a leap year.\")\n\t\t@ValueSource(ints = { 2016, 2020, 2048 })\n\t\tvoid if_it_is_one_of_the_following_years(int year) {\n\t\t}\n\n\t}\n\t// end::user_guide_indicative_sentences[]\n\n\t@Nested\n\t// tag::user_guide_custom_sentence_fragments[]\n\t@SentenceFragment(\"A year is a leap year\")\n\t@IndicativeSentencesGeneration\n\tclass LeapYearTests {\n\n\t\t@SentenceFragment(\"if it is divisible by 4 but not by 100\")\n\t\t@Test\n\t\tvoid divisibleBy4ButNotBy100() {\n\t\t}\n\n\t\t@SentenceFragment(\"if it is one of the following years\")\n\t\t@ParameterizedTest(name = \"{0}\")\n\t\t@ValueSource(ints = { 2016, 2020, 2048 })\n\t\tvoid validLeapYear(int year) {\n\t\t}\n\n\t}\n\t// end::user_guide_custom_sentence_fragments[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/DocumentationTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.platform.suite.api.ExcludeTags;\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 5.0\n */\n@Suite\n@SelectPackages(\"example\")\n@IncludeClassNamePatterns(\".+(Tests|Demo)$\")\n@ExcludeTags(\"exclude\")\nclass DocumentationTestSuite {\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/DynamicTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static example.util.StringUtils.isPalindrome;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.function.Function;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.parallel.Execution;\n\n// end::user_guide[]\n// @formatter:off\n// tag::user_guide[]\nclass DynamicTestsDemo {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\t// This method will not be executed but produce a warning\n\t@TestFactory\n\t// end::user_guide[]\n\t@Tag(\"exclude\")\n\tDynamicTest dummy() { return dynamicTest(\"dummy\", () -> {}); }\n\t// tag::user_guide[]\n\tList<String> dynamicTestsWithInvalidReturnType() {\n\t\treturn Arrays.asList(\"Hello\");\n\t}\n\n\t@TestFactory\n\tCollection<DynamicTest> dynamicTestsFromCollection() {\n\t\treturn Arrays.asList(\n\t\t\tdynamicTest(\"1st dynamic test\", () -> assertTrue(isPalindrome(\"madam\"))),\n\t\t\tdynamicTest(\"2nd dynamic test\", () -> assertEquals(4, calculator.multiply(2, 2)))\n\t\t);\n\t}\n\n\t@TestFactory\n\tIterable<DynamicTest> dynamicTestsFromIterable() {\n\t\treturn Arrays.asList(\n\t\t\tdynamicTest(\"3rd dynamic test\", () -> assertTrue(isPalindrome(\"madam\"))),\n\t\t\tdynamicTest(\"4th dynamic test\", () -> assertEquals(4, calculator.multiply(2, 2)))\n\t\t);\n\t}\n\n\t@TestFactory\n\tIterator<DynamicTest> dynamicTestsFromIterator() {\n\t\treturn Arrays.asList(\n\t\t\tdynamicTest(\"5th dynamic test\", () -> assertTrue(isPalindrome(\"madam\"))),\n\t\t\tdynamicTest(\"6th dynamic test\", () -> assertEquals(4, calculator.multiply(2, 2)))\n\t\t).iterator();\n\t}\n\n\t@TestFactory\n\tDynamicTest[] dynamicTestsFromArray() {\n\t\treturn new DynamicTest[] {\n\t\t\tdynamicTest(\"7th dynamic test\", () -> assertTrue(isPalindrome(\"madam\"))),\n\t\t\tdynamicTest(\"8th dynamic test\", () -> assertEquals(4, calculator.multiply(2, 2)))\n\t\t};\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTestsFromStream() {\n\t\treturn Stream.of(\"racecar\", \"radar\", \"mom\", \"dad\")\n\t\t\t.map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text))));\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTestsFromIntStream() {\n\t\t// Generates tests for the first 10 even integers.\n\t\treturn IntStream.iterate(0, n -> n + 2).limit(10)\n\t\t\t.mapToObj(n -> dynamicTest(\"test\" + n, () -> assertEquals(0, n % 2)));\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> generateRandomNumberOfTests() {\n\n\t\t// Generates random positive integers between 0 and 100 until\n\t\t// a number evenly divisible by 7 is encountered.\n\t\tIterator<Integer> inputGenerator = new Iterator<>() {\n\n\t\t\tRandom random = new Random();\n\t\t\t// end::user_guide[]\n\t\t\t{\n\t\t\t\t// Use fixed seed to always produce the same number of tests for execution on the CI server\n\t\t\t\trandom = new Random(23);\n\t\t\t}\n\t\t\t// tag::user_guide[]\n\t\t\tint current;\n\n\t\t\t@Override\n\t\t\tpublic boolean hasNext() {\n\t\t\t\tcurrent = random.nextInt(100);\n\t\t\t\treturn current % 7 != 0;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Integer next() {\n\t\t\t\treturn current;\n\t\t\t}\n\t\t};\n\n\t\t// Generates display names like: input:5, input:37, input:85, etc.\n\t\tFunction<Integer, String> displayNameGenerator = (input) -> \"input:\" + input;\n\n\t\t// Executes tests based on the current input value.\n\t\tThrowingConsumer<Integer> testExecutor = (input) -> assertTrue(input % 7 != 0);\n\n\t\t// Returns a stream of dynamic tests.\n\t\treturn DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor);\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTestsFromStreamFactoryMethod() {\n\t\t// Stream of palindromes to check\n\t\tStream<String> inputStream = Stream.of(\"racecar\", \"radar\", \"mom\", \"dad\");\n\n\t\t// Generates display names like: racecar is a palindrome\n\t\tFunction<String, String> displayNameGenerator = text -> text + \" is a palindrome\";\n\n\t\t// Executes tests based on the current input value.\n\t\tThrowingConsumer<String> testExecutor = text -> assertTrue(isPalindrome(text));\n\n\t\t// Returns a stream of dynamic tests.\n\t\treturn DynamicTest.stream(inputStream, displayNameGenerator, testExecutor);\n\t}\n\n\t@TestFactory\n\tStream<DynamicNode> dynamicTestsWithContainers() {\n\t\treturn Stream.of(\"A\", \"B\", \"C\")\n\t\t\t.map(input -> dynamicContainer(\"Container \" + input, Stream.of(\n\t\t\t\tdynamicTest(\"not null\", () -> assertNotNull(input)),\n\t\t\t\tdynamicContainer(\"properties\", Stream.of(\n\t\t\t\t\tdynamicTest(\"length > 0\", () -> assertTrue(input.length() > 0)),\n\t\t\t\t\tdynamicTest(\"not empty\", () -> assertFalse(input.isEmpty()))\n\t\t\t\t))\n\t\t\t)));\n\t}\n\n\t// end::user_guide[]\n\t// tag::execution_mode[]\n\t@TestFactory\n\t@Execution(CONCURRENT) // <1>\n\tStream<DynamicNode> dynamicTestsWithConfiguredExecutionMode() {\n\t\treturn Stream.of(\"A\", \"B\", \"C\")\n\t\t\t\t.map(input ->\n\t\t\t\t\tdynamicContainer(outer -> outer\n\t\t\t\t\t\t.displayName(\"Container \" + input)\n\t\t\t\t\t\t.children(\n\t\t\t\t\t\t\tdynamicTest(config -> config\n\t\t\t\t\t\t\t\t.displayName(\"not null\")\n\t\t\t\t\t\t\t\t.executionMode(SAME_THREAD) // <2>\n\t\t\t\t\t\t\t\t.executable(() -> assertNotNull(input))\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\tdynamicContainer(inner -> inner\n\t\t\t\t\t\t\t\t.displayName(\"properties\")\n\t\t\t\t\t\t\t\t.executionMode(CONCURRENT) // <3>\n\t\t\t\t\t\t\t\t.childExecutionMode(SAME_THREAD) // <4>\n\t\t\t\t\t\t\t\t.children(\n\t\t\t\t\t\t\t\t\tdynamicTest(config -> config\n\t\t\t\t\t\t\t\t\t\t.displayName(\"length > 0\")\n\t\t\t\t\t\t\t\t\t\t.executionMode(CONCURRENT) // <5>\n\t\t\t\t\t\t\t\t\t\t.executable(() -> assertTrue(input.length() > 0))\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\tdynamicTest(config -> config\n\t\t\t\t\t\t\t\t\t\t.displayName(\"not empty\")\n\t\t\t\t\t\t\t\t\t\t.executable(() -> assertFalse(input.isEmpty()))\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\t);\n\t}\n\t// end::execution_mode[]\n\n\t// tag::user_guide[]\n\t@TestFactory\n\tDynamicNode dynamicNodeSingleTest() {\n\t\treturn dynamicTest(\"'pop' is a palindrome\", () -> assertTrue(isPalindrome(\"pop\")));\n\t}\n\n\t@TestFactory\n\tDynamicNode dynamicNodeSingleContainer() {\n\t\treturn dynamicContainer(\"palindromes\",\n\t\t\tStream.of(\"racecar\", \"radar\", \"mom\", \"dad\")\n\t\t\t\t.map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text)))\n\t\t));\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/DynamicTestsNamedDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\n\nimport static example.util.StringUtils.isPalindrome;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Named.named;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.NamedExecutable;\nimport org.junit.jupiter.api.TestFactory;\n\npublic class DynamicTestsNamedDemo {\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTestsFromStreamFactoryMethodWithNames() {\n\t\t// Stream of palindromes to check\n\t\t// end::user_guide[]\n\t\t// @formatter:off\n\t\t// tag::user_guide[]\n\t\tvar inputStream = Stream.of(\n\t\t\tnamed(\"racecar is a palindrome\", \"racecar\"),\n\t\t\tnamed(\"radar is also a palindrome\", \"radar\"),\n\t\t\tnamed(\"mom also seems to be a palindrome\", \"mom\"),\n\t\t\tnamed(\"dad is yet another palindrome\", \"dad\")\n\t\t);\n\t\t// end::user_guide[]\n\t\t// @formatter:on\n\t\t// tag::user_guide[]\n\n\t\t// Returns a stream of dynamic tests.\n\t\treturn DynamicTest.stream(inputStream, text -> assertTrue(isPalindrome(text)));\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTestsFromStreamFactoryMethodWithNamedExecutables() {\n\t\t// Stream of palindromes to check\n\t\t// end::user_guide[]\n\t\t// @formatter:off\n\t\t// tag::user_guide[]\n\t\tvar inputStream = Stream.of(\"racecar\", \"radar\", \"mom\", \"dad\")\n\t\t\t\t.map(PalindromeNamedExecutable::new);\n\t\t// end::user_guide[]\n\t\t// @formatter:on\n\t\t// tag::user_guide[]\n\n\t\t// Returns a stream of dynamic tests based on NamedExecutables.\n\t\treturn DynamicTest.stream(inputStream);\n\t}\n\n\trecord PalindromeNamedExecutable(String text) implements NamedExecutable {\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn \"'%s' is a palindrome\".formatted(text);\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute() {\n\t\t\tassertTrue(isPalindrome(text));\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ExampleTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\n@TestMethodOrder(OrderAnnotation.class)\npublic class ExampleTestCase {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\t@Test\n\t@Disabled(\"for demonstration purposes\")\n\t@Order(1)\n\tvoid skippedTest() {\n\t\t// skipped ...\n\t}\n\n\t@Test\n\t@Order(2)\n\tvoid succeedingTest() {\n\t\tassertEquals(42, calculator.multiply(6, 7));\n\t}\n\n\t@Test\n\t@Order(3)\n\tvoid abortedTest() {\n\t\tassumeTrue(\"abc\".contains(\"Z\"), \"abc does not contain Z\");\n\t\t// aborted ...\n\t}\n\n\t@Test\n\t@Order(4)\n\tvoid failingTest() {\n\t\t// The following throws an ArithmeticException: \"/ by zero\"\n\t\tcalculator.divide(1, 0);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ExplicitExecutionModeDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\n\n//tag::user_guide[]\n@Execution(ExecutionMode.CONCURRENT)\nclass ExplicitExecutionModeDemo {\n\n\t@Test\n\tvoid testA() {\n\t\t// concurrent\n\t}\n\n\t@Test\n\t@Execution(ExecutionMode.SAME_THREAD)\n\tvoid testB() {\n\t\t// overrides to same_thread\n\t}\n\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ExternalCustomConditionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide_external_custom_condition[]\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledIf;\n\nclass ExternalCustomConditionDemo {\n\n\t@Test\n\t@EnabledIf(\"example.ExternalCondition#customCondition\")\n\tvoid enabled() {\n\t\t// ...\n\t}\n\n}\n\nclass ExternalCondition {\n\n\tstatic boolean customCondition() {\n\t\treturn true;\n\t}\n\n}\n// end::user_guide_external_custom_condition[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ExternalFieldSourceDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport java.util.List;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.FieldSource;\n\nclass ExternalFieldSourceDemo {\n\n\t// tag::external_field_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource(\"example.FruitUtils#tropicalFruits\")\n\tvoid testWithExternalFieldSource(String tropicalFruit) {\n\t\t// test with tropicalFruit\n\t}\n\t// end::external_field_FieldSource_example[]\n}\n\nclass FruitUtils {\n\n\tpublic static final List<String> tropicalFruits = List.of(\"pineapple\", \"kiwi\");\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/ExternalMethodSourceDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::external_MethodSource_example[]\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nclass ExternalMethodSourceDemo {\n\n\t@ParameterizedTest\n\t@MethodSource(\"example.StringsProviders#tinyStrings\")\n\tvoid testWithExternalMethodSource(String tinyString) {\n\t\t// test with tiny string\n\t}\n}\n\nclass StringsProviders {\n\n\tstatic Stream<String> tinyStrings() {\n\t\treturn Stream.of(\".\", \"oo\", \"OOO\");\n\t}\n}\n// end::external_MethodSource_example[]\n"
  },
  {
    "path": "documentation/src/test/java/example/Fast.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Tag;\n\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Tag(\"fast\")\npublic @interface Fast {\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/FastTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Tag(\"fast\")\n@Test\npublic @interface FastTest {\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/FirstCustomEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n//tag::user_guide[]\nimport static java.net.InetAddress.getLoopbackAddress;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.ServerSocket;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * First custom test engine implementation.\n */\npublic class FirstCustomEngine implements TestEngine {\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tpublic ServerSocket socket;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"first-custom-test-engine\";\n\t}\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tpublic ServerSocket getSocket() {\n\t\treturn this.socket;\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\treturn new EngineDescriptor(uniqueId, \"First Custom Test Engine\");\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\trequest.getEngineExecutionListener()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.executionStarted(request.getRootTestDescriptor());\n\n\t\tNamespacedHierarchicalStore<Namespace> store = request.getStore();\n\t\tsocket = store.computeIfAbsent(Namespace.GLOBAL, \"serverSocket\", key -> {\n\t\t\ttry {\n\t\t\t\treturn new ServerSocket(0, 50, getLoopbackAddress());\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to start ServerSocket\", e);\n\t\t\t}\n\t\t}, ServerSocket.class);\n\n\t\trequest.getEngineExecutionListener()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.executionFinished(request.getRootTestDescriptor(), successful());\n\t}\n\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/HttpServerDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URI;\nimport java.net.URL;\n\nimport com.sun.net.httpserver.HttpServer;\n\nimport example.extensions.HttpServerExtension;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n// tag::user_guide[]\n@ExtendWith(HttpServerExtension.class)\npublic class HttpServerDemo {\n\n\t// end::user_guide[]\n\t@SuppressWarnings(\"HttpUrlsUsage\")\n\t// tag::user_guide[]\n\t@Test\n\tvoid httpCall(HttpServer server) throws Exception {\n\t\tString hostName = server.getAddress().getHostName();\n\t\tint port = server.getAddress().getPort();\n\t\tString rawUrl = \"http://%s:%d/example\".formatted(hostName, port);\n\t\tURL requestUrl = URI.create(rawUrl).toURL();\n\n\t\tString responseBody = sendRequest(requestUrl);\n\n\t\tassertEquals(\"This is a test\", responseBody);\n\t}\n\n\tprivate static String sendRequest(URL url) throws IOException {\n\t\tHttpURLConnection connection = (HttpURLConnection) url.openConnection();\n\t\tint contentLength = connection.getContentLength();\n\t\ttry (InputStream response = url.openStream()) {\n\t\t\tbyte[] content = new byte[contentLength];\n\t\t\tassertEquals(contentLength, response.read(content));\n\t\t\treturn new String(content, UTF_8);\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/IgnoredTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport;\n\n// @ExtendWith(IgnoreCondition.class)\n@SuppressWarnings(\"removal\")\n@EnableJUnit4MigrationSupport\nclass IgnoredTestsDemo {\n\n\t@Ignore\n\t@Test\n\tvoid testWillBeIgnored() {\n\t}\n\n\t@Test\n\tvoid testWillBeExecuted() {\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/JUnit4Tests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.Test;\n\npublic class JUnit4Tests {\n\n\t@Test\n\tpublic void standardJUnit4Test() {\n\t\t// perform assertions\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/MethodSourceParameterResolutionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nclass MethodSourceParameterResolutionDemo {\n\n\t// @formatter:off\n\t// tag::parameter_resolution_MethodSource_example[]\n\t@RegisterExtension\n\tstatic final IntegerResolver integerResolver = new IntegerResolver();\n\n\t@ParameterizedTest\n\t@MethodSource(\"factoryMethodWithArguments\")\n\tvoid testWithFactoryMethodWithArguments(String argument) {\n\t\tassertTrue(argument.startsWith(\"2\"));\n\t}\n\n\tstatic Stream<Arguments> factoryMethodWithArguments(int quantity) {\n\t\treturn Stream.of(\n\t\t\t\targuments(quantity + \" apples\"),\n\t\t\t\targuments(quantity + \" lemons\")\n\t\t);\n\t}\n\n\tstatic class IntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext,\n\t\t\t\tExtensionContext extensionContext) {\n\n\t\t\treturn parameterContext.getParameter().getType() == int.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext,\n\t\t\t\tExtensionContext extensionContext) {\n\n\t\t\treturn 2;\n\t\t}\n\n\t}\n\t// end::parameter_resolution_MethodSource_example[]\n\t// @formatter:on\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/MyFirstJUnitJupiterRecordTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Test;\n\nrecord MyFirstJUnitJupiterRecordTests() {\n\n\t@Test\n\tvoid addition() {\n\t\tassertEquals(2, new Calculator().add(1, 1));\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/MyFirstJUnitJupiterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport example.util.Calculator;\n\nimport org.junit.jupiter.api.Test;\n\nclass MyFirstJUnitJupiterTests {\n\n\tprivate final Calculator calculator = new Calculator();\n\n\t@Test\n\tvoid addition() {\n\t\tassertEquals(2, calculator.add(1, 1));\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/MyRandomParametersTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\n\nimport example.extensions.Random;\n\nimport org.junit.jupiter.api.Test;\n\n// tag::user_guide[]\nclass MyRandomParametersTest {\n\n\tMyRandomParametersTest(@Random int randomNumber) {\n\t\t// Use randomNumber in constructor.\n\t}\n\n\t@Test\n\tvoid injectsInteger(@Random int i, @Random int j) {\n\t\tassertNotEquals(i, j);\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/OrderedNestedTestClassesDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestClassOrder;\n\n@TestClassOrder(ClassOrderer.OrderAnnotation.class)\nclass OrderedNestedTestClassesDemo {\n\n\t@Nested\n\t@Order(1)\n\tclass PrimaryTests {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\t}\n\n\t@Nested\n\t@Order(2)\n\tclass SecondaryTests {\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/OrderedTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\n@TestMethodOrder(OrderAnnotation.class)\nclass OrderedTestsDemo {\n\n\t@Test\n\t@Order(1)\n\tvoid nullValues() {\n\t\t// perform assertions against null values\n\t}\n\n\t@Test\n\t@Order(2)\n\tvoid emptyValues() {\n\t\t// perform assertions against empty values\n\t}\n\n\t@Test\n\t@Order(3)\n\tvoid validValues() {\n\t\t// perform assertions against valid values\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/ParameterizedClassDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\n\nimport java.time.Duration;\nimport java.util.Arrays;\n\nimport example.util.StringUtils;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\npublic class ParameterizedClassDemo {\n\n\t@Nested\n\t// tag::first_example[]\n\t@ParameterizedClass\n\t@ValueSource(strings = { \"racecar\", \"radar\", \"able was I ere I saw elba\" })\n\tclass PalindromeTests {\n\n\t\t@Parameter\n\t\tString candidate;\n\n\t\t@Test\n\t\tvoid palindrome() {\n\t\t\tassertTrue(StringUtils.isPalindrome(candidate));\n\t\t}\n\n\t\t@Test\n\t\tvoid reversePalindrome() {\n\t\t\tString reverseCandidate = new StringBuilder(candidate).reverse().toString();\n\t\t\tassertTrue(StringUtils.isPalindrome(reverseCandidate));\n\t\t}\n\t}\n\t// end::first_example[]\n\n\t@Nested\n\tclass ConstructorInjection {\n\t\t@Nested\n\t\t// tag::constructor_injection[]\n\t\t@ParameterizedClass\n\t\t@CsvSource({ \"apple, 23\", \"banana, 42\" })\n\t\tclass FruitTests {\n\n\t\t\tfinal String fruit;\n\t\t\tfinal int quantity;\n\n\t\t\tFruitTests(String fruit, int quantity) {\n\t\t\t\tthis.fruit = fruit;\n\t\t\t\tthis.quantity = quantity;\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertFruit(fruit);\n\t\t\t\tassertQuantity(quantity);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid anotherTest() {\n\t\t\t\t// ...\n\t\t\t}\n\t\t}\n\t\t// end::constructor_injection[]\n\t}\n\n\t@Nested\n\tclass FieldInjection {\n\t\t@Nested\n\t\t// tag::field_injection[]\n\t\t@ParameterizedClass\n\t\t@CsvSource({ \"apple, 23\", \"banana, 42\" })\n\t\tclass FruitTests {\n\n\t\t\t@Parameter(0)\n\t\t\tString fruit;\n\n\t\t\t@Parameter(1)\n\t\t\tint quantity;\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertFruit(fruit);\n\t\t\t\tassertQuantity(quantity);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid anotherTest() {\n\t\t\t\t// ...\n\t\t\t}\n\t\t}\n\t\t// end::field_injection[]\n\t}\n\n\t@Nested\n\t// tag::nested[]\n\t@Execution(SAME_THREAD)\n\t@ParameterizedClass\n\t@ValueSource(strings = { \"apple\", \"banana\" })\n\tclass FruitTests {\n\n\t\t@Parameter\n\t\tString fruit;\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(ints = { 23, 42 })\n\t\tclass QuantityTests {\n\n\t\t\t@Parameter\n\t\t\tint quantity;\n\n\t\t\t@ParameterizedTest\n\t\t\t@ValueSource(strings = { \"PT1H\", \"PT2H\" })\n\t\t\tvoid test(Duration duration) {\n\t\t\t\tassertFruit(fruit);\n\t\t\t\tassertQuantity(quantity);\n\t\t\t\tassertFalse(duration.isNegative());\n\t\t\t}\n\t\t}\n\t}\n\t// end::nested[]\n\n\tstatic void assertFruit(String fruit) {\n\t\tassertTrue(Arrays.asList(\"apple\", \"banana\", \"cherry\", \"dewberry\").contains(fruit),\n\t\t\t() -> \"not a fruit: \" + fruit);\n\t}\n\n\tstatic void assertQuantity(int quantity) {\n\t\tassertTrue(quantity > 0);\n\t}\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/ParameterizedLifecycleDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.AfterParameterizedClassInvocation;\nimport org.junit.jupiter.params.BeforeParameterizedClassInvocation;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.MethodSource;\n\npublic class ParameterizedLifecycleDemo {\n\n\t@Nested\n\t// tag::example[]\n\t@ParameterizedClass\n\t@MethodSource(\"textFiles\")\n\tclass TextFileTests {\n\n\t\tstatic List<TextFile> textFiles() {\n\t\t\treturn List.of(\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\tnew TextFile(\"file1\", \"first content\"),\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\tnew TextFile(\"file2\", \"second content\")\n\t\t\t// tag::custom_line_break[]\n\t\t\t);\n\t\t}\n\n\t\t@Parameter\n\t\tTextFile textFile;\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void beforeInvocation(TextFile textFile, @TempDir Path tempDir) throws Exception {\n\t\t\tvar filePath = tempDir.resolve(textFile.fileName); // <1>\n\t\t\ttextFile.path = Files.writeString(filePath, textFile.content);\n\t\t}\n\n\t\t//end::user_guide[]\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t//tag::user_guide[]\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void afterInvocation(TextFile textFile) throws Exception {\n\t\t\tvar actualContent = Files.readString(textFile.path); // <3>\n\t\t\tassertEquals(textFile.content, actualContent, \"Content must not have changed\");\n\t\t\t// Custom cleanup logic, if necessary\n\t\t\t// File will be deleted automatically by @TempDir support\n\t\t}\n\n\t\t//end::user_guide[]\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t//tag::user_guide[]\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(Files.exists(textFile.path)); // <2>\n\t\t}\n\n\t\t@Test\n\t\tvoid anotherTest() {\n\t\t\t// ...\n\t\t}\n\n\t\tstatic class TextFile {\n\n\t\t\tfinal String fileName;\n\t\t\tfinal String content;\n\t\t\t// end::example[]\n\t\t\t@Nullable\n\t\t\t// tag::example[]\n\t\t\tPath path;\n\n\t\t\tTextFile(String fileName, String content) {\n\t\t\t\tthis.fileName = fileName;\n\t\t\t\tthis.content = content;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn fileName;\n\t\t\t}\n\t\t}\n\t}\n\t// end::example[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/ParameterizedMigrationDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.params.AfterParameterizedClassInvocation;\nimport org.junit.jupiter.params.BeforeParameterizedClassInvocation;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\n\npublic class ParameterizedMigrationDemo {\n\n\t// tag::before[]\n\t@RunWith(Parameterized.class)\n\t// end::before[]\n\tstatic\n\t// tag::before[]\n\tpublic class JUnit4ParameterizedClassTests {\n\n\t\t@Parameterized.Parameters\n\t\tpublic static Iterable<Object[]> data() {\n\t\t\treturn Arrays.asList(new Object[][] { { 1, \"foo\" }, { 2, \"bar\" } });\n\t\t}\n\n\t\t// end::before[]\n\t\t@SuppressWarnings(\"DefaultAnnotationParam\")\n\t\t// tag::before[]\n\t\t@Parameterized.Parameter(0)\n\t\tpublic int number;\n\n\t\t@Parameterized.Parameter(1)\n\t\tpublic String text;\n\n\t\t@Parameterized.BeforeParam\n\t\tpublic static void before(int number, String text) {\n\t\t}\n\n\t\t@Parameterized.AfterParam\n\t\tpublic static void after() {\n\t\t}\n\n\t\t@org.junit.Test\n\t\tpublic void someTest() {\n\t\t}\n\n\t\t@org.junit.Test\n\t\tpublic void anotherTest() {\n\t\t}\n\t}\n\t// end::before[]\n\n\t// tag::after[]\n\t@ParameterizedClass\n\t@MethodSource(\"data\")\n\t// end::after[]\n\tstatic\n\t// tag::after[]\n\tclass JupiterParameterizedClassTests {\n\n\t\tstatic Iterable<Object[]> data() {\n\t\t\treturn Arrays.asList(new Object[][] { { 1, \"foo\" }, { 2, \"bar\" } });\n\t\t}\n\n\t\t@org.junit.jupiter.params.Parameter(0)\n\t\tint number;\n\n\t\t@org.junit.jupiter.params.Parameter(1)\n\t\tString text;\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(int number, String text) {\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void after() {\n\t\t}\n\n\t\t@org.junit.jupiter.api.Test\n\t\tvoid someTest() {\n\t\t}\n\n\t\t@org.junit.jupiter.api.Test\n\t\tvoid anotherTest() {\n\t\t}\n\t}\n\t// end::after[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/ParameterizedRecordDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.CsvSource;\n\npublic class ParameterizedRecordDemo {\n\n\t// tag::example[]\n\t@ParameterizedClass\n\t@CsvSource({ \"apple, 23\", \"banana, 42\" })\n\trecord FruitTests(String fruit, int quantity) {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertFruit(fruit);\n\t\t\tassertQuantity(quantity);\n\t\t}\n\n\t\t@Test\n\t\tvoid anotherTest() {\n\t\t\t// ...\n\t\t}\n\t}\n\t// end::example[]\n\n\tstatic void assertFruit(String fruit) {\n\t\tassertTrue(Arrays.asList(\"apple\", \"banana\", \"cherry\", \"dewberry\").contains(fruit));\n\t}\n\n\tstatic void assertQuantity(int quantity) {\n\t\tassertTrue(quantity >= 0);\n\t}\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/ParameterizedTestDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static org.junit.jupiter.params.provider.Arguments.argumentSet;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL;\n\nimport java.io.File;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.time.LocalDate;\nimport java.time.temporal.ChronoUnit;\nimport java.time.temporal.TemporalUnit;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport example.domain.Person;\nimport example.domain.Person.Gender;\nimport example.util.StringUtils;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.params.ArgumentCountValidationMode;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.converter.JavaTimeConversionPattern;\nimport org.junit.jupiter.params.converter.SimpleArgumentConverter;\nimport org.junit.jupiter.params.converter.TypedArgumentConverter;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.ArgumentsProvider;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\nimport org.junit.jupiter.params.provider.CsvFileSource;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.EmptySource;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.FieldSource;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\n@Execution(SAME_THREAD)\nclass ParameterizedTestDemo {\n\n\t@BeforeEach\n\tvoid printDisplayName(TestInfo testInfo) {\n\t\tSystem.out.println(testInfo.getDisplayName());\n\t}\n\n\t// tag::first_example[]\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"racecar\", \"radar\", \"able was I ere I saw elba\" })\n\tvoid palindromes(String candidate) {\n\t\tassertTrue(StringUtils.isPalindrome(candidate));\n\t}\n\t// end::first_example[]\n\n\t// tag::ValueSource_example[]\n\t@ParameterizedTest\n\t@ValueSource(ints = { 1, 2, 3 })\n\tvoid testWithValueSource(int argument) {\n\t\tassertTrue(argument > 0 && argument < 4);\n\t}\n\t// end::ValueSource_example[]\n\n\t@Nested\n\tclass NullAndEmptySource_1 {\n\n\t\t// tag::NullAndEmptySource_example1[]\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\t@EmptySource\n\t\t@ValueSource(strings = { \" \", \"   \", \"\\t\", \"\\n\" })\n\t\tvoid nullEmptyAndBlankStrings(String text) {\n\t\t\tassertTrue(text == null || text.isBlank());\n\t\t}\n\t\t// end::NullAndEmptySource_example1[]\n\t}\n\n\t@Nested\n\tclass NullAndEmptySource_2 {\n\n\t\t// tag::NullAndEmptySource_example2[]\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \" \", \"   \", \"\\t\", \"\\n\" })\n\t\tvoid nullEmptyAndBlankStrings(String text) {\n\t\t\tassertTrue(text == null || text.isBlank());\n\t\t}\n\t\t// end::NullAndEmptySource_example2[]\n\t}\n\n\t// tag::EnumSource_example[]\n\t@ParameterizedTest\n\t@EnumSource(ChronoUnit.class)\n\tvoid testWithEnumSource(TemporalUnit unit) {\n\t\tassertNotNull(unit);\n\t}\n\t// end::EnumSource_example[]\n\n\t// tag::EnumSource_example_autodetection[]\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid testWithEnumSourceWithAutoDetection(ChronoUnit unit) {\n\t\tassertNotNull(unit);\n\t}\n\t// end::EnumSource_example_autodetection[]\n\n\t// tag::EnumSource_include_example[]\n\t@ParameterizedTest\n\t@EnumSource(names = { \"DAYS\", \"HOURS\" })\n\tvoid testWithEnumSourceInclude(ChronoUnit unit) {\n\t\tassertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit));\n\t}\n\t// end::EnumSource_include_example[]\n\n\t// tag::EnumSource_range_example[]\n\t@ParameterizedTest\n\t@EnumSource(from = \"HOURS\", to = \"DAYS\")\n\tvoid testWithEnumSourceRange(ChronoUnit unit) {\n\t\tassertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ChronoUnit.DAYS).contains(unit));\n\t}\n\t// end::EnumSource_range_example[]\n\n\t// tag::EnumSource_exclude_example[]\n\t@ParameterizedTest\n\t@EnumSource(mode = EXCLUDE, names = { \"ERAS\", \"FOREVER\" })\n\tvoid testWithEnumSourceExclude(ChronoUnit unit) {\n\t\tassertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER).contains(unit));\n\t}\n\t// end::EnumSource_exclude_example[]\n\n\t// tag::EnumSource_regex_example[]\n\t@ParameterizedTest\n\t@EnumSource(mode = MATCH_ALL, names = \"^.*DAYS$\")\n\tvoid testWithEnumSourceRegex(ChronoUnit unit) {\n\t\tassertTrue(unit.name().endsWith(\"DAYS\"));\n\t}\n\t// end::EnumSource_regex_example[]\n\n\t// tag::EnumSource_range_exclude_example[]\n\t@ParameterizedTest\n\t@EnumSource(from = \"HOURS\", to = \"DAYS\", mode = EXCLUDE, names = { \"HALF_DAYS\" })\n\tvoid testWithEnumSourceRangeExclude(ChronoUnit unit) {\n\t\tassertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.DAYS).contains(unit));\n\t\tassertFalse(EnumSet.of(ChronoUnit.HALF_DAYS).contains(unit));\n\t}\n\t// end::EnumSource_range_exclude_example[]\n\n\t// tag::simple_MethodSource_example[]\n\t@ParameterizedTest\n\t@MethodSource(\"stringProvider\")\n\tvoid testWithExplicitLocalMethodSource(String argument) {\n\t\tassertNotNull(argument);\n\t}\n\n\tstatic Stream<String> stringProvider() {\n\t\treturn Stream.of(\"apple\", \"banana\");\n\t}\n\t// end::simple_MethodSource_example[]\n\n\t// tag::simple_MethodSource_without_value_example[]\n\t@ParameterizedTest\n\t@MethodSource\n\tvoid testWithDefaultLocalMethodSource(String argument) {\n\t\tassertNotNull(argument);\n\t}\n\n\tstatic Stream<String> testWithDefaultLocalMethodSource() {\n\t\treturn Stream.of(\"apple\", \"banana\");\n\t}\n\t// end::simple_MethodSource_without_value_example[]\n\n\t// tag::primitive_MethodSource_example[]\n\t@ParameterizedTest\n\t@MethodSource(\"range\")\n\tvoid testWithRangeMethodSource(int argument) {\n\t\tassertNotEquals(9, argument);\n\t}\n\n\tstatic IntStream range() {\n\t\treturn IntStream.range(0, 20).skip(10);\n\t}\n\t// end::primitive_MethodSource_example[]\n\n\t// @formatter:off\n\t// tag::multi_arg_MethodSource_example[]\n\t@ParameterizedTest\n\t@MethodSource(\"stringIntAndListProvider\")\n\tvoid testWithMultiArgMethodSource(String str, int num, List<String> list) {\n\t\tassertEquals(5, str.length());\n\t\tassertTrue(num >=1 && num <=2);\n\t\tassertEquals(2, list.size());\n\t}\n\n\tstatic Stream<Arguments> stringIntAndListProvider() {\n\t\treturn Stream.of(\n\t\t\targuments(\"apple\", 1, Arrays.asList(\"a\", \"b\")),\n\t\t\targuments(\"lemon\", 2, Arrays.asList(\"x\", \"y\"))\n\t\t);\n\t}\n\t// end::multi_arg_MethodSource_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::default_field_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource\n\tvoid arrayOfFruits(String fruit) {\n\t\tassertFruit(fruit);\n\t}\n\n\tstatic final String[] arrayOfFruits = { \"apple\", \"banana\" };\n\t// end::default_field_FieldSource_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::explicit_field_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource(\"listOfFruits\")\n\tvoid singleFieldSource(String fruit) {\n\t\tassertFruit(fruit);\n\t}\n\n\tstatic final List<String> listOfFruits = Arrays.asList(\"apple\", \"banana\");\n\t// end::explicit_field_FieldSource_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::multiple_fields_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource({ \"listOfFruits\", \"additionalFruits\" })\n\tvoid multipleFieldSources(String fruit) {\n\t\tassertFruit(fruit);\n\t}\n\n\tstatic final Collection<String> additionalFruits = Arrays.asList(\"cherry\", \"dewberry\");\n\t// end::multiple_fields_FieldSource_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::named_arguments_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource\n\tvoid namedArgumentsSupplier(String fruit) {\n\t\tassertFruit(fruit);\n\t}\n\n\tstatic final Supplier<Stream<Arguments>> namedArgumentsSupplier = () -> Stream.of(\n\t\targuments(named(\"Apple\", \"apple\")),\n\t\targuments(named(\"Banana\", \"banana\"))\n\t);\n\t// end::named_arguments_FieldSource_example[]\n\t// @formatter:on\n\n\tprivate static void assertFruit(String fruit) {\n\t\tassertTrue(Arrays.asList(\"apple\", \"banana\", \"cherry\", \"dewberry\").contains(fruit));\n\t}\n\n\t// @formatter:off\n\t// tag::multi_arg_FieldSource_example[]\n\t@ParameterizedTest\n\t@FieldSource(\"stringIntAndListArguments\")\n\tvoid testWithMultiArgFieldSource(String str, int num, List<String> list) {\n\t\tassertEquals(5, str.length());\n\t\tassertTrue(num >=1 && num <=2);\n\t\tassertEquals(2, list.size());\n\t}\n\n\tstatic List<Arguments> stringIntAndListArguments = Arrays.asList(\n\t\targuments(\"apple\", 1, Arrays.asList(\"a\", \"b\")),\n\t\targuments(\"lemon\", 2, Arrays.asList(\"x\", \"y\"))\n\t);\n\t// end::multi_arg_FieldSource_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::CsvSource_example[]\n\t@ParameterizedTest\n\t@CsvSource({\n\t\t\"apple,         1\",\n\t\t\"banana,        2\",\n\t\t\"'lemon, lime', 0xF1\",\n\t\t\"strawberry,    700_000\"\n\t})\n\tvoid testWithCsvSource(String fruit, int rank) {\n\t\tassertNotNull(fruit);\n\t\tassertNotEquals(0, rank);\n\t}\n\t// end::CsvSource_example[]\n\t// @formatter:on\n\n\t// tag::CsvFileSource_example[]\n\t@ParameterizedTest\n\t@CsvFileSource(resources = \"/two-column.csv\", numLinesToSkip = 1)\n\tvoid testWithCsvFileSourceFromClasspath(String country, int reference) {\n\t\tassertNotNull(country);\n\t\tassertNotEquals(0, reference);\n\t}\n\n\t@ParameterizedTest\n\t@CsvFileSource(files = \"src/test/resources/two-column.csv\", numLinesToSkip = 1)\n\tvoid testWithCsvFileSourceFromFile(String country, int reference) {\n\t\tassertNotNull(country);\n\t\tassertNotEquals(0, reference);\n\t}\n\n\t@ParameterizedTest\n\t@CsvFileSource(resources = \"/two-column.csv\", useHeadersInDisplayName = true)\n\tvoid testWithCsvFileSourceAndHeaders(String country, int reference) {\n\t\tassertNotNull(country);\n\t\tassertNotEquals(0, reference);\n\t}\n\t// end::CsvFileSource_example[]\n\n\t// tag::ArgumentsSource_example[]\n\t@ParameterizedTest\n\t@ArgumentsSource(MyArgumentsProvider.class)\n\tvoid testWithArgumentsSource(String argument) {\n\t\tassertNotNull(argument);\n\t}\n\n\t// end::ArgumentsSource_example[]\n\tstatic\n\t// tag::ArgumentsProvider_example[]\n\tpublic class MyArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(\"apple\", \"banana\").map(Arguments::of);\n\t\t}\n\t}\n\t// end::ArgumentsProvider_example[]\n\n\t@ParameterizedTest\n\t@ArgumentsSource(MyArgumentsProviderWithConstructorInjection.class)\n\tvoid testWithArgumentsSourceWithConstructorInjection(String argument) {\n\t\tassertNotNull(argument);\n\t}\n\n\tstatic\n\t// tag::ArgumentsProviderWithConstructorInjection_example[]\n\tpublic class MyArgumentsProviderWithConstructorInjection implements ArgumentsProvider {\n\n\t\tprivate final TestInfo testInfo;\n\n\t\t// end::ArgumentsProviderWithConstructorInjection_example[]\n\t\t@SuppressWarnings(\"RedundantModifier\")\n\t\t// tag::ArgumentsProviderWithConstructorInjection_example[]\n\t\tpublic MyArgumentsProviderWithConstructorInjection(TestInfo testInfo) {\n\t\t\tthis.testInfo = testInfo;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(Arguments.of(testInfo.getDisplayName()));\n\t\t}\n\t}\n\t// end::ArgumentsProviderWithConstructorInjection_example[]\n\n\t// tag::ParameterResolver_example[]\n\t@BeforeEach\n\tvoid beforeEach(TestInfo testInfo) {\n\t\t// ...\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = \"apple\")\n\tvoid testWithRegularParameterResolver(String argument, TestReporter testReporter) {\n\t\ttestReporter.publishEntry(\"argument\", argument);\n\t}\n\n\t@AfterEach\n\tvoid afterEach(TestInfo testInfo) {\n\t\t// ...\n\t}\n\t// end::ParameterResolver_example[]\n\n\t// tag::implicit_conversion_example[]\n\t@ParameterizedTest\n\t@ValueSource(strings = \"SECONDS\")\n\tvoid testWithImplicitArgumentConversion(ChronoUnit argument) {\n\t\tassertNotNull(argument.name());\n\t}\n\t// end::implicit_conversion_example[]\n\n\t// tag::implicit_fallback_conversion_example[]\n\t@ParameterizedTest\n\t@ValueSource(strings = \"42 Cats\")\n\tvoid testWithImplicitFallbackArgumentConversion(Book book) {\n\t\tassertEquals(\"42 Cats\", book.getTitle());\n\t}\n\n\t// end::implicit_fallback_conversion_example[]\n\tstatic\n\t// tag::implicit_fallback_conversion_example_Book[]\n\tpublic class Book {\n\n\t\tprivate final String title;\n\n\t\tprivate Book(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\tpublic static Book fromTitle(String title) {\n\t\t\treturn new Book(title);\n\t\t}\n\n\t\tpublic String getTitle() {\n\t\t\treturn this.title;\n\t\t}\n\t}\n\t// end::implicit_fallback_conversion_example_Book[]\n\n\t// @formatter:off\n\t// tag::explicit_conversion_example[]\n\t@ParameterizedTest\n\t@EnumSource(ChronoUnit.class)\n\tvoid testWithExplicitArgumentConversion(\n\t\t\t@ConvertWith(ToStringArgumentConverter.class) String argument) {\n\n\t\tassertNotNull(ChronoUnit.valueOf(argument));\n\t}\n\n\t// end::explicit_conversion_example[]\n\tstatic\n\t@SuppressWarnings({ \"NullableProblems\", \"NullAway\" })\n\t// tag::explicit_conversion_example_ToStringArgumentConverter[]\n\tpublic class ToStringArgumentConverter extends SimpleArgumentConverter {\n\n\t\t@Override\n\t\tprotected Object convert(Object source, Class<?> targetType) {\n\t\t\tassertEquals(String.class, targetType, \"Can only convert to String\");\n\t\t\tif (source instanceof Enum<?> constant) {\n\t\t\t\treturn constant.name();\n\t\t\t}\n\t\t\treturn String.valueOf(source);\n\t\t}\n\t}\n\t// end::explicit_conversion_example_ToStringArgumentConverter[]\n\n\tstatic\n\t@SuppressWarnings({ \"NullableProblems\", \"NullAway\", \"ConstantValue\" })\n\t// tag::explicit_conversion_example_TypedArgumentConverter[]\n\tpublic class ToLengthArgumentConverter extends TypedArgumentConverter<String, Integer> {\n\n\t\tprotected ToLengthArgumentConverter() {\n\t\t\tsuper(String.class, Integer.class);\n\t\t}\n\n\t\t@Override\n\t\tprotected Integer convert(String source) {\n\t\t\treturn (source != null ? source.length() : 0);\n\t\t}\n\n\t}\n\t// end::explicit_conversion_example_TypedArgumentConverter[]\n\n\t// tag::explicit_java_time_converter[]\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"01.01.2017\", \"31.12.2017\" })\n\tvoid testWithExplicitJavaTimeConverter(\n\t\t\t@JavaTimeConversionPattern(\"dd.MM.yyyy\") LocalDate argument) {\n\n\t\tassertEquals(2017, argument.getYear());\n\t}\n\t// end::explicit_java_time_converter[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::ArgumentsAccessor_example[]\n\t@ParameterizedTest\n\t@CsvSource({\n\t\t\"Jane, Doe, F, 1990-05-20\",\n\t\t\"John, Doe, M, 1990-10-22\"\n\t})\n\tvoid testWithArgumentsAccessor(ArgumentsAccessor arguments) {\n\t\tPerson person = new Person(\n\t\t\t\t\t\t\t\t\targuments.getString(0),\n\t\t\t\t\t\t\t\t\targuments.getString(1),\n\t\t\t\t\t\t\t\t\targuments.get(2, Gender.class),\n\t\t\t\t\t\t\t\t\targuments.get(3, LocalDate.class));\n\n\t\tif (person.getFirstName().equals(\"Jane\")) {\n\t\t\tassertEquals(Gender.F, person.getGender());\n\t\t}\n\t\telse {\n\t\t\tassertEquals(Gender.M, person.getGender());\n\t\t}\n\t\tassertEquals(\"Doe\", person.getLastName());\n\t\tassertEquals(1990, person.getDateOfBirth().getYear());\n\t}\n\t// end::ArgumentsAccessor_example[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::ArgumentsAggregator_example[]\n\t@ParameterizedTest\n\t@CsvSource({\n\t\t\"Jane, Doe, F, 1990-05-20\",\n\t\t\"John, Doe, M, 1990-10-22\"\n\t})\n\tvoid testWithArgumentsAggregator(@AggregateWith(PersonAggregator.class) Person person) {\n\t\t// perform assertions against person\n\t}\n\n\t// end::ArgumentsAggregator_example[]\n\tstatic\n\t// tag::ArgumentsAggregator_example_PersonAggregator[]\n\tpublic class PersonAggregator extends SimpleArgumentsAggregator {\n\t\t@Override\n\t\tprotected Person aggregateArguments(ArgumentsAccessor arguments, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\treturn new Person(\n\t\t\t\t\t\t\t\targuments.getString(0),\n\t\t\t\t\t\t\t\targuments.getString(1),\n\t\t\t\t\t\t\t\targuments.get(2, Gender.class),\n\t\t\t\t\t\t\t\targuments.get(3, LocalDate.class));\n\t\t}\n\t}\n\t// end::ArgumentsAggregator_example_PersonAggregator[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::ArgumentsAggregator_with_custom_annotation_example[]\n\t@ParameterizedTest\n\t@CsvSource({\n\t\t\"Jane, Doe, F, 1990-05-20\",\n\t\t\"John, Doe, M, 1990-10-22\"\n\t})\n\tvoid testWithCustomAggregatorAnnotation(@CsvToPerson Person person) {\n\t\t// perform assertions against person\n\t}\n\t// end::ArgumentsAggregator_with_custom_annotation_example[]\n\n\t// tag::ArgumentsAggregator_with_custom_annotation_example_CsvToPerson[]\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AggregateWith(PersonAggregator.class)\n\tpublic @interface CsvToPerson {\n\t}\n\t// end::ArgumentsAggregator_with_custom_annotation_example_CsvToPerson[]\n\t// @formatter:on\n\n\t// tag::custom_display_names[]\n\t@DisplayName(\"Display name of container\")\n\t@ParameterizedTest(name = \"{index} ==> the rank of {0} is {1}\")\n\t@CsvSource({ \"apple, 1\", \"banana, 2\", \"'lemon, lime', 3\" })\n\tvoid testWithCustomDisplayNames(String fruit, int rank) {\n\t}\n\t// end::custom_display_names[]\n\n\t// @formatter:off\n\t// tag::named_arguments[]\n\t@DisplayName(\"A parameterized test with named arguments\")\n\t@ParameterizedTest(name = \"{index}: {0}\")\n\t@MethodSource(\"namedArguments\")\n\tvoid testWithNamedArguments(File file) {\n\t}\n\n\tstatic Stream<Arguments> namedArguments() {\n\t\treturn Stream.of(\n\t\t\targuments(named(\"An important file\", new File(\"path1\"))),\n\t\t\targuments(named(\"Another file\", new File(\"path2\")))\n\t\t);\n\t}\n\t// end::named_arguments[]\n\t// @formatter:on\n\n\t// @formatter:off\n\t// tag::named_argument_set[]\n\t@DisplayName(\"A parameterized test with named argument sets\")\n\t@ParameterizedTest\n\t@FieldSource(\"argumentSets\")\n\tvoid testWithArgumentSets(File file1, File file2) {\n\t}\n\n\tstatic List<Arguments> argumentSets = Arrays.asList(\n\t\targumentSet(\"Important files\", new File(\"path1\"), new File(\"path2\")),\n\t\targumentSet(\"Other files\", new File(\"path3\"), new File(\"path4\"))\n\t);\n\t// end::named_argument_set[]\n\t// @formatter:on\n\n\t// tag::repeatable_annotations[]\n\t@DisplayName(\"A parameterized test that makes use of repeatable annotations\")\n\t@ParameterizedTest\n\t@MethodSource(\"someProvider\")\n\t@MethodSource(\"otherProvider\")\n\tvoid testWithRepeatedAnnotation(String argument) {\n\t\tassertNotNull(argument);\n\t}\n\n\tstatic Stream<String> someProvider() {\n\t\treturn Stream.of(\"foo\");\n\t}\n\n\tstatic Stream<String> otherProvider() {\n\t\treturn Stream.of(\"bar\");\n\t}\n\t// end::repeatable_annotations[]\n\n\t@Disabled(\"Fails prior to invoking the test method\")\n\t// tag::argument_count_validation[]\n\t@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)\n\t@CsvSource({ \"42, -666\" })\n\tvoid testWithArgumentCountValidation(int number) {\n\t\tassertTrue(number > 0);\n\t}\n\t// end::argument_count_validation[]\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/PollingTimeoutDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\n\nclass PollingTimeoutDemo {\n\n\t// tag::user_guide[]\n\t@Test\n\t@Timeout(5) // Poll at most 5 seconds\n\tvoid pollUntil() throws InterruptedException {\n\t\twhile (asynchronousResultNotAvailable()) {\n\t\t\tThread.sleep(250); // custom poll interval\n\t\t}\n\t\t// Obtain the asynchronous result and perform assertions\n\t}\n\t// end::user_guide[]\n\n\tprivate boolean asynchronousResultNotAvailable() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/RepeatedTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.RepetitionInfo;\nimport org.junit.jupiter.api.TestInfo;\n\n// end::user_guide[]\n// Use fully qualified names to avoid having them show up in the imports.\n@org.junit.jupiter.api.parallel.Execution(org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD)\n// tag::user_guide[]\nclass RepeatedTestsDemo {\n\n\tprivate Logger logger = // ...\n\t\t// end::user_guide[]\n\t\tLogger.getLogger(RepeatedTestsDemo.class.getName());\n\t// tag::user_guide[]\n\n\t@BeforeEach\n\tvoid beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {\n\t\tint currentRepetition = repetitionInfo.getCurrentRepetition();\n\t\tint totalRepetitions = repetitionInfo.getTotalRepetitions();\n\t\tString methodName = testInfo.getTestMethod().get().getName();\n\t\tlogger.info(\"About to execute repetition %d of %d for %s\".formatted( //\n\t\t\tcurrentRepetition, totalRepetitions, methodName));\n\t}\n\n\t@RepeatedTest(10)\n\tvoid repeatedTest() {\n\t\t// ...\n\t}\n\n\t@RepeatedTest(5)\n\tvoid repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) {\n\t\tassertEquals(5, repetitionInfo.getTotalRepetitions());\n\t}\n\n\t// end::user_guide[]\n\t// Use fully qualified name to avoid having it show up in the imports.\n\t@org.junit.jupiter.api.Disabled(\"intentional failures would break the build\")\n\t// tag::user_guide[]\n\t@RepeatedTest(value = 8, failureThreshold = 2)\n\tvoid repeatedTestWithFailureThreshold(RepetitionInfo repetitionInfo) {\n\t\t// Simulate unexpected failure every second repetition\n\t\tif (repetitionInfo.getCurrentRepetition() % 2 == 0) {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\t}\n\n\t@RepeatedTest(value = 1, name = \"{displayName} {currentRepetition}/{totalRepetitions}\")\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayName(TestInfo testInfo) {\n\t\tassertEquals(\"Repeat! 1/1\", testInfo.getDisplayName());\n\t}\n\n\t@RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)\n\t@DisplayName(\"Details...\")\n\tvoid customDisplayNameWithLongPattern(TestInfo testInfo) {\n\t\tassertEquals(\"Details... :: repetition 1 of 1\", testInfo.getDisplayName());\n\t}\n\n\t@RepeatedTest(value = 5, name = \"Wiederholung {currentRepetition} von {totalRepetitions}\")\n\tvoid repeatedTestInGerman() {\n\t\t// ...\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/SecondCustomEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static java.net.InetAddress.getLoopbackAddress;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.ServerSocket;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n//tag::user_guide[]\n/**\n * Second custom test engine implementation.\n */\npublic class SecondCustomEngine implements TestEngine {\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tpublic ServerSocket socket;\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"second-custom-test-engine\";\n\t}\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tpublic ServerSocket getSocket() {\n\t\treturn this.socket;\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\treturn new EngineDescriptor(uniqueId, \"Second Custom Test Engine\");\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\trequest.getEngineExecutionListener()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.executionStarted(request.getRootTestDescriptor());\n\n\t\tNamespacedHierarchicalStore<Namespace> store = request.getStore();\n\t\tsocket = store.computeIfAbsent(Namespace.GLOBAL, \"serverSocket\", key -> {\n\t\t\ttry {\n\t\t\t\treturn new ServerSocket(0, 50, getLoopbackAddress());\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to start ServerSocket\", e);\n\t\t\t}\n\t\t}, ServerSocket.class);\n\n\t\trequest.getEngineExecutionListener()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.executionFinished(request.getRootTestDescriptor(), successful());\n\t}\n\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/SlowTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\n\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\n\n@Tag(\"exclude\")\n@Disabled\nclass SlowTests {\n\n\t@Execution(SAME_THREAD)\n\t@Test\n\tvoid a() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid b() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid c() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid d() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid e() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid f() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid g() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid h() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid i() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid j() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid k() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid l() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid m() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid n() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid o() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid p() {\n\t\tfoo();\n\t}\n\n\t@Execution(SAME_THREAD)\n\t@Test\n\tvoid q() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid r() {\n\t\tfoo();\n\t}\n\n\t@Test\n\tvoid s() {\n\t\tfoo();\n\t}\n\n\tprivate void foo() {\n\t\tIntStream.range(1, 100_000_000).mapToDouble(i -> Math.pow(i, i)).map(Math::sqrt).max();\n\t}\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/StandardTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nclass StandardTests {\n\n\t@BeforeAll\n\tstatic void initAll() {\n\t}\n\n\t@BeforeEach\n\tvoid init() {\n\t}\n\n\t@Test\n\tvoid succeedingTest() {\n\t}\n\n\t// end::user_guide[]\n\t@extensions.ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\tvoid failingTest() {\n\t\tfail(\"a failing test\");\n\t}\n\n\t@Test\n\t@Disabled(\"for demonstration purposes\")\n\tvoid skippedTest() {\n\t\t// not executed\n\t}\n\n\t@Test\n\tvoid abortedTest() {\n\t\tassumeTrue(\"abc\".contains(\"Z\"));\n\t\tfail(\"test should have been aborted\");\n\t}\n\n\t@AfterEach\n\tvoid tearDown() {\n\t}\n\n\t@AfterAll\n\tstatic void tearDownAll() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/SuiteDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n//tag::user_guide[]\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.api.SuiteDisplayName;\n\n@Suite\n@SuiteDisplayName(\"JUnit Platform Suite Demo\")\n@SelectPackages(\"example\")\n@IncludeClassNamePatterns(\".*Tests\")\n//end::user_guide[]\n@org.junit.platform.suite.api.ExcludeTags(\"exclude\")\n//tag::user_guide[]\nclass SuiteDemo {\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/SystemPropertyExtensionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestClassOrder;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.util.ClearSystemProperty;\nimport org.junit.jupiter.api.util.ReadsSystemProperty;\nimport org.junit.jupiter.api.util.RestoreSystemProperties;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\npublic class SystemPropertyExtensionDemo {\n\n\t// tag::systemproperty_clear_simple[]\n\t@Test\n\t@ClearSystemProperty(key = \"some property\")\n\tvoid testClearingProperty() {\n\t\tassertThat(System.getProperty(\"some property\")).isNull();\n\t}\n\t// end::systemproperty_clear_simple[]\n\n\t// tag::systemproperty_set_simple[]\n\t@Test\n\t@SetSystemProperty(key = \"some property\", value = \"new value\")\n\tvoid testSettingProperty() {\n\t\tassertThat(System.getProperty(\"some property\")).isEqualTo(\"new value\");\n\t}\n\t// end::systemproperty_set_simple[]\n\n\t// tag::systemproperty_using_set_and_clear[]\n\t@Test\n\t@ClearSystemProperty(key = \"1st property\")\n\t@ClearSystemProperty(key = \"2nd property\")\n\t@SetSystemProperty(key = \"3rd property\", value = \"new value\")\n\tvoid testClearingAndSettingProperty() {\n\t\tassertThat(System.getProperty(\"1st property\")).isNull();\n\t\tassertThat(System.getProperty(\"2nd property\")).isNull();\n\t\tassertThat(System.getProperty(\"3rd property\")).isEqualTo(\"new value\");\n\t}\n\t// end::systemproperty_using_set_and_clear[]\n\n\t@Nested\n\t// tag::systemproperty_using_at_class_level[]\n\t@ClearSystemProperty(key = \"some property\")\n\tclass MySystemPropertyTest {\n\n\t\t@Test\n\t\t@SetSystemProperty(key = \"some property\", value = \"new value\")\n\t\tvoid clearedAtClasslevel() {\n\t\t\tassertThat(System.getProperty(\"some property\")).isEqualTo(\"new value\");\n\t\t}\n\n\t}\n\t// end::systemproperty_using_at_class_level[]\n\n\t// tag::systemproperty_restore_test[]\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"foo\", \"bar\" })\n\t@RestoreSystemProperties\n\tvoid parameterizedTest(String value) {\n\t\tSystem.setProperty(\"some parameterized property\", value);\n\t\tSystem.setProperty(\"some other dynamic property\", \"my code calculates somehow\");\n\t}\n\t// end::systemproperty_restore_test[]\n\n\t@Nested\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\tclass SystemPropertyRestoreExample {\n\n\t\t@Nested\n\t\t@Order(1)\n\t\t// tag::systemproperty_class_restore_setup[]\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\t@RestoreSystemProperties\n\t\tclass MySystemPropertyRestoreTest {\n\n\t\t\t@BeforeAll\n\t\t\tvoid beforeAll() {\n\t\t\t\tSystem.setProperty(\"A\", \"A value\");\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t\tSystem.setProperty(\"B\", \"B value\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid isolatedTest1() {\n\t\t\t\tSystem.setProperty(\"C\", \"C value\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid isolatedTest2() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"A value\");\n\t\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"B value\");\n\n\t\t\t\t// Class-level @RestoreSystemProperties restores \"C\" to original state\n\t\t\t\tassertThat(System.getProperty(\"C\")).isNull();\n\t\t\t}\n\n\t\t}\n\t\t// end::systemproperty_class_restore_setup[]\n\n\t\t@Nested\n\t\t@Order(2)\n\t\t// tag::systemproperty_class_restore_isolated_class[]\n\t\t@ReadsSystemProperty\n\t\tclass SomeOtherTestClass {\n\n\t\t\t@Test\n\t\t\tvoid isolatedTest() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"C\")).isNull();\n\t\t\t}\n\n\t\t}\n\t\t// end::systemproperty_class_restore_isolated_class[]\n\t}\n\n\t// tag::systemproperty_method_combine_all_test[]\n\t@ParameterizedTest\n\t@ValueSource(ints = { 100, 500, 1000 })\n\t@RestoreSystemProperties\n\t@SetSystemProperty(key = \"DISABLE_CACHE\", value = \"TRUE\")\n\t@ClearSystemProperty(key = \"COPYWRITE_OVERLAY_TEXT\")\n\tvoid imageGenerationTest(int imageSize) {\n\t\tSystem.setProperty(\"IMAGE_SIZE\", String.valueOf(imageSize)); // Requires restore\n\n\t\t// Test your image generation utility with the current system properties\n\t}\n\t// end::systemproperty_method_combine_all_test[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/TaggingDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\n\n@Tag(\"fast\")\n@Tag(\"model\")\nclass TaggingDemo {\n\n\t@Test\n\t@Tag(\"taxes\")\n\tvoid testingTaxCalculation() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/TempDirectoryDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;\n\nimport java.io.IOException;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\nimport example.TempDirectoryDemo.InMemoryTempDirDemo.JimfsTempDirFactory;\nimport example.util.ListWriter;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass TempDirectoryDemo {\n\n\t// tag::user_guide_parameter_injection[]\n\t@Test\n\tvoid writeItemsToFile(@TempDir Path tempDir) throws IOException {\n\t\tPath file = tempDir.resolve(\"test.txt\");\n\n\t\tnew ListWriter(file).write(\"a\", \"b\", \"c\");\n\n\t\tassertEquals(List.of(\"a,b,c\"), Files.readAllLines(file));\n\t}\n\t// end::user_guide_parameter_injection[]\n\n\t// tag::user_guide_multiple_directories[]\n\t@Test\n\tvoid copyFileFromSourceToTarget(@TempDir Path source, @TempDir Path target) throws IOException {\n\t\tPath sourceFile = source.resolve(\"test.txt\");\n\t\tnew ListWriter(sourceFile).write(\"a\", \"b\", \"c\");\n\n\t\tPath targetFile = Files.copy(sourceFile, target.resolve(\"test.txt\"));\n\n\t\tassertNotEquals(sourceFile, targetFile);\n\t\tassertEquals(List.of(\"a,b,c\"), Files.readAllLines(targetFile));\n\t}\n\t// end::user_guide_multiple_directories[]\n\n\tstatic\n\t// tag::user_guide_field_injection[]\n\tclass SharedTempDirectoryDemo {\n\n\t\t@TempDir\n\t\tstatic Path sharedTempDir;\n\n\t\t@Test\n\t\tvoid writeItemsToFile() throws IOException {\n\t\t\tPath file = sharedTempDir.resolve(\"test.txt\");\n\n\t\t\tnew ListWriter(file).write(\"a\", \"b\", \"c\");\n\n\t\t\tassertEquals(List.of(\"a,b,c\"), Files.readAllLines(file));\n\t\t}\n\n\t\t@Test\n\t\tvoid anotherTestThatUsesTheSameTempDir() {\n\t\t\t// use sharedTempDir\n\t\t}\n\n\t}\n\t// end::user_guide_field_injection[]\n\n\tstatic\n\t// tag::user_guide_cleanup_mode[]\n\tclass CleanupModeDemo {\n\n\t\t@Test\n\t\tvoid fileTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {\n\t\t\t// perform test\n\t\t}\n\n\t}\n\t// end::user_guide_cleanup_mode[]\n\n\tstatic\n\t// tag::user_guide_factory_name_prefix[]\n\tclass TempDirFactoryDemo {\n\n\t\t@Test\n\t\tvoid factoryTest(@TempDir(factory = Factory.class) Path tempDir) {\n\t\t\tassertTrue(tempDir.getFileName().toString().startsWith(\"factoryTest\"));\n\t\t}\n\n\t\tstatic class Factory implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows IOException {\n\t\t\t\treturn Files.createTempDirectory(extensionContext.getRequiredTestMethod().getName());\n\t\t\t}\n\n\t\t}\n\n\t}\n\t// end::user_guide_factory_name_prefix[]\n\n\tstatic\n\t// tag::user_guide_factory_jimfs[]\n\tclass InMemoryTempDirDemo {\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = JimfsTempDirFactory.class) Path tempDir) {\n\t\t\t// perform test\n\t\t}\n\n\t\tstatic class JimfsTempDirFactory implements TempDirFactory {\n\n\t\t\tprivate final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows IOException {\n\t\t\t\treturn Files.createTempDirectory(fileSystem.getPath(\"/\"), \"junit-\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\tfileSystem.close();\n\t\t\t}\n\n\t\t}\n\n\t}\n\t// end::user_guide_factory_jimfs[]\n\n\tstatic\n\t// tag::user_guide_deletion_strategy[]\n\tclass DeletionStrategyDemo {\n\n\t\t@Test\n\t\tvoid test(@TempDir(deletionStrategy = TempDirDeletionStrategy.IgnoreFailures.class) Path tempDir) {\n\t\t\t// perform test\n\t\t}\n\n\t}\n\t// end::user_guide_deletion_strategy[]\n\n\t// tag::user_guide_composed_annotation[]\n\t@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@TempDir(factory = JimfsTempDirFactory.class)\n\t@interface JimfsTempDir {\n\t}\n\t// end::user_guide_composed_annotation[]\n\n\tstatic\n\t// tag::user_guide_composed_annotation_usage[]\n\tclass JimfsTempDirAnnotationDemo {\n\n\t\t@Test\n\t\tvoid test(@JimfsTempDir Path tempDir) {\n\t\t\t// perform test\n\t\t}\n\n\t}\n\t// end::user_guide_composed_annotation_usage[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/TestInfoDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\n\n@DisplayName(\"TestInfo Demo\")\nclass TestInfoDemo {\n\n\t@BeforeAll\n\tstatic void beforeAll(TestInfo testInfo) {\n\t\tassertEquals(\"TestInfo Demo\", testInfo.getDisplayName());\n\t}\n\n\tTestInfoDemo(TestInfo testInfo) {\n\t\tString displayName = testInfo.getDisplayName();\n\t\tassertTrue(displayName.equals(\"TEST 1\") || displayName.equals(\"test2()\"));\n\t}\n\n\t@BeforeEach\n\tvoid init(TestInfo testInfo) {\n\t\tString displayName = testInfo.getDisplayName();\n\t\tassertTrue(displayName.equals(\"TEST 1\") || displayName.equals(\"test2()\"));\n\t}\n\n\t@Test\n\t@DisplayName(\"TEST 1\")\n\t@Tag(\"my-tag\")\n\tvoid test1(TestInfo testInfo) {\n\t\tassertEquals(\"TEST 1\", testInfo.getDisplayName());\n\t\tassertTrue(testInfo.getTags().contains(\"my-tag\"));\n\t}\n\n\t@Test\n\tvoid test2() {\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/TestReporterDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.io.TempDir;\n\n// tag::user_guide[]\nclass TestReporterDemo {\n\n\t@Test\n\tvoid reportSingleValue(TestReporter testReporter) {\n\t\ttestReporter.publishEntry(\"a status message\");\n\t}\n\n\t@Test\n\tvoid reportKeyValuePair(TestReporter testReporter) {\n\t\ttestReporter.publishEntry(\"a key\", \"a value\");\n\t}\n\n\t@Test\n\tvoid reportMultipleKeyValuePairs(TestReporter testReporter) {\n\t\tMap<String, String> values = new HashMap<>();\n\t\tvalues.put(\"user name\", \"dk38\");\n\t\tvalues.put(\"award year\", \"1974\");\n\n\t\ttestReporter.publishEntry(values);\n\t}\n\n\t@Test\n\tvoid reportFiles(TestReporter testReporter, @TempDir Path tempDir) throws Exception {\n\n\t\ttestReporter.publishFile(\"test1.txt\", MediaType.TEXT_PLAIN_UTF_8, file -> Files.write(file, List.of(\"Test 1\")));\n\n\t\tPath existingFile = Files.write(tempDir.resolve(\"test2.txt\"), List.of(\"Test 2\"));\n\t\ttestReporter.publishFile(existingFile, MediaType.TEXT_PLAIN_UTF_8);\n\n\t\ttestReporter.publishDirectory(\"test3\", dir -> {\n\t\t\tFiles.write(dir.resolve(\"nested1.txt\"), List.of(\"Nested content 1\"));\n\t\t\tFiles.write(dir.resolve(\"nested2.txt\"), List.of(\"Nested content 2\"));\n\t\t});\n\n\t\tPath existingDir = Files.createDirectory(tempDir.resolve(\"test4\"));\n\t\tFiles.write(existingDir.resolve(\"nested1.txt\"), List.of(\"Nested content 1\"));\n\t\tFiles.write(existingDir.resolve(\"nested2.txt\"), List.of(\"Nested content 2\"));\n\t\ttestReporter.publishDirectory(existingDir);\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/TestTemplateDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\n\nclass TestTemplateDemo {\n\n\t// tag::user_guide[]\n\tfinal List<String> fruits = Arrays.asList(\"apple\", \"banana\", \"lemon\");\n\n\t@TestTemplate\n\t@ExtendWith(MyTestTemplateInvocationContextProvider.class)\n\tvoid testTemplate(String fruit) {\n\t\tassertTrue(fruits.contains(fruit));\n\t}\n\n\t// end::user_guide[]\n\tstatic\n\t// @formatter:off\n\t// tag::user_guide[]\n\tpublic class MyTestTemplateInvocationContextProvider\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(\n\t\t\t\tExtensionContext context) {\n\n\t\t\treturn Stream.of(invocationContext(\"apple\"), invocationContext(\"banana\"));\n\t\t}\n\n\t\tprivate TestTemplateInvocationContext invocationContext(String parameter) {\n\t\t\treturn new TestTemplateInvocationContext() {\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn parameter;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of(new ParameterResolver() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext,\n\t\t\t\t\t\t\t\tExtensionContext extensionContext) {\n\t\t\t\t\t\t\treturn parameterContext.getParameter().getType().equals(String.class);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic Object resolveParameter(ParameterContext parameterContext,\n\t\t\t\t\t\t\t\tExtensionContext extensionContext) {\n\t\t\t\t\t\t\treturn parameter;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\t// end::user_guide[]\n\t// @formatter:on\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/TestingAStackDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.EmptyStackException;\nimport java.util.Stack;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n@DisplayName(\"A stack\")\nclass TestingAStackDemo {\n\n\t@Test\n\t@DisplayName(\"is instantiated with new Stack()\")\n\tvoid isInstantiatedWithNew() {\n\t\tnew Stack<>();\n\t}\n\n\t@Nested\n\t@DisplayName(\"when new\")\n\tclass WhenNew {\n\n\t\tStack<Object> stack;\n\n\t\t@BeforeEach\n\t\tvoid createNewStack() {\n\t\t\tstack = new Stack<>();\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"is empty\")\n\t\tvoid isEmpty() {\n\t\t\tassertTrue(stack.isEmpty());\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"throws EmptyStackException when popped\")\n\t\tvoid throwsExceptionWhenPopped() {\n\t\t\tassertThrows(EmptyStackException.class, stack::pop);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"throws EmptyStackException when peeked\")\n\t\tvoid throwsExceptionWhenPeeked() {\n\t\t\tassertThrows(EmptyStackException.class, stack::peek);\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"after pushing an element\")\n\t\tclass AfterPushing {\n\n\t\t\tString anElement = \"an element\";\n\n\t\t\t@BeforeEach\n\t\t\tvoid pushAnElement() {\n\t\t\t\tstack.push(anElement);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"it is no longer empty\")\n\t\t\tvoid isNotEmpty() {\n\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"returns the element when popped and is empty\")\n\t\t\tvoid returnElementWhenPopped() {\n\t\t\t\tassertEquals(anElement, stack.pop());\n\t\t\t\tassertTrue(stack.isEmpty());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"returns the element when peeked but remains not empty\")\n\t\t\tvoid returnElementWhenPeeked() {\n\t\t\t\tassertEquals(anElement, stack.peek());\n\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t}\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/TimeoutDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.Timeout.ThreadMode;\n\n// tag::user_guide[]\n@Tag(\"timeout\")\nclass TimeoutDemo {\n\n\t@BeforeEach\n\t@Timeout(5)\n\tvoid setUp() {\n\t\t// fails if execution time exceeds 5 seconds\n\t}\n\n\t@Test\n\t@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)\n\tvoid failsIfExecutionTimeExceeds500Milliseconds() {\n\t\t// fails if execution time exceeds 500 milliseconds\n\t}\n\n\t@Test\n\t@Timeout(value = 500, unit = TimeUnit.MILLISECONDS, threadMode = ThreadMode.SEPARATE_THREAD)\n\tvoid failsIfExecutionTimeExceeds500MillisecondsInSeparateThread() {\n\t\t// fails if execution time exceeds 500 milliseconds, the test code is executed in a separate thread\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/UsingTheLauncherDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\n\nimport java.io.PrintWriter;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherConfig;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.launcher.core.LauncherExecutionRequestBuilder;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\nimport org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;\n\n/**\n * @since 5.0\n */\nclass UsingTheLauncherDemo {\n\n\t@Tag(\"exclude\")\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid execution() {\n\t\t// @formatter:off\n\t\t// tag::execution[]\n\t\tLauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t.selectors(\n\t\t\t\tselectPackage(\"com.example.mytests\"),\n\t\t\t\tselectClass(MyTestClass.class)\n\t\t\t)\n\t\t\t.filters(\n\t\t\t\tincludeClassNamePatterns(\".*Tests\")\n\t\t\t)\n\t\t\t// end::execution[]\n\t\t\t.configurationParameter(\"enableHttpServer\", \"false\")\n\t\t\t// tag::execution[]\n\t\t\t.build();\n\n\t\tSummaryGeneratingListener listener = new SummaryGeneratingListener();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession()) {\n\t\t\tLauncher launcher = session.getLauncher();\n\t\t\t// Register one ore more listeners of your choice.\n\t\t\tlauncher.registerTestExecutionListeners(listener);\n\t\t\t// Discover tests and build a test plan.\n\t\t\tTestPlan testPlan = launcher.discover(discoveryRequest);\n\t\t\t// Execute the test plan.\n\t\t\tlauncher.execute(testPlan);\n\t\t\t// Alternatively, execute the discovery request directly.\n\t\t\tlauncher.execute(discoveryRequest);\n\t\t}\n\n\t\tTestExecutionSummary summary = listener.getSummary();\n\t\t// Do something with the summary...\n\n\t\t// end::execution[]\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid launcherConfig() {\n\t\tPath reportsDir = Path.of(\"target\", \"xml-reports\");\n\t\tPrintWriter out = new PrintWriter(System.out);\n\t\t// @formatter:off\n\t\t// tag::launcherConfig[]\n\t\tLauncherConfig launcherConfig = LauncherConfig.builder()\n\t\t\t.enableTestEngineAutoRegistration(false)\n\t\t\t.enableLauncherSessionListenerAutoRegistration(false)\n\t\t\t.enableLauncherDiscoveryListenerAutoRegistration(false)\n\t\t\t.enablePostDiscoveryFilterAutoRegistration(false)\n\t\t\t.enableTestExecutionListenerAutoRegistration(false)\n\t\t\t.addTestEngines(new CustomTestEngine())\n\t\t\t.addLauncherSessionListeners(new CustomLauncherSessionListener())\n\t\t\t.addLauncherDiscoveryListeners(new CustomLauncherDiscoveryListener())\n\t\t\t.addPostDiscoveryFilters(new CustomPostDiscoveryFilter())\n\t\t\t.addTestExecutionListeners(new LegacyXmlReportGeneratingListener(reportsDir, out))\n\t\t\t.addTestExecutionListeners(new CustomTestExecutionListener())\n\t\t\t.build();\n\n\t\tLauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t.selectors(selectPackage(\"com.example.mytests\"))\n\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession(launcherConfig)) {\n\t\t\tsession.getLauncher().execute(discoveryRequest);\n\t\t}\n\t\t// end::launcherConfig[]\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid cancellationDirect() {\n\t\t// tag::cancellation-direct[]\n\t\tCancellationToken cancellationToken = CancellationToken.create(); // <1>\n\n\t\tTestExecutionListener failFastListener = new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestIdentifier identifier, TestExecutionResult result) {\n\t\t\t\tif (result.getStatus() == FAILED) {\n\t\t\t\t\tcancellationToken.cancel(); // <2>\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// end::cancellation-direct[]\n\t\t// @formatter:off\n\t\t// tag::cancellation-direct[]\n\t\tLauncherExecutionRequest executionRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t\t.selectors(selectClass(MyTestClass.class))\n\t\t\t\t.forExecution()\n\t\t\t\t// end::cancellation-direct[]\n\t\t\t\t// @formatter:on\n\t\t\t\t// tag::cancellation-direct[]\n\t\t\t\t.cancellationToken(cancellationToken) // <3>\n\t\t\t\t.listeners(failFastListener) // <4>\n\t\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession()) {\n\t\t\tsession.getLauncher().execute(executionRequest); // <5>\n\t\t}\n\t\t// end::cancellation-direct[]\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid cancellationFromDiscoveryRequest() {\n\t\tCancellationToken cancellationToken = CancellationToken.create();\n\n\t\tTestExecutionListener failFastListener = new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestIdentifier identifier, TestExecutionResult result) {\n\t\t\t\tif (result.getStatus() == FAILED) {\n\t\t\t\t\tcancellationToken.cancel();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// @formatter:off\n\t\t// tag::cancellation-discovery-request[]\n\t\tLauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t\t.selectors(selectClass(MyTestClass.class))\n\t\t\t\t.build(); // <1>\n\t\t// end::cancellation-discovery-request[]\n\t\t// @formatter:on\n\t\t// tag::cancellation-discovery-request[]\n\n\t\tLauncherExecutionRequest executionRequest = LauncherExecutionRequestBuilder.request(discoveryRequest) // <2>\n\t\t\t\t.cancellationToken(cancellationToken) // <3>\n\t\t\t\t.listeners(failFastListener) // <4>\n\t\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession()) {\n\t\t\tsession.getLauncher().execute(executionRequest); // <5>\n\t\t}\n\t\t// end::cancellation-discovery-request[]\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid cancellationFromTestPlan() {\n\t\tCancellationToken cancellationToken = CancellationToken.create();\n\n\t\tTestExecutionListener failFastListener = new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestIdentifier identifier, TestExecutionResult result) {\n\t\t\t\tif (result.getStatus() == FAILED) {\n\t\t\t\t\tcancellationToken.cancel();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// @formatter:off\n\t\t// tag::cancellation-test-plan[]\n\t\tLauncherDiscoveryRequest discoveryRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t\t.selectors(selectClass(MyTestClass.class))\n\t\t\t\t.build(); // <1>\n\t\t// end::cancellation-test-plan[]\n\t\t// @formatter:on\n\t\t// tag::cancellation-test-plan[]\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession()) {\n\t\t\tvar launcher = session.getLauncher();\n\t\t\tTestPlan testPlan = launcher.discover(discoveryRequest); // <2>\n\t\t\tLauncherExecutionRequest executionRequest = LauncherExecutionRequestBuilder.request(testPlan) // <3>\n\t\t\t\t\t.cancellationToken(cancellationToken) // <4>\n\t\t\t\t\t.listeners(failFastListener) // <5>\n\t\t\t\t\t.build();\n\t\t\tlauncher.execute(executionRequest); // <6>\n\t\t}\n\t\t// end::cancellation-test-plan[]\n\t}\n\n}\n\nclass MyTestClass {\n}\n\nclass CustomTestExecutionListener implements TestExecutionListener {\n}\n\nclass CustomLauncherSessionListener implements LauncherSessionListener {\n}\n\nclass CustomLauncherDiscoveryListener implements LauncherDiscoveryListener {\n}\n\nclass CustomPostDiscoveryFilter implements PostDiscoveryFilter {\n\t@Override\n\tpublic FilterResult apply(TestDescriptor object) {\n\t\treturn FilterResult.included(\"includes everything\");\n\t}\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/UsingTheLauncherForDiscoveryDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\n// tag::imports[]\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\n\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherFactory;\n// end::imports[]\n\n/**\n * @since 6.0\n */\nclass UsingTheLauncherForDiscoveryDemo {\n\n\t@org.junit.jupiter.api.Test\n\t@SuppressWarnings(\"unused\")\n\tvoid discovery() {\n\t\t// @formatter:off\n\t\t// tag::discovery[]\n\t\tLauncherDiscoveryRequest discoveryRequest = discoveryRequest()\n\t\t\t.selectors(\n\t\t\t\tselectPackage(\"com.example.mytests\"),\n\t\t\t\tselectClass(MyTestClass.class)\n\t\t\t)\n\t\t\t.filters(\n\t\t\t\tincludeClassNamePatterns(\".*Tests\")\n\t\t\t)\n\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession()) {\n\t\t\tTestPlan testPlan = session.getLauncher().discover(discoveryRequest);\n\n\t\t\t// ... discover additional test plans or execute tests\n\t\t}\n\t\t// end::discovery[]\n\t\t// @formatter:on\n\t}\n\n\tstatic class MyTestClass {\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/AbstractDatabaseTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\n// tag::user_guide[]\n\nimport static example.callbacks.Logger.afterAllMethod;\nimport static example.callbacks.Logger.afterEachMethod;\nimport static example.callbacks.Logger.beforeAllMethod;\nimport static example.callbacks.Logger.beforeEachMethod;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * Abstract base class for tests that use the database.\n */\nabstract class AbstractDatabaseTests {\n\n\t@BeforeAll\n\tstatic void createDatabase() {\n\t\tbeforeAllMethod(AbstractDatabaseTests.class.getSimpleName() + \".createDatabase()\");\n\t}\n\n\t@BeforeEach\n\tvoid connectToDatabase() {\n\t\tbeforeEachMethod(AbstractDatabaseTests.class.getSimpleName() + \".connectToDatabase()\");\n\t}\n\n\t@AfterEach\n\tvoid disconnectFromDatabase() {\n\t\tafterEachMethod(AbstractDatabaseTests.class.getSimpleName() + \".disconnectFromDatabase()\");\n\t}\n\n\t@AfterAll\n\tstatic void destroyDatabase() {\n\t\tafterAllMethod(AbstractDatabaseTests.class.getSimpleName() + \".destroyDatabase()\");\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/BrokenLifecycleMethodConfigDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\n// tag::user_guide[]\n\nimport static example.callbacks.Logger.afterEachMethod;\nimport static example.callbacks.Logger.beforeEachMethod;\nimport static example.callbacks.Logger.testMethod;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Example of \"broken\" lifecycle method configuration.\n *\n * <p>Test data is inserted before the database connection has been opened.\n *\n * <p>Database connection is closed before deleting test data.\n */\n@ExtendWith({ Extension1.class, Extension2.class })\nclass BrokenLifecycleMethodConfigDemo {\n\n\t@BeforeEach\n\tvoid connectToDatabase() {\n\t\tbeforeEachMethod(getClass().getSimpleName() + \".connectToDatabase()\");\n\t}\n\n\t@BeforeEach\n\tvoid insertTestDataIntoDatabase() {\n\t\tbeforeEachMethod(getClass().getSimpleName() + \".insertTestDataIntoDatabase()\");\n\t}\n\n\t@Test\n\tvoid testDatabaseFunctionality() {\n\t\ttestMethod(getClass().getSimpleName() + \".testDatabaseFunctionality()\");\n\t}\n\n\t@AfterEach\n\tvoid deleteTestDataFromDatabase() {\n\t\tafterEachMethod(getClass().getSimpleName() + \".deleteTestDataFromDatabase()\");\n\t}\n\n\t@AfterEach\n\tvoid disconnectFromDatabase() {\n\t\tafterEachMethod(getClass().getSimpleName() + \".disconnectFromDatabase()\");\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/DatabaseTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\n// tag::user_guide[]\n\nimport static example.callbacks.Logger.afterEachMethod;\nimport static example.callbacks.Logger.beforeAllMethod;\nimport static example.callbacks.Logger.beforeEachMethod;\nimport static example.callbacks.Logger.testMethod;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Extension of {@link AbstractDatabaseTests} that inserts test data\n * into the database (after the database connection has been opened)\n * and deletes test data (before the database connection is closed).\n */\n@ExtendWith({ Extension1.class, Extension2.class })\nclass DatabaseTestsDemo extends AbstractDatabaseTests {\n\n\t@BeforeAll\n\tstatic void beforeAll() {\n\t\tbeforeAllMethod(DatabaseTestsDemo.class.getSimpleName() + \".beforeAll()\");\n\t}\n\n\t@BeforeEach\n\tvoid insertTestDataIntoDatabase() {\n\t\tbeforeEachMethod(getClass().getSimpleName() + \".insertTestDataIntoDatabase()\");\n\t}\n\n\t@Test\n\tvoid testDatabaseFunctionality() {\n\t\ttestMethod(getClass().getSimpleName() + \".testDatabaseFunctionality()\");\n\t}\n\n\t@AfterEach\n\tvoid deleteTestDataFromDatabase() {\n\t\tafterEachMethod(getClass().getSimpleName() + \".deleteTestDataFromDatabase()\");\n\t}\n\n\t@AfterAll\n\tstatic void afterAll() {\n\t\tbeforeAllMethod(DatabaseTestsDemo.class.getSimpleName() + \".afterAll()\");\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/Extension1.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\n// tag::user_guide[]\n\nimport static example.callbacks.Logger.afterEachCallback;\nimport static example.callbacks.Logger.beforeEachCallback;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\npublic class Extension1 implements BeforeEachCallback, AfterEachCallback {\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tbeforeEachCallback(this);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\tafterEachCallback(this);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/Extension2.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\n// tag::user_guide[]\n\nimport static example.callbacks.Logger.afterEachCallback;\nimport static example.callbacks.Logger.beforeEachCallback;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\npublic class Extension2 implements BeforeEachCallback, AfterEachCallback {\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tbeforeEachCallback(this);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\tafterEachCallback(this);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/Logger.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.callbacks;\n\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.extension.Extension;\n\nclass Logger {\n\n\tstatic final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(Logger.class.getName());\n\n\tstatic void beforeAllMethod(String text) {\n\t\tlog(() -> \"@BeforeAll \" + text);\n\t}\n\n\tstatic void beforeEachCallback(Extension extension) {\n\t\tlog(() -> \"  \" + extension.getClass().getSimpleName() + \".beforeEach()\");\n\t}\n\n\tstatic void beforeEachMethod(String text) {\n\t\tlog(() -> \"    @BeforeEach \" + text);\n\t}\n\n\tstatic void testMethod(String text) {\n\t\tlog(() -> \"      @Test \" + text);\n\t}\n\n\tstatic void afterEachMethod(String text) {\n\t\tlog(() -> \"    @AfterEach \" + text);\n\t}\n\n\tstatic void afterEachCallback(Extension extension) {\n\t\tlog(() -> \"  \" + extension.getClass().getSimpleName() + \".afterEach()\");\n\t}\n\n\tstatic void afterAllMethod(String text) {\n\t\tlog(() -> \"@AfterAll \" + text);\n\t}\n\n\tprivate static void log(Supplier<String> supplier) {\n\t\t// System.err.println(supplier.get());\n\t\tlogger.info(supplier);\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/callbacks/package-info.java",
    "content": "\n@NullMarked\npackage example.callbacks;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/defaultmethods/ComparableContract.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.defaultmethods;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.Test;\n\n// tag::user_guide[]\npublic interface ComparableContract<T extends Comparable<T>> extends Testable<T> {\n\n\tT createSmallerValue();\n\n\t@Test\n\tdefault void returnsZeroWhenComparedToItself() {\n\t\tT value = createValue();\n\t\tassertEquals(0, value.compareTo(value));\n\t}\n\n\t@Test\n\tdefault void returnsPositiveNumberWhenComparedToSmallerValue() {\n\t\tT value = createValue();\n\t\tT smallerValue = createSmallerValue();\n\t\tassertTrue(value.compareTo(smallerValue) > 0);\n\t}\n\n\t@Test\n\tdefault void returnsNegativeNumberWhenComparedToLargerValue() {\n\t\tT value = createValue();\n\t\tT smallerValue = createSmallerValue();\n\t\tassertTrue(smallerValue.compareTo(value) < 0);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/defaultmethods/EqualsContract.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.defaultmethods;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\n\nimport org.junit.jupiter.api.Test;\n\n// tag::user_guide[]\npublic interface EqualsContract<T> extends Testable<T> {\n\n\tT createNotEqualValue();\n\n\t@Test\n\tdefault void valueEqualsItself() {\n\t\tT value = createValue();\n\t\tassertEquals(value, value);\n\t}\n\n\t@Test\n\tdefault void valueDoesNotEqualNull() {\n\t\tT value = createValue();\n\t\tassertNotEquals(null, value);\n\t}\n\n\t@Test\n\tdefault void valueDoesNotEqualDifferentValue() {\n\t\tT value = createValue();\n\t\tT differentValue = createNotEqualValue();\n\t\tassertNotEquals(value, differentValue);\n\t\tassertNotEquals(differentValue, value);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/defaultmethods/StringTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.defaultmethods;\n\n// tag::user_guide[]\nclass StringTests implements ComparableContract<String>, EqualsContract<String> {\n\n\t@Override\n\tpublic String createValue() {\n\t\treturn \"banana\";\n\t}\n\n\t@Override\n\tpublic String createSmallerValue() {\n\t\treturn \"apple\"; // 'a' < 'b' in \"banana\"\n\t}\n\n\t@Override\n\tpublic String createNotEqualValue() {\n\t\treturn \"cherry\";\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/defaultmethods/Testable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.defaultmethods;\n\n// tag::user_guide[]\npublic interface Testable<T> {\n\n\tT createValue();\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/defaultmethods/package-info.java",
    "content": "\n@NullMarked\npackage example.defaultmethods;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/AssertDoesNotThrowExceptionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\n\nimport org.junit.jupiter.api.Test;\n\nclass AssertDoesNotThrowExceptionDemo {\n\n\t// tag::user_guide[]\n\t@Test\n\tvoid testExceptionIsNotThrown() {\n\t\tassertDoesNotThrow(() -> {\n\t\t\tshouldNotThrowException();\n\t\t});\n\t}\n\n\tvoid shouldNotThrowException() {\n\t}\n\t// end::user_guide[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/ExceptionAssertionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Test;\n\nclass ExceptionAssertionDemo {\n\n\t// @formatter:off\n\t// tag::user_guide[]\n\t@Test\n\tvoid testExpectedExceptionIsThrown() {\n\t\t// The following assertion succeeds because the code under assertion\n\t\t// throws the expected IllegalArgumentException.\n\t\t// The assertion also returns the thrown exception which can be used for\n\t\t// further assertions like asserting the exception message.\n\t\tIllegalArgumentException exception =\n\t\t\tassertThrows(IllegalArgumentException.class, () -> {\n\t\t\t\tthrow new IllegalArgumentException(\"expected message\");\n\t\t\t});\n\t\tassertEquals(\"expected message\", exception.getMessage());\n\n\t\t// The following assertion also succeeds because the code under assertion\n\t\t// throws IllegalArgumentException which is a subclass of RuntimeException.\n\t\tassertThrows(RuntimeException.class, () -> {\n\t\t\tthrow new IllegalArgumentException(\"expected message\");\n\t\t});\n\t}\n\t// end::user_guide[]\n\t// @formatter:on\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/ExceptionAssertionExactDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrowsExactly;\n\nimport extensions.ExpectToFail;\n\nimport org.junit.jupiter.api.Test;\n\npublic class ExceptionAssertionExactDemo {\n\n\t@ExpectToFail\n\t// @formatter:off\n\t// tag::user_guide[]\n\t@Test\n\tvoid testExpectedExceptionIsThrown() {\n\t\t// The following assertion succeeds because the code under assertion throws\n\t\t// IllegalArgumentException which is exactly equal to the expected type.\n\t\t// The assertion also returns the thrown exception which can be used for\n\t\t// further assertions like asserting the exception message.\n\t\tIllegalArgumentException exception =\n\t\t\tassertThrowsExactly(IllegalArgumentException.class, () -> {\n\t\t\t\tthrow new IllegalArgumentException(\"expected message\");\n\t\t\t});\n\t\tassertEquals(\"expected message\", exception.getMessage());\n\n\t\t// The following assertion fails because the assertion expects exactly\n\t\t// RuntimeException to be thrown, not subclasses of RuntimeException.\n\t\tassertThrowsExactly(RuntimeException.class, () -> {\n\t\t\tthrow new IllegalArgumentException(\"expected message\");\n\t\t});\n\t}\n\t// end::user_guide[]\n\t// @formatter:on\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/FailedAssertionDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport example.util.Calculator;\n\nimport extensions.ExpectToFail;\n\nimport org.junit.jupiter.api.Test;\n\nclass FailedAssertionDemo {\n\n\t// tag::user_guide[]\n\tprivate final Calculator calculator = new Calculator();\n\n\t// end::user_guide[]\n\n\t@ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\tvoid failsDueToUncaughtAssertionError() {\n\t\t// The following incorrect assertion will cause a test failure.\n\t\t// The expected value should be 2 instead of 99.\n\t\tassertEquals(99, calculator.add(1, 1));\n\t}\n\t// end::user_guide[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/IgnoreIOExceptionExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport java.io.IOException;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\n\n// @formatter:off\n// tag::user_guide[]\npublic class IgnoreIOExceptionExtension implements TestExecutionExceptionHandler {\n\n\t@Override\n\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\tthrows Throwable {\n\n\t\tif (throwable instanceof IOException) {\n\t\t\treturn;\n\t\t}\n\t\tthrow throwable;\n\t}\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/IgnoreIOExceptionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport java.io.IOException;\n\nimport extensions.ExpectToFail;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n@ExtendWith(IgnoreIOExceptionExtension.class)\nclass IgnoreIOExceptionTests {\n\n\t@Test\n\tvoid shouldSucceed() throws IOException {\n\t\tthrow new IOException(\"any\");\n\t}\n\n\t@Test\n\t@ExpectToFail\n\tvoid shouldFail() {\n\t\tthrow new RuntimeException(\"any\");\n\t}\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/MultipleHandlersTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport example.exception.MultipleHandlersTestCase.ThirdExecutedHandler;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\n\n// @formatter:off\n// tag::user_guide[]\n// Register handlers for @Test, @BeforeEach, @AfterEach as well as @BeforeAll and @AfterAll\n@ExtendWith(ThirdExecutedHandler.class)\nclass MultipleHandlersTestCase {\n\n\t// Register handlers for @Test, @BeforeEach, @AfterEach only\n\t@ExtendWith(SecondExecutedHandler.class)\n\t@ExtendWith(FirstExecutedHandler.class)\n\t@Test\n\tvoid testMethod() {\n\t}\n\n\t// end::user_guide[]\n\n\tstatic class FirstExecutedHandler implements TestExecutionExceptionHandler {\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable ex)\n\t\t\t\tthrows Throwable {\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tstatic class SecondExecutedHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\t\tthrows Throwable {\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tstatic class ThirdExecutedHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\t\tthrows Throwable {\n\t\t\tthrow ex;\n\t\t}\n\t}\n\t// tag::user_guide[]\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/RecordStateOnErrorExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;\n\n// @formatter:off\n// tag::user_guide[]\nclass RecordStateOnErrorExtension implements LifecycleMethodExecutionExceptionHandler {\n\n\t@Override\n\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\tthrows Throwable {\n\t\tmemoryDumpForFurtherInvestigation(\"Failure recorded during class setup\");\n\t\tthrow ex;\n\t}\n\n\t@Override\n\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\tthrows Throwable {\n\t\tmemoryDumpForFurtherInvestigation(\"Failure recorded during test setup\");\n\t\tthrow ex;\n\t}\n\n\t@Override\n\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\tthrows Throwable {\n\t\tmemoryDumpForFurtherInvestigation(\"Failure recorded during test cleanup\");\n\t\tthrow ex;\n\t}\n\n\t@Override\n\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable ex)\n\t\t\tthrows Throwable {\n\t\tmemoryDumpForFurtherInvestigation(\"Failure recorded during class cleanup\");\n\t\tthrow ex;\n\t}\n\t// end::user_guide[]\n\n\tprivate void memoryDumpForFurtherInvestigation(String error) {\n\t}\n\t// tag::user_guide[]\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/UncaughtExceptionHandlingDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.exception;\n\nimport example.util.Calculator;\n\nimport extensions.ExpectToFail;\n\nimport org.junit.jupiter.api.Test;\n\nclass UncaughtExceptionHandlingDemo {\n\n\t// tag::user_guide[]\n\tprivate final Calculator calculator = new Calculator();\n\n\t// end::user_guide[]\n\n\t@ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\tvoid failsDueToUncaughtException() {\n\t\t// The following throws an ArithmeticException due to division by\n\t\t// zero, which causes a test failure.\n\t\tcalculator.divide(1, 0);\n\t}\n\t// end::user_guide[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/exception/package-info.java",
    "content": "\n@NullMarked\npackage example.exception;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/HttpServerExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\n\nimport com.sun.net.httpserver.HttpServer;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n// tag::user_guide[]\npublic class HttpServerExtension implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn HttpServer.class.equals(parameterContext.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\n\t\tExtensionContext rootContext = extensionContext.getRoot();\n\t\tExtensionContext.Store store = rootContext.getStore(Namespace.GLOBAL);\n\t\tClass<HttpServerResource> key = HttpServerResource.class;\n\t\tHttpServerResource resource = store.computeIfAbsent(key, __ -> {\n\t\t\ttry {\n\t\t\t\tHttpServerResource serverResource = new HttpServerResource(0);\n\t\t\t\tserverResource.start();\n\t\t\t\treturn serverResource;\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to create HttpServerResource\", e);\n\t\t\t}\n\t\t}, HttpServerResource.class);\n\t\treturn resource.getHttpServer();\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/HttpServerResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\n\nimport com.sun.net.httpserver.HttpServer;\n\n/**\n * Demonstrates an implementation of {@link AutoCloseable} using an {@link HttpServer}.\n */\n// tag::user_guide[]\nclass HttpServerResource implements AutoCloseable {\n\n\tprivate final HttpServer httpServer;\n\n\t// end::user_guide[]\n\n\t/**\n\t * Initializes the Http server resource, using the given port.\n\t *\n\t * @param port (int) The port number for the server, must be in the range 0-65535.\n\t * @throws IOException if an IOException occurs during initialization.\n\t */\n\t// tag::user_guide[]\n\tHttpServerResource(int port) throws IOException {\n\t\tInetAddress loopbackAddress = InetAddress.getLoopbackAddress();\n\t\tthis.httpServer = HttpServer.create(new InetSocketAddress(loopbackAddress, port), 0);\n\t}\n\n\tHttpServer getHttpServer() {\n\t\treturn httpServer;\n\t}\n\n\t// end::user_guide[]\n\n\t/**\n\t * Starts the Http server with an example handler.\n\t */\n\t// tag::user_guide[]\n\tvoid start() {\n\t\t// Example handler\n\t\thttpServer.createContext(\"/example\", exchange -> {\n\t\t\tString body = \"This is a test\";\n\t\t\texchange.sendResponseHeaders(200, body.length());\n\t\t\ttry (OutputStream os = exchange.getResponseBody()) {\n\t\t\t\tos.write(body.getBytes(UTF_8));\n\t\t\t}\n\t\t});\n\t\thttpServer.setExecutor(null);\n\t\thttpServer.start();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\thttpServer.stop(0);\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/ParameterResolverConflictDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport extensions.ExpectToFail;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n// tag::user_guide[]\npublic class ParameterResolverConflictDemo {\n\n\t// end::user_guide[]\n\t@ExpectToFail\n\t// tag::user_guide[]\n\t@Test\n\t@ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class })\n\tvoid testInt(int i) {\n\t\t// Test will not run due to ParameterResolutionException\n\t\tassertEquals(1, i);\n\t}\n\n\tstatic class FirstIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == int.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tstatic class SecondIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == int.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 2;\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/ParameterResolverCustomAnnotationDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n// tag::user_guide[]\npublic class ParameterResolverCustomAnnotationDemo {\n\n\t@Test\n\tvoid testInt(@FirstInteger Integer first, @SecondInteger Integer second) {\n\t\tassertEquals(1, first);\n\t\tassertEquals(2, second);\n\t}\n\n\t@Target(ElementType.PARAMETER)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ExtendWith(FirstInteger.Extension.class)\n\tpublic @interface FirstInteger {\n\n\t\tclass Extension implements ParameterResolver {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\t\treturn parameterContext.getParameter().getType().equals(Integer.class)\n\t\t\t\t\t\t&& !parameterContext.isAnnotated(SecondInteger.class);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Target(ElementType.PARAMETER)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ExtendWith(SecondInteger.Extension.class)\n\tpublic @interface SecondInteger {\n\n\t\tclass Extension implements ParameterResolver {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\t\treturn parameterContext.isAnnotated(SecondInteger.class);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/ParameterResolverCustomTypeDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n// tag::user_guide[]\npublic class ParameterResolverCustomTypeDemo {\n\n\t@Test\n\t@ExtendWith({ FirstIntegerResolver.class, SecondIntegerResolver.class })\n\tvoid testInt(Integer i, WrappedInteger wrappedInteger) {\n\t\tassertEquals(1, i);\n\t\tassertEquals(2, wrappedInteger.value);\n\t}\n\n\tstatic class FirstIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType().equals(Integer.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tstatic class SecondIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType().equals(WrappedInteger.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn new WrappedInteger(2);\n\t\t}\n\t}\n\n\tstatic class WrappedInteger {\n\n\t\tprivate final int value;\n\n\t\tWrappedInteger(int value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/ParameterResolverNoConflictDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n// tag::user_guide[]\npublic class ParameterResolverNoConflictDemo {\n\n\t@Test\n\t@ExtendWith(FirstIntegerResolver.class)\n\tvoid firstResolution(int i) {\n\t\tassertEquals(1, i);\n\t}\n\n\t@Test\n\t@ExtendWith(SecondIntegerResolver.class)\n\tvoid secondResolution(int i) {\n\t\tassertEquals(2, i);\n\t}\n\n\tstatic class FirstIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == int.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\tstatic class SecondIntegerResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == int.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 2;\n\t\t}\n\t}\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/Random.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n//tag::user_guide[]\n@Target({ ElementType.FIELD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(RandomNumberExtension.class)\npublic @interface Random {\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/RandomNumberDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n// tag::user_guide[]\nclass RandomNumberDemo {\n\n\t// Use static randomNumber0 field anywhere in the test class,\n\t// including @BeforeAll or @AfterEach lifecycle methods.\n\t@Random\n\t// end::user_guide[]\n\t@Nullable\n\t// tag::user_guide[]\n\tprivate static Integer randomNumber0;\n\n\t// Use randomNumber1 field in test methods and @BeforeEach\n\t// or @AfterEach lifecycle methods.\n\t@Random\n\tprivate int randomNumber1;\n\n\tRandomNumberDemo(@Random int randomNumber2) {\n\t\t// Use randomNumber2 in constructor.\n\t}\n\n\t@BeforeEach\n\tvoid beforeEach(@Random int randomNumber3) {\n\t\t// Use randomNumber3 in @BeforeEach method.\n\t}\n\n\t@Test\n\tvoid test(@Random int randomNumber4) {\n\t\t// Use randomNumber4 in test method.\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/RandomNumberExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.extensions;\n\n// tag::user_guide[]\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;\n\nimport java.lang.reflect.Field;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.platform.commons.support.ModifierSupport;\n\n// end::user_guide[]\n// @formatter:off\n// tag::user_guide[]\nclass RandomNumberExtension\n\t\timplements BeforeAllCallback, TestInstancePostProcessor, ParameterResolver {\n\n\tprivate final java.util.Random random = new java.util.Random(System.nanoTime());\n\n\t/**\n\t * Inject a random integer into static fields that are annotated with\n\t * {@code @Random} and can be assigned an integer value.\n\t */\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\tClass<?> testClass = context.getRequiredTestClass();\n\t\tinjectFields(testClass, null, ModifierSupport::isStatic);\n\t}\n\n\t/**\n\t * Inject a random integer into non-static fields that are annotated with\n\t * {@code @Random} and can be assigned an integer value.\n\t */\n\t@Override\n\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\tClass<?> testClass = context.getRequiredTestClass();\n\t\tinjectFields(testClass, testInstance, ModifierSupport::isNotStatic);\n\t}\n\n\t/**\n\t * Determine if the parameter is annotated with {@code @Random} and can be\n\t * assigned an integer value.\n\t */\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {\n\t\treturn pc.isAnnotated(Random.class) && isInteger(pc.getParameter().getType());\n\t}\n\n\t/**\n\t * Resolve a random integer.\n\t */\n\t@Override\n\tpublic Integer resolveParameter(ParameterContext pc, ExtensionContext ec) {\n\t\treturn this.random.nextInt();\n\t}\n\n\tprivate void injectFields(Class<?> testClass, @Nullable Object testInstance,\n\t\t\tPredicate<Field> predicate) {\n\n\t\tpredicate = predicate.and(field -> isInteger(field.getType()));\n\t\tfindAnnotatedFields(testClass, Random.class, predicate)\n\t\t\t.forEach(field -> {\n\t\t\t\ttry {\n\t\t\t\t\tfield.setAccessible(true);\n\t\t\t\t\tfield.set(testInstance, this.random.nextInt());\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new RuntimeException(ex);\n\t\t\t\t}\n\t\t\t});\n\t}\n\n\tprivate static boolean isInteger(Class<?> type) {\n\t\treturn type == Integer.class || type == int.class;\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/extensions/package-info.java",
    "content": "\n@NullMarked\npackage example.extensions;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/interceptor/SwingEdtInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.interceptor;\n\nimport java.lang.reflect.Method;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport javax.swing.SwingUtilities;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\n\n// @formatter:off\n// tag::user_guide[]\npublic class SwingEdtInterceptor implements InvocationInterceptor {\n\n\t//end::user_guide[]\n\t@SuppressWarnings(\"NullAway\")\n\t//tag::user_guide[]\n\t@Override\n\tpublic void interceptTestMethod(Invocation<Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext,\n\t\t\tExtensionContext extensionContext) throws Throwable {\n\n\t\tAtomicReference<Throwable> throwable = new AtomicReference<>();\n\n\t\tSwingUtilities.invokeAndWait(() -> {\n\t\t\ttry {\n\t\t\t\tinvocation.proceed();\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrowable.set(t);\n\t\t\t}\n\t\t});\n\t\tThrowable t = throwable.get();\n\t\tif (t != null) {\n\t\t\tthrow t;\n\t\t}\n\t}\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/interceptor/package-info.java",
    "content": "\n@NullMarked\npackage example.interceptor;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/package-info.java",
    "content": "\n@NullMarked\npackage example;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/registration/DocumentationDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration;\n\nimport java.nio.file.Path;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n//tag::user_guide[]\nclass DocumentationDemo {\n\n\t//end::user_guide[]\n\t@Nullable\n\t//tag::user_guide[]\n\tstatic Path lookUpDocsDir() {\n\t\t// return path to docs dir\n\t\t// end::user_guide[]\n\t\treturn null;\n\t\t// tag::user_guide[]\n\t}\n\n\t@RegisterExtension\n\tDocumentationExtension docs = DocumentationExtension.forPath(lookUpDocsDir());\n\n\t@Test\n\tvoid generateDocumentation() {\n\t\t// use this.docs ...\n\t}\n}\n//end::user_guide[]\n\n@NullMarked\nclass DocumentationExtension implements AfterEachCallback {\n\n\t@SuppressWarnings(\"unused\")\n\tprivate final @Nullable Path path;\n\n\tprivate DocumentationExtension(@Nullable Path path) {\n\t\tthis.path = path;\n\t}\n\n\tstatic DocumentationExtension forPath(@Nullable Path path) {\n\t\treturn new DocumentationExtension(path);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\t/* no-op for demo */\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/registration/WebServerDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n// tag::user_guide[]\nclass WebServerDemo {\n\n\t// end::user_guide[]\n\t// @formatter:off\n\t// tag::user_guide[]\n\t@RegisterExtension\n\tstatic WebServerExtension server = WebServerExtension.builder()\n\t\t.enableSecurity(false)\n\t\t.build();\n\t// end::user_guide[]\n\t// @formatter:on\n\t// tag::user_guide[]\n\n\t@Test\n\tvoid getProductList() {\n\t\t// end::user_guide[]\n\t\t@SuppressWarnings(\"resource\")\n\t\t// tag::user_guide[]\n\t\tWebClient webClient = new WebClient();\n\t\tString serverUrl = server.getServerUrl();\n\t\t// Use WebClient to connect to web server using serverUrl and verify response\n\t\tassertEquals(200, webClient.get(serverUrl + \"/products\").getResponseStatus());\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/session/CloseableHttpServer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.session;\n\n//tag::user_guide[]\nimport java.util.concurrent.ExecutorService;\n\nimport com.sun.net.httpserver.HttpServer;\n\npublic class CloseableHttpServer implements AutoCloseable {\n\n\tprivate final HttpServer server;\n\tprivate final ExecutorService executorService;\n\n\tCloseableHttpServer(HttpServer server, ExecutorService executorService) {\n\t\tthis.server = server;\n\t\tthis.executorService = executorService;\n\t}\n\n\tpublic HttpServer getServer() {\n\t\treturn server;\n\t}\n\n\t@Override\n\tpublic void close() { // <1>\n\t\tserver.stop(0); // <2>\n\t\texecutorService.shutdownNow();\n\t}\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/session/GlobalSetupTeardownListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.session;\n\n//tag::user_guide[]\nimport static java.net.InetAddress.getLoopbackAddress;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.InetSocketAddress;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport com.sun.net.httpserver.HttpServer;\n\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\npublic class GlobalSetupTeardownListener implements LauncherSessionListener {\n\n\t@Override\n\tpublic void launcherSessionOpened(LauncherSession session) {\n\t\t// Avoid setup for test discovery by delaying it until tests are about to be executed\n\t\tsession.getLauncher().registerTestExecutionListeners(new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\t\t\t//end::user_guide[]\n\t\t\t\tif (!testPlan.getConfigurationParameters().getBoolean(\"enableHttpServer\").orElse(true)) {\n\t\t\t\t\t// avoid starting multiple HTTP servers unnecessarily from UsingTheLauncherDemo\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//tag::user_guide[]\n\t\t\t\tNamespacedHierarchicalStore<Namespace> store = session.getStore(); // <1>\n\t\t\t\tstore.computeIfAbsent(Namespace.GLOBAL, \"httpServer\", key -> { // <2>\n\t\t\t\t\tInetSocketAddress address = new InetSocketAddress(getLoopbackAddress(), 0);\n\t\t\t\t\tHttpServer server;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tserver = HttpServer.create(address, 0);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (IOException e) {\n\t\t\t\t\t\tthrow new UncheckedIOException(\"Failed to start HTTP server\", e);\n\t\t\t\t\t}\n\t\t\t\t\tserver.createContext(\"/test\", exchange -> {\n\t\t\t\t\t\texchange.sendResponseHeaders(204, -1);\n\t\t\t\t\t\texchange.close();\n\t\t\t\t\t});\n\t\t\t\t\tExecutorService executorService = Executors.newCachedThreadPool();\n\t\t\t\t\tserver.setExecutor(executorService);\n\t\t\t\t\tserver.start(); // <3>\n\n\t\t\t\t\treturn new CloseableHttpServer(server, executorService);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/session/HttpTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.session;\n\n//tag::user_guide[]\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\nimport java.net.URI;\nimport java.net.URL;\n\nimport com.sun.net.httpserver.HttpServer;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n@ExtendWith(HttpServerParameterResolver.class)\nclass HttpTests {\n\n\t@Test\n\tvoid respondsWith204(HttpServer server) throws IOException {\n\t\tString host = server.getAddress().getHostString(); // <2>\n\t\tint port = server.getAddress().getPort(); // <3>\n\t\tURL url = URI.create(\"http://\" + host + \":\" + port + \"/test\").toURL();\n\n\t\tHttpURLConnection connection = (HttpURLConnection) url.openConnection();\n\t\tconnection.setRequestMethod(\"GET\");\n\t\tint responseCode = connection.getResponseCode(); // <4>\n\n\t\tassertEquals(204, responseCode); // <5>\n\t}\n}\n\nclass HttpServerParameterResolver implements ParameterResolver {\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn HttpServer.class.equals(parameterContext.getParameter().getType());\n\t}\n\n\t//end::user_guide[]\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t//tag::user_guide[]\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn extensionContext\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.getStore(ExtensionContext.Namespace.GLOBAL)\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.get(\"httpServer\", CloseableHttpServer.class) // <1>\n\t\t\t\t.getServer();\n\t}\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/session/package-info.java",
    "content": "\n@NullMarked\npackage example.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/sharedresources/ChildrenSharedResourcesDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.sharedresources;\n\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.jupiter.api.parallel.ResourceLockTarget.CHILDREN;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.ResourceLock;\n\n// tag::user_guide[]\n@Execution(CONCURRENT)\n@ResourceLock(value = \"a\", mode = READ, target = CHILDREN)\npublic class ChildrenSharedResourcesDemo {\n\n\t@ResourceLock(value = \"a\", mode = READ_WRITE)\n\t@Test\n\tvoid test1() throws InterruptedException {\n\t\tThread.sleep(2000L);\n\t}\n\n\t@Test\n\tvoid test2() throws InterruptedException {\n\t\tThread.sleep(2000L);\n\t}\n\n\t@Test\n\tvoid test3() throws InterruptedException {\n\t\tThread.sleep(2000L);\n\t}\n\n\t@Test\n\tvoid test4() throws InterruptedException {\n\t\tThread.sleep(2000L);\n\t}\n\n\t@Test\n\tvoid test5() throws InterruptedException {\n\t\tThread.sleep(2000L);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/sharedresources/DynamicSharedResourcesDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.sharedresources;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\n\n// tag::user_guide[]\n@Execution(CONCURRENT)\n@ResourceLock(providers = DynamicSharedResourcesDemo.Provider.class)\nclass DynamicSharedResourcesDemo {\n\n\tprivate Properties backup;\n\n\t@BeforeEach\n\tvoid backup() {\n\t\tbackup = new Properties();\n\t\tbackup.putAll(System.getProperties());\n\t}\n\n\t@AfterEach\n\tvoid restore() {\n\t\tSystem.setProperties(backup);\n\t}\n\n\t@Test\n\tvoid customPropertyIsNotSetByDefault() {\n\t\tassertNull(System.getProperty(\"my.prop\"));\n\t}\n\n\t@Test\n\tvoid canSetCustomPropertyToApple() {\n\t\tSystem.setProperty(\"my.prop\", \"apple\");\n\t\tassertEquals(\"apple\", System.getProperty(\"my.prop\"));\n\t}\n\n\t@Test\n\tvoid canSetCustomPropertyToBanana() {\n\t\tSystem.setProperty(\"my.prop\", \"banana\");\n\t\tassertEquals(\"banana\", System.getProperty(\"my.prop\"));\n\t}\n\n\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t@Override\n\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\tResourceAccessMode mode = testMethod.getName().startsWith(\"canSet\") ? READ_WRITE : READ;\n\t\t\treturn Set.of(new Lock(SYSTEM_PROPERTIES, mode));\n\t\t}\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/sharedresources/SharedResourceDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.sharedresources;\n\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\n\nimport example.FirstCustomEngine;\nimport example.SecondCustomEngine;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherConfig;\nimport org.junit.platform.launcher.core.LauncherFactory;\n\nclass SharedResourceDemo {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t//tag::user_guide[]\n\t@Test\n\tvoid runBothCustomEnginesTest() {\n\t\tFirstCustomEngine firstCustomEngine = new FirstCustomEngine();\n\t\tSecondCustomEngine secondCustomEngine = new SecondCustomEngine();\n\n\t\tLauncherConfig launcherConfig = LauncherConfig.builder()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.addTestEngines(firstCustomEngine, secondCustomEngine)\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.enableTestEngineAutoRegistration(false)\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.build();\n\n\t\tLauncherDiscoveryRequest discoveryRequest = discoveryRequest()\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.selectors(selectPackage(\"com.example.mytests\"))\n\t\t\t\t// tag::custom_line_break[]\n\t\t\t\t.build();\n\n\t\tLauncher launcher = LauncherFactory.create(launcherConfig);\n\t\tlauncher.execute(discoveryRequest);\n\n\t\tassertSame(firstCustomEngine.getSocket(), secondCustomEngine.getSocket());\n\t\tassertTrue(firstCustomEngine.getSocket().isClosed(), \"socket should be closed\");\n\t}\n\t//end::user_guide[]\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/example/sharedresources/StaticSharedResourcesDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.sharedresources;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES;\n\nimport java.util.Properties;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.ResourceLock;\n\n// tag::user_guide[]\n@Execution(CONCURRENT)\nclass StaticSharedResourcesDemo {\n\n\tprivate Properties backup;\n\n\t@BeforeEach\n\tvoid backup() {\n\t\tbackup = new Properties();\n\t\tbackup.putAll(System.getProperties());\n\t}\n\n\t@AfterEach\n\tvoid restore() {\n\t\tSystem.setProperties(backup);\n\t}\n\n\t@Test\n\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)\n\tvoid customPropertyIsNotSetByDefault() {\n\t\tassertNull(System.getProperty(\"my.prop\"));\n\t}\n\n\t@Test\n\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\tvoid canSetCustomPropertyToApple() {\n\t\tSystem.setProperty(\"my.prop\", \"apple\");\n\t\tassertEquals(\"apple\", System.getProperty(\"my.prop\"));\n\t}\n\n\t@Test\n\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\tvoid canSetCustomPropertyToBanana() {\n\t\tSystem.setProperty(\"my.prop\", \"banana\");\n\t\tassertEquals(\"banana\", System.getProperty(\"my.prop\"));\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/sharedresources/package-info.java",
    "content": "\n@NullMarked\npackage example.sharedresources;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/testinterface/TestInterfaceDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testinterface;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\n\n// @formatter:off\n// tag::user_guide[]\nclass TestInterfaceDemo implements TestLifecycleLogger,\n\t\tTimeExecutionLogger, TestInterfaceDynamicTestsDemo {\n\n\t@Test\n\tvoid isEqualValue() {\n\t\tassertEquals(1, \"a\".length(), \"is always equal\");\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testinterface/TestInterfaceDynamicTestsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testinterface;\n\nimport static example.util.StringUtils.isPalindrome;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\n// @formatter:off\n// tag::user_guide[]\ninterface TestInterfaceDynamicTestsDemo {\n\n\t@TestFactory\n\tdefault Stream<DynamicTest> dynamicTestsForPalindromes() {\n\t\treturn Stream.of(\"racecar\", \"radar\", \"mom\", \"dad\")\n\t\t\t.map(text -> dynamicTest(text, () -> assertTrue(isPalindrome(text))));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testinterface/TestLifecycleLogger.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testinterface;\n\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n// @formatter:off\n// tag::user_guide[]\n@TestInstance(Lifecycle.PER_CLASS)\ninterface TestLifecycleLogger {\n\n\tLogger logger = Logger.getLogger(TestLifecycleLogger.class.getName());\n\n\t@BeforeAll\n\tdefault void beforeAllTests() {\n\t\tlogger.info(\"Before all tests\");\n\t}\n\n\t@AfterAll\n\tdefault void afterAllTests() {\n\t\tlogger.info(\"After all tests\");\n\t}\n\n\t@BeforeEach\n\tdefault void beforeEachTest(TestInfo testInfo) {\n\t\tlogger.info(() -> \"About to execute [%s]\".formatted(\n\t\t\ttestInfo.getDisplayName()));\n\t}\n\n\t@AfterEach\n\tdefault void afterEachTest(TestInfo testInfo) {\n\t\tlogger.info(() -> \"Finished executing [%s]\".formatted(\n\t\t\ttestInfo.getDisplayName()));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testinterface/TimeExecutionLogger.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testinterface;\n\nimport example.timing.TimingExtension;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n//tag::user_guide[]\n@Tag(\"timed\")\n@ExtendWith(TimingExtension.class)\ninterface TimeExecutionLogger {\n}\n//end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/testinterface/package-info.java",
    "content": "\n@NullMarked\npackage example.testinterface;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/EngineTestKitAllEventsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testkit;\n\n// @formatter:off\n// tag::user_guide[]\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.abortedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.io.StringWriter;\nimport java.io.Writer;\n\nimport example.ExampleTestCase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.opentest4j.TestAbortedException;\n\nclass EngineTestKitAllEventsDemo {\n\n\t@Test\n\tvoid verifyAllJupiterEvents() {\n\t\tWriter writer = // create a java.io.Writer for debug output\n\t\t// end::user_guide[]\n\t\t\t\t// For the demo, we are swallowing the debug output.\n\t\t\t\tnew StringWriter();\n\t\t// tag::user_guide[]\n\n\t\tEngineTestKit.engine(\"junit-jupiter\") // <1>\n\t\t\t.selectors(selectClass(ExampleTestCase.class)) // <2>\n\t\t\t.execute() // <3>\n\t\t\t.allEvents() // <4>\n\t\t\t.debug(writer) // <5>\n\t\t\t.assertEventsMatchExactly( // <6>\n\t\t\t\tevent(engine(), started()),\n\t\t\t\tevent(container(ExampleTestCase.class), started()),\n\t\t\t\tevent(test(\"skippedTest\"), skippedWithReason(\"for demonstration purposes\")),\n\t\t\t\tevent(test(\"succeedingTest\"), started()),\n\t\t\t\tevent(test(\"succeedingTest\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"abortedTest\"), started()),\n\t\t\t\tevent(test(\"abortedTest\"),\n\t\t\t\t\tabortedWithReason(instanceOf(TestAbortedException.class),\n\t\t\t\t\t\tmessage(m -> m.contains(\"abc does not contain Z\")))),\n\t\t\t\tevent(test(\"failingTest\"), started()),\n\t\t\t\tevent(test(\"failingTest\"), finishedWithFailure(\n\t\t\t\t\tinstanceOf(ArithmeticException.class), message(it -> it.endsWith(\"by zero\")))),\n\t\t\t\tevent(container(ExampleTestCase.class), finishedSuccessfully()),\n\t\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/EngineTestKitDiscoveryDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testkit;\n\n// tag::user_guide[]\nimport static java.util.Collections.emptyList;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport example.ExampleTestCase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineDiscoveryResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\nclass EngineTestKitDiscoveryDemo {\n\n\t@Test\n\tvoid verifyJupiterDiscovery() {\n\t\tEngineDiscoveryResults results = EngineTestKit.engine(\"junit-jupiter\") // <1>\n\t\t\t\t.selectors(selectClass(ExampleTestCase.class)) // <2>\n\t\t\t\t.discover(); // <3>\n\n\t\tassertEquals(\"JUnit Jupiter\", results.getEngineDescriptor().getDisplayName()); // <4>\n\t\tassertEquals(emptyList(), results.getDiscoveryIssues()); // <5>\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/EngineTestKitFailedMethodDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testkit;\n\n// @formatter:off\n// tag::user_guide[]\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport example.ExampleTestCase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\nclass EngineTestKitFailedMethodDemo {\n\n\t@Test\n\tvoid verifyJupiterMethodFailed() {\n\t\tEngineTestKit.engine(\"junit-jupiter\") // <1>\n\t\t\t.selectors(selectClass(ExampleTestCase.class)) // <2>\n\t\t\t.execute() // <3>\n\t\t\t.testEvents() // <4>\n\t\t\t.assertThatEvents().haveExactly(1, // <5>\n\t\t\t\tevent(test(\"failingTest\"),\n\t\t\t\t\tfinishedWithFailure(\n\t\t\t\t\t\tinstanceOf(ArithmeticException.class), message(it -> it.endsWith(\"by zero\")))));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/EngineTestKitSkippedMethodDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testkit;\n\n// @formatter:off\n// tag::user_guide[]\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport example.ExampleTestCase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\n\nclass EngineTestKitSkippedMethodDemo {\n\n\t@Test\n\tvoid verifyJupiterMethodWasSkipped() {\n\t\tString methodName = \"skippedTest\";\n\n\t\tEvents testEvents = EngineTestKit // <5>\n\t\t\t.engine(\"junit-jupiter\") // <1>\n\t\t\t.selectors(selectMethod(ExampleTestCase.class, methodName)) // <2>\n\t\t\t.execute() // <3>\n\t\t\t.testEvents(); // <4>\n\n\t\ttestEvents.assertStatistics(stats -> stats.skipped(1)); // <6>\n\n\t\ttestEvents.assertThatEvents() // <7>\n\t\t\t.haveExactly(1, event(test(methodName),\n\t\t\t\tskippedWithReason(\"for demonstration purposes\")));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/EngineTestKitStatisticsDemo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.testkit;\n\n// @formatter:off\n// tag::user_guide[]\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport example.ExampleTestCase;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\nclass EngineTestKitStatisticsDemo {\n\n\t@Test\n\tvoid verifyJupiterContainerStats() {\n\t\tEngineTestKit\n\t\t\t.engine(\"junit-jupiter\") // <1>\n\t\t\t.selectors(selectClass(ExampleTestCase.class)) // <2>\n\t\t\t.execute() // <3>\n\t\t\t.containerEvents() // <4>\n\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2)); // <5>\n\t}\n\n\t@Test\n\tvoid verifyJupiterTestStats() {\n\t\tEngineTestKit\n\t\t\t.engine(\"junit-jupiter\") // <1>\n\t\t\t.selectors(selectClass(ExampleTestCase.class)) // <2>\n\t\t\t.execute() // <3>\n\t\t\t.testEvents() // <6>\n\t\t\t.assertStatistics(stats ->\n\t\t\t\tstats.skipped(1).started(3).succeeded(1).aborted(1).failed(1)); // <7>\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/testkit/package-info.java",
    "content": "\n@NullMarked\npackage example.testkit;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/example/timing/TimingExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.timing;\n\n// tag::user_guide[]\nimport java.lang.reflect.Method;\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\n\n// end::user_guide[]\n/**\n * Simple extension that <em>times</em> the execution of test methods and\n * logs the results at {@code INFO} level.\n *\n * @since 5.0\n */\n// @formatter:off\n// tag::user_guide[]\npublic class TimingExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {\n\n\tprivate static final Logger logger = Logger.getLogger(TimingExtension.class.getName());\n\n\tprivate static final String START_TIME = \"start time\";\n\n\t@Override\n\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\tgetStore(context).put(START_TIME, System.currentTimeMillis());\n\t}\n\n\t//end::user_guide[]\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t//tag::user_guide[]\n\t@Override\n\tpublic void afterTestExecution(ExtensionContext context) {\n\t\tMethod testMethod = context.getRequiredTestMethod();\n\t\tlong startTime = getStore(context).remove(START_TIME, long.class);\n\t\tlong duration = System.currentTimeMillis() - startTime;\n\n\t\tlogger.info(() ->\n\t\t\t\"Method [%s] took %s ms.\".formatted(testMethod.getName(), duration));\n\t}\n\n\tprivate Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));\n\t}\n\n}\n// end::user_guide[]\n// @formatter:on\n"
  },
  {
    "path": "documentation/src/test/java/example/timing/TimingExtensionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.timing;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * Tests that demonstrate the example {@link TimingExtension}.\n *\n * @since 5.0\n */\n// tag::user_guide[]\n@ExtendWith(TimingExtension.class)\nclass TimingExtensionTests {\n\n\t@Test\n\tvoid sleep20ms() throws Exception {\n\t\tThread.sleep(20);\n\t}\n\n\t@Test\n\tvoid sleep50ms() throws Exception {\n\t\tThread.sleep(50);\n\t}\n\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/java/example/timing/package-info.java",
    "content": "\n@NullMarked\npackage example.timing;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/java/extensions/DisabledOnOpenJ9.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage extensions;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.condition.DisabledIfSystemProperty;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@DisabledIfSystemProperty(named = \"java.vm.vendor\", matches = \".*OpenJ9.*\")\npublic @interface DisabledOnOpenJ9 {\n}\n"
  },
  {
    "path": "documentation/src/test/java/extensions/ExpectToFail.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage extensions;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(ExpectToFail.Extension.class)\npublic @interface ExpectToFail {\n\n\tclass Extension implements TestExecutionExceptionHandler, AfterEachCallback {\n\n\t\tprivate static final String KEY = \"exception\";\n\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\t\tgetExceptionStore(context).put(KEY, throwable);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) throws Exception {\n\t\t\tassertNotNull(getExceptionStore(context).get(KEY), \"Test should have failed\");\n\t\t}\n\n\t\tprivate Store getExceptionStore(ExtensionContext context) {\n\t\t\treturn context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/test/java/extensions/package-info.java",
    "content": "\n@NullMarked\npackage extensions;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/FibonacciCalculator.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example\n\nclass FibonacciCalculator {\n    private val fibonacci =\n        sequence {\n            yield(0) // 0th Fibonacci number\n            yield(1) // first Fibonacci number\n            var cur = 1\n            var next = 1\n            while (true) {\n                yield(next) // next Fibonacci number\n                val tmp = cur + next\n                cur = next\n                next = tmp\n            }\n        }\n\n    fun fib(fibonacciNumber: Int) = fibonacci.elementAt(fibonacciNumber)\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/KotlinAssertionsDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example\n\n// tag::user_guide[]\n\nimport example.domain.Person\nimport example.util.Calculator\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertAll\nimport org.junit.jupiter.api.assertDoesNotThrow\nimport org.junit.jupiter.api.assertInstanceOf\nimport org.junit.jupiter.api.assertNotNull\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.assertTimeout\nimport org.junit.jupiter.api.assertTimeoutPreemptively\nimport java.time.Duration\n\nclass KotlinAssertionsDemo {\n    private val person = Person(\"Jane\", \"Doe\")\n    private val people = setOf(person, Person(\"John\", \"Doe\"))\n\n    @Test\n    fun `exception absence testing`() {\n        val calculator = Calculator()\n        val result =\n            assertDoesNotThrow(\"Should not throw an exception\") {\n                calculator.divide(0, 1)\n            }\n        assertEquals(0, result)\n    }\n\n    // end::user_guide[]\n    @extensions.DisabledOnOpenJ9\n    // tag::user_guide[]\n    @Test\n    fun `expected exception testing`() {\n        val calculator = Calculator()\n        val exception =\n            assertThrows<ArithmeticException> (\"Should throw an exception\") {\n                calculator.divide(1, 0)\n            }\n        assertEquals(\"/ by zero\", exception.message)\n    }\n\n    @Test\n    fun `grouped assertions`() {\n        assertAll(\n            \"Person properties\",\n            { assertEquals(\"Jane\", person.firstName) },\n            { assertEquals(\"Doe\", person.lastName) }\n        )\n    }\n\n    @Test\n    fun `grouped assertions from a stream`() {\n        assertAll(\n            \"People with first name starting with J\",\n            people\n                .stream()\n                .map {\n                    // This mapping returns Stream<() -> Unit>\n                    { assertTrue(it.firstName.startsWith(\"J\")) }\n                }\n        )\n    }\n\n    @Test\n    fun `grouped assertions from a collection`() {\n        assertAll(\n            \"People with last name of Doe\",\n            people.map { { assertEquals(\"Doe\", it.lastName) } }\n        )\n    }\n\n    // end::user_guide[]\n    @Tag(\"timeout\")\n    // tag::user_guide[]\n    @Test\n    fun `timeout not exceeded testing`() {\n        val fibonacciCalculator = FibonacciCalculator()\n        val result =\n            assertTimeout(Duration.ofMillis(1000)) {\n                fibonacciCalculator.fib(14)\n            }\n        assertEquals(377, result)\n    }\n\n    // end::user_guide[]\n    @Tag(\"timeout\")\n    @extensions.ExpectToFail\n    // tag::user_guide[]\n    @Test\n    fun `timeout exceeded with preemptive termination`() {\n        // The following assertion fails with an error message similar to:\n        // execution timed out after 10 ms\n        assertTimeoutPreemptively(Duration.ofMillis(10)) {\n            // Simulate task that takes more than 10 ms.\n            Thread.sleep(100)\n        }\n    }\n\n    @Test\n    fun `assertNotNull with a smart cast`() {\n        val nullablePerson: Person? = person\n\n        assertNotNull(nullablePerson)\n\n        // The compiler smart casts nullablePerson to a non-nullable object.\n        // The safe call operator (?.) isn't required.\n        assertEquals(person.firstName, nullablePerson.firstName)\n        assertEquals(person.lastName, nullablePerson.lastName)\n    }\n\n    @Test\n    fun `assertInstanceOf with a smart cast`() {\n        val maybePerson: Any = person\n\n        assertInstanceOf<Person>(maybePerson)\n\n        // The compiler smart casts maybePerson to a Person object,\n        // allowing to access the Person properties.\n        assertEquals(person.firstName, maybePerson.firstName)\n        assertEquals(person.lastName, maybePerson.lastName)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/KotlinCoroutinesDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n// end::user_guide[]\n// @formatter:off\n@Suppress(\"JUnitMalformedDeclaration\")\n// tag::user_guide[]\nclass KotlinCoroutinesDemo {\n    // end::user_guide[]\n    // @formatter:on\n    // tag::user_guide[]\n    @BeforeEach\n    fun regularSetUp() {\n    }\n\n    @BeforeEach\n    suspend fun coroutineSetUp() {\n    }\n\n    @Test\n    fun regularTest() {\n    }\n\n    @Test\n    suspend fun coroutineTest() {\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/KotlinCoroutinesRunTestDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example\n\n// tag::user_guide[]\nimport kotlinx.coroutines.test.runTest\nimport org.junit.jupiter.api.Test\n\nclass KotlinCoroutinesRunTestDemo {\n    // end::user_guide[]\n    // @formatter:off\n    // tag::user_guide[]\n    @Test\n    fun coroutineTestUsingRunTest() = runTest {\n        // ...\n    }\n    // end::user_guide[]\n    // @formatter:on\n    // tag::user_guide[]\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/AssumptionsDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\n\nimport example.util.Calculator\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.Assumptions.assumingThat\nimport org.junit.jupiter.api.Test\n\nclass AssumptionsDemo {\n    private val calculator = Calculator()\n\n    @Test\n    fun testOnlyOnCiServer() {\n        assumeTrue(System.getenv(\"ENV\") == \"CI\")\n        // remainder of test\n    }\n\n    @Test\n    fun testOnlyOnDeveloperWorkstation() {\n        assumeTrue(System.getenv(\"ENV\") == \"DEV\") {\n            \"Aborting test: not on developer workstation\"\n        }\n        // remainder of test\n    }\n\n    @Test\n    fun testInAllEnvironments() {\n        assumingThat(System.getenv(\"ENV\") == \"CI\") {\n            // perform these assertions only on the CI server\n            assertEquals(2, calculator.divide(4, 2))\n        }\n\n        // perform these assertions in all environments\n        assertEquals(42, calculator.multiply(6, 7))\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/AutoCloseDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport example.registration.WebClient\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.AutoClose\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide_example[]\nclass AutoCloseDemo {\n    @AutoClose // <1>\n    val webClient = WebClient() // <2>\n\n    val serverUrl = // specify server URL ...\n        // end::user_guide_example[]\n        \"https://localhost\"\n    // tag::user_guide_example[]\n\n    @Test\n    fun getProductList() {\n        // Use WebClient to connect to web server and verify response\n        assertEquals(200, webClient.get(\"$serverUrl/products\").responseStatus)\n    }\n}\n// end::user_guide_example[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/ConditionalTestExecutionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.condition.DisabledForJreRange\nimport org.junit.jupiter.api.condition.DisabledIf\nimport org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\nimport org.junit.jupiter.api.condition.DisabledIfSystemProperty\nimport org.junit.jupiter.api.condition.DisabledInNativeImage\nimport org.junit.jupiter.api.condition.DisabledOnJre\nimport org.junit.jupiter.api.condition.DisabledOnOs\nimport org.junit.jupiter.api.condition.EnabledForJreRange\nimport org.junit.jupiter.api.condition.EnabledIf\nimport org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\nimport org.junit.jupiter.api.condition.EnabledIfSystemProperty\nimport org.junit.jupiter.api.condition.EnabledInNativeImage\nimport org.junit.jupiter.api.condition.EnabledOnJre\nimport org.junit.jupiter.api.condition.EnabledOnOs\nimport org.junit.jupiter.api.condition.JRE.JAVA_17\nimport org.junit.jupiter.api.condition.JRE.JAVA_18\nimport org.junit.jupiter.api.condition.JRE.JAVA_19\nimport org.junit.jupiter.api.condition.JRE.JAVA_21\nimport org.junit.jupiter.api.condition.JRE.JAVA_25\nimport org.junit.jupiter.api.condition.OS.LINUX\nimport org.junit.jupiter.api.condition.OS.MAC\nimport org.junit.jupiter.api.condition.OS.WINDOWS\n\nclass ConditionalTestExecutionDemo {\n    // tag::user_guide_os[]\n    @Test\n    @EnabledOnOs(MAC)\n    fun onlyOnMacOs() {\n        // ...\n    }\n\n    @TestOnMac\n    fun testOnMac() {\n        // ...\n    }\n\n    @Test\n    @EnabledOnOs(LINUX, MAC)\n    fun onLinuxOrMac() {\n        // ...\n    }\n\n    @Test\n    @DisabledOnOs(WINDOWS)\n    fun notOnWindows() {\n        // ...\n    }\n\n    @Target(AnnotationTarget.FUNCTION)\n    @Retention(AnnotationRetention.RUNTIME)\n    @Test\n    @EnabledOnOs(MAC)\n    annotation class TestOnMac\n    // end::user_guide_os[]\n\n    // tag::user_guide_architecture[]\n    @Test\n    @EnabledOnOs(architectures = [\"aarch64\"])\n    fun onAarch64() {\n        // ...\n    }\n\n    @Test\n    @DisabledOnOs(architectures = [\"x86_64\"])\n    fun notOnX86_64() {\n        // ...\n    }\n\n    @Test\n    @EnabledOnOs(value = [MAC], architectures = [\"aarch64\"])\n    fun onNewMacs() {\n        // ...\n    }\n\n    @Test\n    @DisabledOnOs(value = [MAC], architectures = [\"aarch64\"])\n    fun notOnNewMacs() {\n        // ...\n    }\n    // end::user_guide_architecture[]\n\n    // tag::user_guide_jre[]\n    @Test\n    @EnabledOnJre(JAVA_17)\n    fun onlyOnJava17() {\n        // ...\n    }\n\n    @Test\n    @EnabledOnJre(JAVA_17, JAVA_21)\n    fun onJava17And21() {\n        // ...\n    }\n\n    @Test\n    @EnabledForJreRange(min = JAVA_21, max = JAVA_25)\n    fun fromJava21To25() {\n        // ...\n    }\n\n    @Test\n    @EnabledForJreRange(min = JAVA_21)\n    fun onJava21ndHigher() {\n        // ...\n    }\n\n    @Test\n    @EnabledForJreRange(max = JAVA_18)\n    fun fromJava17To18() {\n        // ...\n    }\n\n    @Test\n    @DisabledOnJre(JAVA_19)\n    fun notOnJava19() {\n        // ...\n    }\n\n    @Test\n    @DisabledForJreRange(min = JAVA_17, max = JAVA_17)\n    fun notFromJava17To19() {\n        // ...\n    }\n\n    @Test\n    @DisabledForJreRange(min = JAVA_19)\n    fun notOnJava19AndHigher() {\n        // ...\n    }\n\n    @Test\n    @DisabledForJreRange(max = JAVA_18)\n    fun notFromJava17To18() {\n        // ...\n    }\n    // end::user_guide_jre[]\n\n    // tag::user_guide_jre_arbitrary_versions[]\n    @Test\n    @EnabledOnJre(versions = [26])\n    fun onlyOnJava26() {\n        // ...\n    }\n\n    // Can also be expressed as follows.\n    // @EnabledOnJre(value = [JAVA_25], versions = [26])\n    @Test\n    @EnabledOnJre(versions = [25, 26])\n    fun onJava25And26() {\n        // ...\n    }\n\n    @Test\n    @EnabledForJreRange(minVersion = 26)\n    fun onJava26AndHigher() {\n        // ...\n    }\n\n    // Can also be expressed as follows.\n    // @EnabledForJreRange(min = JAVA_25, maxVersion = 27)\n    @Test\n    @EnabledForJreRange(minVersion = 25, maxVersion = 27)\n    fun fromJava25To27() {\n        // ...\n    }\n\n    @Test\n    @DisabledOnJre(versions = [26])\n    fun notOnJava26() {\n        // ...\n    }\n\n    // Can also be expressed as follows.\n    // @DisabledOnJre(value = [JAVA_25], versions = [26])\n    @Test\n    @DisabledOnJre(versions = [25, 26])\n    fun notOnJava25And26() {\n        // ...\n    }\n\n    @Test\n    @DisabledForJreRange(minVersion = 26)\n    fun notOnJava26AndHigher() {\n        // ...\n    }\n\n    // Can also be expressed as follows.\n    // @DisabledForJreRange(min = JAVA_25, maxVersion = 27)\n    @Test\n    @DisabledForJreRange(minVersion = 25, maxVersion = 27)\n    fun notFromJava25To27() {\n        // ...\n    }\n    // end::user_guide_jre_arbitrary_versions[]\n\n    // tag::user_guide_native[]\n    @Test\n    @EnabledInNativeImage\n    fun onlyWithinNativeImage() {\n        // ...\n    }\n\n    @Test\n    @DisabledInNativeImage\n    fun neverWithinNativeImage() {\n        // ...\n    }\n    // end::user_guide_native[]\n\n    // tag::user_guide_system_property[]\n    @Test\n    @EnabledIfSystemProperty(named = \"os.arch\", matches = \".*64.*\")\n    fun onlyOn64BitArchitectures() {\n        // ...\n    }\n\n    @Test\n    @DisabledIfSystemProperty(named = \"ci-server\", matches = \"true\")\n    fun notOnCiServer() {\n        // ...\n    }\n    // end::user_guide_system_property[]\n\n    // tag::user_guide_environment_variable[]\n    @Test\n    @EnabledIfEnvironmentVariable(named = \"ENV\", matches = \"staging-server\")\n    fun onlyOnStagingServer() {\n        // ...\n    }\n\n    @Test\n    @DisabledIfEnvironmentVariable(named = \"ENV\", matches = \".*development.*\")\n    fun notOnDeveloperWorkstation() {\n        // ...\n    }\n    // end::user_guide_environment_variable[]\n\n    // tag::user_guide_custom[]\n    @Test\n    @EnabledIf(\"customCondition\")\n    fun enabled() {\n        // ...\n    }\n\n    @Test\n    @DisabledIf(\"customCondition\")\n    fun disabled() {\n        // ...\n    }\n\n    fun customCondition(): Boolean = true\n    // end::user_guide_custom[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/DefaultLocaleTimezoneExtensionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.util.DefaultLocale\nimport org.junit.jupiter.api.util.DefaultTimeZone\nimport org.junit.jupiter.api.util.LocaleProvider\nimport org.junit.jupiter.api.util.TimeZoneProvider\nimport java.time.ZoneOffset\nimport java.util.Locale\nimport java.util.TimeZone\n\nclass DefaultLocaleTimezoneExtensionDemo {\n    // tag::default_locale_language[]\n    @Test\n    @DefaultLocale(\"zh-Hant-TW\")\n    fun test_with_language() {\n        assertThat(Locale.getDefault()).isEqualTo(Locale.forLanguageTag(\"zh-Hant-TW\"))\n    }\n    // end::default_locale_language[]\n\n    // tag::default_locale_language_alternatives[]\n    @Test\n    @DefaultLocale(language = \"en\")\n    fun test_with_language_only() {\n        assertThat(Locale.getDefault()).isEqualTo(Locale.Builder().setLanguage(\"en\").build())\n    }\n\n    @Test\n    @DefaultLocale(language = \"en\", country = \"EN\")\n    fun test_with_language_and_country() {\n        assertThat(Locale.getDefault()).isEqualTo(\n            Locale\n                .Builder()\n                .setLanguage(\"en\")\n                .setRegion(\"EN\")\n                .build()\n        )\n    }\n\n    @Test\n    @DefaultLocale(language = \"ja\", country = \"JP\", variant = \"japanese\")\n    fun test_with_language_and_country_and_vairant() {\n        assertThat(Locale.getDefault()).isEqualTo(\n            Locale\n                .Builder()\n                .setLanguage(\"ja\")\n                .setRegion(\"JP\")\n                .setVariant(\"japanese\")\n                .build()\n        )\n    }\n    // end::default_locale_language_alternatives[]\n\n    // @formatter:off\n    @Nested\n    // tag::default_locale_class_level[]\n    @DefaultLocale(language = \"fr\")\n    // end::default_locale_class_level[]\n    inner\n    // tag::default_locale_class_level[]\n    class MyLocaleTests {\n        // end::default_locale_class_level[]\n        // @formatter:on\n        // tag::default_locale_class_level[]\n        @Test\n        fun test_with_class_level_configuration() {\n            assertThat(Locale.getDefault()).isEqualTo(Locale.Builder().setLanguage(\"fr\").build())\n        }\n\n        @Test\n        @DefaultLocale(language = \"en\")\n        fun test_with_method_level_configuration() {\n            assertThat(Locale.getDefault()).isEqualTo(Locale.Builder().setLanguage(\"en\").build())\n        }\n    }\n    // end::default_locale_class_level[]\n\n    // tag::default_locale_with_provider[]\n    @Test\n    @DefaultLocale(localeProvider = EnglishProvider::class)\n    fun test_with_locale_provider() {\n        assertThat(Locale.getDefault()).isEqualTo(Locale.Builder().setLanguage(\"en\").build())\n    }\n\n    class EnglishProvider : LocaleProvider {\n        override fun get(): Locale = Locale.ENGLISH\n    }\n    // end::default_locale_with_provider[]\n\n    // tag::default_timezone_zone[]\n    @Test\n    @DefaultTimeZone(\"CET\")\n    fun test_with_short_zone_id() {\n        assertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"CET\"))\n    }\n\n    @Test\n    @DefaultTimeZone(\"Africa/Juba\")\n    fun test_with_long_zone_id() {\n        assertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"Africa/Juba\"))\n    }\n    // end::default_timezone_zone[]\n\n    // @formatter:off\n    @Nested\n    // tag::default_timezone_class_level[]\n    @DefaultTimeZone(\"CET\")\n    // end::default_timezone_class_level[]\n    inner\n    // tag::default_timezone_class_level[]\n    class MyTimeZoneTests {\n        // end::default_timezone_class_level[]\n        // @formatter:on\n        // tag::default_timezone_class_level[]\n\n        @Test\n        fun test_with_class_level_configuration() {\n            assertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"CET\"))\n        }\n\n        @Test\n        @DefaultTimeZone(\"Africa/Juba\")\n        fun test_with_method_level_configuration() {\n            assertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"Africa/Juba\"))\n        }\n    }\n    // end::default_timezone_class_level[]\n\n    // tag::default_time_zone_with_provider[]\n    @Test\n    @DefaultTimeZone(timeZoneProvider = UtcTimeZoneProvider::class)\n    fun test_with_time_zone_provider() {\n        assertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"UTC\"))\n    }\n\n    class UtcTimeZoneProvider : TimeZoneProvider {\n        override fun get(): TimeZone = TimeZone.getTimeZone(ZoneOffset.UTC)\n    }\n    // end::default_time_zone_with_provider[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/DisplayNameDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Test\n\n@DisplayName(\"A special test case\")\nclass DisplayNameDemo {\n    @Test\n    @DisplayName(\"Custom test name containing spaces\")\n    fun testWithDisplayNameContainingSpaces() {\n    }\n\n    @Test\n    @DisplayName(\"╯°□°）╯\")\n    fun testWithDisplayNameContainingSpecialCharacters() {\n    }\n\n    @Test\n    @DisplayName(\"😱\")\n    fun testWithDisplayNameContainingEmoji() {\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/DisplayNameGeneratorDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.DisplayNameGeneration\nimport org.junit.jupiter.api.DisplayNameGenerator\nimport org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences.SentenceFragment\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores\nimport org.junit.jupiter.api.IndicativeSentencesGeneration\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\n\n@Suppress(\"ClassName\")\nclass DisplayNameGeneratorDemo {\n    // @formatter:off\n    @Nested\n    // tag::user_guide_replace_underscores[]\n    @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores::class)\n    // end::user_guide_replace_underscores[]\n    inner\n    // tag::user_guide_replace_underscores[]\n    class A_year_is_not_supported {\n        // end::user_guide_replace_underscores[]\n        // @formatter:on\n        // tag::user_guide_replace_underscores[]\n        @Test\n        fun if_it_is_zero() {\n        }\n\n        @DisplayName(\"A negative value for year is not supported by the leap year computation.\")\n        @ParameterizedTest(name = \"For example, year {0} is not supported.\")\n        @ValueSource(ints = [-1, -4])\n        fun if_it_is_negative(year: Int) {\n        }\n    }\n    // end::user_guide_replace_underscores[]\n\n    // @formatter:off\n    @Nested\n    // tag::user_guide_indicative_sentences[]\n    @IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores::class)\n    // end::user_guide_indicative_sentences[]\n    inner\n    // tag::user_guide_indicative_sentences[]\n    class A_year_is_a_leap_year {\n        // end::user_guide_indicative_sentences[]\n        // @formatter:on\n        // tag::user_guide_indicative_sentences[]\n        @Test\n        fun if_it_is_divisible_by_4_but_not_by_100() {\n        }\n\n        @ParameterizedTest(name = \"Year {0} is a leap year.\")\n        @ValueSource(ints = [2016, 2020, 2048])\n        fun if_it_is_one_of_the_following_years(year: Int) {\n        }\n    }\n    // end::user_guide_indicative_sentences[]\n\n    // @formatter:off\n    @Nested\n    // tag::user_guide_custom_sentence_fragments[]\n    @SentenceFragment(\"A year is a leap year\")\n    @IndicativeSentencesGeneration\n    // end::user_guide_custom_sentence_fragments[]\n    inner\n    // tag::user_guide_custom_sentence_fragments[]\n    class LeapYearTests {\n        // end::user_guide_custom_sentence_fragments[]\n        // @formatter:on\n        // tag::user_guide_custom_sentence_fragments[]\n        @SentenceFragment(\"if it is divisible by 4 but not by 100\")\n        @Test\n        fun divisibleBy4ButNotBy100() {\n        }\n\n        @SentenceFragment(\"if it is one of the following years\")\n        @ParameterizedTest(name = \"{0}\")\n        @ValueSource(ints = [2016, 2020, 2048])\n        fun validLeapYear(year: Int) {\n        }\n    }\n    // end::user_guide_custom_sentence_fragments[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/DynamicTestsDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport example.util.Calculator\nimport example.util.StringUtils.isPalindrome\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertNotNull\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.DynamicContainer.dynamicContainer\nimport org.junit.jupiter.api.DynamicNode\nimport org.junit.jupiter.api.DynamicTest\nimport org.junit.jupiter.api.DynamicTest.dynamicTest\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.TestFactory\nimport org.junit.jupiter.api.parallel.Execution\nimport org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT\nimport org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD\nimport java.util.Random\nimport java.util.stream.IntStream\nimport java.util.stream.Stream\n\n// end::user_guide[]\n// tag::user_guide[]\nclass DynamicTestsDemo {\n    private val calculator = Calculator()\n\n    // This method will not be executed but produce a warning\n    @TestFactory\n    // end::user_guide[]\n    @Tag(\"exclude\")\n    fun dummy(): DynamicTest = dynamicTest(\"dummy\") {}\n\n    // tag::user_guide[]\n    fun dynamicTestsWithInvalidReturnType(): List<String> = listOf(\"Hello\")\n\n    @TestFactory\n    fun dynamicTestsFromCollection(): Collection<DynamicTest> =\n        listOf(\n            dynamicTest(\"1st dynamic test\") { assertTrue(isPalindrome(\"madam\")) },\n            dynamicTest(\"2nd dynamic test\") { assertEquals(4, calculator.multiply(2, 2)) }\n        )\n\n    @TestFactory\n    fun dynamicTestsFromIterable(): Iterable<DynamicTest> =\n        listOf(\n            dynamicTest(\"3rd dynamic test\") { assertTrue(isPalindrome(\"madam\")) },\n            dynamicTest(\"4th dynamic test\") { assertEquals(4, calculator.multiply(2, 2)) }\n        )\n\n    @TestFactory\n    fun dynamicTestsFromIterator(): Iterator<DynamicTest> =\n        listOf(\n            dynamicTest(\"5th dynamic test\") { assertTrue(isPalindrome(\"madam\")) },\n            dynamicTest(\"6th dynamic test\") { assertEquals(4, calculator.multiply(2, 2)) }\n        ).iterator()\n\n    @TestFactory\n    fun dynamicTestsFromArray(): Array<DynamicTest> =\n        arrayOf(\n            dynamicTest(\"7th dynamic test\") { assertTrue(isPalindrome(\"madam\")) },\n            dynamicTest(\"8th dynamic test\") { assertEquals(4, calculator.multiply(2, 2)) }\n        )\n\n    @TestFactory\n    fun dynamicTestsFromStream(): Stream<DynamicTest> =\n        Stream\n            .of(\"racecar\", \"radar\", \"mom\", \"dad\")\n            .map { text -> dynamicTest(text) { assertTrue(isPalindrome(text)) } }\n\n    @TestFactory\n    fun dynamicTestsFromSequence(): Sequence<DynamicTest> =\n        sequenceOf(\"racecar\", \"radar\", \"mom\", \"dad\")\n            .map { text -> dynamicTest(text) { assertTrue(isPalindrome(text)) } }\n\n    @TestFactory\n    fun dynamicTestsFromIntStream(): Stream<DynamicTest> {\n        // Generates tests for the first 10 even integers.\n        return IntStream\n            .iterate(0) { n -> n + 2 }\n            .limit(10)\n            .mapToObj { n -> dynamicTest(\"test$n\") { assertEquals(0, n % 2) } }\n    }\n\n    @TestFactory\n    fun generateRandomNumberOfTests(): Stream<DynamicTest> {\n        // Generates random positive integers between 0 and 100 until\n        // a number evenly divisible by 7 is encountered.\n        val inputGenerator =\n            object : Iterator<Int> {\n                var random = Random()\n\n                // end::user_guide[]\n                init {\n                    // Use fixed seed to always produce the same number of tests for execution on the CI server\n                    random = Random(23)\n                }\n\n                // tag::user_guide[]\n                var current = 0\n\n                override fun hasNext(): Boolean {\n                    current = random.nextInt(100)\n                    return current % 7 != 0\n                }\n\n                override fun next(): Int = current\n            }\n\n        // Generates display names like: input:5, input:37, input:85, etc.\n        val displayNameGenerator = { input: Int -> \"input:$input\" }\n\n        // Executes tests based on the current input value.\n        val testExecutor = { input: Int -> assertTrue(input % 7 != 0) }\n\n        // Returns a stream of dynamic tests.\n        return DynamicTest.stream(inputGenerator, displayNameGenerator, testExecutor)\n    }\n\n    @TestFactory\n    fun dynamicTestsFromStreamFactoryMethod(): Stream<DynamicTest> {\n        // Stream of palindromes to check\n        val inputStream = Stream.of(\"racecar\", \"radar\", \"mom\", \"dad\")\n\n        // Generates display names like: racecar is a palindrome\n        val displayNameGenerator = { text: String -> \"$text is a palindrome\" }\n\n        // Executes tests based on the current input value.\n        val testExecutor = { text: String -> assertTrue(isPalindrome(text)) }\n\n        // Returns a stream of dynamic tests.\n        return DynamicTest.stream(inputStream, displayNameGenerator, testExecutor)\n    }\n\n    @TestFactory\n    fun dynamicTestsWithContainers(): Stream<DynamicNode> =\n        Stream\n            .of(\"A\", \"B\", \"C\")\n            .map { input ->\n                dynamicContainer(\n                    \"Container $input\",\n                    Stream.of(\n                        dynamicTest(\"not null\") { assertNotNull(input) },\n                        dynamicContainer(\n                            \"properties\",\n                            Stream.of(\n                                dynamicTest(\"length > 0\") { assertTrue(input.length > 0) },\n                                dynamicTest(\"not empty\") { assertFalse(input.isEmpty()) }\n                            )\n                        )\n                    )\n                )\n            }\n\n    // end::user_guide[]\n    // tag::execution_mode[]\n    @TestFactory\n    @Execution(CONCURRENT) // <1>\n    fun dynamicTestsWithConfiguredExecutionMode(): Stream<DynamicNode> =\n        Stream\n            .of(\"A\", \"B\", \"C\")\n            .map { input ->\n                dynamicContainer { outer ->\n                    outer\n                        .displayName(\"Container $input\")\n                        .children(\n                            dynamicTest { config ->\n                                config\n                                    .displayName(\"not null\")\n                                    .executionMode(SAME_THREAD) // <2>\n                                    .executable { assertNotNull(input) }\n                            },\n                            dynamicContainer { inner ->\n                                inner\n                                    .displayName(\"properties\")\n                                    .executionMode(CONCURRENT) // <3>\n                                    .childExecutionMode(SAME_THREAD) // <4>\n                                    .children(\n                                        dynamicTest { config ->\n                                            config\n                                                .displayName(\"length > 0\")\n                                                .executionMode(CONCURRENT) // <5>\n                                                .executable { assertTrue(input.length > 0) }\n                                        },\n                                        dynamicTest { config ->\n                                            config\n                                                .displayName(\"not empty\")\n                                                .executable { assertFalse(input.isEmpty()) }\n                                        }\n                                    )\n                            }\n                        )\n                }\n            }\n    // end::execution_mode[]\n\n    // tag::user_guide[]\n    @TestFactory\n    fun dynamicNodeSingleTest(): DynamicNode = dynamicTest(\"'pop' is a palindrome\") { assertTrue(isPalindrome(\"pop\")) }\n\n    @TestFactory\n    fun dynamicNodeSingleContainer(): DynamicNode =\n        dynamicContainer(\n            \"palindromes\",\n            Stream\n                .of(\"racecar\", \"radar\", \"mom\", \"dad\")\n                .map { text -> dynamicTest(text) { assertTrue(isPalindrome(text)) } }\n        )\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/DynamicTestsNamedDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\n\nimport example.util.StringUtils.isPalindrome\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.DynamicTest\nimport org.junit.jupiter.api.Named.named\nimport org.junit.jupiter.api.NamedExecutable\nimport org.junit.jupiter.api.TestFactory\nimport java.util.stream.Stream\n\nclass DynamicTestsNamedDemo {\n    @TestFactory\n    fun dynamicTestsFromStreamFactoryMethodWithNames(): Stream<DynamicTest> {\n        // Stream of palindromes to check\n        // end::user_guide[]\n        // tag::user_guide[]\n        val inputStream =\n            Stream.of(\n                named(\"racecar is a palindrome\", \"racecar\"),\n                named(\"radar is also a palindrome\", \"radar\"),\n                named(\"mom also seems to be a palindrome\", \"mom\"),\n                named(\"dad is yet another palindrome\", \"dad\")\n            )\n        // end::user_guide[]\n        // tag::user_guide[]\n\n        // Returns a stream of dynamic tests.\n        return DynamicTest.stream(inputStream) { text -> assertTrue(isPalindrome(text)) }\n    }\n\n    @TestFactory\n    fun dynamicTestsFromStreamFactoryMethodWithNamedExecutables(): Stream<DynamicTest> {\n        // Stream of palindromes to check\n        // end::user_guide[]\n        // tag::user_guide[]\n        val inputStream =\n            Stream\n                .of(\"racecar\", \"radar\", \"mom\", \"dad\")\n                .map { PalindromeNamedExecutable(it) }\n        // end::user_guide[]\n        // tag::user_guide[]\n\n        // Returns a stream of dynamic tests based on NamedExecutables.\n        return DynamicTest.stream(inputStream)\n    }\n\n    class PalindromeNamedExecutable(\n        private val text: String\n    ) : NamedExecutable {\n        override fun getName(): String = \"'$text' is a palindrome\"\n\n        override fun execute() {\n            assertTrue(isPalindrome(text))\n        }\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/ExternalCustomConditionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide_external_custom_condition[]\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.condition.EnabledIf\n\nclass ExternalCustomConditionDemo {\n    @Test\n    @EnabledIf(\"example.kotlin.ExternalCondition#customCondition\")\n    fun enabled() {\n        // ...\n    }\n}\n\nclass ExternalCondition {\n    companion object {\n        @JvmStatic\n        fun customCondition(): Boolean = true\n    }\n}\n// end::user_guide_external_custom_condition[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/Fast.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Tag\n\n@Target(AnnotationTarget.TYPE, AnnotationTarget.FUNCTION)\n@Retention(AnnotationRetention.RUNTIME)\n@Tag(\"fast\")\nannotation class Fast\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/FastTest.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\n\n@Target(AnnotationTarget.FUNCTION)\n@Retention(AnnotationRetention.RUNTIME)\n@Tag(\"fast\")\n@Test\nannotation class FastTest\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/HttpServerDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport com.sun.net.httpserver.HttpServer\nimport example.kotlin.extensions.HttpServerExtension\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport java.net.HttpURLConnection\nimport java.net.URI\nimport java.net.URL\n\n// tag::user_guide[]\n@ExtendWith(HttpServerExtension::class)\nclass HttpServerDemo {\n    // end::user_guide[]\n    @Suppress(\"HttpUrlsUsage\")\n    // tag::user_guide[]\n    @Test\n    fun httpCall(server: HttpServer) {\n        val address = server.address\n        val requestUrl = URI(\"http://${address.hostName}:${address.port}/example\").toURL()\n\n        val responseBody = sendRequest(requestUrl)\n\n        assertEquals(\"This is a test\", responseBody)\n    }\n\n    private fun sendRequest(url: URL): String {\n        val connection = url.openConnection() as HttpURLConnection\n        val contentLength = connection.contentLength\n        url.openStream().use { response ->\n            val content = ByteArray(contentLength)\n            assertEquals(contentLength, response.read(content))\n            return String(content, Charsets.UTF_8)\n        }\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/MyFirstJUnitJupiterTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\n\nimport example.util.Calculator\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass MyFirstJUnitJupiterTests {\n    private val calculator = Calculator()\n\n    @Test\n    fun addition() {\n        assertEquals(2, calculator.add(1, 1))\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/MyRandomParametersTest.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport example.extensions.Random\nimport org.junit.jupiter.api.Assertions.assertNotEquals\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide[]\nclass MyRandomParametersTest(\n    @Random randomNumber: Int\n) {\n    @Test\n    fun injectsInteger(\n        @Random i: Int,\n        @Random j: Int\n    ) {\n        assertNotEquals(i, j)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.parallel.Execution\nimport org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD\nimport org.junit.jupiter.params.Parameter\nimport org.junit.jupiter.params.ParameterizedClass\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport java.time.Duration\n\nclass ParameterizedClassDemo {\n    // @formatter:off\n    @Nested\n    // tag::nested[]\n    @Execution(SAME_THREAD)\n    @ParameterizedClass\n    @ValueSource(strings = [\"apple\", \"banana\"])\n    // end::nested[]\n    inner\n    // tag::nested[]\n    class FruitTests {\n        // end::nested[]\n        // @formatter:on\n        // tag::nested[]\n        @Parameter\n        lateinit var fruit: String\n\n        @Nested\n        @ParameterizedClass\n        @ValueSource(ints = [23, 42])\n        inner class QuantityTests {\n            @Parameter\n            var quantity: Int = 0\n\n            @ParameterizedTest\n            @ValueSource(strings = [\"PT1H\", \"PT2H\"])\n            fun test(duration: Duration) {\n                assertFruit(fruit)\n                assertQuantity(quantity)\n                assertFalse(duration.isNegative)\n            }\n        }\n    }\n    // end::nested[]\n\n    private fun assertFruit(fruit: String) {\n        assertTrue(\n            listOf(\"apple\", \"banana\", \"cherry\", \"dewberry\").contains(fruit)\n        ) { \"not a fruit: $fruit\" }\n    }\n\n    private fun assertQuantity(quantity: Int) {\n        assertTrue(quantity > 0)\n    }\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/PollingTimeoutDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\n\nclass PollingTimeoutDemo {\n    // tag::user_guide[]\n    @Test\n    @Timeout(5) // Poll at most 5 seconds\n    fun pollUntil() {\n        while (asynchronousResultNotAvailable()) {\n            Thread.sleep(250) // custom poll interval\n        }\n        // Obtain the asynchronous result and perform assertions\n    }\n    // end::user_guide[]\n\n    private fun asynchronousResultNotAvailable(): Boolean = false\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/RepeatedTestsDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.RepeatedTest\nimport org.junit.jupiter.api.RepetitionInfo\nimport org.junit.jupiter.api.TestInfo\nimport org.junit.jupiter.api.fail\nimport org.junit.jupiter.api.parallel.Execution\nimport org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD\nimport java.util.logging.Logger\n\n@Execution(SAME_THREAD)\nclass RepeatedTestsDemo {\n    private val logger = // ...\n        // end::user_guide[]\n        Logger.getLogger(RepeatedTestsDemo::class.java.name)\n    // tag::user_guide[]\n\n    @BeforeEach\n    fun beforeEach(\n        testInfo: TestInfo,\n        repetitionInfo: RepetitionInfo\n    ) {\n        val currentRepetition = repetitionInfo.currentRepetition\n        val totalRepetitions = repetitionInfo.totalRepetitions\n        val methodName = testInfo.testMethod.get().name\n        logger.info(\"About to execute repetition $currentRepetition of $totalRepetitions for $methodName\")\n    }\n\n    @RepeatedTest(10)\n    fun repeatedTest() {\n        // ...\n    }\n\n    @RepeatedTest(5)\n    fun repeatedTestWithRepetitionInfo(repetitionInfo: RepetitionInfo) {\n        assertEquals(5, repetitionInfo.totalRepetitions)\n    }\n\n    // end::user_guide[]\n    // Use fully qualified name to avoid having it show up in the imports.\n    @org.junit.jupiter.api.Disabled(\"intentional failures would break the build\")\n    // tag::user_guide[]\n    @RepeatedTest(value = 8, failureThreshold = 2)\n    fun repeatedTestWithFailureThreshold(repetitionInfo: RepetitionInfo) {\n        // Simulate unexpected failure every second repetition\n        if (repetitionInfo.currentRepetition % 2 == 0) {\n            fail(\"Boom!\")\n        }\n    }\n\n    @RepeatedTest(value = 1, name = \"{displayName} {currentRepetition}/{totalRepetitions}\")\n    @DisplayName(\"Repeat!\")\n    fun customDisplayName(testInfo: TestInfo) {\n        assertEquals(\"Repeat! 1/1\", testInfo.displayName)\n    }\n\n    @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)\n    @DisplayName(\"Details...\")\n    fun customDisplayNameWithLongPattern(testInfo: TestInfo) {\n        assertEquals(\"Details... :: repetition 1 of 1\", testInfo.displayName)\n    }\n\n    @RepeatedTest(value = 5, name = \"Wiederholung {currentRepetition} von {totalRepetitions}\")\n    fun repeatedTestInGerman() {\n        // ...\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/StandardTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.AfterAll\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.Assumptions.assumeTrue\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.fail\n\nclass StandardTests {\n    @BeforeEach\n    fun init() {\n    }\n\n    @Test\n    fun succeedingTest() {\n    }\n\n    // end::user_guide[]\n    @extensions.ExpectToFail\n    // tag::user_guide[]\n    @Test\n    fun failingTest() {\n        fail(\"a failing test\")\n    }\n\n    @Test\n    @Disabled(\"for demonstration purposes\")\n    fun skippedTest() {\n        // not executed\n    }\n\n    @Test\n    fun abortedTest() {\n        assumeTrue(\"abc\".contains(\"Z\"))\n        fail(\"test should have been aborted\")\n    }\n\n    @AfterEach\n    fun tearDown() {\n    }\n\n    companion object {\n        @JvmStatic\n        @BeforeAll\n        fun initAll() {\n        }\n\n        @JvmStatic\n        @AfterAll\n        fun tearDownAll() {\n        }\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/SystemPropertyExtensionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.ClassOrderer\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.Order\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestClassOrder\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.api.util.ClearSystemProperty\nimport org.junit.jupiter.api.util.ReadsSystemProperty\nimport org.junit.jupiter.api.util.RestoreSystemProperties\nimport org.junit.jupiter.api.util.SetSystemProperty\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\n\nclass SystemPropertyExtensionDemo {\n    // tag::systemproperty_clear_simple[]\n    @Test\n    @ClearSystemProperty(key = \"some property\")\n    fun testClearingProperty() {\n        assertThat(System.getProperty(\"some property\")).isNull()\n    }\n    // end::systemproperty_clear_simple[]\n\n    // tag::systemproperty_set_simple[]\n    @Test\n    @SetSystemProperty(key = \"some property\", value = \"new value\")\n    fun testSettingProperty() {\n        assertThat(System.getProperty(\"some property\")).isEqualTo(\"new value\")\n    }\n    // end::systemproperty_set_simple[]\n\n    // tag::systemproperty_using_set_and_clear[]\n    @Test\n    @ClearSystemProperty(key = \"1st property\")\n    @ClearSystemProperty(key = \"2nd property\")\n    @SetSystemProperty(key = \"3rd property\", value = \"new value\")\n    fun testClearingAndSettingProperty() {\n        assertThat(System.getProperty(\"1st property\")).isNull()\n        assertThat(System.getProperty(\"2nd property\")).isNull()\n        assertThat(System.getProperty(\"3rd property\")).isEqualTo(\"new value\")\n    }\n    // end::systemproperty_using_set_and_clear[]\n\n    // @formatter:off\n    @Nested\n    // tag::systemproperty_using_at_class_level[]\n    @ClearSystemProperty(key = \"some property\")\n    // end::systemproperty_using_at_class_level[]\n    inner\n    // tag::systemproperty_using_at_class_level[]\n    class MySystemPropertyTest {\n        // end::systemproperty_using_at_class_level[]\n        // @formatter:on\n        // tag::systemproperty_using_at_class_level[]\n\n        @Test\n        @SetSystemProperty(key = \"some property\", value = \"new value\")\n        fun clearedAtClasslevel() {\n            assertThat(System.getProperty(\"some property\")).isEqualTo(\"new value\")\n        }\n    }\n    // end::systemproperty_using_at_class_level[]\n\n    // tag::systemproperty_restore_test[]\n    @ParameterizedTest\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @RestoreSystemProperties\n    fun parameterizedTest(value: String) {\n        System.setProperty(\"some parameterized property\", value)\n        System.setProperty(\"some other dynamic property\", \"my code calculates somehow\")\n    }\n    // end::systemproperty_restore_test[]\n\n    @Nested\n    @TestClassOrder(ClassOrderer.OrderAnnotation::class)\n    inner class SystemPropertyRestoreExample {\n        // @formatter:off\n        @Nested\n        @Order(1)\n        // tag::systemproperty_class_restore_setup[]\n        @TestInstance(TestInstance.Lifecycle.PER_CLASS)\n        @RestoreSystemProperties\n        // end::systemproperty_class_restore_setup[]\n        inner\n        // tag::systemproperty_class_restore_setup[]\n        class MySystemPropertyRestoreTest {\n            // end::systemproperty_class_restore_setup[]\n            // @formatter:on\n            // tag::systemproperty_class_restore_setup[]\n            @BeforeAll\n            fun beforeAll() {\n                System.setProperty(\"A\", \"A value\")\n            }\n\n            @BeforeEach\n            fun beforeEach() {\n                System.setProperty(\"B\", \"B value\")\n            }\n\n            @Test\n            fun isolatedTest1() {\n                System.setProperty(\"C\", \"C value\")\n            }\n\n            @Test\n            fun isolatedTest2() {\n                assertThat(System.getProperty(\"A\")).isEqualTo(\"A value\")\n                assertThat(System.getProperty(\"B\")).isEqualTo(\"B value\")\n\n                // Class-level @RestoreSystemProperties restores \"C\" to original state\n                assertThat(System.getProperty(\"C\")).isNull()\n            }\n        }\n        // end::systemproperty_class_restore_setup[]\n\n        // @formatter:off\n        @Nested\n        @Order(2)\n        // tag::systemproperty_class_restore_isolated_class[]\n        @ReadsSystemProperty\n        // end::systemproperty_class_restore_isolated_class[]\n        inner\n        // tag::systemproperty_class_restore_isolated_class[]\n        class SomeOtherTestClass {\n            // end::systemproperty_class_restore_isolated_class[]\n            // @formatter:on\n            // tag::systemproperty_class_restore_isolated_class[]\n            @Test\n            fun isolatedTest() {\n                assertThat(System.getProperty(\"A\")).isNull()\n                assertThat(System.getProperty(\"B\")).isNull()\n                assertThat(System.getProperty(\"C\")).isNull()\n            }\n        }\n        // end::systemproperty_class_restore_isolated_class[]\n    }\n\n    // tag::systemproperty_method_combine_all_test[]\n    @ParameterizedTest\n    @ValueSource(ints = [100, 500, 1000])\n    @RestoreSystemProperties\n    @SetSystemProperty(key = \"DISABLE_CACHE\", value = \"TRUE\")\n    @ClearSystemProperty(key = \"COPYWRITE_OVERLAY_TEXT\")\n    fun imageGenerationTest(imageSize: Int) {\n        System.setProperty(\"IMAGE_SIZE\", \"$imageSize\") // Requires restore\n\n        // Test your image generation utility with the current system properties\n    }\n    // end::systemproperty_method_combine_all_test[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/TempDirectoryDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport com.google.common.jimfs.Configuration\nimport com.google.common.jimfs.Jimfs\nimport example.kotlin.TempDirectoryDemo.InMemoryTempDirDemo.JimfsTempDirFactory\nimport example.util.ListWriter\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertNotEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.AnnotatedElementContext\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS\nimport org.junit.jupiter.api.io.TempDir\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy\nimport org.junit.jupiter.api.io.TempDirFactory\nimport java.nio.file.FileSystem\nimport java.nio.file.Files\nimport java.nio.file.Path\n\n@Suppress(\"ClassName\")\nclass TempDirectoryDemo {\n    // tag::user_guide_parameter_injection[]\n    @Test\n    fun writeItemsToFile(\n        @TempDir tempDir: Path\n    ) {\n        val file = tempDir.resolve(\"test.txt\")\n\n        ListWriter(file).write(\"a\", \"b\", \"c\")\n\n        assertEquals(listOf(\"a,b,c\"), Files.readAllLines(file))\n    }\n    // end::user_guide_parameter_injection[]\n\n    // tag::user_guide_multiple_directories[]\n    @Test\n    fun copyFileFromSourceToTarget(\n        @TempDir source: Path,\n        @TempDir target: Path\n    ) {\n        val sourceFile = source.resolve(\"test.txt\")\n        ListWriter(sourceFile).write(\"a\", \"b\", \"c\")\n\n        val targetFile = Files.copy(sourceFile, target.resolve(\"test.txt\"))\n\n        assertNotEquals(sourceFile, targetFile)\n        assertEquals(listOf(\"a,b,c\"), Files.readAllLines(targetFile))\n    }\n    // end::user_guide_multiple_directories[]\n\n    // tag::user_guide_field_injection[]\n    class SharedTempDirectoryDemo {\n        @Test\n        fun writeItemsToFile() {\n            val file = sharedTempDir.resolve(\"test.txt\")\n\n            ListWriter(file).write(\"a\", \"b\", \"c\")\n\n            assertEquals(listOf(\"a,b,c\"), Files.readAllLines(file))\n        }\n\n        @Test\n        fun anotherTestThatUsesTheSameTempDir() {\n            // use sharedTempDir\n        }\n\n        companion object {\n            @TempDir\n            @JvmStatic\n            lateinit var sharedTempDir: Path\n        }\n    }\n    // end::user_guide_field_injection[]\n\n    // tag::user_guide_cleanup_mode[]\n    class CleanupModeDemo {\n        @Test\n        fun fileTest(\n            @TempDir(cleanup = ON_SUCCESS) tempDir: Path\n        ) {\n            // perform test\n        }\n    }\n    // end::user_guide_cleanup_mode[]\n\n    // tag::user_guide_factory_name_prefix[]\n    class TempDirFactoryDemo {\n        @Test\n        fun factoryTest(\n            @TempDir(factory = Factory::class) tempDir: Path\n        ) {\n            assertTrue(tempDir.fileName.toString().startsWith(\"factoryTest\"))\n        }\n\n        class Factory : TempDirFactory {\n            override fun createTempDirectory(\n                elementContext: AnnotatedElementContext,\n                extensionContext: ExtensionContext\n            ): Path = Files.createTempDirectory(extensionContext.requiredTestMethod.name)\n        }\n    }\n    // end::user_guide_factory_name_prefix[]\n\n    // tag::user_guide_factory_jimfs[]\n    class InMemoryTempDirDemo {\n        @Test\n        fun test(\n            @TempDir(factory = JimfsTempDirFactory::class) tempDir: Path\n        ) {\n            // perform test\n        }\n\n        class JimfsTempDirFactory : TempDirFactory {\n            private val fileSystem: FileSystem = Jimfs.newFileSystem(Configuration.unix())\n\n            override fun createTempDirectory(\n                elementContext: AnnotatedElementContext,\n                extensionContext: ExtensionContext\n            ): Path = Files.createTempDirectory(fileSystem.getPath(\"/\"), \"junit-\")\n\n            override fun close() {\n                fileSystem.close()\n            }\n        }\n    }\n    // end::user_guide_factory_jimfs[]\n\n    // tag::user_guide_deletion_strategy[]\n    class DeletionStrategyDemo {\n        @Test\n        fun test(\n            @TempDir(deletionStrategy = TempDirDeletionStrategy.IgnoreFailures::class)\n            tempDir: Path\n        ) {\n            // perform test\n        }\n    }\n    // end::user_guide_deletion_strategy[]\n\n    // tag::user_guide_composed_annotation[]\n    @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)\n    @Retention(AnnotationRetention.RUNTIME)\n    @TempDir(factory = JimfsTempDirFactory::class)\n    annotation class JimfsTempDir\n    // end::user_guide_composed_annotation[]\n\n    // tag::user_guide_composed_annotation_usage[]\n    class JimfsTempDirAnnotationDemo {\n        @Test\n        fun test(\n            @JimfsTempDir tempDir: Path\n        ) {\n            // perform test\n        }\n    }\n    // end::user_guide_composed_annotation_usage[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/TestInfoDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInfo\n\n@DisplayName(\"TestInfo Demo\")\nclass TestInfoDemo(\n    testInfo: TestInfo\n) {\n    companion object {\n        @JvmStatic\n        @BeforeAll\n        fun beforeAll(testInfo: TestInfo) {\n            assertEquals(\"TestInfo Demo\", testInfo.displayName)\n        }\n    }\n\n    init {\n        assertTrue(testInfo.displayName in listOf(\"TEST 1\", \"test2()\"))\n    }\n\n    @BeforeEach\n    fun init(testInfo: TestInfo) {\n        assertTrue(testInfo.displayName in listOf(\"TEST 1\", \"test2()\"))\n    }\n\n    @Test\n    @DisplayName(\"TEST 1\")\n    @Tag(\"my-tag\")\n    fun test1(testInfo: TestInfo) {\n        assertEquals(\"TEST 1\", testInfo.displayName)\n        assertTrue(\"my-tag\" in testInfo.tags)\n    }\n\n    @Test\n    fun test2() {\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/TestReporterDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.MediaType\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestReporter\nimport org.junit.jupiter.api.io.TempDir\nimport java.nio.file.Files\nimport java.nio.file.Path\n\n// tag::user_guide[]\nclass TestReporterDemo {\n    @Test\n    fun reportSingleValue(testReporter: TestReporter) {\n        testReporter.publishEntry(\"a status message\")\n    }\n\n    @Test\n    fun reportKeyValuePair(testReporter: TestReporter) {\n        testReporter.publishEntry(\"a key\", \"a value\")\n    }\n\n    @Test\n    fun reportMultipleKeyValuePairs(testReporter: TestReporter) {\n        testReporter.publishEntry(\n            mapOf(\n                \"user name\" to \"dk38\",\n                \"award year\" to \"1974\"\n            )\n        )\n    }\n\n    @Test\n    fun reportFiles(\n        testReporter: TestReporter,\n        @TempDir tempDir: Path\n    ) {\n        testReporter.publishFile(\"test1.txt\", MediaType.TEXT_PLAIN_UTF_8) { file ->\n            Files.write(file, listOf(\"Test 1\"))\n        }\n\n        val existingFile = Files.write(tempDir.resolve(\"test2.txt\"), listOf(\"Test 2\"))\n        testReporter.publishFile(existingFile, MediaType.TEXT_PLAIN_UTF_8)\n\n        testReporter.publishDirectory(\"test3\") { dir ->\n            Files.write(dir.resolve(\"nested1.txt\"), listOf(\"Nested content 1\"))\n            Files.write(dir.resolve(\"nested2.txt\"), listOf(\"Nested content 2\"))\n        }\n\n        val existingDir = Files.createDirectory(tempDir.resolve(\"test4\"))\n        Files.write(existingDir.resolve(\"nested1.txt\"), listOf(\"Nested content 1\"))\n        Files.write(existingDir.resolve(\"nested2.txt\"), listOf(\"Nested content 2\"))\n        testReporter.publishDirectory(existingDir)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/TestingAStackDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.DisplayName\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport java.util.EmptyStackException\nimport java.util.Stack\n\n@DisplayName(\"A stack\")\nclass TestingAStackDemo {\n    @Test\n    @DisplayName(\"is instantiated with new Stack()\")\n    fun isInstantiatedWithNew() {\n        Stack<Any>()\n    }\n\n    @Nested\n    @DisplayName(\"when new\")\n    inner class WhenNew {\n        lateinit var stack: Stack<Any>\n\n        @BeforeEach\n        fun createNewStack() {\n            stack = Stack()\n        }\n\n        @Test\n        @DisplayName(\"is empty\")\n        fun isEmpty() {\n            assertTrue(stack.isEmpty())\n        }\n\n        @Test\n        @DisplayName(\"throws EmptyStackException when popped\")\n        fun throwsExceptionWhenPopped() {\n            assertThrows<EmptyStackException> { stack.pop() }\n        }\n\n        @Test\n        @DisplayName(\"throws EmptyStackException when peeked\")\n        fun throwsExceptionWhenPeeked() {\n            assertThrows<EmptyStackException> { stack.peek() }\n        }\n\n        @Nested\n        @DisplayName(\"after pushing an element\")\n        inner class AfterPushing {\n            val anElement = \"an element\"\n\n            @BeforeEach\n            fun pushAnElement() {\n                stack.push(anElement)\n            }\n\n            @Test\n            @DisplayName(\"it is no longer empty\")\n            fun isNotEmpty() {\n                assertFalse(stack.isEmpty())\n            }\n\n            @Test\n            @DisplayName(\"returns the element when popped and is empty\")\n            fun returnElementWhenPopped() {\n                assertEquals(anElement, stack.pop())\n                assertTrue(stack.isEmpty())\n            }\n\n            @Test\n            @DisplayName(\"returns the element when peeked but remains not empty\")\n            fun returnElementWhenPeeked() {\n                assertEquals(anElement, stack.peek())\n                assertFalse(stack.isEmpty())\n            }\n        }\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/TimeoutDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin\n\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.Timeout\nimport org.junit.jupiter.api.Timeout.ThreadMode\nimport java.util.concurrent.TimeUnit\n\n// tag::user_guide[]\n@Tag(\"timeout\")\nclass TimeoutDemo {\n    @BeforeEach\n    @Timeout(5)\n    fun setUp() {\n        // fails if execution time exceeds 5 seconds\n    }\n\n    @Test\n    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS)\n    fun failsIfExecutionTimeExceeds500Milliseconds() {\n        // fails if execution time exceeds 500 milliseconds\n    }\n\n    @Test\n    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS, threadMode = ThreadMode.SEPARATE_THREAD)\n    fun failsIfExecutionTimeExceeds500MillisecondsInSeparateThread() {\n        // fails if execution time exceeds 500 milliseconds, the test code is executed in a separate thread\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/defaultmethods/ComparableContract.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.defaultmethods\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide[]\ninterface ComparableContract<T : Comparable<T>> : Testable<T> {\n    fun createSmallerValue(): T\n\n    @Test\n    fun returnsZeroWhenComparedToItself() {\n        val value = createValue()\n        assertEquals(0, value.compareTo(value))\n    }\n\n    @Test\n    fun returnsPositiveNumberWhenComparedToSmallerValue() {\n        val value = createValue()\n        val smallerValue = createSmallerValue()\n        assertTrue(value > smallerValue)\n    }\n\n    @Test\n    fun returnsNegativeNumberWhenComparedToLargerValue() {\n        val value = createValue()\n        val smallerValue = createSmallerValue()\n        assertTrue(smallerValue < value)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/defaultmethods/EqualsContract.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.defaultmethods\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertNotEquals\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide[]\ninterface EqualsContract<T> : Testable<T> {\n    fun createNotEqualValue(): T\n\n    @Test\n    fun valueEqualsItself() {\n        val value = createValue()\n        assertEquals(value, value)\n    }\n\n    @Test\n    fun valueDoesNotEqualNull() {\n        val value = createValue()\n        assertNotEquals(null, value)\n    }\n\n    @Test\n    fun valueDoesNotEqualDifferentValue() {\n        val value = createValue()\n        val differentValue = createNotEqualValue()\n        assertNotEquals(value, differentValue)\n        assertNotEquals(differentValue, value)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/defaultmethods/StringTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.defaultmethods\n\n// tag::user_guide[]\nclass StringTests :\n    ComparableContract<String>,\n    EqualsContract<String> {\n    override fun createValue() = \"banana\"\n\n    override fun createSmallerValue() = \"apple\" // 'a' < 'b' in \"banana\"\n\n    override fun createNotEqualValue() = \"cherry\"\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/defaultmethods/Testable.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.defaultmethods\n\n// tag::user_guide[]\ninterface Testable<T> {\n    fun createValue(): T\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/exception/AssertDoesNotThrowExceptionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.exception\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertDoesNotThrow\n\nclass AssertDoesNotThrowExceptionDemo {\n    // tag::user_guide[]\n    @Test\n    fun testExceptionIsNotThrown() {\n        assertDoesNotThrow {\n            shouldNotThrowException()\n        }\n    }\n\n    fun shouldNotThrowException() {\n    }\n    // end::user_guide[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/exception/ExceptionAssertionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.exception\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\n\nclass ExceptionAssertionDemo {\n    // tag::user_guide[]\n    @Test\n    fun testExpectedExceptionIsThrown() {\n        // The following assertion succeeds because the code under assertion\n        // throws the expected IllegalArgumentException.\n        // The assertion also returns the thrown exception which can be used for\n        // further assertions like asserting the exception message.\n        val exception =\n            assertThrows<IllegalArgumentException> {\n                throw IllegalArgumentException(\"expected message\")\n            }\n        assertEquals(\"expected message\", exception.message)\n\n        // The following assertion also succeeds because the code under assertion\n        // throws IllegalArgumentException which is a subclass of RuntimeException.\n        assertThrows<RuntimeException> {\n            throw IllegalArgumentException(\"expected message\")\n        }\n    }\n    // end::user_guide[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/exception/ExceptionAssertionExactDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.exception\n\nimport extensions.ExpectToFail\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrowsExactly\n\nclass ExceptionAssertionExactDemo {\n    @ExpectToFail\n    // tag::user_guide[]\n    @Test\n    fun testExpectedExceptionIsThrown() {\n        // The following assertion succeeds because the code under assertion throws\n        // IllegalArgumentException which is exactly equal to the expected type.\n        // The assertion also returns the thrown exception which can be used for\n        // further assertions like asserting the exception message.\n        val exception =\n            assertThrowsExactly<IllegalArgumentException> {\n                throw IllegalArgumentException(\"expected message\")\n            }\n        assertEquals(\"expected message\", exception.message)\n\n        // The following assertion fails because the assertion expects exactly\n        // RuntimeException to be thrown, not subclasses of RuntimeException.\n        assertThrowsExactly<RuntimeException> {\n            throw IllegalArgumentException(\"expected message\")\n        }\n    }\n    // end::user_guide[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/exception/FailedAssertionDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.exception\n\nimport example.util.Calculator\nimport extensions.ExpectToFail\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\nclass FailedAssertionDemo {\n    // tag::user_guide[]\n    private val calculator = Calculator()\n\n    // end::user_guide[]\n\n    @ExpectToFail\n    // tag::user_guide[]\n    @Test\n    fun failsDueToUncaughtAssertionError() {\n        // The following incorrect assertion will cause a test failure.\n        // The expected value should be 2 instead of 99.\n        assertEquals(99, calculator.add(1, 1))\n    }\n    // end::user_guide[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/exception/UncaughtExceptionHandlingDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.exception\n\nimport example.util.Calculator\nimport extensions.ExpectToFail\nimport org.junit.jupiter.api.Test\n\nclass UncaughtExceptionHandlingDemo {\n    // tag::user_guide[]\n    private val calculator = Calculator()\n\n    // end::user_guide[]\n\n    @ExpectToFail\n    // tag::user_guide[]\n    @Test\n    fun failsDueToUncaughtException() {\n        // The following throws an ArithmeticException due to division by\n        // zero, which causes a test failure.\n        calculator.divide(1, 0)\n    }\n    // end::user_guide[]\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/HttpServerExtension.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport com.sun.net.httpserver.HttpServer\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\nimport java.io.IOException\nimport java.io.UncheckedIOException\n\n// tag::user_guide[]\nclass HttpServerExtension : ParameterResolver {\n    override fun supportsParameter(\n        parameterContext: ParameterContext,\n        extensionContext: ExtensionContext\n    ): Boolean = HttpServer::class.java == parameterContext.parameter.type\n\n    override fun resolveParameter(\n        parameterContext: ParameterContext,\n        extensionContext: ExtensionContext\n    ): Any {\n        val rootContext = extensionContext.root\n        val store = rootContext.getStore(Namespace.GLOBAL)\n        val key = HttpServerResource::class.java\n        val resource =\n            store.computeIfAbsent(key, {\n                try {\n                    HttpServerResource(0).apply { start() }\n                } catch (e: IOException) {\n                    throw UncheckedIOException(\"Failed to create HttpServerResource\", e)\n                }\n            }, HttpServerResource::class.java)\n        return resource.httpServer\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/HttpServerResource.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport com.sun.net.httpserver.HttpServer\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\n\n// tag::user_guide[]\nclass HttpServerResource(\n    port: Int\n) : AutoCloseable {\n    val httpServer: HttpServer\n\n    init {\n        val loopbackAddress = InetAddress.getLoopbackAddress()\n        httpServer = HttpServer.create(InetSocketAddress(loopbackAddress, port), 0)\n    }\n\n    // end::user_guide[]\n\n    // tag::user_guide[]\n    fun start() {\n        // Example handler\n        httpServer.createContext(\"/example\") { exchange ->\n            val body = \"This is a test\"\n            exchange.sendResponseHeaders(200, body.length.toLong())\n            exchange.responseBody.use { os ->\n                os.write(body.toByteArray(Charsets.UTF_8))\n            }\n        }\n        httpServer.executor = null\n        httpServer.start()\n    }\n\n    override fun close() {\n        httpServer.stop(0)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/ParameterResolverConflictDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\n\n// tag::user_guide[]\nclass ParameterResolverConflictDemo {\n    // end::user_guide[]\n    @extensions.ExpectToFail\n    // tag::user_guide[]\n    @Test\n    @ExtendWith(FirstIntegerResolver::class, SecondIntegerResolver::class)\n    fun testInt(i: Int) {\n        // Test will not run due to ParameterResolutionException\n        assertEquals(1, i)\n    }\n\n    class FirstIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 1\n    }\n\n    class SecondIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 2\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/ParameterResolverCustomAnnotationDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\n\n// tag::user_guide[]\nclass ParameterResolverCustomAnnotationDemo {\n    @Test\n    fun testInt(\n        @FirstInteger first: Int,\n        @SecondInteger second: Int\n    ) {\n        assertEquals(1, first)\n        assertEquals(2, second)\n    }\n\n    @Target(AnnotationTarget.VALUE_PARAMETER)\n    @Retention(AnnotationRetention.RUNTIME)\n    @ExtendWith(FirstIntegerExtension::class)\n    annotation class FirstInteger\n\n    class FirstIntegerExtension : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean =\n            parameterContext.parameter.type == Int::class.javaPrimitiveType &&\n                !parameterContext.isAnnotated(SecondInteger::class.java)\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 1\n    }\n\n    @Target(AnnotationTarget.VALUE_PARAMETER)\n    @Retention(AnnotationRetention.RUNTIME)\n    @ExtendWith(SecondIntegerExtension::class)\n    annotation class SecondInteger\n\n    class SecondIntegerExtension : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.isAnnotated(SecondInteger::class.java)\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 2\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/ParameterResolverCustomTypeDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\n\n// tag::user_guide[]\nclass ParameterResolverCustomTypeDemo {\n    @Test\n    @ExtendWith(FirstIntegerResolver::class, SecondIntegerResolver::class)\n    fun testInt(\n        i: Int,\n        wrappedInteger: WrappedInteger\n    ) {\n        assertEquals(1, i)\n        assertEquals(2, wrappedInteger.value)\n    }\n\n    class FirstIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 1\n    }\n\n    class SecondIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == WrappedInteger::class.java\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = WrappedInteger(2)\n    }\n\n    data class WrappedInteger(\n        val value: Int\n    )\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/ParameterResolverNoConflictDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\n\n// tag::user_guide[]\nclass ParameterResolverNoConflictDemo {\n    @Test\n    @ExtendWith(FirstIntegerResolver::class)\n    fun firstResolution(i: Int) {\n        assertEquals(1, i)\n    }\n\n    @Test\n    @ExtendWith(SecondIntegerResolver::class)\n    fun secondResolution(i: Int) {\n        assertEquals(2, i)\n    }\n\n    class FirstIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 1\n    }\n\n    class SecondIntegerResolver : ParameterResolver {\n        override fun supportsParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType\n\n        override fun resolveParameter(\n            parameterContext: ParameterContext,\n            extensionContext: ExtensionContext\n        ): Any = 2\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/Random.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.extension.ExtendWith\n\n// tag::user_guide[]\n@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)\n@Retention(AnnotationRetention.RUNTIME)\n@ExtendWith(RandomNumberExtension::class)\nannotation class Random\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/RandomNumberDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide[]\nclass RandomNumberDemo(\n    @Random randomNumber2: Int\n) {\n    // Use randomNumber1 field in test methods and @BeforeEach\n    // or @AfterEach lifecycle methods.\n    @Random\n    var randomNumber1: Int = 0\n\n    // randomNumber2 is available in the constructor via primary constructor above.\n\n    @BeforeEach\n    fun beforeEach(\n        @Random randomNumber3: Int\n    ) {\n        // Use randomNumber3 in @BeforeEach method.\n    }\n\n    @Test\n    fun test(\n        @Random randomNumber4: Int\n    ) {\n        // Use randomNumber4 in test method.\n    }\n\n    companion object {\n        // Use static randomNumber0 field anywhere in the test class,\n        // including @BeforeAll or @AfterAll lifecycle methods.\n        @Random\n        @JvmStatic\n        var randomNumber0: Int = 0\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/extensions/RandomNumberExtension.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.extensions\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.extension.BeforeAllCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.junit.jupiter.api.extension.ParameterResolver\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor\nimport org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields\nimport org.junit.platform.commons.support.ModifierSupport\nimport java.lang.reflect.Field\nimport java.util.function.Predicate\n\nclass RandomNumberExtension :\n    BeforeAllCallback,\n    TestInstancePostProcessor,\n    ParameterResolver {\n    private val random = java.util.Random(System.nanoTime())\n\n    /**\n     * Inject a random integer into static fields that are annotated with\n     * `@Random` and can be assigned an integer value.\n     */\n    override fun beforeAll(context: ExtensionContext) {\n        val testClass = context.requiredTestClass\n        injectFields(testClass, null, ModifierSupport::isStatic)\n    }\n\n    /**\n     * Inject a random integer into non-static fields that are annotated with\n     * `@Random` and can be assigned an integer value.\n     */\n    override fun postProcessTestInstance(\n        testInstance: Any,\n        context: ExtensionContext\n    ) {\n        val testClass = context.requiredTestClass\n        injectFields(testClass, testInstance, ModifierSupport::isNotStatic)\n    }\n\n    /**\n     * Determine if the parameter is annotated with `@Random` and can be\n     * assigned an integer value.\n     */\n    override fun supportsParameter(\n        pc: ParameterContext,\n        ec: ExtensionContext\n    ): Boolean = pc.isAnnotated(Random::class.java) && isInteger(pc.parameter.type)\n\n    /**\n     * Resolve a random integer.\n     */\n    override fun resolveParameter(\n        pc: ParameterContext,\n        ec: ExtensionContext\n    ): Int = random.nextInt()\n\n    private fun injectFields(\n        testClass: Class<*>,\n        testInstance: Any?,\n        predicate: Predicate<Field>\n    ) {\n        val combined = predicate.and { field -> isInteger(field.type) }\n        findAnnotatedFields(testClass, Random::class.java, combined)\n            .forEach { field ->\n                field.isAccessible = true\n                field.set(testInstance, random.nextInt())\n            }\n    }\n\n    private fun isInteger(type: Class<*>): Boolean = type == Int::class.javaObjectType || type == Int::class.javaPrimitiveType\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/registration/DocumentationDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.registration\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\nimport java.nio.file.Path\n\n// tag::user_guide[]\nclass DocumentationDemo {\n    @JvmField\n    @RegisterExtension\n    val docs: DocumentationExtension =\n        DocumentationExtension.forPath(lookUpDocsDir())\n\n    @Test\n    fun generateDocumentation() {\n        // use this.docs ...\n    }\n\n    companion object {\n        fun lookUpDocsDir(): Path? {\n            // return path to docs dir\n            return null\n        }\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/registration/DocumentationExtension.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.registration\n\nimport org.junit.jupiter.api.extension.AfterEachCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport java.nio.file.Path\n\nclass DocumentationExtension private constructor(\n    private val path: Path?\n) : AfterEachCallback {\n    override fun afterEach(context: ExtensionContext) {\n        // no-op for demo\n    }\n\n    companion object {\n        @JvmStatic\n        fun forPath(path: Path?): DocumentationExtension = DocumentationExtension(path)\n    }\n}\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/testinterface/TestInterfaceDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.testinterface\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\n\n// tag::user_guide[]\nclass TestInterfaceDemo :\n    TestLifecycleLogger,\n    TimeExecutionLogger,\n    TestInterfaceDynamicTestsDemo {\n    @Test\n    fun isEqualValue() {\n        assertEquals(1, \"a\".length, \"is always equal\")\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/testinterface/TestInterfaceDynamicTestsDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.testinterface\n\nimport example.util.StringUtils.isPalindrome\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.DynamicTest\nimport org.junit.jupiter.api.DynamicTest.dynamicTest\nimport org.junit.jupiter.api.TestFactory\n\n// tag::user_guide[]\ninterface TestInterfaceDynamicTestsDemo {\n    @TestFactory\n    fun dynamicTestsForPalindromes(): Sequence<DynamicTest> =\n        sequenceOf(\"racecar\", \"radar\", \"mom\", \"dad\")\n            .map { text -> dynamicTest(text) { assertTrue(isPalindrome(text)) } }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/testinterface/TestLifecycleLogger.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.testinterface\n\nimport org.junit.jupiter.api.AfterAll\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.TestInfo\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.api.TestInstance.Lifecycle\nimport java.util.logging.Logger\n\n// tag::user_guide[]\n@TestInstance(Lifecycle.PER_CLASS)\ninterface TestLifecycleLogger {\n    @BeforeAll\n    fun beforeAllTests() {\n        logger.info(\"Before all tests\")\n    }\n\n    @AfterAll\n    fun afterAllTests() {\n        logger.info(\"After all tests\")\n    }\n\n    @BeforeEach\n    fun beforeEachTest(testInfo: TestInfo) {\n        logger.info { \"About to execute [${testInfo.displayName}]\" }\n    }\n\n    @AfterEach\n    fun afterEachTest(testInfo: TestInfo) {\n        logger.info { \"Finished executing [${testInfo.displayName}]\" }\n    }\n\n    companion object {\n        private val logger: Logger = Logger.getLogger(TestLifecycleLogger::class.java.name)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/testinterface/TimeExecutionLogger.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.testinterface\n\nimport example.timing.TimingExtension\nimport org.junit.jupiter.api.Tag\nimport org.junit.jupiter.api.extension.ExtendWith\n\n// tag::user_guide[]\n@Tag(\"timed\")\n@ExtendWith(TimingExtension::class)\ninterface TimeExecutionLogger\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/timing/TimingExtension.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.timing\n\n// tag::user_guide[]\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace\nimport org.junit.jupiter.api.extension.ExtensionContext.Store\nimport java.util.logging.Logger\n\n// end::user_guide[]\n\n// tag::user_guide[]\nclass TimingExtension :\n    BeforeTestExecutionCallback,\n    AfterTestExecutionCallback {\n    override fun beforeTestExecution(context: ExtensionContext) {\n        getStore(context).put(START_TIME, System.currentTimeMillis())\n    }\n\n    override fun afterTestExecution(context: ExtensionContext) {\n        val testMethod = context.requiredTestMethod\n        val startTime = getStore(context).remove(START_TIME, Long::class.javaObjectType)!!\n        val duration = System.currentTimeMillis() - startTime\n\n        logger.info { \"Method [${testMethod.name}] took $duration ms.\" }\n    }\n\n    private fun getStore(context: ExtensionContext): Store = context.getStore(Namespace.create(javaClass, context.requiredTestMethod))\n\n    companion object {\n        private val logger: Logger = Logger.getLogger(TimingExtension::class.java.name)\n        private const val START_TIME = \"start time\"\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/kotlin/timing/TimingExtensionTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.kotlin.timing\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.ExtendWith\n\n// tag::user_guide[]\n@ExtendWith(TimingExtension::class)\nclass TimingExtensionTests {\n    @Test\n    fun sleep20ms() {\n        Thread.sleep(20)\n    }\n\n    @Test\n    fun sleep50ms() {\n        Thread.sleep(50)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/kotlin/example/registration/KotlinWebServerDemo.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example.registration\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.extension.RegisterExtension\n\n// tag::user_guide[]\nclass KotlinWebServerDemo {\n    companion object {\n        @JvmField\n        @RegisterExtension\n        val server =\n            WebServerExtension\n                .builder()\n                .enableSecurity(false)\n                .build()!!\n    }\n\n    @Test\n    fun getProductList() {\n        // Use WebClient to connect to web server using serverUrl and verify response\n        val webClient = WebClient()\n        val serverUrl = server.serverUrl\n        assertEquals(200, webClient.get(\"$serverUrl/products\").responseStatus)\n    }\n}\n// end::user_guide[]\n"
  },
  {
    "path": "documentation/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener",
    "content": "example.session.GlobalSetupTeardownListener\n"
  },
  {
    "path": "documentation/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.execution.parallel.enabled=true\njunit.jupiter.execution.parallel.mode.default=concurrent\njunit.jupiter.execution.parallel.config.executor-service=worker_thread_pool\njunit.jupiter.execution.parallel.config.strategy=fixed\njunit.jupiter.execution.parallel.config.fixed.parallelism=6\n\njunit.platform.stacktrace.pruning.enabled=false\n"
  },
  {
    "path": "documentation/src/test/resources/log4j2-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\"\n\t\t\t   xmlns=\"https://logging.apache.org/xml/ns\"\n\t\t\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t   xsi:schemaLocation=\"https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd\">\n\t<Appenders>\n\t\t<Console name=\"Console\" target=\"SYSTEM_OUT\">\n\t\t\t<PatternLayout pattern=\"%d{HH:mm:ss.SSSSSS} [%-18t] %-5level %logger{1.} - %msg%n\"/>\n\t\t</Console>\n\t</Appenders>\n\t<Loggers>\n\t\t<Logger name=\"org.junit\" level=\"WARN\"/>\n\t\t<Logger name=\"example\" level=\"WARN\"/>\n\t\t<!--\n\t\t<Logger name=\"example.callbacks\" level=\"INFO\"/>\n\t\t-->\n\t\t<Logger name=\"org.junit.vintage.engine.discovery\" level=\"ERROR\"/>\n\t\t<Root level=\"ERROR\">\n\t\t\t<AppenderRef ref=\"Console\"/>\n\t\t</Root>\n\t</Loggers>\n</Configuration>\n"
  },
  {
    "path": "documentation/src/test/resources/two-column.csv",
    "content": "COUNTRY, REFERENCE\nSweden, 1\nPoland, 2\n\"United States of America\", 3\nFrance, 700_000\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport static java.util.stream.Collectors.groupingBy;\nimport static java.util.stream.Collectors.toList;\n\nimport java.io.PrintWriter;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeMap;\n\nimport org.apiguardian.api.API.Status;\n\n/**\n * @since 1.0\n */\nabstract class AbstractApiReportWriter implements ApiReportWriter {\n\n\tprotected static final int NAME_COLUMN_WIDTH = 128;\n\n\tprivate final ApiReport apiReport;\n\n\tAbstractApiReportWriter(ApiReport apiReport) {\n\t\tthis.apiReport = apiReport;\n\t}\n\n\t@Override\n\tpublic void printReportHeader(PrintWriter out) {\n\t\tout.println(h1(\"@API Declarations\"));\n\t\tout.println();\n\t\tout.println(paragraph(\n\t\t\t\"Discovered %d types with %s declarations.\".formatted(this.apiReport.types().size(), code(\"@API\"))));\n\t\tout.println();\n\t}\n\n\t@Override\n\tpublic void printDeclarationInfo(PrintWriter out, Set<Status> statuses) {\n\t\tstatuses.forEach(\n\t\t\tstatus -> printDeclarationSection(statuses, status, this.apiReport.declarations().get(status), out));\n\t}\n\n\tprotected void printDeclarationSection(Set<Status> statuses, Status status, List<Declaration> declarations,\n\t\t\tPrintWriter out) {\n\t\tprintDeclarationSectionHeader(statuses, status, declarations, out);\n\t\tMap<String, List<Declaration>> declarationsByModule = declarations.stream() //\n\t\t\t\t.collect(groupingBy(Declaration::moduleName, TreeMap::new, toList()));\n\t\tif (declarationsByModule.isEmpty()) {\n\t\t\tout.println(paragraph(\"NOTE: There are currently no APIs annotated with %s.\".formatted(\n\t\t\t\tcode(\"@API(status = %s)\".formatted(status.name())))));\n\t\t\treturn;\n\t\t}\n\t\tdeclarationsByModule.forEach((moduleName, moduleDeclarations) -> {\n\t\t\tout.println(h3(\"Module \" + moduleName));\n\t\t\tout.println();\n\t\t\tmoduleDeclarations.stream() //\n\t\t\t\t\t.collect(groupingBy(Declaration::packageName, TreeMap::new, toList())) //\n\t\t\t\t\t.forEach((packageName, packageDeclarations) -> {\n\t\t\t\t\t\tout.println(h4(\"Package \" + packageName));\n\t\t\t\t\t\tout.println();\n\t\t\t\t\t\tprintDeclarationTableHeader(out);\n\t\t\t\t\t\tpackageDeclarations.forEach(it -> printDeclarationTableRow(it, out));\n\t\t\t\t\t\tprintDeclarationTableFooter(out);\n\t\t\t\t\t\tout.println();\n\t\t\t\t\t});\n\t\t});\n\t}\n\n\tprotected void printDeclarationSectionHeader(Set<Status> statuses, Status status, List<Declaration> declarations,\n\t\t\tPrintWriter out) {\n\t\tif (statuses.size() < 2) {\n\t\t\t// omit section header when only a single status is printed\n\t\t\treturn;\n\t\t}\n\t\tout.println(h2(\"@API(%s)\".formatted(status)));\n\t\tout.println();\n\t\tout.println(paragraph(\n\t\t\t\"Discovered %d %s declarations.\".formatted(declarations.size(), code(\"@API(%s)\".formatted(status)))));\n\t\tout.println();\n\t}\n\n\tprotected abstract String h1(String header);\n\n\tprotected abstract String h2(String header);\n\n\tprotected abstract String h3(String header);\n\n\tprotected abstract String h4(String header);\n\n\tprotected abstract String code(String element);\n\n\tprotected abstract String italic(String element);\n\n\tprotected String paragraph(String element) {\n\t\treturn element;\n\t}\n\n\tprotected abstract void printDeclarationTableHeader(PrintWriter out);\n\n\tprotected abstract void printDeclarationTableRow(Declaration declaration, PrintWriter out);\n\n\tprotected abstract void printDeclarationTableFooter(PrintWriter out);\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/ApiReport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.SortedSet;\n\nimport io.github.classgraph.ClassInfo;\n\nimport org.apiguardian.api.API.Status;\n\n/**\n * @since 1.0\n */\nrecord ApiReport(SortedSet<ClassInfo> types, Map<Status, List<Declaration>> declarations) {\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/ApiReportGenerator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toUnmodifiableSet;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.io.UncheckedIOException;\nimport java.lang.module.ModuleFinder;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.stream.Stream;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.ClassInfo;\nimport io.github.classgraph.MethodInfo;\nimport io.github.classgraph.ScanResult;\n\nimport org.apiguardian.api.API;\nimport org.apiguardian.api.API.Status;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\n\n/**\n * @since 1.0\n */\nclass ApiReportGenerator {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ApiReportGenerator.class);\n\tprivate static final String EOL = System.lineSeparator();\n\n\tpublic static void main(String... args) {\n\n\t\t// CAUTION: The output produced by this method is used to\n\t\t//          generate a table in the User Guide.\n\n\t\ttry (var scanResult = scanClasspath()) {\n\n\t\t\tvar apiReport = generateReport(scanResult);\n\n\t\t\t// ApiReportWriter reportWriter = new MarkdownApiReportWriter(apiReport);\n\t\t\tApiReportWriter reportWriter = new AsciidocApiReportWriter(apiReport);\n\t\t\t// ApiReportWriter reportWriter = new HtmlApiReportWriter(apiReport);\n\n\t\t\t// reportWriter.printReportHeader(new PrintWriter(System.out, true));\n\n\t\t\t// Print report for all Usage enum constants\n\t\t\t// reportWriter.printDeclarationInfo(new PrintWriter(System.out, true), EnumSet.allOf(Status.class));\n\n\t\t\t// Print report only for specific Status constants, defaults to only EXPERIMENTAL\n\t\t\tparseArgs(args).forEach((status, opener) -> {\n\t\t\t\ttry (var stream = opener.openStream()) {\n\t\t\t\t\tvar writer = new PrintWriter(stream == null ? System.out : stream, true, UTF_8);\n\t\t\t\t\treportWriter.printDeclarationInfo(writer, EnumSet.of(status));\n\t\t\t\t}\n\t\t\t\tcatch (IOException e) {\n\t\t\t\t\tthrow new UncheckedIOException(\"Failed to write report\", e);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static Map<Status, StreamOpener> parseArgs(String[] args) {\n\t\tMap<Status, StreamOpener> outputByStatus = new EnumMap<>(Status.class);\n\t\tif (args.length == 0) {\n\t\t\toutputByStatus.put(Status.EXPERIMENTAL, () -> null);\n\t\t}\n\t\telse {\n\t\t\tArrays.stream(args) //\n\t\t\t\t\t.map(arg -> arg.split(\"=\", 2)) //\n\t\t\t\t\t.forEach(parts -> outputByStatus.put(//\n\t\t\t\t\t\tStatus.valueOf(parts[0]), //\n\t\t\t\t\t\t() -> parts.length < 2 //\n\t\t\t\t\t\t\t\t? null //\n\t\t\t\t\t\t\t\t: new BufferedOutputStream(Files.newOutputStream(Path.of(parts[1]))) //\n\t\t\t\t\t));\n\t\t}\n\t\treturn outputByStatus;\n\t}\n\n\tprivate interface StreamOpener {\n\t\tOutputStream openStream() throws IOException;\n\t}\n\n\tprivate static ApiReport generateReport(ScanResult scanResult) {\n\t\tMap<Status, List<Declaration>> declarations = new EnumMap<>(Status.class);\n\t\tfor (var status : Status.values()) {\n\t\t\tdeclarations.put(status, new ArrayList<>());\n\t\t}\n\n\t\tvar types = collectTypes(scanResult);\n\t\ttypes.stream() //\n\t\t\t\t.map(Declaration.Type::new) //\n\t\t\t\t.forEach(type -> declarations.get(type.status()).add(type));\n\n\t\tcollectMethods(scanResult) //\n\t\t\t\t.map(Declaration.Method::new) //\n\t\t\t\t.filter(method -> !declarations.get(method.status()) //\n\t\t\t\t\t\t.contains(new Declaration.Type(method.classInfo()))) //\n\t\t\t\t.forEach(method -> {\n\t\t\t\t\ttypes.add(method.classInfo());\n\t\t\t\t\tdeclarations.get(method.status()).add(method);\n\t\t\t\t});\n\n\t\tdeclarations.values().forEach(list -> list.sort(null));\n\n\t\treturn new ApiReport(types, declarations);\n\t}\n\n\tprivate static ScanResult scanClasspath() {\n\t\t// scan all types below \"org.junit\" package\n\t\tvar classGraph = new ClassGraph() //\n\t\t\t\t.acceptPackages(\"org.junit\") //\n\t\t\t\t.rejectPackages(\"*.shadow.*\", \"org.opentest4j.*\", \"org.junit.platform.commons.logging\",\n\t\t\t\t\t\"org.junit.platform.commons.util\") //\n\t\t\t\t.disableNestedJarScanning() //\n\t\t\t\t.enableClassInfo() //\n\t\t\t\t.enableMethodInfo() //\n\t\t\t\t.enableAnnotationInfo(); //\n\t\tvar apiClasspath = System.getProperty(\"api.modulePath\");\n\t\tvar apiModules = System.getProperty(\"api.moduleNames\");\n\t\tif (apiClasspath != null && apiModules != null) {\n\t\t\tvar paths = Arrays.stream(apiClasspath.split(File.pathSeparator)).map(Path::of).toArray(Path[]::new);\n\t\t\tvar bootLayer = ModuleLayer.boot();\n\t\t\tvar roots = Arrays.stream(apiModules.split(\",\")).collect(toUnmodifiableSet());\n\t\t\tvar configuration = bootLayer.configuration().resolveAndBind(ModuleFinder.of(), ModuleFinder.of(paths),\n\t\t\t\troots);\n\t\t\tvar layer = bootLayer.defineModulesWithOneLoader(configuration, ClassLoader.getPlatformClassLoader());\n\t\t\tclassGraph = classGraph.overrideModuleLayers(layer);\n\t\t}\n\t\treturn classGraph.scan();\n\t}\n\n\tprivate static SortedSet<ClassInfo> collectTypes(ScanResult scanResult) {\n\t\tvar types = scanResult.getClassesWithAnnotation(API.class).stream() //\n\t\t\t\t.filter(it -> !it.getAnnotationInfo(API.class).isInherited()) //\n\t\t\t\t.collect(toCollection(TreeSet::new));\n\n\t\tlogger.debug(() -> {\n\t\t\tvar builder = new StringBuilder(\"Listing of all \" + types.size() + \" annotated types:\");\n\t\t\tbuilder.append(EOL);\n\t\t\ttypes.forEach(e -> builder.append(e.getName()).append(EOL));\n\t\t\treturn builder.toString();\n\t\t});\n\n\t\treturn types;\n\t}\n\n\tprivate static Stream<MethodInfo> collectMethods(ScanResult scanResult) {\n\t\treturn scanResult.getClassesWithMethodAnnotation(API.class).stream() //\n\t\t\t\t.flatMap(type -> type.getDeclaredMethodAndConstructorInfo().stream()) //\n\t\t\t\t.filter(m -> m.getAnnotationInfo(API.class) != null);\n\t}\n\n\tprivate ApiReportGenerator() {\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/ApiReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport java.io.PrintWriter;\nimport java.util.Set;\n\nimport org.apiguardian.api.API.Status;\n\n/**\n * @since 1.0\n */\ninterface ApiReportWriter {\n\n\tvoid printReportHeader(PrintWriter out);\n\n\tvoid printDeclarationInfo(PrintWriter out, Set<Status> statuses);\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport java.io.PrintWriter;\n\n/**\n * @since 1.0\n */\nclass AsciidocApiReportWriter extends AbstractApiReportWriter {\n\n\tprivate static final String ASCIIDOC_FORMAT = \"|%-\" + NAME_COLUMN_WIDTH + \"s | %-12s%n\";\n\n\tAsciidocApiReportWriter(ApiReport apiReport) {\n\t\tsuper(apiReport);\n\t}\n\n\t@Override\n\tprotected String h1(String header) {\n\t\treturn \"= \" + header;\n\t}\n\n\t@Override\n\tprotected String h2(String header) {\n\t\treturn \"== \" + header;\n\t}\n\n\t@Override\n\tprotected String h3(String header) {\n\t\treturn \"%n=== %s\".formatted(header);\n\t}\n\n\t@Override\n\tprotected String h4(String header) {\n\t\treturn \"%n==== %s\".formatted(header);\n\t}\n\n\t@Override\n\tprotected String code(String element) {\n\t\treturn \"`\" + element + \"`\";\n\t}\n\n\t@Override\n\tprotected String italic(String element) {\n\t\treturn \"_\" + element + \"_\";\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableHeader(PrintWriter out) {\n\t\tout.println(\"[cols=\\\"99,1\\\"]\");\n\t\tout.println(\"|===\");\n\t\tout.printf(ASCIIDOC_FORMAT, \"Name\", \"Since\");\n\t\tout.println();\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableRow(Declaration declaration, PrintWriter out) {\n\t\tout.printf(ASCIIDOC_FORMAT, //\n\t\t\tcode(declaration.name().replace(\".\", \".&ZeroWidthSpace;\")) + \" \" + italic(\"(\" + declaration.kind() + \")\"), //\n\t\t\tcode(declaration.since()) //\n\t\t);\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableFooter(PrintWriter out) {\n\t\tout.println(\"|===\");\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/Declaration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.Arrays;\n\nimport io.github.classgraph.AnnotationEnumValue;\nimport io.github.classgraph.AnnotationParameterValueList;\nimport io.github.classgraph.ClassInfo;\nimport io.github.classgraph.MethodInfo;\n\nimport org.apiguardian.api.API;\nimport org.apiguardian.api.API.Status;\n\nsealed interface Declaration extends Comparable<Declaration> {\n\n\tString moduleName();\n\n\tString packageName();\n\n\tString fullName();\n\n\tString name();\n\n\tString kind();\n\n\tStatus status();\n\n\tString since();\n\n\t@Override\n\tdefault int compareTo(Declaration o) {\n\t\treturn fullName().compareTo(o.fullName());\n\t}\n\n\trecord Type(ClassInfo classInfo) implements Declaration {\n\n\t\t@Override\n\t\tpublic String moduleName() {\n\t\t\treturn classInfo.getModuleRef().getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String packageName() {\n\t\t\treturn classInfo.getPackageName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String fullName() {\n\t\t\treturn classInfo.getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String name() {\n\t\t\tvar shortClassName = getShortClassName(classInfo);\n\t\t\treturn classInfo.isAnnotation() ? \"@\" + shortClassName : shortClassName;\n\t\t}\n\n\t\t@Override\n\t\tpublic String kind() {\n\t\t\treturn switch (classInfo) {\n\t\t\t\tcase ClassInfo ignored when classInfo.isRecord() -> \"record\";\n\t\t\t\tcase ClassInfo ignored when classInfo.isAnnotation() -> \"annotation\";\n\t\t\t\tcase ClassInfo ignored when classInfo.isEnum() -> \"enum\";\n\t\t\t\tcase ClassInfo ignored when classInfo.isInterface() -> \"interface\";\n\t\t\t\tdefault -> \"class\";\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic Status status() {\n\t\t\treturn readStatus(getParameterValues());\n\t\t}\n\n\t\t@Override\n\t\tpublic String since() {\n\t\t\treturn readSince(getParameterValues());\n\t\t}\n\n\t\tprivate AnnotationParameterValueList getParameterValues() {\n\t\t\treturn classInfo.getAnnotationInfo(API.class).getParameterValues();\n\t\t}\n\t}\n\n\trecord Method(MethodInfo methodInfo) implements Declaration {\n\n\t\t@Override\n\t\tpublic String moduleName() {\n\t\t\treturn classInfo().getModuleRef().getName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String packageName() {\n\t\t\treturn classInfo().getPackageName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String fullName() {\n\t\t\treturn \"%s.%s\".formatted(classInfo().getName(), methodSignature());\n\t\t}\n\n\t\t@Override\n\t\tpublic String name() {\n\t\t\tif (classInfo().isAnnotation()) {\n\t\t\t\treturn \"@%s(%s=...)\".formatted(getShortClassName(classInfo()), methodInfo.getName());\n\t\t\t}\n\t\t\tif (methodInfo.isConstructor()) {\n\t\t\t\treturn \"%s%s\".formatted(getShortClassName(classInfo()), methodParameters());\n\t\t\t}\n\t\t\treturn \"%s.%s\".formatted(getShortClassName(classInfo()), methodSignature());\n\t\t}\n\n\t\tprivate String methodSignature() {\n\t\t\treturn methodInfo.getName() + methodParameters();\n\t\t}\n\n\t\tprivate String methodParameters() {\n\t\t\treturn Arrays.stream(methodInfo.getParameterInfo()) //\n\t\t\t\t\t.map(parameterInfo -> parameterInfo.getTypeSignatureOrTypeDescriptor().toStringWithSimpleNames()) //\n\t\t\t\t\t.collect(joining(\", \", \"(\", \")\"));\n\t\t}\n\n\t\t@Override\n\t\tpublic String kind() {\n\t\t\tif (methodInfo.isConstructor()) {\n\t\t\t\treturn \"constructor\";\n\t\t\t}\n\t\t\tif (classInfo().isAnnotation()) {\n\t\t\t\treturn \"annotation attribute\";\n\t\t\t}\n\t\t\treturn \"method\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Status status() {\n\t\t\treturn readStatus(getParameterValues());\n\t\t}\n\n\t\t@Override\n\t\tpublic String since() {\n\t\t\treturn readSince(getParameterValues());\n\t\t}\n\n\t\tprivate AnnotationParameterValueList getParameterValues() {\n\t\t\treturn methodInfo.getAnnotationInfo(API.class).getParameterValues();\n\t\t}\n\n\t\tpublic ClassInfo classInfo() {\n\t\t\treturn methodInfo.getClassInfo();\n\t\t}\n\t}\n\n\tprivate static Status readStatus(AnnotationParameterValueList parameterValues) {\n\t\treturn Status.valueOf(((AnnotationEnumValue) parameterValues.getValue(\"status\")).getValueName());\n\t}\n\n\tprivate static String readSince(AnnotationParameterValueList parameterValues) {\n\t\treturn (String) parameterValues.getValue(\"since\");\n\t}\n\n\tprivate static String getShortClassName(ClassInfo classInfo) {\n\t\tvar typeName = classInfo.getName();\n\t\tvar packageName = classInfo.getPackageName();\n\t\tif (typeName.startsWith(packageName + '.')) {\n\t\t\ttypeName = typeName.substring(packageName.length() + 1);\n\t\t}\n\t\treturn typeName.replace('$', '.');\n\t}\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport java.io.PrintWriter;\n\n/**\n * @since 1.0\n */\nclass HtmlApiReportWriter extends AbstractApiReportWriter {\n\n\tprivate static final String HTML_HEADER_FORMAT = \"\\t<tr><th>%s</th><th>%s</th></tr>%n\";\n\tprivate static final String HTML_ROW_FORMAT = \"\\t<tr><td>%s</td><td>%s</td></tr>%n\";\n\n\tHtmlApiReportWriter(ApiReport apiReport) {\n\t\tsuper(apiReport);\n\t}\n\n\t@Override\n\tprotected String h1(String header) {\n\t\treturn \"<h1>\" + header + \"</h1>\";\n\t}\n\n\t@Override\n\tprotected String h2(String header) {\n\t\treturn \"<h2>\" + header + \"</h2>\";\n\t}\n\n\t@Override\n\tprotected String h3(String header) {\n\t\treturn \"<h3>\" + header + \"</h3>\";\n\t}\n\n\t@Override\n\tprotected String h4(String header) {\n\t\treturn \"<h4>\" + header + \"</h4>\";\n\t}\n\n\t@Override\n\tprotected String code(String element) {\n\t\treturn \"<span class='code'>\" + element + \"</span>\";\n\t}\n\n\t@Override\n\tprotected String italic(String element) {\n\t\treturn \"<em>\" + element + \"</em>\";\n\t}\n\n\t@Override\n\tprotected String paragraph(String element) {\n\t\treturn \"<p>\" + element + \"</p>\";\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableHeader(PrintWriter out) {\n\t\tout.println(\"<table>\");\n\t\tout.printf(HTML_HEADER_FORMAT, \"Name\", \"Since\");\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableRow(Declaration declaration, PrintWriter out) {\n\t\tout.printf(HTML_ROW_FORMAT, //\n\t\t\tcode(declaration.name()) + \" \" + italic(\"(\" + declaration.kind() + \")\"), //\n\t\t\tcode(declaration.since()) //\n\t\t);\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableFooter(PrintWriter out) {\n\t\tout.println(\"</table>\");\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/MarkdownApiReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.api.tools;\n\nimport java.io.PrintWriter;\nimport java.nio.CharBuffer;\n\n/**\n * @since 1.0\n */\nclass MarkdownApiReportWriter extends AbstractApiReportWriter {\n\n\tprivate static final String MARKDOWN_FORMAT = \"%-\" + NAME_COLUMN_WIDTH + \"s | %-12s%n\";\n\n\tMarkdownApiReportWriter(ApiReport apiReport) {\n\t\tsuper(apiReport);\n\t}\n\n\t@Override\n\tprotected String h1(String header) {\n\t\treturn \"# \" + header;\n\t}\n\n\t@Override\n\tprotected String h2(String header) {\n\t\treturn \"## \" + header;\n\t}\n\n\t@Override\n\tprotected String h3(String header) {\n\t\treturn \"### \" + header;\n\t}\n\n\t@Override\n\tprotected String h4(String header) {\n\t\treturn \"#### \" + header;\n\t}\n\n\t@Override\n\tprotected String code(String element) {\n\t\treturn \"`\" + element + \"`\";\n\t}\n\n\t@Override\n\tprotected String italic(String element) {\n\t\treturn \"_\" + element + \"_\";\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableHeader(PrintWriter out) {\n\t\tout.printf(MARKDOWN_FORMAT, \"Name\", \"Since\");\n\t\tout.printf(MARKDOWN_FORMAT, dashes(NAME_COLUMN_WIDTH), dashes(12));\n\t}\n\n\tprivate String dashes(int length) {\n\t\treturn CharBuffer.allocate(length).toString().replace('\\0', '-');\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableRow(Declaration declaration, PrintWriter out) {\n\t\tout.printf(MARKDOWN_FORMAT, //\n\t\t\tcode(declaration.name()) + \" \" + italic(\"(\" + declaration.kind() + \")\"), //\n\t\t\tcode(declaration.since()) //\n\t\t);\n\t}\n\n\t@Override\n\tprotected void printDeclarationTableFooter(PrintWriter out) {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "documentation/src/tools/java/org/junit/api/tools/package-info.java",
    "content": "/**\n * Tools to generate reports based on {@link org.apiguardian.api.API} annotations.\n */\n\npackage org.junit.api.tools;\n"
  },
  {
    "path": "gradle/base/code-generator-model/.gitignore",
    "content": "/bin/\n"
  },
  {
    "path": "gradle/base/code-generator-model/build.gradle.kts",
    "content": "plugins {\n    `kotlin-dsl`\n}\n"
  },
  {
    "path": "gradle/base/code-generator-model/src/main/kotlin/junitbuild/generator/model/JRE.kt",
    "content": "package junitbuild.generator.model\n\ndata class JRE(val version: Int, val since: String?)\n"
  },
  {
    "path": "gradle/base/code-generator-model/src/main/resources/jre.yaml",
    "content": "- version: 8\n- version: 9\n- version: 10\n- version: 11\n- version: 12\n  since: '5.4'\n- version: 13\n  since: '5.4'\n- version: 14\n  since: '5.5'\n- version: 15\n  since: '5.6'\n- version: 16\n  since: '5.7'\n- version: 17\n  since: '5.7.1'\n- version: 18\n  since: '5.8.1'\n- version: 19\n  since: '5.9'\n- version: 20\n  since: '5.9'\n- version: 21\n  since: '5.9.2'\n- version: 22\n  since: '5.10'\n- version: 23\n  since: '5.11'\n- version: 24\n  since: '5.11'\n- version: 25\n  since: '5.11.4'\n- version: 26\n  since: '5.13.2'\n- version: 27\n  since: '6.1'\n"
  },
  {
    "path": "gradle/base/dsl-extensions/.gitignore",
    "content": "/bin/\n"
  },
  {
    "path": "gradle/base/dsl-extensions/build.gradle.kts",
    "content": "plugins {\n    `kotlin-dsl`\n}\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/DependencyExtensions.kt",
    "content": "package junitbuild.extensions\n\nimport org.gradle.api.provider.Provider\nimport org.gradle.plugin.use.PluginDependency\n\n// see https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers\nval Provider<PluginDependency>.markerCoordinates: Provider<String>\n    get() = map { \"${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}\" }\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/ProjectExtensions.kt",
    "content": "package junitbuild.extensions\n\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.ProjectDependency\nimport org.gradle.api.artifacts.VersionCatalog\nimport org.gradle.api.artifacts.VersionCatalogsExtension\nimport org.gradle.kotlin.dsl.the\n\nval Project.javaModuleName: String\n    get() = toModuleName(name)\n\nval ProjectDependency.javaModuleName: String\n    get() = toModuleName(name)\n\nprivate fun toModuleName(name: String) = \"org.${name.replace('-', '.')}\"\n\nfun Project.dependencyProject(dependency: ProjectDependency) =\n    project(dependency.path)\n\nfun Project.requiredVersionFromLibs(name: String) =\n    libsVersionCatalog.findVersion(name).get().requiredVersion\n\nfun Project.dependencyFromLibs(name: String) =\n    libsVersionCatalog.findLibrary(name).get()\n\nfun Project.bundleFromLibs(name: String) =\n    libsVersionCatalog.findBundle(name).get()\n\nprivate val Project.libsVersionCatalog: VersionCatalog\n    get() = the<VersionCatalogsExtension>().named(\"libs\")\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/StringExtensions.kt",
    "content": "package junitbuild.extensions\n\nimport java.util.Locale\n\nfun String.capitalized() = replaceFirstChar {\n    it.uppercase(Locale.US)\n}\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/TaskExtensions.kt",
    "content": "package junitbuild.extensions\n\nimport org.gradle.api.Task\nimport org.gradle.api.file.ArchiveOperations\nimport org.gradle.internal.os.OperatingSystem\nimport org.gradle.kotlin.dsl.newInstance\nimport javax.inject.Inject\n\nfun Task.trackOperationSystemAsInput() =\n    inputs.property(\"os\", OperatingSystem.current().familyName)\n\nfun <T> Task.withArchiveOperations(action: (ArchiveOperations) -> T): T =\n    archiveOperations.run { action(this) }\n\nprivate val Task.archiveOperations: ArchiveOperations\n    get() = project.objects.newInstance(DummyObject::class).archiveOperations\n\nprivate abstract class DummyObject {\n    @get:Inject\n    abstract val archiveOperations: ArchiveOperations\n}\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/VersionExtensions.kt",
    "content": "package junitbuild.extensions\n\nfun Any.isSnapshot(): Boolean = toString().contains(\"SNAPSHOT\")\n"
  },
  {
    "path": "gradle/base/dsl-extensions/src/main/kotlin/junitbuild/extensions/junitbuild.dsl-extensions.gradle.kts",
    "content": "// Just a dummy plugin to get the extensions on the classpath of downstream builds\n"
  },
  {
    "path": "gradle/base/gradle.properties",
    "content": "group = junitbuild.base\n"
  },
  {
    "path": "gradle/base/settings.gradle.kts",
    "content": "rootProject.name = \"base\"\n\ndependencyResolutionManagement {\n    repositories {\n        gradlePluginPortal()\n    }\n}\n\ninclude(\"code-generator-model\")\ninclude(\"dsl-extensions\")\n"
  },
  {
    "path": "gradle/config/checkstyle/checkstyleMain.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\" \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n\t<property name=\"severity\" value=\"error\" />\n\n\t<module name=\"TreeWalker\">\n\t\t<module name=\"SuppressWarningsHolder\" />\t\n\t\t<module name=\"JavadocMethod\">\n\t\t\t<property name=\"allowMissingParamTags\" value=\"true\" />\n\t\t\t<property name=\"allowMissingReturnTag\" value=\"true\" />\n\t\t</module>\n\t\t<module name=\"AtclauseOrder\">\n\t\t\t<property name=\"tagOrder\" value=\"@param, @return, @throws, @exception, @since, @see\" />\n\t\t</module>\n\t\t<module name=\"NonEmptyAtclauseDescription\" />\n\t\t<module name=\"UnusedImports\">\n\t\t\t<property name=\"processJavadoc\" value=\"true\" />\n\t\t</module>\n\t\t<module name=\"AvoidStarImport\"/>\n\t\t<module name=\"IllegalImport\">\n\t\t\t<property name=\"illegalPkgs\" value=\"org.jetbrains.annotations\" />\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"primitiveClassLiterals\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"(Boolean|Character|Byte|Short|Integer|Long|Float|Double|Void)\\.TYPE\"/>\n\t\t\t<property name=\"message\" value=\"Please use class literals for primitives and void -- for example, int.class instead of Integer.TYPE.\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"stringFormattingMethod\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"String\\.format\"/>\n\t\t\t<property name=\"message\" value=\"Please use String.formatted() (non-static) rather than String.format() (static).\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<!-- see https://github.com/junit-team/junit-framework/issues/4604 -->\n\t\t\t<property name=\"id\" value=\"jupiterAssertions\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"org\\.junit\\.jupiter\\.api\\.(Assertions|Assumptions)\\.\"/>\n\t\t\t<property name=\"message\" value=\"Assertions/Assumptions should not be used in production code.\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"HideUtilityClassConstructor\"/>\n\t\t<module name=\"ModifierOrder\"/>\n\t\t<module name=\"RedundantModifier\"/>\n\t\t<module name=\"EqualsAvoidNull\"/>\n\t\t<module name=\"EmptyStatement\"/>\n\t\t<module name=\"MissingDeprecated\"/>\n\t\t<!-- non-private static final fields must be constants and named accordingly.\n\t\t \t See CONTRIBUTING.md - Constant fields -->\n\t\t<module name=\"ConstantName\">\n\t\t\t<property name=\"applyToPrivate\" value=\"false\"/>\n\t\t</module>\n\t\t<module name=\"ConstantName\">\n\t\t\t<!-- private static final fields may be mutable or immutable.\n\t\t\t\t But logger is always mutable. See CONTRIBUTING.md - Constant fields -->\n\t\t\t<property name=\"format\" value=\"^(?!^LOGGER$).*$\"/>\n\t\t\t<property name=\"applyToPublic\" value=\"false\"/>\n\t\t\t<property name=\"applyToProtected\" value=\"false\"/>\n\t\t\t<property name=\"applyToPackage\" value=\"false\"/>\n\t\t</module>\n\n\t</module>\n\n\t<module name=\"JavadocPackage\" />\n\n\t<module name=\"SuppressWarningsFilter\" />\n\n\t<module name=\"RegexpSingleline\">\n\t\t<property name=\"format\" value=\"@author\" />\n\t\t<property name=\"message\" value=\"Don't use Javadoc @author tags\" />\n\t\t<property name=\"fileExtensions\" value=\"java,groovy,kt\" />\n\t</module>\n\n\t<module name=\"SuppressionFilter\">\n\t\t<property name=\"file\" value=\"${config_loc}/suppressions.xml\"/>\n\t</module>\n\n\t<module name=\"BeforeExecutionExclusionFileFilter\">\n\t\t<property name=\"fileNamePattern\" value=\"module\\-info\\.java$\"/>\n\t</module>\n\n</module>\n"
  },
  {
    "path": "gradle/config/checkstyle/checkstyleNohttp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"\n        \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n    <module name=\"io.spring.nohttp.checkstyle.check.NoHttpCheck\"/>\n    <module name=\"SuppressWithPlainTextCommentFilter\"/>\n    <module name=\"SuppressionFilter\">\n        <property name=\"file\" value=\"${config_loc}/suppressions.xml\"/>\n    </module>\n</module>\n"
  },
  {
    "path": "gradle/config/checkstyle/checkstyleTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE module PUBLIC \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\" \"https://checkstyle.org/dtds/configuration_1_3.dtd\">\n<module name=\"Checker\">\n\t<property name=\"severity\" value=\"error\" />\n\n\t<module name=\"TreeWalker\">\n\t\t<module name=\"SuppressWarningsHolder\" />\n\t\t<module name=\"UnusedImports\">\n\t\t\t<property name=\"processJavadoc\" value=\"true\" />\n\t\t</module>\n\t\t<module name=\"AvoidStarImport\"/>\n\t\t<module name=\"IllegalImport\">\n\t\t\t<property name=\"illegalPkgs\" value=\"org.jetbrains.annotations\" />\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"primitiveClassLiterals\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"(Boolean|Character|Byte|Short|Integer|Long|Float|Double|Void)\\.TYPE\"/>\n\t\t\t<property name=\"message\" value=\"Please use class literals for primitives and void -- for example, int.class instead of Integer.TYPE.\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck\">\n\t\t\t<property name=\"id\" value=\"stringFormattingMethod\"/>\n\t\t\t<property name=\"maximum\" value=\"0\"/>\n\t\t\t<property name=\"format\" value=\"String\\.format\"/>\n\t\t\t<property name=\"message\" value=\"Please use String.formatted() (non-static) rather than String.format() (static).\"/>\n\t\t\t<property name=\"ignoreComments\" value=\"true\"/>\n\t\t</module>\n\t\t<module name=\"RedundantModifier\"/>\n\t</module>\n\n\t<module name=\"SuppressWarningsFilter\" />\n\n\t<module name=\"RegexpSingleline\">\n\t\t<property name=\"format\" value=\"@author\" />\n\t\t<property name=\"message\" value=\"Don't use Javadoc @author tags\" />\n\t\t<property name=\"fileExtensions\" value=\"java,groovy,kt\" />\n\t</module>\n\n</module>\n"
  },
  {
    "path": "gradle/config/checkstyle/suppressions.xml",
    "content": "<!DOCTYPE suppressions PUBLIC \"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN\" \"https://checkstyle.org/dtds/suppressions_1_2.dtd\">\n<suppressions>\n\t<suppress checks=\"JavadocPackage\"\n\t\tfiles=\"junit-jupiter-api[\\\\/]build[\\\\/]generated[\\\\/]sources[\\\\/]jte[\\\\/]main[\\\\/]org[\\\\/]junit[\\\\/]jupiter[\\\\/]api[\\\\/]condition[\\\\/]*\"/>\n\t<suppress checks=\"NoHttp\"\n\t\tfiles=\"documentation[\\\\/]modules[\\\\/]ROOT[\\\\/]images[\\\\/]extensions_StoreHierarchy.svg\"/>\n</suppressions>\n"
  },
  {
    "path": "gradle/config/eclipse/junit-eclipse-formatter-settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"23\">\n    <profile kind=\"CodeFormatterProfile\" name=\"JUnit\" version=\"23\">\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_with_spaces\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_record_components\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_logical_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"20\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_shift_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_parameters\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_loops\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.text_block_indentation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_module_statements\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_annotations\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"20\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines\" value=\"2147483647\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_not_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_type_arguments\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.javadoc_do_not_separate_block_tags\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_tag_description\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_record_constructor\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_string_concatenation\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_shift_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_shift_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_additive_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case_after_arrow\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_relational_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_logical_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"48\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_method_body_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_additive_operator\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_relational_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_lambda_body\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"20\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_relational_operator\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_arrows_in_switch_on_columns\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_additive_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_record_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"80\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_additive_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_conditional_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.join_line_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_shift_operator\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_code_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_assignment_operator\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method\" value=\"49\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assertion_message\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_logical_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"20\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_relational_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"16\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_logical_operator\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration\" value=\"common_lines\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line\" value=\"one_line_never\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"1\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"false\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block\" value=\"0\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"tab\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.wrap_before_string_concatenation\" value=\"true\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"120\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n        <setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n    </profile>\n</profiles>\n"
  },
  {
    "path": "gradle/config/eclipse/junit-eclipse.importorder",
    "content": "#Organize Import Order\n0=java\n1=javax\n2=jdk\n3=aQute\n4=junit\n5=de\n6=com\n7=example\n8=extensions\n9=io\n10=org\n"
  },
  {
    "path": "gradle/config/roseau/config.yaml",
    "content": "common:\n  excludes:\n    annotations:\n      - name: org.apiguardian.api.API\n        args: { status: org.apiguardian.api.API$Status.INTERNAL }\n"
  },
  {
    "path": "gradle/config/spotless/eclipse-public-license-2.0.java",
    "content": "/*\n * Copyright $YEAR the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n"
  },
  {
    "path": "gradle/gradle-daemon-jvm.properties",
    "content": "#This file is generated by updateDaemonJvm\ntoolchainVersion=25\n"
  },
  {
    "path": "gradle/libs.versions.toml",
    "content": "[versions]\nant = \"1.10.17\"\napiguardian = \"1.1.2\"\nassertj = \"3.27.7\"\nbnd = \"7.2.3\"\ncheckstyle = \"13.4.2\"\neclipse = \"4.39.0\"\njackson = \"3.1.3\"\njacoco = \"0.8.14\"\njmh = \"1.37\"\njunit4 = \"4.13.2\"\njunit4Min = \"4.12\"\nktlint = \"1.8.0\"\nlog4j = \"2.26.0\"\nopentest4j = \"1.3.0\"\nopenTestReporting = \"0.2.5\"\nsnapshotTests = \"1.11.0\"\nsurefire = \"3.5.5\"\nxmlunit = \"2.11.0\"\n\n[libraries]\nant = { module = \"org.apache.ant:ant\", version.ref = \"ant\" }\nant-junit = { module = \"org.apache.ant:ant-junit\", version.ref = \"ant\" }\nant-junitlauncher = { module = \"org.apache.ant:ant-junitlauncher\", version.ref = \"ant\" }\napiguardian = { module = \"org.apiguardian:apiguardian-api\", version.ref = \"apiguardian\" }\narchunit = { module = \"com.tngtech.archunit:archunit-junit5\", version = \"1.4.2\" }\nassertj = { module = \"org.assertj:assertj-core\", version.ref = \"assertj\" }\nbndlib = { module = \"biz.aQute.bnd:biz.aQute.bndlib\", version.ref = \"bnd\" }\nbyteBuddy = { module = \"net.bytebuddy:byte-buddy\", version = \"1.18.8\" }\ncheckstyle = { module = \"com.puppycrawl.tools:checkstyle\", version.ref = \"checkstyle\" }\nclassgraph = { module = \"io.github.classgraph:classgraph\", version = \"4.8.184\" }\ncommons-io = { module = \"commons-io:commons-io\", version = \"2.22.0\" }\nerror-prone-contrib = { module = \"tech.picnic.error-prone-support:error-prone-contrib\", version = \"0.29.0\" }\nerror-prone-core = { module = \"com.google.errorprone:error_prone_core\", version = \"2.49.0\" }\nfastcsv = { module = \"de.siegmar:fastcsv\", version = \"4.2.0\" }\ngroovy = { module = \"org.apache.groovy:groovy\", version = \"5.0.5\" }\ngroovy2-bom = { module = \"org.codehaus.groovy:groovy-bom\", version = \"2.5.23\" }\nhamcrest = { module = \"org.hamcrest:hamcrest\", version = \"3.0\" }\njackson-dataformat-yaml = { module = \"tools.jackson.dataformat:jackson-dataformat-yaml\", version.ref = \"jackson\" }\njackson-module-kotlin = { module = \"tools.jackson.module:jackson-module-kotlin\", version.ref = \"jackson\" }\njaxb-api = { module = \"jakarta.xml.bind:jakarta.xml.bind-api\", version = \"4.0.5\" }\njaxb-runtime = { module = \"org.glassfish.jaxb:jaxb-runtime\", version = \"4.0.8\" }\njfrunit = { module = \"org.moditect.jfrunit:jfrunit-core\", version = \"1.0.0.Alpha2\" }\njimfs = { module = \"com.google.jimfs:jimfs\", version = \"1.3.1\" }\njmh-core = { module = \"org.openjdk.jmh:jmh-core\", version.ref = \"jmh\" }\njmh-generator-annprocess = { module = \"org.openjdk.jmh:jmh-generator-annprocess\", version.ref = \"jmh\" }\njoox = { module = \"org.jooq:joox\", version = \"2.0.1\" }\njspecify = { module = \"org.jspecify:jspecify\", version = \"1.0.0\" }\njte = { module = \"gg.jte:jte\", version = \"3.2.4\" }\njunit4 = { module = \"junit:junit\", version = { require = \"[4.12,)\", prefer = \"4.13.2\" } }\nkotlinx-coroutines-core = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-core\", version = \"1.11.0\" }\nkotlinx-coroutines-test = { module = \"org.jetbrains.kotlinx:kotlinx-coroutines-test\", version = \"1.11.0\" }\nlog4j-bom = { module = \"org.apache.logging.log4j:log4j-bom\", version.ref = \"log4j\" }\nlog4j-core = { module = \"org.apache.logging.log4j:log4j-core\", version.ref = \"log4j\" }\nlog4j-jul = { module = \"org.apache.logging.log4j:log4j-jul\", version.ref = \"log4j\" }\nmaven = { module = \"org.apache.maven:apache-maven\", version = \"3.9.15\" }\nmavenSurefirePlugin = { module = \"org.apache.maven.plugins:maven-surefire-plugin\", version.ref = \"surefire\" }\nmemoryfilesystem = { module = \"com.github.marschall:memoryfilesystem\", version = \"2.8.2\" }\nmockito-bom = { module = \"org.mockito:mockito-bom\", version = \"5.23.0\" }\nmockito-core = { module = \"org.mockito:mockito-core\" }\nmockito-junit-jupiter = { module = \"org.mockito:mockito-junit-jupiter\" }\nnohttp-checkstyle = { module = \"io.spring.nohttp:nohttp-checkstyle\", version = \"0.0.11\" }\nnullaway = { module = \"com.uber.nullaway:nullaway\", version = \"0.13.4\" }\nopentest4j = { module = \"org.opentest4j:opentest4j\", version.ref = \"opentest4j\" }\nopenTestReporting-cli = { module = \"org.opentest4j.reporting:open-test-reporting-cli\", version.ref = \"openTestReporting\" }\nopenTestReporting-events = { module = \"org.opentest4j.reporting:open-test-reporting-events\", version.ref = \"openTestReporting\" }\nopenTestReporting-tooling-core = { module = \"org.opentest4j.reporting:open-test-reporting-tooling-core\", version.ref = \"openTestReporting\" }\nopenTestReporting-tooling-spi = { module = \"org.opentest4j.reporting:open-test-reporting-tooling-spi\", version.ref = \"openTestReporting\" }\npicocli = { module = \"info.picocli:picocli\", version = \"4.7.7\" }\nroseau-cli = { module = \"io.github.alien-tools:roseau-cli\", version = \"0.5.0\" }\nslf4j-julBinding = { module = \"org.slf4j:slf4j-jdk14\", version = \"2.0.17\" }\nsnapshotTests-junit5 = { module = \"de.skuzzle.test:snapshot-tests-junit5\", version.ref = \"snapshotTests\" }\nsnapshotTests-xml = { module = \"de.skuzzle.test:snapshot-tests-xml\", version.ref = \"snapshotTests\" }\nspock1 = { module = \"org.spockframework:spock-core\", version = \"1.3-groovy-2.5\" }\nxmlunit-assertj = { module = \"org.xmlunit:xmlunit-assertj3\", version.ref = \"xmlunit\" }\nxmlunit-placeholders = { module = \"org.xmlunit:xmlunit-placeholders\", version.ref = \"xmlunit\" }\nxmlunit-jakarta-jaxb-impl = { module = \"org.xmlunit:xmlunit-jakarta-jaxb-impl\", version.ref = \"xmlunit\" }\ntestingAnnotations = { module = \"com.gradle:develocity-testing-annotations\", version = \"2.0.1\" }\nwoodstox = { module = \"com.fasterxml.woodstox:woodstox-core\", version = \"7.1.1\" }\n\n# Only declared here so Dependabot knows when to update the referenced versions\neclipse-platform = { module = \"org.eclipse.platform:org.eclipse.platform\", version.ref = \"eclipse\" }\njacoco = { module = \"org.jacoco:jacoco\", version.ref = \"jacoco\" }\njunit4-latest = { module = \"junit:junit\", version.ref = \"junit4\" }\njunit4-bundle = { module = \"org.apache.servicemix.bundles:org.apache.servicemix.bundles.junit\", version = \"4.13.2_1\" }\nktlint-cli = { module = \"com.pinterest.ktlint:ktlint-cli\", version.ref = \"ktlint\" }\n\n[bundles]\nant = [\"ant\", \"ant-junit\", \"ant-junitlauncher\"]\nlog4j = [\"log4j-core\", \"log4j-jul\"]\nxmlunit = [\"xmlunit-assertj\", \"xmlunit-placeholders\", \"xmlunit-jakarta-jaxb-impl\", \"jaxb-api\", \"jaxb-runtime\"]\n\n[plugins]\nbnd = { id = \"biz.aQute.bnd\", version.ref = \"bnd\" }\nbuildParameters = { id = \"org.gradlex.build-parameters\", version = \"1.4.5\" }\ncommonCustomUserData = { id = \"com.gradle.common-custom-user-data-gradle-plugin\", version = \"2.6.0\" }\ndevelocity = { id = \"com.gradle.develocity\", version = \"4.4.1\" }\ndownload = { id = \"de.undercouch.download\", version = \"5.7.0\" }\nerrorProne = { id = \"net.ltgt.errorprone\", version = \"5.1.0\" }\nfoojayResolver = { id = \"org.gradle.toolchains.foojay-resolver\", version = \"1.0.0\" }\njmh = { id = \"me.champeau.jmh\", version = \"0.7.3\" }\n# check if workaround in gradle.properties can be removed when updating\nkotlin = { id = \"org.jetbrains.kotlin.jvm\", version = \"2.3.21\" }\nnmcp-settings = { id = \"com.gradleup.nmcp.settings\", version = \"1.5.0\" }\nnode = { id = \"com.github.node-gradle.node\", version = \"7.1.0\" }\nnullaway = { id = \"net.ltgt.nullaway\", version = \"3.0.0\" }\nplantuml = { id = \"io.freefair.plantuml\", version = \"9.5.0\" }\nshadow = { id = \"com.gradleup.shadow\", version = \"9.4.1\" }\nspotless = { id = \"com.diffplug.spotless\", version = \"8.4.0\" }\nspring-antora = { id = \"io.spring.antora.generate-antora-yml\", version = \"0.0.1\" }\n"
  },
  {
    "path": "gradle/plugins/antora/build.gradle.kts",
    "content": "import junitbuild.extensions.markerCoordinates\n\nplugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(projects.buildParameters)\n\timplementation(libs.plugins.node.markerCoordinates)\n\tconstraints {\n\t\timplementation(\"com.fasterxml.jackson.core:jackson-core\") {\n\t\t\tversion {\n\t\t\t\trequire(\"2.21.1\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for GHSA-72hv-8253-57qq\")\n\t\t}\n\t}\n\timplementation(libs.plugins.spring.antora.markerCoordinates)\n\tconstraints {\n\t\timplementation(\"org.yaml:snakeyaml\") {\n\t\t\tversion {\n\t\t\t\trequire(\"2.0\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2022-1471\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/antora/src/main/kotlin/junitbuild/antora/AntoraConfiguration.kt",
    "content": "package junitbuild.antora\n\nimport org.gradle.api.file.DirectoryProperty\n\ninterface AntoraConfiguration {\n    val siteDir: DirectoryProperty\n}\n"
  },
  {
    "path": "gradle/plugins/antora/src/main/kotlin/junitbuild.antora-conventions.gradle.kts",
    "content": "import com.github.gradle.node.npm.task.NpxTask\nimport junitbuild.antora.AntoraConfiguration\n\nplugins {\n\tid(\"com.github.node-gradle.node\")\n\tid(\"io.spring.antora.generate-antora-yml\")\n\tid(\"junitbuild.build-parameters\")\n}\n\nval configuration = extensions.create<AntoraConfiguration>(\"antora\")\n\nval siteDir = layout.buildDirectory.dir(\"antora-site\")\nconfiguration.siteDir.value(siteDir).finalizeValue()\n\nrepositories {\n\t// Redefined here because the Node.js plugin adds a repo\n\tmavenCentral()\n}\n\ntasks.register(\"generateAntoraResources\") {\n\tdependsOn(\"generateAntoraYml\")\n}\n\nval generateAntoraPlaybook by tasks.registering(Copy::class) {\n\n\tval gitRepoRoot = providers.exec {\n\t\tcommandLine(\"git\", \"worktree\", \"list\", \"--porcelain\", \"-z\")\n\t}.standardOutput.asText.map { it.substringBefore('\\u0000').substringAfter(' ') }\n\tinputs.property(\"gitRepoRoot\", gitRepoRoot)\n\n\tval gitBranchName = providers.exec {\n\t\tcommandLine(\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\")\n\t}.standardOutput.asText.map { it.trim() }\n\tinputs.property(\"gitBranchName\", gitBranchName)\n\n\tfrom(layout.projectDirectory.file(\"antora-playbook.yml\").asFile)\n\tfilter { line ->\n\t\tvar result = line\n\t\tif (line.contains(\"@GIT_REPO_ROOT@\")) {\n\t\t\tresult = result.replace(\"@GIT_REPO_ROOT@\", gitRepoRoot.get())\n\t\t}\n\t\tif (line.contains(\"@GIT_BRANCH_NAME@\")) {\n\t\t\tresult = result.replace(\"@GIT_BRANCH_NAME@\", gitBranchName.get())\n\t\t}\n\t\treturn@filter result\n\t}\n\tinto(layout.buildDirectory.dir(\"antora-playbook\"))\n}\n\nnode {\n\tdownload = buildParameters.antora.downloadNode\n\tversion = providers.fileContents(layout.projectDirectory.file(\".tool-versions\")).asText.map {\n\t\tit.substringAfter(\"nodejs\").trim()\n\t}\n}\n\ntasks.npmInstall {\n\targs.addAll(\"--no-audit\", \"--no-package-lock\", \"--no-fund\")\n}\n\ntasks.register<NpxTask>(\"antora\") {\n\tdependsOn(tasks.npmInstall)\n\tdescription = \"Runs Antora to generate a documentation site described by the playbook file.\"\n\n\tcommand = \"antora\"\n\targs.addAll(\"--clean\", \"--stacktrace\", \"--fetch\", \"--log-format=pretty\", \"--log-level=all\")\n\n\targs.add(\"--to-dir\")\n\targs.add(siteDir.map { it.asFile.toRelativeString(layout.projectDirectory.asFile) })\n\toutputs.dir(siteDir)\n\n\toutputs.upToDateWhen { false } // not all inputs are tracked\n\n\tval playbook = generateAntoraPlaybook.map { it.rootSpec.destinationDir!!.resolve(\"antora-playbook.yml\") }\n\targs.add(playbook.map { it.toRelativeString(layout.projectDirectory.asFile) })\n\tinputs.file(playbook)\n\n\texecOverrides {\n\t\tenvironment[\"IS_TTY\"] = true\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/backward-compatibility/build.gradle.kts",
    "content": "import junitbuild.extensions.markerCoordinates\n\nplugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(\"junitbuild.base:dsl-extensions\")\n\timplementation(libs.plugins.download.markerCoordinates)\n\timplementation(libs.jackson.dataformat.yaml)\n}\n"
  },
  {
    "path": "gradle/plugins/backward-compatibility/src/main/kotlin/junitbuild/compatibility/BackwardCompatibilityChecksExtension.kt",
    "content": "package junitbuild.compatibility\n\nimport org.gradle.api.provider.Property\n\nabstract class BackwardCompatibilityChecksExtension {\n\n    abstract val enabled: Property<Boolean>\n\n    abstract val previousVersion: Property<String>\n\n}\n"
  },
  {
    "path": "gradle/plugins/backward-compatibility/src/main/kotlin/junitbuild/compatibility/roseau/RoseauDiff.kt",
    "content": "package junitbuild.compatibility.roseau\n\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.GradleException\nimport org.gradle.api.file.ConfigurableFileCollection\nimport org.gradle.api.file.DirectoryProperty\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Classpath\nimport org.gradle.api.tasks.CompileClasspath\nimport org.gradle.api.tasks.InputFile\nimport org.gradle.api.tasks.Optional\nimport org.gradle.api.tasks.OutputDirectory\nimport org.gradle.api.tasks.PathSensitive\nimport org.gradle.api.tasks.PathSensitivity\nimport org.gradle.api.tasks.TaskAction\nimport org.gradle.kotlin.dsl.assign\nimport org.gradle.process.ExecOperations\nimport tools.jackson.databind.ObjectMapper\nimport tools.jackson.dataformat.yaml.YAMLFactory\nimport tools.jackson.dataformat.yaml.YAMLWriteFeature.WRITE_DOC_START_MARKER\nimport java.io.ByteArrayOutputStream\nimport java.io.File\nimport java.io.FileOutputStream\nimport javax.inject.Inject\n\n@CacheableTask\nabstract class RoseauDiff : DefaultTask() {\n\n    @get:Inject\n    abstract val execOperations: ExecOperations\n\n    @get:Classpath\n    abstract val toolClasspath: ConfigurableFileCollection\n\n    @get:CompileClasspath\n    abstract val libraryClasspath: ConfigurableFileCollection\n\n    @get:CompileClasspath\n    abstract val v1: RegularFileProperty\n\n    @get:CompileClasspath\n    abstract val v2: RegularFileProperty\n\n    @get:InputFile\n    @get:PathSensitive(PathSensitivity.NONE)\n    abstract val configFile: RegularFileProperty\n\n    @get:InputFile\n    @get:PathSensitive(PathSensitivity.NONE)\n    @get:Optional\n    abstract val acceptedChangesCsvFile: RegularFileProperty\n\n    @get:OutputDirectory\n    abstract val reportDir: DirectoryProperty\n\n    @TaskAction\n    fun run() {\n        val reportDir = reportDir.get().asFile.absoluteFile\n        val reports = listOf(\n            Report(reportDir.resolve(\"breaking-changes.html\"), Report.Format.HTML),\n            Report(reportDir.resolve(\"breaking-changes.csv\"), Report.Format.CSV)\n        )\n        reports.forEach { report -> report.file.delete() }\n\n        val effectiveConfigFile = writeEffectiveConfigFile(reports)\n\n        val output = ByteArrayOutputStream()\n        val result = execOperations.javaexec {\n            mainClass = \"io.github.alien.roseau.cli.RoseauCLI\"\n            classpath = toolClasspath\n            args(\n                \"--classpath\", libraryClasspath.asPath,\n                \"--v1\", v1.get().asFile.absolutePath,\n                \"--v2\", v2.get().asFile.absolutePath,\n                \"--diff\",\n                \"--fail-on-bc\",\n                \"--config\", effectiveConfigFile.absolutePath,\n                \"-vv\",\n            )\n            if (acceptedChangesCsvFile.isPresent) {\n                args(\"--ignored\", acceptedChangesCsvFile.get().asFile.absolutePath)\n            }\n            standardOutput = output\n            errorOutput = output\n            isIgnoreExitValue = true\n        }\n        if (result.exitValue != 0) {\n            System.out.write(output.toByteArray())\n            System.out.flush()\n            if (result.exitValue == 1) {\n                throw GradleException(\"Breaking API changes detected\")\n            }\n            result.assertNormalExitValue()\n        }\n    }\n\n    private fun writeEffectiveConfigFile(reports: List<Report>): File {\n        val effectiveConfigFile = temporaryDir.resolve(\"roseau.yaml\")\n        configFile.get().asFile.copyTo(effectiveConfigFile, overwrite = true)\n        FileOutputStream(effectiveConfigFile, true).bufferedWriter().use { writer ->\n            val yamlFactory = YAMLFactory.builder().disable(WRITE_DOC_START_MARKER).build()\n            val mapper = ObjectMapper(yamlFactory)\n            mapper.writeValue(\n                writer, mapOf(\n                    \"reports\" to reports\n                )\n            )\n        }\n        return effectiveConfigFile\n    }\n\n    private data class Report(val file: File, val format: Format) {\n        enum class Format {\n            HTML, CSV\n        }\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/backward-compatibility/src/main/kotlin/junitbuild.backward-compatibility.gradle.kts",
    "content": "\nimport de.undercouch.gradle.tasks.download.Download\nimport junitbuild.compatibility.BackwardCompatibilityChecksExtension\nimport junitbuild.compatibility.roseau.RoseauDiff\nimport junitbuild.extensions.dependencyFromLibs\n\nplugins {\n\tjava\n\tid(\"de.undercouch.download\")\n}\n\nval roseauDependencies = configurations.dependencyScope(\"roseau\")\nval roseauClasspath = configurations.resolvable(\"roseauClasspath\") {\n\textendsFrom(roseauDependencies.get())\n}\ndependencies {\n\troseauDependencies(dependencyFromLibs(\"roseau-cli\"))\n\troseauDependencies(platform(dependencyFromLibs(\"log4j-bom\"))) {\n\t\tbecause(\"Workaround for CVE-2025-68161\")\n\t}\n\tconstraints {\n\t\troseauDependencies(\"org.apache.commons:commons-lang3\") {\n\t\t\tversion {\n\t\t\t\trequire(\"3.18.0\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2025-48924\")\n\t\t}\n\t\troseauDependencies(\"com.fasterxml.jackson.core:jackson-core\") {\n\t\t\tversion {\n\t\t\t\trequire(\"2.21.1\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for GHSA-72hv-8253-57qq\")\n\t\t}\n\t}\n}\n\nval extension = extensions.create<BackwardCompatibilityChecksExtension>(\"backwardCompatibilityChecks\").apply {\n\tenabled.convention(true)\n\tpreviousVersion.apply {\n\t\tconvention(providers.gradleProperty(\"apiBaselineVersion\"))\n\t\tfinalizeValueOnRead()\n\t}\n}\n\nval downloadPreviousReleaseJar by tasks.registering(Download::class) {\n\tif (gradle.startParameter.isOffline) {\n\t\tenabled = false\n\t}\n\tonlyIf { extension.enabled.get() }\n\tval previousVersion = extension.previousVersion.get()\n\tsrc(\"https://repo1.maven.org/maven2/${project.group.toString().replace(\".\", \"/\")}/${project.name}/$previousVersion/${project.name}-$previousVersion.jar\")\n\tdest(layout.buildDirectory.dir(\"previousRelease\"))\n\toverwrite(false)\n\tquiet(true)\n\tretries(2)\n\toutputs.cacheIf { true }\n}\n\nval roseauCsvFile = layout.buildDirectory.file(\"reports/roseau/breaking-changes.csv\")\n\nval roseau by tasks.registering(RoseauDiff::class) {\n\tif (gradle.startParameter.isOffline) {\n\t\tenabled = false\n\t}\n\tonlyIf { extension.enabled.get() }\n\n\ttoolClasspath.from(roseauClasspath)\n\tlibraryClasspath.from(configurations.compileClasspath)\n\tv1 = downloadPreviousReleaseJar.map { it.outputFiles.single() }\n\tv2 = tasks.jar.flatMap { it.archiveFile }.map { it.asFile }\n\tconfigFile = rootProject.layout.projectDirectory.file(\"gradle/config/roseau/config.yaml\")\n\trootProject.layout.projectDirectory.file(\"gradle/config/roseau/accepted-breaking-changes.csv\").asFile.let {\n\t\tif (it.exists()) {\n\t\t\tacceptedChangesCsvFile = it\n\t\t}\n\t}\n\treportDir = layout.buildDirectory.dir(\"reports/roseau\")\n}\n\nval checkBackwardCompatibility by tasks.registering {\n\tdependsOn(roseau)\n}\n\ntasks.check {\n\tdependsOn(checkBackwardCompatibility)\n}\n"
  },
  {
    "path": "gradle/plugins/build-parameters/build.gradle.kts",
    "content": "plugins {\n\talias(libs.plugins.buildParameters)\n}\n\ngroup = \"junitbuild\"\n\nbuildParameters {\n\tpluginId(\"junitbuild.build-parameters\")\n\tbool(\"ci\") {\n\t\tdescription = \"Whether or not this build is running in a CI environment\"\n\t\tdefaultValue = false\n\t\tfromEnvironment()\n\t}\n\tgroup(\"javaToolchain\") {\n\t\tdescription = \"Parameters controlling the Java toolchain used for compiling code and running tests\"\n\t\tinteger(\"version\") {\n\t\t\tdescription = \"JDK version\"\n\t\t}\n\t\tstring(\"implementation\") {\n\t\t\tdescription = \"JDK implementation (for example, 'j9')\"\n\t\t}\n\t}\n\tgroup(\"documentation\") {\n\t\tdescription = \"Parameters controlling how the documentation is built\"\n\t\tbool(\"replaceCurrentDocs\") {\n\t\t\tdescription = \"The documentation that is being deployed will replace what's currently deployed as 'current'\"\n\t\t\tdefaultValue = false\n\t\t}\n\t}\n\tgroup(\"junit\") {\n\t\tgroup(\"develocity\") {\n\t\t\tdescription = \"Parameters controlling Develocity features\"\n\t\t\tgroup(\"buildCache\") {\n\t\t\t\tstring(\"server\") {\n\t\t\t\t\tdescription =\n\t\t\t\t\t\t\"Remote build cache server address (protocol and hostname), e.g. https://eu-build-cache-ge.junit.org\"\n\t\t\t\t}\n\t\t\t\tbool(\"pushEnabled\") {\n\t\t\t\t\tdescription =\n\t\t\t\t\t\t\"Whether to push to the remote build cache\"\n\t\t\t\t\tdefaultValue = false\n\t\t\t\t}\n\t\t\t}\n\t\t\tgroup(\"predictiveTestSelection\") {\n\t\t\t\tbool(\"enabled\") {\n\t\t\t\t\tdescription = \"Whether or not to use Predictive Test Selection for selecting tests to execute\"\n\t\t\t\t\tdefaultValue = true\n\t\t\t\t}\n\t\t\t\tbool(\"selectRemainingTests\") {\n\t\t\t\t\t// see https://docs.gradle.com/develocity/predictive-test-selection/#gradle-selection-mode\n\t\t\t\t\tdescription = \"Whether or not to use PTS' 'remaining tests' selection mode\"\n\t\t\t\t\tdefaultValue = false\n\t\t\t\t}\n\t\t\t}\n\t\t\tgroup(\"testDistribution\") {\n\t\t\t\tbool(\"enabled\") {\n\t\t\t\t\tdescription = \"Whether or not to use Test Distribution for executing tests\"\n\t\t\t\t\tdefaultValue = false\n\t\t\t\t\tfromEnvironment()\n\t\t\t\t}\n\t\t\t\tinteger(\"maxLocalExecutors\") {\n\t\t\t\t\tdescription = \"How many local executors to use for executing tests\"\n\t\t\t\t\tdefaultValue = 1\n\t\t\t\t}\n\t\t\t\tinteger(\"maxRemoteExecutors\") {\n\t\t\t\t\tdescription = \"How many remote executors to request for executing tests\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tgroup(\"testing\") {\n\t\tdescription = \"Testing related parameters\"\n\t\tbool(\"dryRun\") {\n\t\t\tdescription = \"Enables dry run mode for tests\"\n\t\t\tdefaultValue = false\n\t\t}\n\t\tbool(\"enableJaCoCo\") {\n\t\t\tdescription = \"Enables JaCoCo test coverage reporting\"\n\t\t\tdefaultValue = true\n\t\t}\n\t\tbool(\"enableJFR\") {\n\t\t\tdescription = \"Enables Java Flight Recorder functionality\"\n\t\t\tdefaultValue = false\n\t\t}\n\t\tinteger(\"retries\") {\n\t\t\tdescription = \"Configures the number of times failing test are retried\"\n\t\t}\n\t\tbool(\"hideOpenTestReportHtmlGeneratorOutput\") {\n\t\t\tdescription = \"Whether or not to hide the output of the OpenTestReportHtmlGenerator\"\n\t\t\tdefaultValue = true\n\t\t}\n\t}\n\tgroup(\"publishing\") {\n\t\tbool(\"signArtifacts\") {\n\t\t\tdescription = \"Sign artifacts before publishing them to Maven repos\"\n\t\t}\n\t\tstring(\"group\") {\n\t\t\tdescription = \"Group ID for published Maven artifacts\"\n\t\t}\n\t}\n\tgroup(\"jitpack\") {\n\t\tstring(\"version\") {\n\t\t\tdescription = \"The version computed by Jitpack\"\n\t\t}\n\t}\n\tgroup(\"manifest\") {\n\t\tstring(\"buildTimestamp\") {\n\t\t\tdescription = \"Overrides the value of the 'Build-Date' and 'Build-Time' jar manifest entries. Can be set as a String (e.g. '2023-11-05 17:49:13.996+0100') or as seconds since the epoch.\"\n\t\t\tfromEnvironment(\"SOURCE_DATE_EPOCH\") // see https://reproducible-builds.org/docs/source-date-epoch/\n\t\t}\n\t\tstring(\"builtBy\") {\n\t\t\tdescription = \"Overrides the value of the 'Built-By' jar manifest entry\"\n\t\t}\n\t\tstring(\"createdBy\") {\n\t\t\tdescription = \"Overrides the value of the 'Created-By' jar manifest entry\"\n\t\t}\n\t}\n\tgroup(\"antora\") {\n\t\tbool(\"downloadNode\") {\n\t\t\tdescription = \"Whether to download Node.js from the Gradle build\"\n\t\t\tdefaultValue = true\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/code-generator/build.gradle.kts",
    "content": "plugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(\"junitbuild.base:code-generator-model\")\n\timplementation(\"junitbuild.base:dsl-extensions\")\n\timplementation(projects.common)\n\timplementation(libs.jackson.dataformat.yaml)\n\timplementation(libs.jackson.module.kotlin)\n\timplementation(libs.jte)\n}\n"
  },
  {
    "path": "gradle/plugins/code-generator/src/main/kotlin/junitbuild/generator/GenerateJreRelatedSourceCode.kt",
    "content": "package junitbuild.generator\n\nimport gg.jte.ContentType\nimport gg.jte.TemplateEngine\nimport gg.jte.output.FileOutput\nimport gg.jte.resolve.DirectoryCodeResolver\nimport junitbuild.generator.model.JRE\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.file.DirectoryProperty\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.provider.MapProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.InputDirectory\nimport org.gradle.api.tasks.InputFile\nimport org.gradle.api.tasks.Optional\nimport org.gradle.api.tasks.OutputDirectory\nimport org.gradle.api.tasks.PathSensitive\nimport org.gradle.api.tasks.PathSensitivity\nimport org.gradle.api.tasks.SkipWhenEmpty\nimport org.gradle.api.tasks.TaskAction\nimport tools.jackson.core.type.TypeReference\nimport tools.jackson.dataformat.yaml.YAMLMapper\nimport tools.jackson.module.kotlin.KotlinModule\nimport java.time.Year\n\n@CacheableTask\nabstract class GenerateJreRelatedSourceCode : DefaultTask() {\n\n    @get:InputDirectory\n    @get:SkipWhenEmpty\n    @get:PathSensitive(PathSensitivity.RELATIVE)\n    abstract val templateDir: DirectoryProperty\n\n    @get:OutputDirectory\n    abstract val targetDir: DirectoryProperty\n\n    @get:InputFile\n    @get:PathSensitive(PathSensitivity.NONE)\n    abstract val licenseHeaderFile: RegularFileProperty\n\n    @get:Input\n    abstract val licenseHeaderYear: Property<Year>\n\n    @get:Input\n    @get:Optional\n    abstract val maxVersion: Property<Int>\n\n    @get:Input\n    @get:Optional\n    abstract val fileNamePrefix: Property<String>\n\n    @get:Input\n    abstract val additionalTemplateParameters: MapProperty<String, String>\n\n    @TaskAction\n    fun generateSourceCode() {\n        val mainTargetDir = targetDir.get().asFile\n        mainTargetDir.deleteRecursively()\n\n        val templateDir = templateDir.get().asFile\n        val codeResolver = DirectoryCodeResolver(templateDir.toPath())\n        val templateEngine =\n            TemplateEngine.create(codeResolver, temporaryDir.toPath(), ContentType.Plain, javaClass.classLoader)\n\n        val templates = templateDir.walkTopDown()\n            .filter { it.extension == \"jte\" }\n            .map { it.relativeTo(templateDir) }\n            .toList()\n\n        if (templates.isNotEmpty()) {\n            var jres = javaClass.getResourceAsStream(\"/jre.yaml\").use { input ->\n                val mapper = YAMLMapper.builder()\n                    .addModule(KotlinModule.Builder().build())\n                    .build()\n                mapper.readValue(input, object : TypeReference<List<JRE>>() {})\n            }\n            if (maxVersion.isPresent) {\n                jres = jres.filter { it.version <= maxVersion.get() }\n            }\n            val minRuntimeVersion = 17\n            val supportedJres = jres.filter { it.version >= minRuntimeVersion }\n            val licenseHeader = licenseHeaderFile.asFile.get().readText().trimEnd()\n                .replace($$\"$YEAR\", licenseHeaderYear.get().toString()) + \"\\n\"\n            val params = additionalTemplateParameters.get() + mapOf(\n                \"minRuntimeVersion\" to minRuntimeVersion,\n                \"allJres\" to jres,\n                \"supportedJres\" to supportedJres,\n                \"supportedJresSortedByStringValue\" to supportedJres.sortedBy { it.version.toString() },\n                \"licenseHeader\" to licenseHeader,\n            )\n            templates.forEach {\n                val fileName = \"${fileNamePrefix.getOrElse(\"\")}${it.nameWithoutExtension}\"\n                val targetFile = mainTargetDir.toPath().resolve(it.resolveSibling(fileName).path)\n\n                FileOutput(targetFile).use { output ->\n                    // JTE does not support Windows paths, so we need to replace them\n                    val safePath = it.path.replace('\\\\', '/')\n                    templateEngine.render(safePath, params, output)\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/code-generator/src/main/kotlin/junitbuild.code-generator.gradle.kts",
    "content": "import junitbuild.extensions.dependencyFromLibs\nimport junitbuild.generator.GenerateJreRelatedSourceCode\nimport java.time.Year\n\nplugins {\n\tjava\n}\n\nval templates by sourceSets.creating\nval templatesCompileOnly = configurations[templates.compileOnlyConfigurationName]\n\ndependencies {\n\ttemplatesCompileOnly(dependencyFromLibs(\"jte\"))\n\ttemplatesCompileOnly(\"junitbuild.base:code-generator-model\")\n}\n\nval license: License by rootProject.extra\nval rootTargetDir = layout.buildDirectory.dir(\"generated/sources/jte\")\n\nval generateCode by tasks.registering {\n\tdependsOn(tasks.withType<GenerateJreRelatedSourceCode>())\n\tgroup = LifecycleBasePlugin.BUILD_GROUP\n\tdescription = \"Generates JRE-related source code.\"\n}\n\ntasks.withType<GenerateJreRelatedSourceCode>().configureEach {\n\tlicenseHeaderFile.convention(license.headerFile)\n\tlicenseHeaderYear.convention(Year.now())\n\tadditionalTemplateParameters.convention(emptyMap())\n}\n\nsourceSets.named { it != templates.name }.configureEach {\n\n\tval sourceSetName = name\n\n\tval task = tasks.register(getTaskName(\"generateJreRelated\", \"SourceCode\"), GenerateJreRelatedSourceCode::class) {\n\t\ttemplateDir.convention(layout.dir(provider {\n\t\t\ttemplates.resources.srcDirs.single().resolve(sourceSetName)\n\t\t}))\n\t\ttargetDir.convention(rootTargetDir.map { it.dir(sourceSetName) })\n\t}\n\n\tjava.srcDir(task.map { it.targetDir })\n}\n"
  },
  {
    "path": "gradle/plugins/common/build.gradle.kts",
    "content": "import junitbuild.extensions.dependencyFromLibs\nimport junitbuild.extensions.markerCoordinates\n\nplugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(\"junitbuild.base:dsl-extensions\")\n\timplementation(projects.buildParameters)\n\timplementation(projects.backwardCompatibility)\n\timplementation(libs.plugins.kotlin.markerCoordinates)\n\timplementation(libs.plugins.bnd.markerCoordinates)\n\timplementation(libs.plugins.commonCustomUserData.markerCoordinates)\n\timplementation(libs.plugins.develocity.markerCoordinates)\n\timplementation(libs.plugins.errorProne.markerCoordinates)\n\timplementation(libs.plugins.foojayResolver.markerCoordinates)\n\timplementation(libs.plugins.jmh.markerCoordinates)\n\timplementation(libs.plugins.nullaway.markerCoordinates)\n\timplementation(libs.plugins.shadow.markerCoordinates)\n\timplementation(libs.plugins.spotless.markerCoordinates)\n\timplementation(platform(dependencyFromLibs(\"log4j-bom\"))) {\n\t\tbecause(\"Workaround for CVE-2025-68161\")\n\t}\n\tconstraints {\n\t\timplementation(\"org.codehaus.plexus:plexus-utils\") {\n\t\t\tversion {\n\t\t\t\trequire(\"4.0.3\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2025-67030 (used by shadow plugin)\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/JavaLibraryExtension.kt",
    "content": "import org.gradle.api.JavaVersion\nimport org.gradle.api.provider.Property\n\n@Suppress(\"LeakingThis\")\nabstract class JavaLibraryExtension {\n\n    abstract val mainJavaVersion: Property<JavaVersion>\n    abstract val testJavaVersion: Property<JavaVersion>\n\n    init {\n        mainJavaVersion.convention(JavaVersion.VERSION_17)\n        testJavaVersion.convention(JavaVersion.VERSION_25)\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/License.kt",
    "content": "import org.gradle.api.file.RegularFile\nimport java.net.URI\n\ndata class License(val name: String, val url: URI, val headerFile: RegularFile)\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/deps/ByteBuddyAlignmentRule.kt",
    "content": "package junitbuild.deps\n\nimport org.gradle.api.artifacts.ComponentMetadataContext\nimport org.gradle.api.artifacts.ComponentMetadataRule\n\nabstract class ByteBuddyAlignmentRule : ComponentMetadataRule {\n    override fun execute(ctx: ComponentMetadataContext) {\n        ctx.details.run {\n            if (id.group.startsWith(\"net.bytebuddy\")) {\n                belongsTo(\"net.bytebuddy:byte-buddy-virtual-platform:${id.version}\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/eclipse/EclipseConventionsExtension.kt",
    "content": "package junitbuild.eclipse\n\nimport org.gradle.api.provider.Property\n\nabstract class EclipseConventionsExtension {\n    abstract val hideModularity: Property<Boolean>\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/exec/CaptureJavaExecOutput.kt",
    "content": "package junitbuild.exec\n\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.file.ConfigurableFileCollection\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.provider.ListProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Classpath\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.Nested\nimport org.gradle.api.tasks.OutputFile\nimport org.gradle.api.tasks.TaskAction\nimport org.gradle.process.CommandLineArgumentProvider\nimport org.gradle.process.ExecOperations\nimport java.nio.file.Files\nimport javax.inject.Inject\n\n@CacheableTask\nabstract class CaptureJavaExecOutput @Inject constructor(private val execOperations: ExecOperations) : DefaultTask() {\n\n    @get:Classpath\n    abstract val classpath: ConfigurableFileCollection\n\n    @get:Input\n    abstract val mainClass: Property<String>\n\n    @get:Input\n    abstract val args: ListProperty<String>\n\n    @get:Nested\n    val jvmArgumentProviders = mutableListOf<CommandLineArgumentProvider>()\n\n    @get:OutputFile\n    abstract val outputFile: RegularFileProperty\n\n    @TaskAction\n    fun execute() {\n        val outputFile = outputFile.get().asFile.toPath()\n        Files.newOutputStream(outputFile).use { out ->\n            execOperations.javaexec {\n                classpath = this@CaptureJavaExecOutput.classpath\n                mainClass.set(this@CaptureJavaExecOutput.mainClass)\n                args = this@CaptureJavaExecOutput.args.get()\n                jvmArgumentProviders.addAll(this@CaptureJavaExecOutput.jvmArgumentProviders)\n                standardOutput = out\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/exec/ClasspathSystemPropertyProvider.kt",
    "content": "package junitbuild.exec\n\nimport org.gradle.api.file.FileCollection\nimport org.gradle.api.tasks.Classpath\nimport org.gradle.process.CommandLineArgumentProvider\n\nclass ClasspathSystemPropertyProvider(private val propertyName: String, @get:Classpath val files: FileCollection) : CommandLineArgumentProvider {\n    override fun asArguments() = listOf(\"-D$propertyName=${files.asPath}\")\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/exec/GenerateStandaloneConsoleLauncherShadowedArtifactsFile.kt",
    "content": "package junitbuild.exec\n\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.file.ArchiveOperations\nimport org.gradle.api.file.FileSystemOperations\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.file.RelativePath\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Classpath\nimport org.gradle.api.tasks.OutputFile\nimport org.gradle.api.tasks.TaskAction\nimport javax.inject.Inject\n\n@CacheableTask\nabstract class GenerateStandaloneConsoleLauncherShadowedArtifactsFile @Inject constructor(\n    private val fileSystem: FileSystemOperations,\n    private val archives: ArchiveOperations\n) : DefaultTask() {\n\n    @get:Classpath\n    abstract val inputJar: RegularFileProperty\n\n    @get:OutputFile\n    abstract val outputFile: RegularFileProperty\n\n    @TaskAction\n    fun execute() {\n        fileSystem.copy {\n            from(archives.zipTree(inputJar)) {\n                include(\"META-INF/shadowed-artifacts\")\n                includeEmptyDirs = false\n                eachFile {\n                    relativePath = RelativePath(true, outputFile.get().asFile.name)\n                }\n                filter { line -> \"- `${line}`\" }\n            }\n            into(outputFile.get().asFile.parentFile)\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/exec/RunConsoleLauncher.kt",
    "content": "package junitbuild.exec\n\nimport org.apache.tools.ant.types.Commandline\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.file.ConfigurableFileCollection\nimport org.gradle.api.plugins.JavaPluginExtension\nimport org.gradle.api.provider.ListProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.api.tasks.CacheableTask\nimport org.gradle.api.tasks.Classpath\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.Internal\nimport org.gradle.api.tasks.Nested\nimport org.gradle.api.tasks.SourceSetContainer\nimport org.gradle.api.tasks.TaskAction\nimport org.gradle.api.tasks.options.Option\nimport org.gradle.jvm.toolchain.JavaLauncher\nimport org.gradle.jvm.toolchain.JavaToolchainService\nimport org.gradle.kotlin.dsl.get\nimport org.gradle.kotlin.dsl.the\nimport org.gradle.process.CommandLineArgumentProvider\nimport org.gradle.process.ExecOperations\nimport junitbuild.extensions.trackOperationSystemAsInput\nimport java.io.ByteArrayOutputStream\nimport javax.inject.Inject\n\n@CacheableTask\nabstract class RunConsoleLauncher @Inject constructor(private val execOperations: ExecOperations) : DefaultTask() {\n\n    @get:Classpath\n    abstract val runtimeClasspath: ConfigurableFileCollection\n\n    @get:Input\n    abstract val args: ListProperty<String>\n\n    @get:Nested\n    abstract val argumentProviders: ListProperty<CommandLineArgumentProvider>\n\n    @get:Input\n    abstract val commandLineArgs: ListProperty<String>\n\n    @get:Nested\n    abstract val javaLauncher: Property<JavaLauncher>\n\n    @get:Internal\n    abstract val debugging: Property<Boolean>\n\n    @get:Internal\n    abstract val hideOutput: Property<Boolean>\n\n    init {\n        runtimeClasspath.from(project.the<SourceSetContainer>()[\"test\"].runtimeClasspath)\n        javaLauncher.set(project.the<JavaToolchainService>().launcherFor(project.the<JavaPluginExtension>().toolchain))\n\n        debugging.convention(false)\n        commandLineArgs.convention(emptyList())\n        outputs.cacheIf { !debugging.get() }\n        outputs.upToDateWhen { !debugging.get() }\n\n        hideOutput.convention(debugging.map { !it })\n\n        trackOperationSystemAsInput()\n    }\n\n    @TaskAction\n    fun execute() {\n        val output = ByteArrayOutputStream()\n        val result = execOperations.javaexec {\n            executable = javaLauncher.get().executablePath.asFile.absolutePath\n            classpath = runtimeClasspath\n            mainClass.set(\"org.junit.platform.console.ConsoleLauncher\")\n            args(this@RunConsoleLauncher.args.get())\n            args(this@RunConsoleLauncher.commandLineArgs.get())\n            argumentProviders.addAll(this@RunConsoleLauncher.argumentProviders.get())\n            systemProperty(\"java.util.logging.manager\", \"org.apache.logging.log4j.jul.LogManager\")\n            debug = debugging.get()\n            if (hideOutput.get()) {\n                standardOutput = output\n                errorOutput = output\n            }\n            isIgnoreExitValue = true\n        }\n        if (result.exitValue != 0 && hideOutput.get()) {\n            System.out.write(output.toByteArray())\n            System.out.flush()\n        }\n        result.rethrowFailure().assertNormalExitValue()\n    }\n\n    @Suppress(\"unused\")\n    @Option(option = \"args\", description = \"Additional command line arguments for the console launcher\")\n    fun setCliArgs(args: String) {\n        commandLineArgs.set(Commandline.translateCommandline(args).toList())\n    }\n\n    @Suppress(\"unused\")\n    @Option(\n        option = \"debug-jvm\",\n        description = \"Enable debugging. The process is started suspended and listening on port 5005.\"\n    )\n    fun setDebug(enabled: Boolean) {\n        debugging.set(enabled)\n    }\n\n    @Suppress(\"unused\")\n    @Option(\n        option = \"show-output\",\n        description = \"Show output\"\n    )\n    fun setShowOutput(showOutput: Boolean) {\n        hideOutput.set(!showOutput)\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/graalvm/NativeImagePropertiesExtension.kt",
    "content": "package junitbuild.graalvm\n\nimport org.gradle.api.provider.SetProperty\n\nabstract class NativeImagePropertiesExtension {\n    abstract val initializeAtBuildTime: SetProperty<String>\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/java/UpdateJarAction.kt",
    "content": "package junitbuild.java\n\nimport org.gradle.api.Action\nimport org.gradle.api.Task\nimport org.gradle.api.internal.file.archive.ZipEntryConstants\nimport org.gradle.api.provider.ListProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.jvm.toolchain.JavaLauncher\nimport org.gradle.process.ExecOperations\nimport java.time.Instant\nimport java.time.LocalDateTime\nimport java.time.ZoneId\nimport java.time.ZoneOffset\nimport javax.inject.Inject\n\nabstract class UpdateJarAction @Inject constructor(private val operations: ExecOperations): Action<Task> {\n\n    companion object {\n        // Since ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES is in the default time zone (see its Javadoc),\n        // we're converting it to the same time in UTC here to make the jar reproducible regardless of the\n        // build's time zone.\n        private val CONSTANT_TIME_FOR_ZIP_ENTRIES = LocalDateTime.ofInstant(Instant.ofEpochMilli(ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES), ZoneId.systemDefault())\n            .toInstant(ZoneOffset.UTC)\n            .toString()\n    }\n\n    abstract val javaLauncher: Property<JavaLauncher>\n\n    abstract val args: ListProperty<String>\n\n    init {\n        args.addAll(\n            \"--update\",\n            // Use a constant time to make the JAR reproducible.\n            \"--date=$CONSTANT_TIME_FOR_ZIP_ENTRIES\",\n        )\n    }\n\n    override fun execute(t: Task) {\n        operations.exec {\n            executable = javaLauncher.get()\n                .metadata.installationPath.file(\"bin/jar\").asFile.absolutePath\n            args = this@UpdateJarAction.args.get()\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/java/WriteArtifactsFile.kt",
    "content": "package junitbuild.java\n\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.artifacts.Configuration\nimport org.gradle.api.artifacts.ModuleVersionIdentifier\nimport org.gradle.api.file.RegularFileProperty\nimport org.gradle.api.provider.Provider\nimport org.gradle.api.provider.SetProperty\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.OutputFile\nimport org.gradle.api.tasks.TaskAction\n\nabstract class WriteArtifactsFile : DefaultTask() {\n\n    @get:OutputFile\n    abstract val outputFile: RegularFileProperty\n\n    @get:Input\n    abstract val moduleVersions: SetProperty<ModuleVersionIdentifier>\n\n    fun from(configuration: Provider<Configuration>) {\n        moduleVersions.addAll(configuration.map {\n            it.resolvedConfiguration.resolvedArtifacts.map { it.moduleVersion.id }\n        })\n    }\n\n    @TaskAction\n    fun writeFile() {\n        outputFile.get().asFile.printWriter().use { out ->\n            moduleVersions.get()\n                .map { \"${it.group}:${it.name}:${it.version}\" }\n                .sorted()\n                .forEach(out::println)\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/javadoc/JavadocConventionsExtension.kt",
    "content": "package junitbuild.javadoc\n\nimport junitbuild.extensions.javaModuleName\nimport org.gradle.api.Project\nimport org.gradle.api.artifacts.Configuration\nimport org.gradle.api.artifacts.ProjectDependency\nimport org.gradle.api.tasks.TaskProvider\nimport org.gradle.api.tasks.javadoc.Javadoc\nimport org.gradle.external.javadoc.StandardJavadocDocletOptions\nimport org.gradle.kotlin.dsl.dependencies\n\nclass JavadocConventionsExtension(val project: Project, val dependencyScope: Configuration, val task: TaskProvider<Javadoc>) {\n\n    fun addExtraModuleReferences(vararg projectDependencies: ProjectDependency) {\n        project.dependencies {\n            projectDependencies.forEach { dependencyScope(it) }\n        }\n        task.configure {\n            options {\n                (this as StandardJavadocDocletOptions).apply {\n                    val referencedModuleNames = projectDependencies.joinToString(\",\") { it.javaModuleName }\n                    addStringOption(\"-add-modules\", referencedModuleNames)\n                    addStringOption(\"-add-reads\", \"${project.javaModuleName}=$referencedModuleNames\")\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/javadoc/JavadocValuesOption.kt",
    "content": "package junitbuild.javadoc\n\nimport org.gradle.api.provider.Provider\nimport org.gradle.external.javadoc.JavadocOptionFileOption\nimport org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext\n\nclass JavadocValuesOption(private val option: String, private var values: Provider<List<String>>) :\n    JavadocOptionFileOption<Provider<List<String>>> {\n\n    override fun getOption() = option\n\n    override fun getValue() = values\n\n    override fun setValue(value: Provider<List<String>>) {\n        this.values = value\n    }\n\n    override fun write(writerContext: JavadocOptionFileWriterContext) {\n        writerContext.writeValuesOption(option, values.get(), \",\")\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/javadoc/ModuleSpecificJavadocFileOption.kt",
    "content": "package junitbuild.javadoc\n\nimport org.gradle.api.provider.Provider\nimport org.gradle.external.javadoc.JavadocOptionFileOption\nimport org.gradle.external.javadoc.internal.JavadocOptionFileWriterContext\n\nclass ModuleSpecificJavadocFileOption(private val option: String, private var valuePerModule: Map<String, Provider<String>>) : JavadocOptionFileOption<Map<String, Provider<String>>> {\n\n    override fun getOption() = option\n\n    override fun getValue() = valuePerModule\n\n    override fun setValue(value: Map<String, Provider<String>>) {\n        this.valuePerModule = value\n    }\n\n    override fun write(writerContext: JavadocOptionFileWriterContext) {\n        valuePerModule.forEach { (moduleName, value) ->\n            writerContext\n                    .writeOptionHeader(option)\n                    .write(moduleName)\n                    .write(\"=\")\n                    .write(value.get())\n                    .newLine()\n        }\n    }\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild/javadoc/VersionNumber.kt",
    "content": "package junitbuild.javadoc\n\ndata class VersionNumber(val components: List<Int>) : Comparable<VersionNumber> {\n\n    constructor(version: String) : this(version.split('.').map { it.toInt() })\n\n    override fun compareTo(other: VersionNumber): Int {\n        for (i in 0 until maxOf(this.components.size, other.components.size)) {\n            val thisComponent = this.components.getOrElse(i) { 0 }\n            val otherComponent = other.components.getOrElse(i) { 0 }\n            if (thisComponent != otherComponent) {\n                return thisComponent - otherComponent\n            }\n        }\n        return 0\n    }\n\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.base-conventions.gradle.kts",
    "content": "plugins {\n\teclipse\n\tid(\"junitbuild.java-toolchain-conventions\")\n\tid(\"junitbuild.spotless-conventions\")\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.build-metadata.gradle.kts",
    "content": "import java.time.Instant\nimport java.time.OffsetDateTime\nimport java.time.ZoneOffset\nimport java.time.format.DateTimeFormatter\nimport java.time.format.DateTimeFormatterBuilder\n\nplugins {\n\tid(\"junitbuild.build-parameters\")\n}\n\nval dateFormatter = DateTimeFormatter.ISO_LOCAL_DATE\nval timeFormatter = DateTimeFormatter.ofPattern(\"HH:mm:ss.SSSZ\")\n\nval buildTimeAndDate = buildParameters.manifest.buildTimestamp\n\t.map {\n\t\tit.toLongOrNull()\n\t\t\t?.let { s -> Instant.ofEpochSecond(s).atOffset(ZoneOffset.UTC) }\n\t\t\t?: DateTimeFormatterBuilder()\n\t\t\t\t.append(dateFormatter)\n\t\t\t\t.appendLiteral(' ')\n\t\t\t\t.append(timeFormatter)\n\t\t\t\t.toFormatter()\n\t\t\t\t.parse(it)\n\t}\n\t.orNull\n\t?: OffsetDateTime.now()\n\nval buildDate: String by extra { dateFormatter.format(buildTimeAndDate) }\nval buildTime: String by extra { timeFormatter.format(buildTimeAndDate) }\nval buildRevision: String by extra {\n\tproviders.exec {\n\t\tcommandLine(\"git\", \"rev-parse\", \"--verify\", \"HEAD\")\n\t}.standardOutput.asText.get().trim()\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-conventions.gradle.kts",
    "content": "import junitbuild.extensions.requiredVersionFromLibs\n\nplugins {\n\tbase\n\tcheckstyle\n}\n\ndependencies {\n\tconstraints {\n\t\tcheckstyle(\"org.apache.commons:commons-lang3\") {\n\t\t\tversion {\n\t\t\t\trequire(\"3.18.0\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2025-48924\")\n\t\t}\n\t\tcheckstyle(\"org.codehaus.plexus:plexus-utils\") {\n\t\t\tversion {\n\t\t\t\trequire(\"3.6.1\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2025-67030\")\n\t\t}\n\t}\n}\n\ncheckstyle {\n\ttoolVersion = requiredVersionFromLibs(\"checkstyle\")\n\tconfigDirectory = rootProject.layout.projectDirectory.dir(\"gradle/config/checkstyle\")\n}\n\ntasks.check {\n\tdependsOn(tasks.withType<Checkstyle>())\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.checkstyle-nohttp.gradle.kts",
    "content": "import junitbuild.extensions.dependencyFromLibs\nimport junitbuild.extensions.requiredVersionFromLibs\n\nplugins {\n\tid(\"junitbuild.checkstyle-conventions\")\n}\n\ndependencies {\n\tcheckstyle(dependencyFromLibs(\"nohttp-checkstyle\"))\n\tconstraints {\n\t\tcheckstyle(\"com.puppycrawl.tools:checkstyle\") {\n\t\t\tversion {\n\t\t\t\trequire(requiredVersionFromLibs(\"checkstyle\"))\n\t\t\t}\n\t\t}\n\t\tcheckstyle(\"ch.qos.logback:logback-classic\") {\n\t\t\tversion {\n\t\t\t\trequire(\"1.5.25\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2026-1225\")\n\t\t}\n\t\tcheckstyle(\"org.codehaus.plexus:plexus-utils\") {\n\t\t\tversion {\n\t\t\t\trequire(\"3.6.1\")\n\t\t\t}\n\t\t\tbecause(\"Workaround for CVE-2025-67030\")\n\t\t}\n\t}\n}\n\ntasks.register<Checkstyle>(\"checkstyleNohttp\") {\n\tgroup = \"verification\"\n\tdescription = \"Checks for illegal uses of http://\"\n\tclasspath = files(configurations.checkstyle)\n\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleNohttp.xml\"))\n\tsource = fileTree(layout.projectDirectory) {\n\t\texclude(\".git/**\", \"**/.gradle/**\")\n\t\texclude(\".idea/**\", \"**/.settings/**\", \"**/.classpath\", \"**/.project\")\n\t\texclude(\"**/*.class\")\n\t\texclude(\"**/*.hprof\")\n\t\texclude(\"**/*.jar\")\n\t\texclude(\"**/*.jpg\", \"**/*.png\")\n\t\texclude(\"**/*.jks\")\n\t\texclude(\"**/build/**\")\n\t\texclude(\"**/.kotlin\")\n\t\texclude(\"**/node_modules/**\")\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.eclipse-conventions.gradle.kts",
    "content": "import junitbuild.eclipse.EclipseConventionsExtension\nimport org.gradle.plugins.ide.eclipse.model.Classpath\nimport org.gradle.plugins.ide.eclipse.model.Library\nimport org.gradle.plugins.ide.eclipse.model.ProjectDependency\nimport org.gradle.plugins.ide.eclipse.model.SourceFolder\n\nplugins {\n\teclipse\n}\n\nval extension = extensions.create<EclipseConventionsExtension>(\"eclipseConventions\").apply {\n\thideModularity.convention(true)\n}\n\neclipse {\n\tjdt {\n\t\tsourceCompatibility = JavaVersion.VERSION_25\n\t\ttargetCompatibility = JavaVersion.VERSION_25\n\t\tfile {\n\t\t\t// Set properties for org.eclipse.jdt.core.prefs\n\t\t\twithProperties {\n\t\t\t\t// Configure Eclipse projects with -release compiler flag.\n\t\t\t\tsetProperty(\"org.eclipse.jdt.core.compiler.release\", \"enabled\")\n\t\t\t\t// Configure Eclipse projects with -parameters compiler flag.\n\t\t\t\tsetProperty(\"org.eclipse.jdt.core.compiler.codegen.methodParameters\", \"generate\")\n\t\t\t}\n\t\t}\n\t}\n\tclasspath.file.whenMerged {\n\t\tthis as Classpath\n\t\t// Remove classpath entries for non-existent libraries added by various\n\t\t// plugins, such as \"junit-jupiter-api/build/classes/kotlin/testFixtures\".\n\t\tentries.removeIf { it is Library && !file(it.path).exists() }\n\t\t// Remove classpath entries for the code generator model used by the\n\t\t// Java Template Engine (JTE) which is used to generate the JRE enum and\n\t\t// dependent tests.\n\t\tentries.removeIf { it is ProjectDependency && it.path.equals(\"/code-generator-model\") }\n\t\t// Remove classpath entries for anything used by the Gradle Wrapper.\n\t\tentries.removeIf { it is Library && it.path.contains(\"gradle/wrapper\") }\n\t\tif (extension.hideModularity.get()) {\n\t\t\tentries.filterIsInstance<SourceFolder>().forEach {\n\t\t\t\tit.excludes.add(\"**/module-info.java\")\n\t\t\t}\n\t\t\tentries.filterIsInstance<ProjectDependency>().forEach {\n\t\t\t\tit.entryAttributes.remove(\"module\")\n\t\t\t}\n\t\t\tentries.filterIsInstance<Library>().forEach {\n\t\t\t\tit.entryAttributes.remove(\"module\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-aggregation-conventions.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.jacoco-conventions\")\n\t`jacoco-report-aggregation`\n}\n\nval jacocoRootReport by reporting.reports.creating(JacocoCoverageReport::class) {\n\ttestSuiteName = \"test\"\n}\n\nval classesView = configurations[\"aggregateCodeCoverageReportResults\"].incoming.artifactView {\n\twithVariantReselection() // Required to ensure the transformed classes are selected\n\tcomponentFilter { it is ProjectComponentIdentifier }\n\tattributes {\n\t\tattribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(LibraryElements.CLASSES))\n\t}\n}\n\ntasks {\n\tval reportTask = named<JacocoReport>(jacocoRootReport.reportTask.name)\n\tval jacocoRootCoverageVerification by registering(JacocoCoverageVerification::class) {\n\t\tenabled = !buildParameters.junit.develocity.predictiveTestSelection.enabled\n\t\texecutionData.from(reportTask.map { it.executionData })\n\t\tclassDirectories.from(reportTask.map { it.classDirectories })\n\t\tsourceDirectories.from(reportTask.map { it.sourceDirectories })\n\t\tviolationRules {\n\t\t\trule {\n\t\t\t\tlimit {\n\t\t\t\t\t// In order to detect problems with coverage aggregation, we require a minimum coverage percentage\n\t\t\t\t\tminimum = \"0.90\".toBigDecimal()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treportTask {\n\t\t// Override to restore behavior of pre-8.2.1 Gradle (see https://github.com/gradle/gradle/issues/25618)\n\t\tclassDirectories.setFrom(classesView.files)\n\t\tfinalizedBy(jacocoRootCoverageVerification)\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-conventions.gradle.kts",
    "content": "import junitbuild.extensions.requiredVersionFromLibs\n\nplugins {\n\tjacoco\n\tid(\"junitbuild.build-parameters\")\n}\n\njacoco {\n\ttoolVersion = requiredVersionFromLibs(\"jacoco\")\n}\n\ntasks.withType<JacocoReport>().configureEach {\n\tenabled = buildParameters.testing.enableJaCoCo\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.jacoco-java-conventions.gradle.kts",
    "content": "import org.gradle.api.attributes.LibraryElements.CLASSES\nimport org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE\n\nplugins {\n\tjava\n\tid(\"junitbuild.build-parameters\")\n\tid(\"junitbuild.jacoco-conventions\")\n}\n\nval mavenizedProjects: List<Project> by rootProject.extra\n\ntasks.withType<Test>().configureEach {\n\tconfigure<JacocoTaskExtension> {\n\t\tisEnabled = buildParameters.testing.enableJaCoCo\n\t}\n}\n\nval codeCoverageClassesJar by tasks.registering(Jar::class) {\n\tfrom(tasks.jar.map { zipTree(it.archiveFile) })\n\tarchiveClassifier = \"jacoco\"\n\tenabled = project in mavenizedProjects\n\tduplicatesStrategy = DuplicatesStrategy.INCLUDE\n}\n\nconfigurations.consumable(\"codeCoverageReportClasses\") {\n\tattributes {\n\t\tattribute(LIBRARY_ELEMENTS_ATTRIBUTE, named(CLASSES))\n\t}\n\toutgoing.artifact(codeCoverageClassesJar)\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.java-aggregator-conventions.gradle.kts",
    "content": "import junitbuild.compatibility.BackwardCompatibilityChecksExtension\n\nplugins {\n\tid(\"junitbuild.java-library-conventions\")\n}\n\ntasks.javadoc {\n\t// Since this JAR contains no classes, running Javadoc fails with:\n\t// \"No public or protected classes found to document\"\n\tenabled = false\n}\n\nthe<BackwardCompatibilityChecksExtension>().apply {\n\tenabled = false // already checked by individual projects\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.java-errorprone-conventions.gradle.kts",
    "content": "import junitbuild.extensions.dependencyFromLibs\nimport net.ltgt.gradle.errorprone.errorprone\nimport net.ltgt.gradle.nullaway.nullaway\n\nplugins {\n\t`java-library`\n\tid(\"net.ltgt.errorprone\")\n\tid(\"net.ltgt.nullaway\")\n}\n\ndependencies {\n\terrorprone(dependencyFromLibs(\"error-prone-contrib\"))\n\terrorprone(dependencyFromLibs(\"error-prone-core\"))\n\terrorprone(dependencyFromLibs(\"nullaway\"))\n}\n\ntasks.withType<JavaCompile>().configureEach {\n\toptions.errorprone {\n\t\tval shouldDisableErrorProne = java.toolchain.implementation.orNull == JvmImplementation.J9\n\t\tif (name == \"compileJava\" && !shouldDisableErrorProne) {\n\t\t\tdisable(\n\t\t\t\t\"AnnotateFormatMethod\", // We don`t want to use ErrorProne's annotations.\n\t\t\t\t\"BadImport\", // This check is opinionated wrt. which method names it considers unsuitable for import which includes a few of our own methods in `ReflectionUtils` etc.\n\t\t\t\t\"DoNotCallSuggester\", // We don`t want to use ErrorProne's annotations.\n\t\t\t\t\"ImmutableEnumChecker\", // We don`t want to use ErrorProne's annotations.\n\t\t\t\t\"InlineMeSuggester\", // We don`t want to use ErrorProne's annotations.\n\t\t\t\t\"MissingSummary\", // Produces a lot of findings that we consider to be false positives, for example for package-private classes and methods.\n\t\t\t\t\"StringSplitter\", // We don`t want to use Guava.\n\t\t\t\t\"UnnecessaryLambda\", // The findings of this check are subjective because a named constant can be more readable in many cases.\n\t\t\t\t// picnic (https://error-prone.picnic.tech)\n\t\t\t\t\"ConstantNaming\",\n\t\t\t\t\"DirectReturn\", // We don`t want to use this: https://github.com/junit-team/junit-framework/pull/5006#discussion_r2403984446\n\t\t\t\t\"FormatStringConcatenation\",\n\t\t\t\t\"IdentityConversion\",\n\t\t\t\t\"LexicographicalAnnotationAttributeListing\", // We don`t want to use this: https://github.com/junit-team/junit-framework/pull/5043#pullrequestreview-3330615838\n\t\t\t\t\"LexicographicalAnnotationListing\",\n\t\t\t\t\"MissingTestCall\",\n\t\t\t\t\"NestedOptionals\",\n\t\t\t\t\"NonStaticImport\",\n\t\t\t\t\"OptionalOrElseGet\",\n\t\t\t\t\"PrimitiveComparison\",\n\t\t\t\t\"StaticImport\",\n\t\t\t\t\"TimeZoneUsage\",\n\t\t\t)\n\t\t\terror(\n\t\t\t\t\"CanonicalAnnotationSyntax\",\n\t\t\t\t\"IsInstanceLambdaUsage\",\n\t\t\t\t\"PackageLocation\",\n\t\t\t\t\"RedundantStringConversion\",\n\t\t\t\t\"RedundantStringEscape\",\n\t\t\t)\n\t\t} else {\n\t\t\tdisableAllChecks = true\n\t\t}\n\t\tnullaway {\n\t\t\tif (shouldDisableErrorProne) {\n\t\t\t\tdisable()\n\t\t\t} else {\n\t\t\t\tenable()\n\t\t\t}\n\t\t\tonlyNullMarked = true\n\t\t\tjspecifyMode = true\n\t\t\tcheckContracts = true\n\t\t\tsuppressionNameAliases.add(\"DataFlowIssue\")\n\t\t}\n\t}\n}\n\ntasks.withType<JavaCompile>().named { it.startsWith(\"compileTest\") }.configureEach {\n\toptions.errorprone.nullaway {\n\t\thandleTestAssertionLibraries = true\n\t\texcludedFieldAnnotations.addAll(\n\t\t\t\"org.junit.jupiter.params.Parameter\",\n\t\t\t\"org.junit.runners.Parameterized.Parameter\",\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.java-library-conventions.gradle.kts",
    "content": "import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar\nimport junitbuild.extensions.isSnapshot\n\nplugins {\n\t`java-library`\n\tid(\"junitbuild.base-conventions\")\n\tid(\"junitbuild.build-parameters\")\n\tid(\"junitbuild.checkstyle-conventions\")\n\tid(\"junitbuild.eclipse-conventions\")\n\tid(\"junitbuild.jacoco-java-conventions\")\n\tid(\"junitbuild.java-errorprone-conventions\")\n}\n\nval mavenizedProjects: List<Project> by rootProject.extra\nval buildDate: String by rootProject.extra\nval buildTime: String by rootProject.extra\nval buildRevision: Any by rootProject.extra\n\nval extension = extensions.create<JavaLibraryExtension>(\"javaLibrary\")\n\njava {\n\tmodularity.inferModulePath = true\n}\n\nif (project in mavenizedProjects) {\n\n\tapply(plugin = \"junitbuild.javadoc-conventions\")\n\tapply(plugin = \"junitbuild.publishing-conventions\")\n\tapply(plugin = \"junitbuild.osgi-conventions\")\n\tapply(plugin = \"junitbuild.backward-compatibility\")\n\n\tjava {\n\t\twithSourcesJar()\n\t}\n\n\ttasks.named<Jar>(\"sourcesJar\").configure {\n\t\tduplicatesStrategy = DuplicatesStrategy.EXCLUDE\n\t}\n\n\tpluginManager.withPlugin(\"java-test-fixtures\") {\n\t\tval javaComponent = components[\"java\"] as AdhocComponentWithVariants\n\t\tjavaComponent.withVariantsFromConfiguration(configurations[\"testFixturesApiElements\"]) { skip() }\n\t\tjavaComponent.withVariantsFromConfiguration(configurations[\"testFixturesRuntimeElements\"]) { skip() }\n\t}\n\n\tconfigure<PublishingExtension> {\n\t\tpublications {\n\t\t\tnamed<MavenPublication>(\"maven\") {\n\t\t\t\tfrom(components[\"java\"])\n\t\t\t\tif (!buildParameters.jitpack.version.isPresent) {\n\t\t\t\t\tversionMapping {\n\t\t\t\t\t\tallVariants {\n\t\t\t\t\t\t\tfromResolutionResult()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpom {\n\t\t\t\t\tdescription = provider { \"Module \\\"${project.name}\\\" of JUnit\" }\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!project.version.isSnapshot()) {\n\t\tconfigurations {\n\t\t\tcompileClasspath {\n\t\t\t\tresolutionStrategy.failOnChangingVersions()\n\t\t\t}\n\t\t\truntimeClasspath {\n\t\t\t\tresolutionStrategy.failOnChangingVersions()\n\t\t\t}\n\t\t}\n\t}\n\n} else {\n\ttasks {\n\t\tjar {\n\t\t\tenabled = false\n\t\t}\n\t\tjavadoc {\n\t\t\tenabled = false\n\t\t}\n\t}\n}\n\ntasks.withType<AbstractArchiveTask>().configureEach {\n\tisPreserveFileTimestamps = false\n\tisReproducibleFileOrder = true\n\tdirPermissions {\n\t\tunix(\"rwxr-xr-x\")\n\t}\n\tfilePermissions {\n\t\tunix(\"rw-r--r--\")\n\t}\n}\n\nnormalization {\n\truntimeClasspath {\n\t\tmetaInf {\n\t\t\t// Ignore inconsequential JAR manifest attributes such as timestamps and the commit checksum.\n\t\t\t// This is used when checking whether runtime classpaths, e.g. of test tasks, have changed and\n\t\t\t// improves cacheability of such tasks.\n\t\t\tignoreAttribute(\"Built-By\")\n\t\t\tignoreAttribute(\"Build-Date\")\n\t\t\tignoreAttribute(\"Build-Time\")\n\t\t\tignoreAttribute(\"Build-Revision\")\n\t\t\tignoreAttribute(\"Created-By\")\n\t\t}\n\t}\n}\n\ntasks.withType<Jar>().configureEach {\n\tfrom(rootDir) {\n\t\tinclude(\"LICENSE.md\")\n\t\tinto(\"META-INF\")\n\t}\n\tfrom(rootDir) {\n\t\tinclude(\"NOTICE.md\")\n\t\trename {\n\t\t\t\"LICENSE-notice.md\"\n\t\t}\n\t\tinto(\"META-INF\")\n\t}\n}\n\ntasks.jar {\n\tmanifest {\n\t\tattributes(\n\t\t\t\t\"Created-By\" to (buildParameters.manifest.createdBy.orNull\n\t\t\t\t\t?: \"${System.getProperty(\"java.version\")} (${System.getProperty(\"java.vendor\")} ${System.getProperty(\"java.vm.version\")})\"),\n\t\t\t\t\"Built-By\" to buildParameters.manifest.builtBy.orElse(\"JUnit Team\"),\n\t\t\t\t\"Build-Date\" to buildDate,\n\t\t\t\t\"Build-Time\" to buildTime,\n\t\t\t\t\"Build-Revision\" to buildRevision,\n\t\t\t\t\"Specification-Title\" to project.name,\n\t\t\t\t\"Specification-Version\" to (project.version as String).substringBefore('-'),\n\t\t\t\t\"Specification-Vendor\" to \"junit.org\",\n\t\t\t\t\"Implementation-Title\" to project.name,\n\t\t\t\t\"Implementation-Version\" to project.version,\n\t\t\t\t\"Implementation-Vendor\" to \"junit.org\"\n\t\t)\n\t}\n}\n\ntasks.withType<ShadowJar>().configureEach {\n\toutputs.doNotCacheIf(\"Shadow jar contains a Manifest with Build-Time\") { true }\n}\n\ntasks.withType<JavaCompile>().configureEach {\n\toptions.encoding = \"UTF-8\"\n\toptions.compilerArgs.addAll(listOf(\n\t\t\"-Xlint:all\", // Enables all recommended warnings.\n\t\t\"-Werror\", // Terminates compilation when warnings occur.\n\t\t\"-parameters\", // Generates metadata for reflection on method parameters.\n\t))\n}\n\ntasks.compileJava {\n\toptions.compilerArgs.addAll(listOf(\n\t\t\"--module-version\", \"${project.version}\"\n\t))\n}\n\nconfigurations {\n\tapiElements {\n\t\tattributes {\n\t\t\tattributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, extension.mainJavaVersion.map { it.majorVersion.toInt() })\n\t\t}\n\t}\n\truntimeElements {\n\t\tattributes {\n\t\t\tattributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, extension.mainJavaVersion.map { it.majorVersion.toInt() })\n\t\t}\n\t}\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.release = extension.mainJavaVersion.map { it.majorVersion.toInt() }\n\t}\n\tcompileTestJava {\n\t\toptions.release = extension.testJavaVersion.map { it.majorVersion.toInt() }\n\t}\n}\n\nafterEvaluate {\n\tpluginManager.withPlugin(\"groovy\") {\n\t\ttasks.named<GroovyCompile>(\"compileGroovy\").configure {\n\t\t\t// Groovy compiler does not support the --release flag.\n\t\t\tsourceCompatibility = extension.mainJavaVersion.get().majorVersion\n\t\t\ttargetCompatibility = extension.mainJavaVersion.get().majorVersion\n\t\t}\n\t\ttasks.withType<GroovyCompile>().named { it.startsWith(\"compileTest\") }.configureEach {\n\t\t\t// Groovy compiler does not support the --release flag.\n\t\t\tsourceCompatibility = extension.testJavaVersion.get().majorVersion\n\t\t\ttargetCompatibility = extension.testJavaVersion.get().majorVersion\n\t\t}\n\t}\n}\n\ntasks {\n\tcheckstyleMain {\n\t\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleMain.xml\"))\n\t}\n\tcheckstyleTest {\n\t\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleTest.xml\"))\n\t}\n}\n\npluginManager.withPlugin(\"java-test-fixtures\") {\n\ttasks.named<Checkstyle>(\"checkstyleTestFixtures\") {\n\t\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleTest.xml\"))\n\t}\n\ttasks.named<JavaCompile>(\"compileTestFixturesJava\") {\n\t\toptions.release = extension.testJavaVersion.map { it.majorVersion.toInt() }\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.java-toolchain-conventions.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension\n\nplugins {\n\tid(\"junitbuild.build-parameters\")\n}\n\nproject.pluginManager.withPlugin(\"java\") {\n\tval defaultLanguageVersion = JavaLanguageVersion.of(25)\n\tval javaLanguageVersion = buildParameters.javaToolchain.version.map { JavaLanguageVersion.of(it) }.getOrElse(defaultLanguageVersion)\n\tval jvmImplementation = buildParameters.javaToolchain.implementation.map {\n\t\twhen(it) {\n\t\t\t\"j9\" -> JvmImplementation.J9\n\t\t\telse -> throw InvalidUserDataException(\"Unsupported JDK implementation: $it\")\n\t\t}\n\t}.getOrElse(JvmImplementation.VENDOR_SPECIFIC)\n\n\tval extension = the<JavaPluginExtension>()\n\tval javaToolchainService = the<JavaToolchainService>()\n\n\textension.toolchain {\n\t\tlanguageVersion = javaLanguageVersion\n\t\timplementation = jvmImplementation\n\t}\n\n\tpluginManager.withPlugin(\"org.jetbrains.kotlin.jvm\") {\n\t\tconfigure<KotlinJvmProjectExtension> {\n\t\t\tjvmToolchain {\n\t\t\t\tlanguageVersion = javaLanguageVersion\n\t\t\t}\n\t\t}\n\t}\n\n\ttasks.withType<JavaExec>().configureEach {\n\t\tjavaLauncher = javaToolchainService.launcherFor(extension.toolchain)\n\t\tif (javaLanguageVersion != defaultLanguageVersion) {\n\t\t\t// Track exact version of Java to detect changes in behavior between EA builds sooner\n\t\t\tinputs.property(\"javaRuntimeVersion\", javaLauncher.get().metadata.javaRuntimeVersion)\n\t\t}\n\t}\n\n\ttasks.withType<Test>().configureEach {\n\t\tif (javaLanguageVersion != defaultLanguageVersion) {\n\t\t\t// Track exact version of Java to detect changes in behavior between EA builds sooner\n\t\t\tinputs.property(\"javaRuntimeVersion\", javaLauncher.get().metadata.javaRuntimeVersion)\n\t\t}\n\t}\n\n\ttasks.withType<JavaCompile>().configureEach {\n\t\toutputs.cacheIf { javaLanguageVersion == defaultLanguageVersion }\n\t\tdoFirst {\n\t\t\tif (options.release.orNull == 8 && javaLanguageVersion.asInt() >= 20) {\n\t\t\t\toptions.compilerArgs.add(\n\t\t\t\t\t\"-Xlint:-options\" // see https://github.com/junit-team/junit-framework/issues/3029\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\ttasks.withType<GroovyCompile>().configureEach {\n\t\tjavaLauncher.set(javaToolchainService.launcherFor {\n\t\t\t// Groovy does not yet support JDK 19, see https://issues.apache.org/jira/browse/GROOVY-10569\n\t\t\tlanguageVersion = defaultLanguageVersion\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.javadoc-conventions.gradle.kts",
    "content": "import junitbuild.javadoc.JavadocConventionsExtension\nimport java.nio.file.Files\nimport kotlin.io.path.writeLines\n\nplugins {\n\t`java-library`\n}\n\njava {\n\twithJavadocJar()\n}\n\nval javadocReference = configurations.dependencyScope(\"javadocReference\")\n\nval extension = JavadocConventionsExtension(project, javadocReference.get(), tasks.javadoc)\nproject.extensions.add(\"javadocConventions\", extension)\n\nval javadocClasspath = configurations.resolvable(\"javadocClasspath\") {\n\textendsFrom(configurations.compileClasspath.get())\n\textendsFrom(javadocReference.get())\n}\n\ntasks.javadoc {\n\tclasspath = javadocClasspath.get()\n\toptions {\n\t\tmemberLevel = JavadocMemberLevel.PROTECTED\n\t\theader = project.name\n\t\tencoding = \"UTF-8\"\n\t\tlocale = \"en\"\n\t\t(this as StandardJavadocDocletOptions).apply {\n\t\t\taddBooleanOption(\"Xdoclint:all,-missing\", true)\n\t\t\taddBooleanOption(\"html5\", true)\n\t\t\taddBooleanOption(\"Werror\", true)\n\t\t\taddMultilineStringsOption(\"tag\").value = listOf(\n\t\t\t\t\"apiNote:a:API Note:\",\n\t\t\t\t\"implNote:a:Implementation Note:\"\n\t\t\t)\n\t\t\tuse(true)\n\t\t\tnoTimestamp(true)\n\t\t}\n\t}\n\n\tval sourceUrl = \"https://docs.junit.org/current\"\n\tval targetUrl = \"https://docs.junit.org/${version.toString().replace(\"-SNAPSHOT\", \"\")}\"\n\tdoLast {\n\t\tdestinationDir!!.walkTopDown()\n\t\t\t.filter { it.extension == \"html\" }\n\t\t\t.forEach { file ->\n\t\t\t\tval content = file.readText()\n\t\t\t\tif (content.contains(sourceUrl)) {\n\t\t\t\t\tval updatedContent = content.replace(sourceUrl, targetUrl)\n\t\t\t\t\tfile.writeText(updatedContent)\n\t\t\t\t}\n\t\t\t}\n\t}\n}\n\ntasks.named<Jar>(\"javadocJar\").configure {\n\tfrom(tasks.javadoc.map { File(it.destinationDir, \"element-list\") }) {\n\t\t// For compatibility with older tools, e.g. NetBeans 11\n\t\trename { \"package-list\" }\n\t}\n}\n\nval extractJavadocSinceValues by tasks.registering {\n\tinputs.files(sourceSets.main.get().allJava).withPathSensitivity(PathSensitivity.NONE)\n\tval outputFile = layout.buildDirectory.file(\"docs/javadoc-since-values.txt\")\n\toutputs.file(outputFile)\n\toutputs.cacheIf { true }\n\tdoFirst {\n\t\tval regex = \"\\\\s+(?:\\\\*|///) @since ([0-9.]+).*\".toRegex()\n\t\tval values = sourceSets.main.get().allJava.files.asSequence()\n\t\t\t.flatMap { file -> file.readLines().asSequence() }\n\t\t\t.mapNotNull(regex::matchEntire)\n\t\t\t.map { result -> result.groupValues[1] }\n\t\t\t.sorted()\n\t\t\t.distinct()\n\t\twith(outputFile.get().asFile.toPath()) {\n\t\t\tFiles.createDirectories(parent)\n\t\t\twriteLines(values)\n\t\t}\n\t}\n}\n\nconfigurations.consumable(\"javadocSinceValues\") {\n\tattributes {\n\t\tattribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(\"javadoc-since-values\"))\n\t}\n\toutgoing {\n\t\tartifact(extractJavadocSinceValues)\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.jmh-conventions.gradle.kts",
    "content": "import junitbuild.extensions.requiredVersionFromLibs\nimport junitbuild.extensions.dependencyFromLibs\n\nplugins {\n\tid(\"me.champeau.jmh\")\n}\n\njmh {\n\tjmhVersion = requiredVersionFromLibs(\"jmh\")\n}\n\ndependencies {\n\tjmh(dependencyFromLibs(\"jmh-core\"))\n\tjmhAnnotationProcessor(dependencyFromLibs(\"jmh-generator-annprocess\"))\n}\n\npluginManager.withPlugin(\"checkstyle\") {\n\ttasks.named<Checkstyle>(\"checkstyleJmh\").configure {\n\t\t// use same style rules as defined for tests\n\t\tconfig = resources.text.fromFile(project.the<CheckstyleExtension>().configDirectory.file(\"checkstyleTest.xml\"))\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.junit4-compatibility.gradle.kts",
    "content": "import junitbuild.extensions.dependencyFromLibs\n\nplugins {\n\t`java-library`\n}\n\nval junit_4_12 = configurations.dependencyScope(\"junit_4_12\")\nval junit_4_12_classpath = configurations.resolvable(\"junit_4_12_classpath\") {\n\textendsFrom(configurations.testRuntimeClasspath.get())\n\textendsFrom(junit_4_12.get())\n}\n\ndependencies {\n\tconstraints {\n\t\tjunit_4_12(\"junit:junit\") {\n\t\t\tversion {\n\t\t\t\tstrictly(\"4.12\")\n\t\t\t}\n\t\t}\n\t}\n\tpluginManager.withPlugin(\"junitbuild.osgi-conventions\") {\n\t\t\"osgiVerification\"(dependencyFromLibs(\"junit4-bundle\"))\n\t}\n}\n\ntasks {\n\tval test_4_12 by registering(Test::class) {\n\t\tval test by testing.suites.existing(JvmTestSuite::class)\n\t\ttestClassesDirs = files(test.map { it.sources.output.classesDirs })\n\t\tclasspath = files(sourceSets.main.map { it.output }) + files(test.map { it.sources.output }) + junit_4_12_classpath.get()\n\t}\n\tcheck {\n\t\tdependsOn(test_4_12)\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.kotlin-library-conventions.gradle.kts",
    "content": "\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget\nimport org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1\nimport org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\nplugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tkotlin(\"jvm\")\n}\n\ntasks.named(\"kotlinSourcesJar\") {\n\tenabled = false\n}\n\nval javaLibraryExtension = project.the<JavaLibraryExtension>()\n\ntasks.withType<KotlinCompile>().configureEach {\n\tcompilerOptions {\n\t\tjvmTarget = javaLibraryExtension.mainJavaVersion.map { JvmTarget.fromTarget(it.toString()) }\n\t\tapiVersion = KOTLIN_2_1\n\t\tlanguageVersion = apiVersion\n\t\tallWarningsAsErrors.convention(true)\n\t\tjavaParameters = true\n\t\tfreeCompilerArgs.add(\"-opt-in=kotlin.RequiresOptIn\")\n\t\tfreeCompilerArgs.add(jvmTarget.map { \"-Xjdk-release=${JavaVersion.toVersion(it.target).majorVersion}\" })\n\t}\n}\n\ntasks.named<KotlinCompile>(\"compileTestKotlin\") {\n\tcompilerOptions.jvmTarget = javaLibraryExtension.testJavaVersion.map { JvmTarget.fromTarget(it.toString()) }\n}\n\nconfigurations.named { it == \"kotlinBouncyCastleConfiguration\" }.configureEach {\n\tresolutionStrategy {\n\t\teachDependency {\n\t\t\tif (requested.group == \"org.bouncycastle\") {\n\t\t\t\tuseVersion(\"1.84\")\n\t\t\t\tbecause(\"Workaround for CVE-2026-3505 et al (used by kotlin plugin)\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.osgi-conventions.gradle.kts",
    "content": "import aQute.bnd.gradle.BundleTaskExtension\nimport aQute.bnd.gradle.Resolve\n\nplugins {\n\t`java-library`\n}\n\nval projectDescription = objects.property<String>().convention(provider { project.description })\n\n// This task enhances `jar` and `shadowJar` tasks with the bnd\n// `BundleTaskExtension` extension which allows for generating OSGi\n// metadata into the jar\ntasks.withType<Jar>().named {\n\tit == \"jar\" || it == \"shadowJar\"\n}.all { // configure tasks eagerly as workaround for https://github.com/bndtools/bnd/issues/5695\n\n\tval importAPIGuardian by extra { \"org.apiguardian.*;resolution:=\\\"optional\\\"\" }\n\tval importJSpecify by extra { \"org.jspecify.*;resolution:=\\\"optional\\\"\" }\n\tval importCommonsLogging by extra { \"org.junit.platform.commons.logging;status=INTERNAL\" }\n\n\textensions.create<BundleTaskExtension>(BundleTaskExtension.NAME, this).apply {\n\t\tproperties.set(projectDescription.map {\n\t\t\tmapOf(\"project.description\" to it)\n\t\t})\n\t\t// These are bnd instructions necessary for generating OSGi metadata.\n\t\t// We've generalized these so that they are widely applicable limiting\n\t\t// module configurations to special cases.\n\t\tsetBnd(\n\t\t\t\"\"\"\n\t\t\t\t# Set the Bundle-SymbolicName to the archiveBaseName.\n\t\t\t\t# We don't use the archiveClassifier which Bnd will use\n\t\t\t\t# in the default Bundle-SymbolicName value.\n\t\t\t\tBundle-SymbolicName: ${'$'}{task.archiveBaseName}\n\n\t\t\t\t# Set the Bundle-Name from the project description\n\t\t\t\tBundle-Name: ${'$'}{project.description}\n\n\t\t\t\t# These are the general rules for package imports.\n\t\t\t\tImport-Package: \\\n\t\t\t\t\t${importAPIGuardian},\\\n\t\t\t\t\t${importJSpecify},\\\n\t\t\t\t\t${importCommonsLogging},\\\n\t\t\t\t\tkotlin.*;resolution:=\"optional\",\\\n\t\t\t\t\t*\n\n\t\t\t\t# This tells bnd not to complain if a module doesn't actually import\n\t\t\t\t# the kotlin and apiguardian packages, but enough modules do to make it a default.\n\t\t\t\t-fixupmessages.kotlin.import: \"Unused Import-Package instructions: \\\\[kotlin.*\\\\]\";is:=ignore\n\t\t\t\t-fixupmessages.apiguardian.import: \"Unused Import-Package instructions: \\\\[org.apiguardian.*\\\\]\";is:=ignore\n\t\t\t\t-fixupmessages.warningsAsErrors: \".*\";restrict:=warning;is:=error\n\n\t\t\t\t# Don't scan for Class.forName package imports.\n\t\t\t\t# See https://bnd.bndtools.org/instructions/noclassforname.html\n\t\t\t\t-noclassforname: true\n\n\t\t\t\t# Don't add all the extra headers bnd normally adds.\n\t\t\t\t# See https://bnd.bndtools.org/instructions/noextraheaders.html\n\t\t\t\t-noextraheaders: true\n\n\t\t\t\t# Don't add the Private-Package header.\n\t\t\t\t# See https://bnd.bndtools.org/instructions/removeheaders.html\n\t\t\t\t-removeheaders: Private-Package\n\n\t\t\t\t# Instruct the APIGuardianAnnotations how to operate.\n\t\t\t\t# See https://bnd.bndtools.org/instructions/export_apiguardian.html\n\t\t\t\t-export-apiguardian: *;version=${'$'}{versionmask;===;${'$'}{version_cleanup;${'$'}{task.archiveVersion}}}\n\n\t\t\t\t# Avoid including java packages in Import-Package header to maximize compatibility with older OSGi runtimes.\n\t\t\t\t# See https://bnd.bndtools.org/instructions/noimportjava.html\n\t\t\t\t# Issue: https://github.com/junit-team/junit-framework/issues/4733\n\t\t\t\t-noimportjava: true\n\t\t\t\"\"\"\n\t\t)\n\n\t\t// Do the actual work putting OSGi stuff in the jar.\n\t\tdoLast(buildAction())\n\t}\n}\n\n// Bnd's Resolve task uses a properties file for its configuration. This\n// task writes out the properties necessary for it to verify the OSGi\n// metadata.\nval osgiProperties by tasks.registering(WriteProperties::class) {\n\tdestinationFile = layout.buildDirectory.file(\"verifyOSGiProperties.bndrun\")\n\tproperty(\"-standalone\", true)\n\tproject.extensions.getByType(JavaLibraryExtension::class).let { javaLibrary ->\n\t\tproperty(\"-runee\", Callable { \"JavaSE-${javaLibrary.mainJavaVersion.get()}\" })\n\t}\n\tproperty(\"-runrequires\", \"osgi.identity;filter:='(osgi.identity=${project.name})'\")\n\tproperty(\"-runsystempackages\", \"jdk.internal.misc,sun.misc\")\n\t// API Guardian and JDK JFR should be optional -> instruct resolver to ignore them\n\t// during resolution. Resolve should still pass.\n\tproperty(\"-runblacklist\", \"org.apiguardian.api,jdk.jfr\")\n}\n\nval osgiVerification = configurations.dependencyScope(\"osgiVerification\")\nval osgiVerificationClasspath = configurations.resolvable(\"osgiVerificationClasspath\") {\n\textendsFrom(configurations.runtimeClasspath.get())\n\textendsFrom(osgiVerification.get())\n}\n\n// Bnd's Resolve task is what verifies that a jar can be used in OSGi and\n// that its metadata is valid. If the metadata is invalid this task will\n// fail.\nval verifyOSGi by tasks.registering(Resolve::class) {\n\tbndrun = osgiProperties.flatMap { it.destinationFile }\n\toutputBndrun = layout.buildDirectory.file(\"resolvedOSGiProperties.bndrun\")\n\tisReportOptional = false\n\t// By default bnd will use jars found in:\n\t// 1. project.sourceSets.main.runtimeClasspath\n\t// 2. project.configurations.archives.artifacts.files\n\t// to validate the metadata.\n\t// This adds jars defined in `osgiVerification` also so that bnd\n\t// can use them to validate the metadata without causing those to\n\t// end up in the dependencies of those projects.\n\tbundles(osgiVerificationClasspath)\n\tproperties.empty()\n}\n\ntasks.check {\n\tdependsOn(verifyOSGi)\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.publishing-conventions.gradle.kts",
    "content": "import junitbuild.extensions.isSnapshot\n\nplugins {\n\t`maven-publish`\n\tsigning\n\tid(\"junitbuild.base-conventions\")\n\tid(\"junitbuild.build-parameters\")\n}\n\nval jupiterProjects: List<Project> by rootProject\nval platformProjects: List<Project> by rootProject\nval vintageProjects: List<Project> by rootProject\n\ngroup = buildParameters.publishing.group\n\t.getOrElse(when (project) {\n\t\tin jupiterProjects -> \"org.junit.jupiter\"\n\t\tin platformProjects -> \"org.junit.platform\"\n\t\tin vintageProjects -> \"org.junit.vintage\"\n\t\telse -> \"org.junit\"\n\t})\n\nval signArtifacts = buildParameters.publishing.signArtifacts.getOrElse(!(project.version.isSnapshot() || buildParameters.ci))\n\nsigning {\n\tuseGpgCmd()\n\tsign(publishing.publications)\n\tisRequired = signArtifacts\n}\n\ntasks.withType<Sign>().configureEach {\n\tenabled = signArtifacts\n}\n\npublishing {\n\tpublications {\n\t\tcreate<MavenPublication>(\"maven\") {\n\t\t\tversion = buildParameters.jitpack.version\n\t\t\t\t.map { value -> \"(.+)-[0-9a-f]+-\\\\d+\".toRegex().matchEntire(value)!!.groupValues[1] + \"-SNAPSHOT\" }\n\t\t\t\t.getOrElse(project.version.toString())\n\t\t\tpom {\n\t\t\t\tname.set(provider {\n\t\t\t\t\tproject.description ?: \"${project.group}:${project.name}\"\n\t\t\t\t})\n\t\t\t\turl = \"https://junit.org/\"\n\t\t\t\tscm {\n\t\t\t\t\tconnection = \"scm:git:git://github.com/junit-team/junit-framework.git\"\n\t\t\t\t\tdeveloperConnection = \"scm:git:git://github.com/junit-team/junit-framework.git\"\n\t\t\t\t\turl = \"https://github.com/junit-team/junit-framework\"\n\t\t\t\t}\n\t\t\t\tlicenses {\n\t\t\t\t\tlicense {\n\t\t\t\t\t\tval license: License by rootProject.extra\n\t\t\t\t\t\tname = license.name\n\t\t\t\t\t\turl = license.url.toString()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdevelopers {\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"bechte\"\n\t\t\t\t\t\tname = \"Stefan Bechtold\"\n\t\t\t\t\t\temail = \"stefan.bechtold@me.com\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"jlink\"\n\t\t\t\t\t\tname = \"Johannes Link\"\n\t\t\t\t\t\temail = \"business@johanneslink.net\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"marcphilipp\"\n\t\t\t\t\t\tname = \"Marc Philipp\"\n\t\t\t\t\t\temail = \"mail@marcphilipp.de\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"mmerdes\"\n\t\t\t\t\t\tname = \"Matthias Merdes\"\n\t\t\t\t\t\temail = \"matthias.merdes@heidelpay.com\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"sbrannen\"\n\t\t\t\t\t\tname = \"Sam Brannen\"\n\t\t\t\t\t\temail = \"sam@sambrannen.com\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"sormuras\"\n\t\t\t\t\t\tname = \"Christian Stein\"\n\t\t\t\t\t\temail = \"sormuras@gmail.com\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"juliette-derancourt\"\n\t\t\t\t\t\tname = \"Juliette de Rancourt\"\n\t\t\t\t\t\temail = \"derancourt.juliette@gmail.com\"\n\t\t\t\t\t}\n\t\t\t\t\tdeveloper {\n\t\t\t\t\t\tid = \"mpkorstanje\"\n\t\t\t\t\t\tname = \"M.P. Korstanje\"\n\t\t\t\t\t\temail = \"mpkorstanje@junit.org\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.settings-conventions.settings.gradle.kts",
    "content": "plugins {\n\tid(\"com.gradle.develocity\")\n\tid(\"com.gradle.common-custom-user-data-gradle-plugin\")\n\tid(\"org.gradle.toolchains.foojay-resolver-convention\")\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.shadow-conventions.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"com.gradleup.shadow\")\n}\n\nval shadowed = configurations.dependencyScope(\"shadowed\")\nval shadowedClasspath = configurations.resolvable(\"shadowedClasspath\") {\n\textendsFrom(shadowed.get())\n}\n\nconfigurations {\n\tlistOf(apiElements, runtimeElements).forEach {\n\t\tit.configure {\n\t\t\toutgoing {\n\t\t\t\tartifacts.clear()\n\t\t\t\tartifact(tasks.shadowJar) {\n\t\t\t\t\tclassifier = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcompileClasspath {\n\t\textendsFrom(shadowed.get())\n\t}\n}\n\nshadow {\n\taddShadowVariantIntoJavaComponent = false\n}\n\ntasks {\n\tjavadoc {\n\t\tclasspath += shadowedClasspath.get()\n\t}\n\tcheckstyleMain {\n\t\tclasspath += shadowedClasspath.get()\n\t}\n\tshadowJar {\n\t\tconfigurations = listOf(shadowedClasspath.get())\n\t\texclude(\"META-INF/maven/**\")\n\t\texcludes.remove(\"module-info.class\")\n\t\tarchiveClassifier = \"\"\n\t\tfrom(sourceSets.main.get().output.classesDirs) {\n\t\t\tinclude(\"module-info.class\")\n\t\t}\n\t\taddMultiReleaseAttribute = false\n\t}\n\tjar {\n\t\tdependsOn(shadowJar)\n\t\tenabled = false\n\t}\n\tnamed<Jar>(\"codeCoverageClassesJar\") {\n\t\tfrom(shadowJar.map { zipTree(it.archiveFile) })\n\t\texclude(\"**/shadow/**\")\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.spotless-conventions.gradle.kts",
    "content": "import com.diffplug.spotless.LineEnding\nimport junitbuild.extensions.requiredVersionFromLibs\n\nplugins {\n\tid(\"com.diffplug.spotless\")\n}\n\nval license: License by rootProject.extra\n\nspotless {\n\n\tformat(\"misc\") {\n\t\ttarget(\"*.gradle.kts\", \"gradle/plugins/**/*.gradle.kts\", \"*.gitignore\")\n\t\ttargetExclude(\"gradle/plugins/**/build/**\")\n\t\tleadingSpacesToTabs()\n\t\ttrimTrailingWhitespace()\n\t\tendWithNewline()\n\t}\n\n\tformat(\"documentation\") {\n\t\ttarget(\"*.adoc\", \"*.md\", \"src/**/*.adoc\", \"src/**/*.md\")\n\t\ttrimTrailingWhitespace()\n\t\tendWithNewline()\n\t}\n\n\tpluginManager.withPlugin(\"java\") {\n\n\t\tval configDir = rootProject.layout.projectDirectory.dir(\"gradle/config/eclipse\")\n\t\tval importOrderConfigFile = configDir.file(\"junit-eclipse.importorder\")\n\t\tval javaFormatterConfigFile = configDir.file(\"junit-eclipse-formatter-settings.xml\")\n\n\t\tjava {\n\t\t\ttargetExclude(\"**/module-info.java\", \"**/package-info.java\")\n\t\t\tlicenseHeaderFile(license.headerFile, \"(package|import) \")\n\t\t\timportOrderFile(importOrderConfigFile)\n\t\t\tval fullVersion = requiredVersionFromLibs(\"eclipse\")\n\t\t\tval majorMinorVersion = \"([0-9]+\\\\.[0-9]+).*\".toRegex().matchEntire(fullVersion)!!.let { it.groups[1]!!.value }\n\t\t\teclipse(majorMinorVersion).configFile(javaFormatterConfigFile)\n\t\t\ttrimTrailingWhitespace()\n\t\t\tendWithNewline()\n\t\t\tremoveUnusedImports()\n\t\t}\n\n\t\tformat(\"moduleAndPackageInfo\") {\n\t\t\ttarget(fileTree(layout.projectDirectory.dir(\"src/main/java\")) {\n\t\t\t\tinclude(\"module-info.java\", \"**/package-info.java\")\n\t\t\t})\n\t\t\tlicenseHeaderFile(license.headerFile, \"((/(//|\\\\*\\\\*))|((open )?module )|package|@.+)\")\n\t\t\ttrimTrailingWhitespace()\n\t\t\tendWithNewline()\n\t\t\tleadingSpacesToTabs()\n\t\t}\n\t}\n\n\tpluginManager.withPlugin(\"org.jetbrains.kotlin.jvm\") {\n\t\tkotlin {\n\t\t\ttargetExclude(\"**/src/test/resources/**\")\n\t\t\tktlint(requiredVersionFromLibs(\"ktlint\"))\n\t\t\tlicenseHeaderFile(license.headerFile)\n\t\t\ttrimTrailingWhitespace()\n\t\t\tendWithNewline()\n\t\t\ttoggleOffOn(\"formatter:off\", \"formatter:on\")\n\t\t}\n\t}\n\n\tpluginManager.withPlugin(\"groovy\") {\n\t\tgroovy {\n\t\t\tlicenseHeaderFile(license.headerFile)\n\t\t\ttrimTrailingWhitespace()\n\t\t\tendWithNewline()\n\t\t}\n\t}\n\n\t// Explicitly configure line endings to avoid Spotless to search for .gitattributes file\n\t// see https://github.com/gradle/gradle/issues/25469#issuecomment-3444231151\n\tlineEndings = LineEnding.UNIX\n}\n\ntasks {\n\tnamed(\"spotlessDocumentation\") {\n\t\toutputs.doNotCacheIf(\"negative avoidance savings\") { true }\n\t}\n\tnamed(\"spotlessMisc\") {\n\t\toutputs.doNotCacheIf(\"negative avoidance savings\") { true }\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/common/src/main/kotlin/junitbuild.testing-conventions.gradle.kts",
    "content": "import com.gradle.develocity.agent.gradle.internal.test.PredictiveTestSelectionConfigurationInternal\nimport com.gradle.develocity.agent.gradle.test.PredictiveTestSelectionMode\nimport junitbuild.deps.ByteBuddyAlignmentRule\nimport junitbuild.extensions.bundleFromLibs\nimport junitbuild.extensions.dependencyFromLibs\nimport junitbuild.extensions.trackOperationSystemAsInput\nimport org.gradle.api.tasks.PathSensitivity.RELATIVE\nimport org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL\nimport org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED\nimport org.gradle.internal.os.OperatingSystem\nimport java.io.IOException\nimport java.io.OutputStream\nimport java.nio.file.FileVisitResult\nimport java.nio.file.Files\nimport java.nio.file.NoSuchFileException\nimport java.nio.file.Path\nimport java.nio.file.SimpleFileVisitor\nimport java.nio.file.attribute.BasicFileAttributes\n\nplugins {\n\t`java-library`\n\tid(\"junitbuild.build-parameters\")\n}\n\nvar javaAgent = configurations.dependencyScope(\"javaAgent\")\nvar javaAgentClasspath = configurations.resolvable(\"javaAgentClasspath\") {\n\textendsFrom(javaAgent.get())\n}\n\nvar openTestReportingCli = configurations.dependencyScope(\"openTestReportingCli\")\nvar openTestReportingCliClasspath = configurations.resolvable(\"openTestReportingCliClasspath\") {\n\textendsFrom(openTestReportingCli.get())\n\tattributes {\n\t\t// Avoid using the shadowed variant of junit-platform-reporting\n\t\tattribute(Bundling.BUNDLING_ATTRIBUTE, named(Bundling.EXTERNAL))\n\t}\n}\n\nval generateOpenTestHtmlReport by tasks.registering(JavaExec::class) {\n\tmustRunAfter(tasks.withType<Test>())\n\tmainModule.set(\"org.opentest4j.reporting.cli\")\n\tmodularity.inferModulePath = true\n\targs(\"html-report\")\n\tclasspath(openTestReportingCliClasspath)\n\targumentProviders += objects.newInstance(HtmlReportParameters::class).apply {\n\t\teventXmlFiles.from(tasks.withType<Test>().map {\n\t\t\tobjects.fileTree()\n\t\t\t\t.from(it.reports.junitXml.outputLocation)\n\t\t\t\t.include(\"junit-*/open-test-report.xml\")\n\t\t})\n\t\toutputLocation = layout.buildDirectory.file(\"reports/open-test-report.html\")\n\t}\n\tif (buildParameters.testing.hideOpenTestReportHtmlGeneratorOutput) {\n\t\tstandardOutput = object : OutputStream() {\n\t\t\toverride fun write(b: Int) {\n\t\t\t\t// discard output\n\t\t\t}\n\t\t}\n\t}\n\toutputs.cacheIf { true }\n}\n\nabstract class HtmlReportParameters : CommandLineArgumentProvider {\n\n\t@get:InputFiles\n\t@get:PathSensitive(RELATIVE)\n\t@get:SkipWhenEmpty\n\tabstract val eventXmlFiles: ConfigurableFileCollection\n\n\t@get:OutputFile\n\tabstract val outputLocation: RegularFileProperty\n\n\toverride fun asArguments() = listOf(\"--output\", outputLocation.get().asFile.absolutePath) +\n\t\t\teventXmlFiles.map { it.absolutePath }.toList()\n}\n\ntasks.withType<Test>().configureEach {\n\tuseJUnitPlatform {\n\t\tincludeEngines(\"junit-jupiter\")\n\t}\n\tinclude(\"**/*Test.class\", \"**/*Tests.class\")\n\ttestLogging {\n\t\tevents = setOf(FAILED)\n\t\texceptionFormat = FULL\n\t}\n\tdevelocity {\n\t\ttestRetry {\n\t\t\tmaxRetries.convention(buildParameters.testing.retries.orElse(if (buildParameters.ci) 2 else 0))\n\t\t}\n\t\ttestDistribution {\n\t\t\tenabled.convention(buildParameters.junit.develocity.testDistribution.enabled && (!buildParameters.ci || !System.getenv(\"DEVELOCITY_ACCESS_KEY\").isNullOrBlank()))\n\t\t\tmaxLocalExecutors.convention(buildParameters.junit.develocity.testDistribution.maxLocalExecutors)\n\t\t\tmaxRemoteExecutors.convention(buildParameters.junit.develocity.testDistribution.maxRemoteExecutors)\n\t\t\tif (buildParameters.ci) {\n\t\t\t\twhen {\n\t\t\t\t\tOperatingSystem.current().isLinux -> requirements.add(\"os=linux\")\n\t\t\t\t\tOperatingSystem.current().isWindows -> requirements.add(\"os=windows\")\n\t\t\t\t\tOperatingSystem.current().isMacOsX -> requirements.add(\"os=macos\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tpredictiveTestSelection {\n\t\t\tenabled.convention(buildParameters.junit.develocity.predictiveTestSelection.enabled)\n\n\t\t\tif (buildParameters.junit.develocity.predictiveTestSelection.selectRemainingTests) {\n\t\t\t\tmode.convention(PredictiveTestSelectionMode.REMAINING_TESTS)\n\t\t\t}\n\n\t\t\t// Ensure PTS works when publishing Build Scans to scans.gradle.com\n\t\t\tthis as PredictiveTestSelectionConfigurationInternal\n\t\t\tserver = uri(\"https://ge.junit.org\")\n\n\t\t\tmergeCodeCoverage = true\n\t\t}\n\t}\n\tsystemProperty(\"java.util.logging.manager\", \"org.apache.logging.log4j.jul.LogManager\")\n\t// https://github.com/gradle/gradle/issues/30554\n\tsystemProperty(\"log4j2.julLoggerAdapter\", \"org.apache.logging.log4j.jul.CoreLoggerAdapter\")\n\t// Avoid overhead (see https://logging.apache.org/log4j/2.x/manual/jmx.html#enabling-jmx)\n\tsystemProperty(\"log4j2.disableJmx\", \"true\")\n\t// https://github.com/raphw/byte-buddy/issues/1803\n\tsystemProperty(\"net.bytebuddy.safe\", true)\n\t// Required until ASM officially supports the JDK 14\n\tsystemProperty(\"net.bytebuddy.experimental\", true)\n\tif (buildParameters.testing.enableJFR) {\n\t\tjvmArgs(\n\t\t\t\"-XX:+UnlockDiagnosticVMOptions\",\n\t\t\t\"-XX:+DebugNonSafepoints\",\n\t\t\t\"-XX:StartFlightRecording=filename=${reports.junitXml.outputLocation.get()},dumponexit=true,settings=profile.jfc\",\n\t\t\t\"-XX:FlightRecorderOptions=stackdepth=1024\"\n\t\t)\n\t}\n\tsystemProperty(\"junit.platform.execution.dryRun.enabled\", buildParameters.testing.dryRun)\n\n\t// Track OS as input so that tests are executed on all configured operating systems on CI\n\ttrackOperationSystemAsInput()\n\n\t// Avoid passing unnecessary environment variables to the JVM (from GitHub Actions)\n\tif (buildParameters.ci) {\n\t\tenvironment.remove(\"RUNNER_TEMP\")\n\t\tenvironment.remove(\"GITHUB_ACTION\")\n\t}\n\n\tjvmArgumentProviders += CommandLineArgumentProvider {\n\t\tlistOf(\n\t\t\t\"-Djunit.platform.reporting.open.xml.enabled=true\",\n\t\t\t\"-Djunit.platform.reporting.open.xml.git.enabled=true\",\n\t\t\t\"-Djunit.platform.reporting.output.dir=${reports.junitXml.outputLocation.get().asFile.absolutePath}/junit-{uniqueNumber}\",\n\t\t)\n\t}\n\tsystemProperty(\"junit.platform.output.capture.stdout\", \"true\")\n\tsystemProperty(\"junit.platform.output.capture.stderr\", \"true\")\n\tsystemProperty(\"junit.platform.discovery.issue.severity.critical\", \"info\")\n\n\tjvmArgumentProviders += objects.newInstance(JavaAgentArgumentProvider::class).apply {\n\t\tclasspath.from(javaAgentClasspath)\n\t}\n\tjvmArgs(\"-Xshare:off\") // https://github.com/mockito/mockito/issues/3111\n\n\tdoFirst {\n\t\treports.junitXml.outputLocation.asFile.get()\n\t\t\t.listFiles { _, name -> name.startsWith(\"junit-\") }\n\t\t\t?.forEach { dir ->\n\t\t\t\tFiles.walkFileTree(dir.toPath(), object : SimpleFileVisitor<Path>() {\n\t\t\t\t\toverride fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {\n\t\t\t\t\t\treturn deleteIfExistsAndContinue(file)\n\t\t\t\t\t}\n\n\t\t\t\t\toverride fun postVisitDirectory(dir: Path, ex: IOException?): FileVisitResult {\n\t\t\t\t\t\tif (ex is NoSuchFileException) {\n\t\t\t\t\t\t\treturn FileVisitResult.CONTINUE\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (ex != null) {\n\t\t\t\t\t\t\tthrow ex\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn deleteIfExistsAndContinue(dir)\n\t\t\t\t\t}\n\n\t\t\t\t\tprivate fun deleteIfExistsAndContinue(dir: Path): FileVisitResult {\n\t\t\t\t\t\tFiles.deleteIfExists(dir)\n\t\t\t\t\t\treturn FileVisitResult.CONTINUE\n\t\t\t\t\t}\n\n\t\t\t\t\toverride fun visitFileFailed(file: Path, ex: IOException): FileVisitResult {\n\t\t\t\t\t\tif (ex is NoSuchFileException) {\n\t\t\t\t\t\t\treturn FileVisitResult.CONTINUE\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow ex\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t}\n\n\tfinalizedBy(generateOpenTestHtmlReport)\n}\n\ndependencies {\n\tcomponents.all<ByteBuddyAlignmentRule>()\n\n\tconstraints {\n\t\ttestImplementation(dependencyFromLibs(\"byteBuddy\"))\n\t}\n\n\ttestImplementation(platform(dependencyFromLibs(\"mockito-bom\")))\n\ttestImplementation(dependencyFromLibs(\"assertj\"))\n\ttestImplementation(dependencyFromLibs(\"mockito-junit-jupiter\"))\n\ttestImplementation(dependencyFromLibs(\"testingAnnotations\"))\n\ttestImplementation(project(\":junit-jupiter\"))\n\n\ttestRuntimeOnly(project(\":junit-platform-launcher\"))\n\ttestRuntimeOnly(project(\":junit-platform-reporting\"))\n\n\ttestRuntimeOnly(bundleFromLibs(\"log4j\"))\n\ttestRuntimeOnly(dependencyFromLibs(\"openTestReporting-events\")) {\n\t\tbecause(\"it's required to run tests via IntelliJ which does not consumed the shadowed jar of junit-platform-reporting\")\n\t}\n\n\topenTestReportingCli(dependencyFromLibs(\"openTestReporting-cli\"))\n\topenTestReportingCli(project(\":junit-platform-reporting\"))\n\topenTestReportingCli(platform(dependencyFromLibs(\"log4j-bom\"))) {\n\t\tbecause(\"Workaround for CVE-2025-68161\")\n\t}\n\n\tjavaAgent(platform(dependencyFromLibs(\"mockito-bom\")))\n\tjavaAgent(dependencyFromLibs(\"mockito-core\")) {\n\t\tisTransitive = false\n\t}\n}\n\nabstract class JavaAgentArgumentProvider : CommandLineArgumentProvider {\n\n\t@get:Classpath\n\tabstract val classpath: ConfigurableFileCollection\n\n\toverride fun asArguments() = listOf(\"-javaagent:${classpath.singleFile.absolutePath}\")\n\n}\n"
  },
  {
    "path": "gradle/plugins/publishing/build.gradle.kts",
    "content": "import junitbuild.extensions.markerCoordinates\n\nplugins {\n\t`kotlin-dsl`\n}\n\ndependencies {\n\timplementation(\"junitbuild.base:dsl-extensions\")\n\timplementation(libs.plugins.nmcp.settings.markerCoordinates)\n}\n"
  },
  {
    "path": "gradle/plugins/publishing/src/main/kotlin/junitbuild/release/VerifyBinaryArtifactsAreIdentical.kt",
    "content": "package junitbuild.release\n\nimport org.gradle.api.DefaultTask\nimport org.gradle.api.file.DirectoryProperty\nimport org.gradle.api.provider.Property\nimport org.gradle.api.provider.ProviderFactory\nimport org.gradle.api.tasks.Input\nimport org.gradle.api.tasks.InputDirectory\nimport org.gradle.api.tasks.Internal\nimport org.gradle.api.tasks.PathSensitive\nimport org.gradle.api.tasks.PathSensitivity\nimport org.gradle.api.tasks.TaskAction\nimport org.gradle.api.tasks.options.Option\nimport java.io.File\nimport java.net.URI\nimport java.net.http.HttpClient\nimport java.net.http.HttpRequest\nimport java.net.http.HttpResponse.BodyHandlers\nimport javax.inject.Inject\n\nabstract class VerifyBinaryArtifactsAreIdentical @Inject constructor(providers: ProviderFactory): DefaultTask() {\n\n    @get:InputDirectory\n    @get:PathSensitive(PathSensitivity.RELATIVE)\n    abstract val localRepoDir: DirectoryProperty\n\n    @get:Input\n    abstract val remoteRepoUrl: Property<String>\n\n    @get:Internal\n    abstract val remoteRepoBearerToken: Property<String>\n\n    init {\n        // Depends on contents of remote repository\n        outputs.upToDateWhen { false }\n        remoteRepoBearerToken.convention(providers.environmentVariable(\"MAVEN_CENTRAL_USER_TOKEN\"))\n    }\n\n    @Suppress(\"unused\")\n    @Option(\n        option = \"remote-repo-url\",\n        description = \"The URL of the remote repository to compare the local repository against\"\n    )\n    fun remoteRepo(url: String) {\n        remoteRepoUrl.set(url)\n    }\n\n    @TaskAction\n    fun execute() {\n        val localRootDir = localRepoDir.get().asFile\n        val baseUrl = remoteRepoUrl.get()\n        val mismatches = mutableListOf<Mismatch>()\n        var numChecks = 0\n        HttpClient.newHttpClient().use { httpClient ->\n            localRootDir.walk().forEach { file ->\n                if (file.isFile && file.name.endsWith(\".jar.sha512\") && !file.name.endsWith(\"-javadoc.jar.sha512\")) {\n                    val localSha512 = file.readText()\n                    val relativeFile = file.relativeTo(localRootDir)\n                    val url = URI.create(\"${baseUrl}/${relativeFile.path}\")\n                    logger.info(\"Checking {}...\", url)\n                    val request = HttpRequest.newBuilder().GET()\n                        .uri(url)\n                        .header(\"Authorization\", \"Bearer ${remoteRepoBearerToken.get()}\")\n                        .build()\n                    val response = httpClient.send(request, BodyHandlers.ofString())\n                    val remoteSha512 = if (response.statusCode() == 200) response.body() else \"status=${response.statusCode()}\"\n                    if (localSha512 != remoteSha512) {\n                        mismatches.add(Mismatch(relativeFile, localSha512, remoteSha512))\n                    }\n                    numChecks++\n                }\n            }\n        }\n        require(numChecks > 0) {\n            \"No files found to compare\"\n        }\n        require(mismatches.isEmpty()) {\n            \"The following files have different SHA-512 checksums in the local and remote repositories:\\n\\n\" +\n                    mismatches.joinToString(\"\\n\\n\") {\n                        \"\"\"\n                            ${it.file}\n                            local:  ${it.localSha512}\n                            remote: ${it.remoteSha512}\n                        \"\"\".trimIndent()\n                    }\n        }\n    }\n\n    private data class Mismatch(val file: File, val localSha512: String, val remoteSha512: String)\n}\n"
  },
  {
    "path": "gradle/plugins/publishing/src/main/kotlin/junitbuild.maven-central-publishing.settings.gradle.kts",
    "content": "import kotlin.time.Duration.Companion.minutes\nimport kotlin.time.toJavaDuration\n\nplugins {\n\tid(\"com.gradleup.nmcp.settings\")\n}\n\nnmcpSettings {\n\tcentralPortal {\n\t\tusername = providers.gradleProperty(\"mavenCentralUsername\")\n\t\tpassword = providers.gradleProperty(\"mavenCentralPassword\")\n\t\tpublishingType = \"USER_MANAGED\"\n\t\tvalidationTimeout = 10.minutes.toJavaDuration()\n\t\tpublishingTimeout = 30.minutes.toJavaDuration()\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/publishing/src/main/kotlin/junitbuild.temp-maven-repo.gradle.kts",
    "content": "import junitbuild.extensions.capitalized\nimport junitbuild.release.VerifyBinaryArtifactsAreIdentical\n\nval tempRepoName by extra(\"temp\")\nval tempRepoDir by extra {\n\tlayout.buildDirectory.dir(\"repo\").get().asFile\n}\n\nval clearTempRepoDir by tasks.registering {\n\tval dir = tempRepoDir\n\tdoFirst {\n\t\tdir.deleteRecursively()\n\t}\n}\n\nval publishAllSubprojectsToTempRepository by tasks.registering\n\ntasks.register<VerifyBinaryArtifactsAreIdentical>(\"verifyArtifactsInStagingRepositoryAreReproducible\") {\n\tdependsOn(publishAllSubprojectsToTempRepository)\n\tlocalRepoDir.set(tempRepoDir)\n}\n\nsubprojects {\n\tpluginManager.withPlugin(\"maven-publish\") {\n\t\tconfigure<PublishingExtension> {\n\t\t\trepositories {\n\t\t\t\tmaven {\n\t\t\t\t\tname = tempRepoName\n\t\t\t\t\turl = uri(tempRepoDir)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tval publishingTasks = tasks.withType<PublishToMavenRepository>()\n\t\t\t.named { it.endsWith(\"To${tempRepoName.capitalized()}Repository\") }\n\t\tpublishingTasks.configureEach {\n\t\t\tdependsOn(clearTempRepoDir)\n\t\t}\n\t\tpublishAllSubprojectsToTempRepository {\n\t\t\tdependsOn(publishingTasks)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "gradle/plugins/settings.gradle.kts",
    "content": "pluginManagement {\n\tincludeBuild(\"../base\")\n}\n\nplugins {\n\tid(\"junitbuild.dsl-extensions\") apply false\n}\n\ndependencyResolutionManagement {\n\tversionCatalogs {\n\t\tcreate(\"libs\") {\n\t\t\tfrom(files(\"../libs.versions.toml\"))\n\t\t}\n\t}\n\trepositories {\n\t\tgradlePluginPortal()\n\t}\n}\n\nrootProject.name = \"plugins\"\n\ninclude(\"antora\")\ninclude(\"backward-compatibility\")\ninclude(\"build-parameters\")\ninclude(\"common\")\ninclude(\"code-generator\")\ninclude(\"publishing\")\n\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-9.5.0-bin.zip\nnetworkTimeout=10000\nretries=0\nretryBackOffMs=500\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "version = 6.1.0-SNAPSHOT\n\n# For backward compatibility checks\napiBaselineVersion = 6.0.2\n\norg.gradle.jvmargs=-Xmx1g -XX:+HeapDumpOnOutOfMemoryError\norg.gradle.caching=true\norg.gradle.parallel=true\norg.gradle.configuration-cache.parallel=true\norg.gradle.java.installations.fromEnv=GRAALVM_HOME,JDK17,JDK21,JDK24,JDK25\norg.gradle.kotlin.dsl.allWarningsAsErrors=true\n\n# Test Distribution\ndevelocity.internal.testdistribution.writeTraceFile=true\n\n# Omit automatic compile dependency on kotlin-stdlib\n# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library\nkotlin.stdlib.default.dependency=false\n\n# Avoid Gradle deprecation warnings from Kotlin plugin\nkotlin.mpp.keepMppDependenciesIntactInPoms=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015 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# SPDX-License-Identifier: Apache-2.0\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/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/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\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\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\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    if ! command -v java >/dev/null 2>&1\n    then\n        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.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\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        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\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\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\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# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -jar \"$APP_HOME/gradle/wrapper/gradle-wrapper.jar\" \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\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\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables, and ensure extensions are enabled\r\nsetlocal EnableExtensions\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\n\"%COMSPEC%\" /c exit 1\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\n\"%COMSPEC%\" /c exit 1\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\n\r\n\r\n@rem Execute Gradle\r\n@rem endlocal doesn't take effect until after the line is parsed and variables are expanded\r\n@rem which allows us to clear the local environment before executing the java command\r\nendlocal & \"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -jar \"%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\" %* & call :exitWithErrorLevel\r\n\r\n:exitWithErrorLevel\r\n@rem Use \"%COMSPEC%\" /c exit to allow operators to work properly in scripts\r\n\"%COMSPEC%\" /c exit %ERRORLEVEL%\r\n"
  },
  {
    "path": "junit-bom/README.md",
    "content": "# JUnit Bill of Materials (BOM)\n\nThis module provides a Bill of Materials POM to ease dependency management using [Maven]\nor [Gradle]. Please refer to the [User Guide] for details.\n\n[Maven]:      https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies\n[Gradle]:     https://docs.gradle.org/current/userguide/platforms.html#sub:bom_import\n[User Guide]: https://docs.junit.org/current/appendix.html#dependency-metadata-junit-bom\n"
  },
  {
    "path": "junit-bom/junit-bom.gradle.kts",
    "content": "plugins {\n\t`java-platform`\n\tid(\"junitbuild.publishing-conventions\")\n}\n\ndescription = \"${rootProject.description} (Bill of Materials)\"\n\ndependencies {\n\tconstraints {\n\t\tval mavenizedProjects: List<Project> by rootProject.extra\n\t\tmavenizedProjects.sorted()\n\t\t\t\t.filter { it.name != \"junit-platform-console-standalone\" }\n\t\t\t\t.forEach {\n\t\t\t\t\tval version = buildParameters.jitpack.version\n\t\t\t\t\t\t.map { value -> \"(.+)-[0-9a-f]+-\\\\d+\".toRegex().matchEntire(value)!!.groupValues[1] + \"-SNAPSHOT\" }\n\t\t\t\t\t\t.getOrElse(it.version.toString())\n\t\t\t\t\tapi(\"${it.group}:${it.name}:${version}\")\n\t\t\t\t}\n\t}\n}\n\npublishing.publications.named<MavenPublication>(\"maven\") {\n\tfrom(components[\"javaPlatform\"])\n\tpom {\n\t\tdescription = \"This Bill of Materials POM can be used to ease dependency management \" +\n\t\t\t\t\"when referencing multiple JUnit artifacts using Gradle or Maven.\"\n\t\twithXml {\n\t\t\tval filteredContent = asString().replace(\"\\\\s*<scope>compile</scope>\".toRegex(), \"\")\n\t\t\tasString().clear().append(filteredContent)\n\t\t}\n\t}\n}\n\ntasks.withType<GenerateMavenPom>().configureEach {\n\tdoLast {\n\t\tval xml = destination.readText()\n\t\trequire(xml.indexOf(\"<dependencies>\") == xml.lastIndexOf(\"<dependencies>\")) {\n\t\t\t\"BOM must contain exactly one <dependencies> element but contained multiple:\\n$destination\"\n\t\t}\n\t\trequire(xml.contains(\"<dependencyManagement>\")) {\n\t\t\t\"BOM must contain a <dependencyManagement> element:\\n$destination\"\n\t\t}\n\t\trequire(!xml.contains(\"<scope>\")) {\n\t\t\t\"BOM must not contain <scope> elements:\\n$destination\"\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter/junit-jupiter.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-aggregator-conventions\")\n}\n\ndescription = \"JUnit Jupiter (Aggregator)\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitJupiterApi)\n\tapi(projects.junitJupiterParams)\n\n\timplementation(projects.junitJupiterEngine)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-jupiter/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Aggregates all JUnit Jupiter modules.\n *\n * @since 5.4\n */\nmodule org.junit.jupiter {\n\trequires transitive org.junit.jupiter.api;\n\trequires transitive org.junit.jupiter.engine;\n\trequires transitive org.junit.jupiter.params;\n}\n"
  },
  {
    "path": "junit-jupiter-api/junit-jupiter-api.gradle.kts",
    "content": "import junitbuild.generator.GenerateJreRelatedSourceCode\n\nplugins {\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.code-generator\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Jupiter API\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(libs.opentest4j)\n\tapi(projects.junitPlatformCommons)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tcompileOnly(kotlin(\"stdlib\"))\n\n\ttestFixturesImplementation(libs.assertj)\n\ttestFixturesImplementation(testFixtures(projects.junitPlatformCommons))\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\njavadocConventions {\n\taddExtraModuleReferences(projects.junitPlatformEngine, projects.junitPlatformLauncher, projects.junitJupiterParams)\n}\n\neclipseConventions {\n\thideModularity = false\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.compilerArgs.add(\"-Xlint:-module\") // due to qualified exports\n\t}\n\tjar {\n\t\tbundle {\n\t\t\tval version = project.version\n\t\t\tbnd(\"\"\"\n\t\t\t\tRequire-Capability:\\\n\t\t\t\t\torg.junit.platform.engine;\\\n\t\t\t\t\t\tfilter:='(&(org.junit.platform.engine=junit-jupiter)(version>=${'$'}{version_cleanup;${version}})(!(version>=${'$'}{versionmask;+;${'$'}{version_cleanup;${version}}})))';\\\n\t\t\t\t\t\teffective:=active\n\t\t\t\"\"\")\n\t\t}\n\t}\n\tval generateJreTestDouble by registering(GenerateJreRelatedSourceCode::class) {\n\t\ttemplateDir = layout.projectDirectory.dir(\"src/templates/resources/main\")\n\t\ttargetDir = layout.buildDirectory.dir(\"generated/sources/jte/testDouble\")\n\t\tmaxVersion = 22\n\t\tfileNamePrefix = \"TestDouble\"\n\t\tadditionalTemplateParameters = mapOf(\"classNamePrefix\" to \"TestDouble\")\n\t}\n\tsourceSets.testFixtures.get().java.srcDir(generateJreTestDouble.map { it.targetDir })\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Defines the JUnit Jupiter API for writing tests.\n *\n * @since 5.0\n */\nmodule org.junit.jupiter.api {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.junit.platform.commons;\n\trequires transitive org.opentest4j;\n\n\trequires static kotlin.stdlib;\n\n\texports org.junit.jupiter.api;\n\texports org.junit.jupiter.api.condition;\n\texports org.junit.jupiter.api.extension;\n\texports org.junit.jupiter.api.extension.support;\n\texports org.junit.jupiter.api.function;\n\texports org.junit.jupiter.api.io;\n\texports org.junit.jupiter.api.parallel;\n\texports org.junit.jupiter.api.timeout to org.junit.jupiter.engine;\n\texports org.junit.jupiter.api.util;\n\n\topens org.junit.jupiter.api.condition to org.junit.platform.commons;\n\topens org.junit.jupiter.api.util to org.junit.platform.commons;\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterAll.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @AfterAll} is used to signal that the annotated method should be\n * executed <em>after</em> <strong>all</strong> tests in the current test class.\n *\n * <p>In contrast to {@link AfterEach @AfterEach} methods, {@code @AfterAll}\n * methods are only executed once per execution of a given test class. If the\n * test class is annotated with {@link ClassTemplate @ClassTemplate}, the\n * {@code @AfterAll} methods are executed once after the last invocation of the\n * class template. If a {@link Nested @Nested} test class is declared in a\n * {@link ClassTemplate @ClassTemplate}, its {@code @AfterAll} methods are\n * called once per execution of the nested test class, namely, once per\n * invocation of the outer class template.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @AfterAll} methods must have a {@code void} return type and must\n * be {@code static} unless the test class is annotated with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. In addition,\n * {@code @AfterAll} methods may optionally declare parameters to be resolved by\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>Using {@code private} visibility for {@code @AfterAll} methods is strongly\n * discouraged and will be disallowed in a future release.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @AfterAll} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @AfterAll} methods from superclasses will be\n * executed after {@code @AfterAll} methods in subclasses.\n *\n * <p>Similarly, {@code @AfterAll} methods declared in an interface are inherited\n * as long as they are not overridden, and {@code @AfterAll} methods from an\n * interface will be executed after {@code @AfterAll} methods in the class that\n * implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @AfterAll} methods that are declared within a single test class or\n * test interface. While it may at times appear that these methods are invoked\n * in alphabetical order, they are in fact sorted using an algorithm that is\n * deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @AfterAll} methods are in no way linked to\n * {@code @BeforeAll} methods. Consequently, there are no guarantees with regard\n * to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeAll} methods {@code createA()} and {@code createB()} as well as\n * two {@code @AfterAll} methods {@code destroyA()} and {@code destroyB()}, the\n * order in which the {@code @BeforeAll} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterAll} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeAll} method and at most one\n * {@code @AfterAll} method per test class or test interface unless there are no\n * dependencies between the {@code @BeforeAll} methods or between the\n * {@code @AfterAll} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @AfterAll} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @AfterAll}.\n *\n * @since 5.0\n * @see BeforeAll\n * @see BeforeEach\n * @see AfterEach\n * @see Test\n * @see TestFactory\n * @see TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface AfterAll {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AfterEach.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @AfterEach} is used to signal that the annotated method should be\n * executed <em>after</em> <strong>each</strong> {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory},\n * and {@code @TestTemplate} method in the current test class.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @AfterEach} methods must have a {@code void} return type and must\n * not be {@code static}. In addition, {@code @AfterEach} methods may optionally\n * declare parameters to be resolved by\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>Using {@code private} visibility for {@code @AfterEach} methods is strongly\n * discouraged and will be disallowed in a future release.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @AfterEach} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @AfterEach} methods from superclasses will be\n * executed after {@code @AfterEach} methods in subclasses.\n *\n * <p>Similarly, {@code @AfterEach} methods declared as <em>interface default\n * methods</em> are inherited as long as they are not overridden, and\n * {@code @AfterEach} default methods will be executed after {@code @AfterEach}\n * methods in the class that implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @AfterEach} methods that are declared within a single test class or\n * test interface. While it may at times appear that these methods are invoked\n * in alphabetical order, they are in fact sorted using an algorithm that is\n * deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @AfterEach} methods are in no way linked to\n * {@code @BeforeEach} methods. Consequently, there are no guarantees with\n * regard to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeEach} methods {@code createA()} and {@code createB()} as well\n * as two {@code @AfterEach} methods {@code destroyA()} and {@code destroyB()},\n * the order in which the {@code @BeforeEach} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterEach} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeEach} method and at most one\n * {@code @AfterEach} method per test class or test interface unless there are\n * no dependencies between the {@code @BeforeEach} methods or between the\n * {@code @AfterEach} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @AfterEach} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @AfterEach}.\n *\n * @since 5.0\n * @see BeforeEach\n * @see BeforeAll\n * @see AfterAll\n * @see Test\n * @see RepeatedTest\n * @see TestFactory\n * @see TestTemplate\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface AfterEach {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertAll.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * {@code AssertAll} is a collection of utility methods that support asserting\n * multiple conditions in tests at once.\n *\n * @since 5.0\n */\nclass AssertAll {\n\n\tprivate AssertAll() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertAll(Executable... executables) {\n\t\tassertAll(null, executables);\n\t}\n\n\tstatic void assertAll(@Nullable String heading, Executable... executables) {\n\t\tPreconditions.notEmpty(executables, \"executables array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(executables, \"individual executables must not be null\");\n\t\tassertAll(heading, Arrays.stream(executables));\n\t}\n\n\tstatic void assertAll(Collection<Executable> executables) {\n\t\tassertAll(null, executables);\n\t}\n\n\tstatic void assertAll(@Nullable String heading, Collection<Executable> executables) {\n\t\tPreconditions.notNull(executables, \"executables collection must not be null\");\n\t\tPreconditions.containsNoNullElements(executables, \"individual executables must not be null\");\n\t\tassertAll(heading, executables.stream());\n\t}\n\n\tstatic void assertAll(Stream<Executable> executables) {\n\t\tassertAll(null, executables);\n\t}\n\n\tstatic void assertAll(@Nullable String heading, Stream<Executable> executables) {\n\t\tPreconditions.notNull(executables, \"executables stream must not be null\");\n\n\t\tList<Throwable> failures = executables //\n\t\t\t\t.map(executable -> {\n\t\t\t\t\tPreconditions.notNull(executable, \"individual executables must not be null\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\texecutable.execute();\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable t) {\n\t\t\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\t\t\treturn t;\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.toList();\n\n\t\tif (!failures.isEmpty()) {\n\t\t\tMultipleFailuresError multipleFailuresError = new MultipleFailuresError(heading, failures);\n\t\t\tfailures.forEach(multipleFailuresError::addSuppressed);\n\t\t\tthrow multipleFailuresError;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.formatIndexes;\nimport static org.junit.platform.commons.util.ReflectionUtils.isArray;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code AssertArrayEquals} is a collection of utility methods that support asserting\n * array equality in tests.\n *\n * @since 5.0\n */\nclass AssertArrayEquals {\n\n\tprivate AssertArrayEquals() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual,\n\t\t\t@Nullable Supplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta) {\n\t\tassertArrayEquals(expected, actual, delta, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta,\n\t\t\t@Nullable String message) {\n\t\tassertArrayEquals(expected, actual, delta, null, message);\n\t}\n\n\tstatic void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, delta, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, @Nullable String message) {\n\t\tassertArrayEquals(expected, actual, null, message);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta) {\n\t\tassertArrayEquals(expected, actual, delta, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta,\n\t\t\t@Nullable String message) {\n\t\tassertArrayEquals(expected, actual, delta, null, message);\n\t}\n\n\tstatic void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, delta, null, messageSupplier);\n\t}\n\n\tstatic void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual) {\n\t\tassertArrayEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tassertArrayEquals(expected, actual, new ArrayDeque<>(), message);\n\t}\n\n\tstatic void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertArrayEquals(expected, actual, new ArrayDeque<>(), messageSupplier);\n\t}\n\n\tprivate static void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (expected[i] != actual[i]) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (!AssertionUtils.floatsAreEqual(expected[i], actual[i])) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tAssertionUtils.assertValidDelta(delta);\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (!AssertionUtils.floatsAreEqual(expected[i], actual[i], delta)) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (!AssertionUtils.doublesAreEqual(expected[i], actual[i])) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tAssertionUtils.assertValidDelta(delta);\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tif (!AssertionUtils.doublesAreEqual(expected[i], actual[i], delta)) {\n\t\t\t\tfailArraysNotEqual(expected[i], actual[i], nullSafeIndexes(indexes, i), messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (expected == null) {\n\t\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tassertArraysHaveSameLength(expected.length, actual.length, indexes, messageOrSupplier);\n\n\t\tfor (int i = 0; i < expected.length; i++) {\n\t\t\tObject expectedElement = expected[i];\n\t\t\tObject actualElement = actual[i];\n\n\t\t\tif (expectedElement == actualElement) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tindexes.addLast(i);\n\t\t\tassertArrayElementsEqual(expectedElement, actualElement, indexes, messageOrSupplier);\n\t\t\tindexes.removeLast();\n\t\t}\n\t}\n\n\tprivate static void assertArrayElementsEqual(@Nullable Object expected, @Nullable Object actual,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected instanceof Object[] expectedArray && actual instanceof Object[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof byte[] expectedArray && actual instanceof byte[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof short[] expectedArray && actual instanceof short[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof int[] expectedArray && actual instanceof int[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof long[] expectedArray && actual instanceof long[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof char[] expectedArray && actual instanceof char[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof float[] expectedArray && actual instanceof float[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof double[] expectedArray && actual instanceof double[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (expected instanceof boolean[] expectedArray && actual instanceof boolean[] actualArray) {\n\t\t\tassertArrayEquals(expectedArray, actualArray, indexes, messageOrSupplier);\n\t\t}\n\t\telse if (!Objects.equals(expected, actual)) {\n\t\t\tif (expected == null && isArray(actual)) {\n\t\t\t\tfailExpectedArrayIsNull(indexes, messageOrSupplier);\n\t\t\t}\n\t\t\telse if (isArray(expected) && actual == null) {\n\t\t\t\tfailActualArrayIsNull(indexes, messageOrSupplier);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfailArraysNotEqual(expected, actual, indexes, messageOrSupplier);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void failExpectedArrayIsNull(@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\t\tthrow expectedArrayIsNullFailure(indexes, messageOrSupplier);\n\t}\n\n\tprivate static AssertionFailedError expectedArrayIsNullFailure(@Nullable Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"expected array was <null>\" + formatIndexes(indexes)) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static void failActualArrayIsNull(@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\t\tthrow actualArrayIsNullFailure(indexes, messageOrSupplier);\n\t}\n\n\tprivate static AssertionFailedError actualArrayIsNullFailure(@Nullable Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"actual array was <null>\" + formatIndexes(indexes)) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static void assertArraysHaveSameLength(int expected, int actual, @Nullable Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\n\t\tif (expected != actual) {\n\t\t\tassertionFailure() //\n\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t.reason(\"array lengths differ\" + formatIndexes(indexes)) //\n\t\t\t\t\t.expected(expected) //\n\t\t\t\t\t.actual(actual) //\n\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t.buildAndThrow();\n\t\t}\n\t}\n\n\tprivate static void failArraysNotEqual(@Nullable Object expected, @Nullable Object actual,\n\t\t\t@Nullable Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"array contents differ\" + formatIndexes(indexes)) //\n\t\t\t\t.expected(expected) //\n\t\t\t\t.actual(actual) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n\tprivate static Deque<Integer> nullSafeIndexes(@Nullable Deque<Integer> indexes, int newIndex) {\n\t\tDeque<Integer> result = (indexes != null ? indexes : new ArrayDeque<>());\n\t\tresult.addLast(newIndex);\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code AssertDoesNotThrow} is a collection of utility methods that support\n * explicitly asserting that a given code block does not throw an exception.\n *\n * @since 5.2\n */\nclass AssertDoesNotThrow {\n\n\tprivate AssertDoesNotThrow() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertDoesNotThrow(Executable executable) {\n\t\tassertDoesNotThrow(executable, (Object) null);\n\t}\n\n\tstatic void assertDoesNotThrow(Executable executable, @Nullable String message) {\n\t\tassertDoesNotThrow(executable, (Object) message);\n\t}\n\n\tstatic void assertDoesNotThrow(Executable executable, Supplier<@Nullable String> messageSupplier) {\n\t\tassertDoesNotThrow(executable, (Object) messageSupplier);\n\t}\n\n\tprivate static void assertDoesNotThrow(Executable executable, @Nullable Object messageOrSupplier) {\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tthrow createAssertionFailedError(messageOrSupplier, t);\n\t\t}\n\t}\n\n\tstatic <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier) {\n\t\treturn assertDoesNotThrow(supplier, (Object) null);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier, @Nullable String message) {\n\t\treturn assertDoesNotThrow(supplier, (Object) message);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn assertDoesNotThrow(supplier, (Object) messageSupplier);\n\t}\n\n\tprivate static <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\ttry {\n\t\t\treturn supplier.get();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tthrow createAssertionFailedError(messageOrSupplier, t);\n\t\t}\n\t}\n\n\t@API(status = INTERNAL, since = \"6.0\")\n\tpublic static AssertionFailedError createAssertionFailedError(@Nullable Object messageOrSupplier, Throwable t) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"Unexpected exception thrown: \" + t.getClass().getName() + buildSuffix(t.getMessage())) //\n\t\t\t\t.cause(t) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static String buildSuffix(@Nullable String message) {\n\t\treturn StringUtils.isNotBlank(message) ? \": \" + message : \"\";\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.doublesAreEqual;\nimport static org.junit.jupiter.api.AssertionUtils.floatsAreEqual;\nimport static org.junit.jupiter.api.AssertionUtils.objectsAreEqual;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code AssertEquals} is a collection of utility methods that support asserting\n * equality on objects and primitives in tests.\n *\n * @since 5.0\n */\nclass AssertEquals {\n\n\tprivate AssertEquals() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertEquals(byte expected, byte actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(byte expected, byte actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(byte expected, byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(char expected, char actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(char expected, char actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(char expected, char actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(double expected, double actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(double expected, double actual, @Nullable String message) {\n\t\tif (!doublesAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(double expected, double actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (!doublesAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(double expected, double actual, double delta) {\n\t\tassertEquals(expected, actual, delta, (String) null);\n\t}\n\n\tstatic void assertEquals(double expected, double actual, double delta, @Nullable String message) {\n\t\tif (!doublesAreEqual(expected, actual, delta)) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(double expected, double actual, double delta, Supplier<@Nullable String> messageSupplier) {\n\t\tif (!doublesAreEqual(expected, actual, delta)) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(float expected, float actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(float expected, float actual, @Nullable String message) {\n\t\tif (!floatsAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(float expected, float actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (!floatsAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(float expected, float actual, float delta) {\n\t\tassertEquals(expected, actual, delta, (String) null);\n\t}\n\n\tstatic void assertEquals(float expected, float actual, float delta, @Nullable String message) {\n\t\tif (!floatsAreEqual(expected, actual, delta)) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(float expected, float actual, float delta, Supplier<@Nullable String> messageSupplier) {\n\t\tif (!floatsAreEqual(expected, actual, delta)) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(short expected, short actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(short expected, short actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(short expected, short actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(int expected, int actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(int expected, int actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(int expected, int actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(long expected, long actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(long expected, long actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(long expected, long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertEquals(@Nullable Object expected, @Nullable Object actual) {\n\t\tassertEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertEquals(@Nullable Object expected, @Nullable Object actual, @Nullable String message) {\n\t\tif (!objectsAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertEquals(@Nullable Object expected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (!objectsAreEqual(expected, actual)) {\n\t\t\tfailNotEqual(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failNotEqual(@Nullable Object expected, @Nullable Object actual,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.expected(expected) //\n\t\t\t\t.actual(actual) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.BooleanSupplier;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * {@code AssertFalse} is a collection of utility methods that support asserting\n * {@code false} in tests.\n *\n * @since 5.0\n */\nclass AssertFalse {\n\n\tprivate AssertFalse() {\n\t\t/* no-op */\n\t}\n\n\t@Contract(\"true -> fail\")\n\tstatic void assertFalse(boolean condition) {\n\t\tassertFalse(condition, (String) null);\n\t}\n\n\t@Contract(\"true, _ -> fail\")\n\tstatic void assertFalse(boolean condition, @Nullable String message) {\n\t\tif (condition) {\n\t\t\tfailNotFalse(message);\n\t\t}\n\t}\n\n\t@Contract(\"true, _ -> fail\")\n\tstatic void assertFalse(boolean condition, Supplier<@Nullable String> messageSupplier) {\n\t\tif (condition) {\n\t\t\tfailNotFalse(messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertFalse(BooleanSupplier booleanSupplier) {\n\t\tassertFalse(booleanSupplier.getAsBoolean(), (String) null);\n\t}\n\n\tstatic void assertFalse(BooleanSupplier booleanSupplier, @Nullable String message) {\n\t\tassertFalse(booleanSupplier.getAsBoolean(), message);\n\t}\n\n\tstatic void assertFalse(BooleanSupplier booleanSupplier, Supplier<@Nullable String> messageSupplier) {\n\t\tassertFalse(booleanSupplier.getAsBoolean(), messageSupplier);\n\t}\n\n\tprivate static void failNotFalse(@Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.expected(false) //\n\t\t\t\t.actual(true) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * {@code AssertInstanceOf} is a collection of utility methods that support\n * asserting that an object is of an expected type &mdash; in other words, if it\n * can be assigned to the expected type.\n *\n * @since 5.8\n */\nclass AssertInstanceOf {\n\n\tprivate AssertInstanceOf() {\n\t\t/* no-op */\n\t}\n\n\t@Contract(\"_, null -> fail\")\n\tstatic <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue) {\n\t\treturn assertInstanceOf(expectedType, actualValue, (Object) null);\n\t}\n\n\t@Contract(\"_, null, _ -> fail\")\n\tstatic <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue, @Nullable String message) {\n\t\treturn assertInstanceOf(expectedType, actualValue, (Object) message);\n\t}\n\n\t@Contract(\"_, null, _ -> fail\")\n\tstatic <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn assertInstanceOf(expectedType, actualValue, (Object) messageSupplier);\n\t}\n\n\tprivate static <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tif (!expectedType.isInstance(actualValue)) {\n\t\t\tthrow assertionFailure() //\n\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t.reason(actualValue == null ? \"Unexpected null value\" : \"Unexpected type\") //\n\t\t\t\t\t.expected(expectedType) //\n\t\t\t\t\t.actual(actualValue == null ? null : actualValue.getClass()) //\n\t\t\t\t\t.cause(actualValue instanceof Throwable t ? t : null) //\n\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t.build();\n\t\t}\n\t\treturn expectedType.cast(actualValue);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.formatIndexes;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code AssertIterable} is a collection of utility methods that support asserting\n * Iterable equality in tests.\n *\n * @since 5.0\n */\nclass AssertIterableEquals {\n\n\tprivate AssertIterableEquals() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual) {\n\t\tassertIterableEquals(expected, actual, (String) null);\n\t}\n\n\tstatic void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\t@Nullable String message) {\n\t\tassertIterableEquals(expected, actual, new ArrayDeque<>(), message);\n\t}\n\n\tstatic void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertIterableEquals(expected, actual, new ArrayDeque<>(), messageSupplier);\n\t}\n\n\tprivate static void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\t\tassertIterableEquals(expected, actual, indexes, messageOrSupplier, new LinkedHashMap<>());\n\t}\n\n\tprivate static void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier, Map<Pair, Status> investigatedElements) {\n\n\t\tif (expected == actual) {\n\t\t\treturn;\n\t\t}\n\t\tif (expected == null) {\n\t\t\tthrow expectedIterableIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tthrow actualIterableIsNullFailure(indexes, messageOrSupplier);\n\t\t}\n\n\t\tIterator<?> expectedIterator = expected.iterator();\n\t\tIterator<?> actualIterator = actual.iterator();\n\n\t\tint processed = 0;\n\t\twhile (expectedIterator.hasNext() && actualIterator.hasNext()) {\n\t\t\tObject expectedElement = expectedIterator.next();\n\t\t\tObject actualElement = actualIterator.next();\n\n\t\t\tindexes.addLast(processed);\n\n\t\t\tassertIterableElementsEqual(expectedElement, actualElement, indexes, messageOrSupplier,\n\t\t\t\tinvestigatedElements);\n\n\t\t\tindexes.removeLast();\n\t\t\tprocessed++;\n\t\t}\n\n\t\tassertIteratorsAreEmpty(expectedIterator, actualIterator, processed, indexes, messageOrSupplier);\n\t}\n\n\tprivate static void assertIterableElementsEqual(Object expected, Object actual, Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier, Map<Pair, Status> investigatedElements) {\n\n\t\t// If both are equal, we don't need to check recursively.\n\t\tif (Objects.equals(expected, actual)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// If both are iterables, we need to check whether they contain the same elements.\n\t\tif (expected instanceof Iterable<?> expectedIterable && actual instanceof Iterable<?> actualIterable) {\n\n\t\t\tPair pair = new Pair(expected, actual);\n\n\t\t\t// Before comparing their elements, we check whether we have already checked this pair.\n\t\t\tStatus status = investigatedElements.get(pair);\n\n\t\t\t// If we've already determined that both contain the same elements, we don't need to check them again.\n\t\t\tif (status == Status.CONTAIN_SAME_ELEMENTS) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the pair is already under investigation, we fail in order to avoid infinite recursion.\n\t\t\tif (status == Status.UNDER_INVESTIGATION) {\n\t\t\t\tindexes.removeLast();\n\t\t\t\tfailIterablesNotEqual(expected, actual, indexes, messageOrSupplier);\n\t\t\t}\n\n\t\t\t// Otherwise, we put the pair under investigation and recurse.\n\t\t\tinvestigatedElements.put(pair, Status.UNDER_INVESTIGATION);\n\n\t\t\tassertIterableEquals(expectedIterable, actualIterable, indexes, messageOrSupplier, investigatedElements);\n\n\t\t\t// If we reach this point, we've checked that the two iterables contain the same elements so we store this information\n\t\t\t// in case we come across the same pair again.\n\t\t\tinvestigatedElements.put(pair, Status.CONTAIN_SAME_ELEMENTS);\n\t\t}\n\n\t\t// Otherwise, they are neither equal nor iterables, so we fail.\n\t\telse {\n\t\t\tassertIterablesNotNull(expected, actual, indexes, messageOrSupplier);\n\t\t\tfailIterablesNotEqual(expected, actual, indexes, messageOrSupplier);\n\t\t}\n\t}\n\n\tprivate static void assertIterablesNotNull(@Nullable Object expected, @Nullable Object actual,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected == null) {\n\t\t\tfailExpectedIterableIsNull(indexes, messageOrSupplier);\n\t\t}\n\t\tif (actual == null) {\n\t\t\tfailActualIterableIsNull(indexes, messageOrSupplier);\n\t\t}\n\t}\n\n\tprivate static void failExpectedIterableIsNull(Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\t\tthrow expectedIterableIsNullFailure(indexes, messageOrSupplier);\n\t}\n\n\tprivate static AssertionFailedError expectedIterableIsNullFailure(Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"expected iterable was <null>\" + formatIndexes(indexes)) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static void failActualIterableIsNull(Deque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\t\tthrow actualIterableIsNullFailure(indexes, messageOrSupplier);\n\t}\n\n\tprivate static AssertionFailedError actualIterableIsNullFailure(Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"actual iterable was <null>\" + formatIndexes(indexes)) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static void assertIteratorsAreEmpty(Iterator<?> expected, Iterator<?> actual, int processed,\n\t\t\tDeque<Integer> indexes, @Nullable Object messageOrSupplier) {\n\n\t\tif (expected.hasNext() || actual.hasNext()) {\n\t\t\tAtomicInteger expectedCount = new AtomicInteger(processed);\n\t\t\texpected.forEachRemaining(e -> expectedCount.incrementAndGet());\n\n\t\t\tAtomicInteger actualCount = new AtomicInteger(processed);\n\t\t\tactual.forEachRemaining(e -> actualCount.incrementAndGet());\n\n\t\t\tassertionFailure() //\n\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t.reason(\"iterable lengths differ\" + formatIndexes(indexes)) //\n\t\t\t\t\t.expected(expectedCount.get()) //\n\t\t\t\t\t.actual(actualCount.get()) //\n\t\t\t\t\t.buildAndThrow();\n\t\t}\n\t}\n\n\tprivate static void failIterablesNotEqual(Object expected, Object actual, Deque<Integer> indexes,\n\t\t\t@Nullable Object messageOrSupplier) {\n\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"iterable contents differ\" + formatIndexes(indexes)) //\n\t\t\t\t.expected(expected) //\n\t\t\t\t.actual(actual) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n\tprivate record Pair(Object left, Object right) {\n\t}\n\n\tprivate enum Status {\n\t\tUNDER_INVESTIGATION, CONTAIN_SAME_ELEMENTS\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.lang.String.join;\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.platform.commons.util.Preconditions.condition;\nimport static org.junit.platform.commons.util.Preconditions.notNull;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.regex.PatternSyntaxException;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code AssertLinesMatch} is a collection of utility methods that support asserting\n * lines of {@link String} equality or {@link java.util.regex.Pattern}-match in tests.\n *\n * @since 5.0\n */\nclass AssertLinesMatch {\n\n\tprivate AssertLinesMatch() {\n\t\t/* no-op */\n\t}\n\n\tprivate static final int MAX_SNIPPET_LENGTH = 21;\n\n\tstatic void assertLinesMatch(List<String> expectedLines, List<String> actualLines) {\n\t\tassertLinesMatch(expectedLines, actualLines, (Object) null);\n\t}\n\n\tstatic void assertLinesMatch(List<String> expectedLines, List<String> actualLines, @Nullable String message) {\n\t\tassertLinesMatch(expectedLines, actualLines, (Object) message);\n\t}\n\n\tstatic void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines) {\n\t\tassertLinesMatch(expectedLines, actualLines, (Object) null);\n\t}\n\n\tstatic void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines, @Nullable String message) {\n\t\tassertLinesMatch(expectedLines, actualLines, (Object) message);\n\t}\n\n\t@SuppressWarnings(\"ReferenceEquality\")\n\tstatic void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tnotNull(expectedLines, \"expectedLines must not be null\");\n\t\tnotNull(actualLines, \"actualLines must not be null\");\n\n\t\t// trivial case: same stream instance\n\t\tif (expectedLines == actualLines) {\n\t\t\treturn;\n\t\t}\n\n\t\tList<String> expectedListOfStrings = expectedLines.toList();\n\t\tList<String> actualListOfStrings = actualLines.toList();\n\t\tassertLinesMatch(expectedListOfStrings, actualListOfStrings, messageOrSupplier);\n\t}\n\n\t@SuppressWarnings(\"ReferenceEquality\")\n\tstatic void assertLinesMatch(List<String> expectedLines, List<String> actualLines,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tnotNull(expectedLines, \"expectedLines must not be null\");\n\t\tnotNull(actualLines, \"actualLines must not be null\");\n\n\t\t// trivial case: same list instance\n\t\tif (expectedLines == actualLines) {\n\t\t\treturn;\n\t\t}\n\n\t\tnew LinesMatcher(expectedLines, actualLines, messageOrSupplier).assertLinesMatch();\n\t}\n\n\tprivate record LinesMatcher(List<String> expectedLines, List<String> actualLines,\n\t\t\t@Nullable Object messageOrSupplier) {\n\n\t\tvoid assertLinesMatch() {\n\t\t\tint expectedSize = expectedLines.size();\n\t\t\tint actualSize = actualLines.size();\n\n\t\t\t// trivial case: when expecting more than actual lines available, something is wrong\n\t\t\tif (expectedSize > actualSize) {\n\t\t\t\tfail(\"expected %d lines, but only got %d\", expectedSize, actualSize);\n\t\t\t}\n\n\t\t\t// simple case: both list are equally sized, compare them line-by-line\n\t\t\tif (expectedSize == actualSize) {\n\t\t\t\tif (IntStream.range(0, expectedSize).allMatch(i -> matches(expectedLines.get(i), actualLines.get(i)))) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t// else fall-through to \"with fast-forward\" matching\n\t\t\t}\n\n\t\t\tassertLinesMatchWithFastForward();\n\t\t}\n\n\t\tvoid assertLinesMatchWithFastForward() {\n\t\t\tDeque<String> expectedDeque = new ArrayDeque<>(expectedLines);\n\t\t\tDeque<String> actualDeque = new ArrayDeque<>(actualLines);\n\n\t\t\tmain: while (!expectedDeque.isEmpty()) {\n\t\t\t\tString expectedLine = expectedDeque.pop();\n\t\t\t\tint expectedLineNumber = expectedLines.size() - expectedDeque.size(); // 1-based line number\n\t\t\t\t// trivial case: no more actual lines available\n\t\t\t\tif (actualDeque.isEmpty()) {\n\t\t\t\t\tfail(\"expected line #%d:`%s` not found - actual lines depleted\", expectedLineNumber,\n\t\t\t\t\t\tsnippet(expectedLine));\n\t\t\t\t}\n\n\t\t\t\tString actualLine = actualDeque.peek();\n\t\t\t\t// trivial case: take the fast path when they match\n\t\t\t\tif (matches(expectedLine, actualLine)) {\n\t\t\t\t\tactualDeque.pop();\n\t\t\t\t\tcontinue; // main\n\t\t\t\t}\n\n\t\t\t\t// fast-forward marker found in expected line: fast-forward actual line...\n\t\t\t\tif (isFastForwardLine(expectedLine)) {\n\t\t\t\t\tint fastForwardLimit = parseFastForwardLimit(expectedLine);\n\t\t\t\t\tint actualRemaining = actualDeque.size();\n\n\t\t\t\t\t// trivial case: fast-forward marker was in last expected line\n\t\t\t\t\tif (expectedDeque.isEmpty()) {\n\t\t\t\t\t\t// no limit given or perfect match? we're done.\n\t\t\t\t\t\tif (fastForwardLimit == Integer.MAX_VALUE || fastForwardLimit == actualRemaining) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfail(\"terminal fast-forward(%d) error: fast-forward(%d) expected\", fastForwardLimit,\n\t\t\t\t\t\t\tactualRemaining);\n\t\t\t\t\t}\n\n\t\t\t\t\t// fast-forward limit was given: use it\n\t\t\t\t\tif (fastForwardLimit != Integer.MAX_VALUE) {\n\t\t\t\t\t\tif (actualRemaining < fastForwardLimit) {\n\t\t\t\t\t\t\tfail(\"fast-forward(%d) error: not enough actual lines remaining (%s)\", fastForwardLimit,\n\t\t\t\t\t\t\t\tactualRemaining);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// fast-forward now: actualDeque.pop(fastForwardLimit)\n\t\t\t\t\t\tfor (int i = 0; i < fastForwardLimit; i++) {\n\t\t\t\t\t\t\tactualDeque.pop();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue; // main\n\t\t\t\t\t}\n\n\t\t\t\t\t// peek next expected line\n\t\t\t\t\texpectedLine = expectedDeque.peek();\n\t\t\t\t\t// fast-forward \"unlimited\": until next match\n\t\t\t\t\twhile (true) {\n\t\t\t\t\t\tif (actualDeque.isEmpty()) {\n\t\t\t\t\t\t\tfail(\"fast-forward(∞) didn't find: `%s`\", snippet(expectedLine));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (matches(expectedLine, actualDeque.peek())) {\n\t\t\t\t\t\t\tcontinue main;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tactualDeque.pop();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tint actualLineNumber = actualLines.size() - actualDeque.size() + 1; // 1-based line number\n\t\t\t\tfail(\"expected line #%d doesn't match actual line #%d%n\" + \"\\texpected: `%s`%n\" + \"\\t  actual: `%s`\",\n\t\t\t\t\texpectedLineNumber, actualLineNumber, expectedLine, actualLine);\n\t\t\t}\n\n\t\t\t// after math\n\t\t\tif (!actualDeque.isEmpty()) {\n\t\t\t\tfail(\"more actual lines than expected: %d\", actualDeque.size());\n\t\t\t}\n\t\t}\n\n\t\tString snippet(String line) {\n\t\t\tif (line.length() <= MAX_SNIPPET_LENGTH) {\n\t\t\t\treturn line;\n\t\t\t}\n\t\t\treturn line.substring(0, MAX_SNIPPET_LENGTH - 5) + \"[...]\";\n\t\t}\n\n\t\tvoid fail(String format, Object... args) {\n\t\t\tString newLine = System.lineSeparator();\n\t\t\tassertionFailure() //\n\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t.reason(format.formatted(args)) //\n\t\t\t\t\t.expected(join(newLine, expectedLines)) //\n\t\t\t\t\t.actual(join(newLine, actualLines)) //\n\t\t\t\t\t.includeValuesInMessage(false) //\n\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t.buildAndThrow();\n\t\t}\n\t}\n\n\tstatic boolean isFastForwardLine(String line) {\n\t\tline = line.strip();\n\t\treturn line.length() >= 4 && line.startsWith(\">>\") && line.endsWith(\">>\");\n\t}\n\n\tstatic int parseFastForwardLimit(String fastForwardLine) {\n\t\tfastForwardLine = fastForwardLine.strip();\n\t\tString text = fastForwardLine.substring(2, fastForwardLine.length() - 2).strip();\n\t\ttry {\n\t\t\tint limit = Integer.parseInt(text);\n\t\t\tcondition(limit > 0, () -> \"fast-forward(%d) limit must be greater than zero\".formatted(limit));\n\t\t\treturn limit;\n\t\t}\n\t\tcatch (NumberFormatException e) {\n\t\t\treturn Integer.MAX_VALUE;\n\t\t}\n\t}\n\n\tstatic boolean matches(String expectedLine, String actualLine) {\n\t\tnotNull(expectedLine, \"expected line must not be null\");\n\t\tnotNull(actualLine, \"actual line must not be null\");\n\t\tif (expectedLine.equals(actualLine)) {\n\t\t\treturn true;\n\t\t}\n\t\ttry {\n\t\t\treturn actualLine.matches(expectedLine);\n\t\t}\n\t\tcatch (PatternSyntaxException ignore) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.doublesAreEqual;\nimport static org.junit.jupiter.api.AssertionUtils.floatsAreEqual;\nimport static org.junit.jupiter.api.AssertionUtils.objectsAreEqual;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code AssertNotEquals} is a collection of utility methods that support asserting\n * inequality in objects and primitive values in tests.\n *\n * @since 5.0\n */\nclass AssertNotEquals {\n\n\tprivate AssertNotEquals() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(byte unexpected, byte actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(byte unexpected, byte actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(byte unexpected, byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(short unexpected, short actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(short unexpected, short actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(short unexpected, short actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(int unexpected, int actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(int unexpected, int actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(int unexpected, int actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(long unexpected, long actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(long unexpected, long actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(long unexpected, long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual, @Nullable String message) {\n\t\tif (floatsAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (floatsAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual, float delta) {\n\t\tassertNotEquals(unexpected, actual, delta, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual, float delta, @Nullable String message) {\n\t\tif (floatsAreEqual(unexpected, actual, delta)) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(float unexpected, float actual, float delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (floatsAreEqual(unexpected, actual, delta)) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual, @Nullable String message) {\n\t\tif (doublesAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (doublesAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual, double delta) {\n\t\tassertNotEquals(unexpected, actual, delta, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual, double delta, @Nullable String message) {\n\t\tif (doublesAreEqual(unexpected, actual, delta)) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(double unexpected, double actual, double delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (doublesAreEqual(unexpected, actual, delta)) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(char unexpected, char actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(char unexpected, char actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tstatic void assertNotEquals(char unexpected, char actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\tstatic void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual) {\n\t\tassertNotEquals(unexpected, actual, (String) null);\n\t}\n\n\tstatic void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual, @Nullable String message) {\n\t\tif (objectsAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, message);\n\t\t}\n\t}\n\n\tstatic void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (objectsAreEqual(unexpected, actual)) {\n\t\t\tfailEqual(actual, messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failEqual(@Nullable Object actual, @Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"expected: not equal but was: <\" + actual + \">\") //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * {@code AssertNotNull} is a collection of utility methods that support asserting\n * that there is an object.\n *\n * @since 5.0\n */\nclass AssertNotNull {\n\n\tprivate AssertNotNull() {\n\t\t/* no-op */\n\t}\n\n\t@Contract(\"null -> fail\")\n\tstatic void assertNotNull(@Nullable Object actual) {\n\t\tassertNotNull(actual, (String) null);\n\t}\n\n\t@Contract(\"null, _ -> fail\")\n\tstatic void assertNotNull(@Nullable Object actual, @Nullable String message) {\n\t\tif (actual == null) {\n\t\t\tfailNull(message);\n\t\t}\n\t}\n\n\t@Contract(\"null, _ -> fail\")\n\tstatic void assertNotNull(@Nullable Object actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (actual == null) {\n\t\t\tfailNull(messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failNull(@Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"expected: not <null>\") //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code AssertNotSame} is a collection of utility methods that support asserting\n * two objects are not the same.\n *\n * @since 5.0\n */\nclass AssertNotSame {\n\n\tprivate AssertNotSame() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertNotSame(@Nullable Object unexpected, @Nullable Object actual) {\n\t\tassertNotSame(unexpected, actual, (String) null);\n\t}\n\n\tstatic void assertNotSame(@Nullable Object unexpected, @Nullable Object actual, @Nullable String message) {\n\t\tif (unexpected == actual) {\n\t\t\tfailSame(actual, message);\n\t\t}\n\t}\n\n\tstatic void assertNotSame(@Nullable Object unexpected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (unexpected == actual) {\n\t\t\tfailSame(actual, messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failSame(@Nullable Object actual, @Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"expected: not same but was: <\" + actual + \">\") //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * {@code AssertNull} is a collection of utility methods that support asserting\n * there is no object.\n *\n * @since 5.0\n */\nclass AssertNull {\n\n\tprivate AssertNull() {\n\t\t/* no-op */\n\t}\n\n\t@Contract(\"!null -> fail\")\n\tstatic void assertNull(@Nullable Object actual) {\n\t\tassertNull(actual, (String) null);\n\t}\n\n\t@Contract(\"!null, _ -> fail\")\n\tstatic void assertNull(@Nullable Object actual, @Nullable String message) {\n\t\tif (actual != null) {\n\t\t\tfailNotNull(actual, message);\n\t\t}\n\t}\n\n\t@Contract(\"!null, _ -> fail\")\n\tstatic void assertNull(@Nullable Object actual, Supplier<@Nullable String> messageSupplier) {\n\t\tif (actual != null) {\n\t\t\tfailNotNull(actual, messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failNotNull(@Nullable Object actual, @Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.expected(null) //\n\t\t\t\t.actual(actual) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code AssertSame} is a collection of utility methods that support asserting\n * two objects are the same.\n *\n * @since 5.0\n */\nclass AssertSame {\n\n\tprivate AssertSame() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertSame(@Nullable Object expected, @Nullable Object actual) {\n\t\tassertSame(expected, actual, (String) null);\n\t}\n\n\tstatic void assertSame(@Nullable Object expected, @Nullable Object actual, @Nullable String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotSame(expected, actual, message);\n\t\t}\n\t}\n\n\tstatic void assertSame(@Nullable Object expected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tif (expected != actual) {\n\t\t\tfailNotSame(expected, actual, messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failNotSame(@Nullable Object expected, @Nullable Object actual,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.expected(expected) //\n\t\t\t\t.actual(actual) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.getCanonicalName;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * {@code AssertThrows} is a collection of utility methods that support asserting\n * an exception of an expected type is thrown.\n *\n * @since 5.0\n */\nclass AssertThrows {\n\n\tprivate AssertThrows() {\n\t\t/* no-op */\n\t}\n\n\tstatic <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {\n\t\treturn assertThrows(expectedType, executable, (Object) null);\n\t}\n\n\tstatic <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable String message) {\n\t\treturn assertThrows(expectedType, executable, (Object) message);\n\t}\n\n\tstatic <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\n\t\treturn assertThrows(expectedType, executable, (Object) messageSupplier);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable Object messageOrSupplier) {\n\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t}\n\t\tcatch (Throwable actualException) {\n\t\t\tif (expectedType.isInstance(actualException)) {\n\t\t\t\treturn (T) actualException;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(actualException);\n\t\t\t\tthrow assertionFailure() //\n\t\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t\t.expected(expectedType) //\n\t\t\t\t\t\t.actual(actualException.getClass()) //\n\t\t\t\t\t\t.reason(\"Unexpected exception type thrown\") //\n\t\t\t\t\t\t.cause(actualException) //\n\t\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t\t.build();\n\t\t\t}\n\t\t}\n\t\tthrow assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"Expected %s to be thrown, but nothing was thrown.\".formatted(getCanonicalName(expectedType))) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.AssertionUtils.getCanonicalName;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * {@code AssertThrowsExactly} is a collection of utility methods that support asserting\n * an exception of an exact type is thrown.\n *\n * @since 5.8\n */\nclass AssertThrowsExactly {\n\n\tprivate AssertThrowsExactly() {\n\t\t/* no-op */\n\t}\n\n\tstatic <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable) {\n\t\treturn assertThrowsExactly(expectedType, executable, (Object) null);\n\t}\n\n\tstatic <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable String message) {\n\t\treturn assertThrowsExactly(expectedType, executable, (Object) message);\n\t}\n\n\tstatic <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\n\t\treturn assertThrowsExactly(expectedType, executable, (Object) messageSupplier);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable Object messageOrSupplier) {\n\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t}\n\t\tcatch (Throwable actualException) {\n\t\t\tif (expectedType.equals(actualException.getClass())) {\n\t\t\t\treturn (T) actualException;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(actualException);\n\t\t\t\tthrow assertionFailure() //\n\t\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t\t.expected(expectedType) //\n\t\t\t\t\t\t.actual(actualException.getClass()) //\n\t\t\t\t\t\t.reason(\"Unexpected exception type thrown\") //\n\t\t\t\t\t\t.cause(actualException) //\n\t\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t\t.build();\n\t\t\t}\n\t\t}\n\n\t\tthrow assertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.reason(\"Expected %s to be thrown, but nothing was thrown.\".formatted(getCanonicalName(expectedType))) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\n\nimport java.time.Duration;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\n\n/**\n * {@code AssertTimeout} is a collection of utility methods that support asserting\n * the execution of the code under test did not take longer than the timeout duration.\n *\n * @since 5.0\n */\nclass AssertTimeout {\n\n\tprivate AssertTimeout() {\n\t\t/* no-op */\n\t}\n\n\tstatic void assertTimeout(Duration timeout, Executable executable) {\n\t\tassertTimeout(timeout, executable, (String) null);\n\t}\n\n\tstatic void assertTimeout(Duration timeout, Executable executable, @Nullable String message) {\n\t\tAssertTimeout.<@Nullable Object> assertTimeout(timeout, () -> {\n\t\t\texecutable.execute();\n\t\t\treturn null;\n\t\t}, message);\n\t}\n\n\tstatic void assertTimeout(Duration timeout, Executable executable, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertTimeout.<@Nullable Object> assertTimeout(timeout, () -> {\n\t\t\texecutable.execute();\n\t\t\treturn null;\n\t\t}, messageSupplier);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier) {\n\t\treturn assertTimeout(timeout, supplier, (Object) null);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\t@Nullable String message) {\n\t\treturn assertTimeout(timeout, supplier, (Object) message);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn assertTimeout(timeout, supplier, (Object) messageSupplier);\n\t}\n\n\tprivate static <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\t@Nullable Object messageOrSupplier) {\n\t\tlong timeoutInMillis = timeout.toMillis();\n\t\tlong start = System.currentTimeMillis();\n\t\tT result;\n\t\ttry {\n\t\t\tresult = supplier.get();\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tthrow throwAsUncheckedException(ex);\n\t\t}\n\n\t\tlong timeElapsed = System.currentTimeMillis() - start;\n\t\tif (timeElapsed > timeoutInMillis) {\n\t\t\tassertionFailure() //\n\t\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t\t.reason(\"execution exceeded timeout of \" + timeoutInMillis + \" ms by \"\n\t\t\t\t\t\t\t+ (timeElapsed - timeoutInMillis) + \" ms\") //\n\t\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t\t.buildAndThrow();\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.timeout.PreemptiveTimeoutUtils.executeWithPreemptiveTimeout;\n\nimport java.time.Duration;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code AssertTimeout} is a collection of utility methods that support asserting\n * the execution of the code under test did not take longer than the timeout duration\n * using a preemptive approach.\n *\n * @since 5.9.1\n */\nclass AssertTimeoutPreemptively {\n\n\tstatic void assertTimeoutPreemptively(Duration timeout, Executable executable) {\n\t\tassertTimeoutPreemptively(timeout, executable, (String) null);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\tstatic void assertTimeoutPreemptively(Duration timeout, Executable executable, @Nullable String message) {\n\t\tassertTimeoutPreemptively(timeout, () -> {\n\t\t\texecutable.execute();\n\t\t\treturn null;\n\t\t}, message);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\tstatic void assertTimeoutPreemptively(Duration timeout, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tassertTimeoutPreemptively(timeout, () -> {\n\t\t\texecutable.execute();\n\t\t\treturn null;\n\t\t}, messageSupplier);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier) {\n\t\treturn executeWithPreemptiveTimeout(timeout, supplier, null, AssertTimeoutPreemptively::createAssertionFailure);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\t@Nullable String message) {\n\t\treturn executeWithPreemptiveTimeout(timeout, supplier, message == null ? null : () -> message,\n\t\t\tAssertTimeoutPreemptively::createAssertionFailure);\n\t}\n\n\tstatic <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn executeWithPreemptiveTimeout(timeout, supplier, messageSupplier,\n\t\t\tAssertTimeoutPreemptively::createAssertionFailure);\n\t}\n\n\tprivate static AssertionFailedError createAssertionFailure(Duration timeout,\n\t\t\t@Nullable Supplier<@Nullable String> messageSupplier, @Nullable Throwable cause, @Nullable Thread thread) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(messageSupplier) //\n\t\t\t\t.reason(\"execution timed out after \" + timeout.toMillis() + \" ms\") //\n\t\t\t\t.cause(cause) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tprivate AssertTimeoutPreemptively() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * {@code AssertTrue} is a collection of utility methods that support asserting\n * {@code true} in tests.\n *\n * @since 5.0\n */\nclass AssertTrue {\n\n\tprivate AssertTrue() {\n\t\t/* no-op */\n\t}\n\n\t@Contract(\"false -> fail\")\n\tstatic void assertTrue(boolean condition) {\n\t\tassertTrue(condition, (String) null);\n\t}\n\n\t@Contract(\"false, _ -> fail\")\n\tstatic void assertTrue(boolean condition, @Nullable String message) {\n\t\tif (!condition) {\n\t\t\tfailNotTrue(message);\n\t\t}\n\t}\n\n\t@Contract(\"false, _ -> fail\")\n\tstatic void assertTrue(boolean condition, Supplier<@Nullable String> messageSupplier) {\n\t\tif (!condition) {\n\t\t\tfailNotTrue(messageSupplier);\n\t\t}\n\t}\n\n\tprivate static void failNotTrue(@Nullable Object messageOrSupplier) {\n\t\tassertionFailure() //\n\t\t\t\t.message(messageOrSupplier) //\n\t\t\t\t.expected(true) //\n\t\t\t\t.actual(false) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.buildAndThrow();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.jupiter.api.AssertionUtils.getCanonicalName;\n\nimport java.util.Arrays;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Builder for {@link AssertionFailedError AssertionFailedErrors}.\n *\n * <p>Using this builder ensures consistency in how failure message are formatted\n * within JUnit Jupiter and for custom user-defined assertions.\n *\n * @since 5.9\n * @see AssertionFailedError\n */\n@API(status = STABLE, since = \"5.9\")\npublic class AssertionFailureBuilder {\n\n\tprivate static final int DEFAULT_RETAIN_STACKTRACE_ELEMENTS = 1;\n\n\tprivate @Nullable Object message;\n\n\tprivate @Nullable Throwable cause;\n\n\tprivate boolean mismatch;\n\n\tprivate @Nullable Object expected;\n\n\tprivate @Nullable Object actual;\n\n\tprivate @Nullable String reason;\n\n\tprivate boolean includeValuesInMessage = true;\n\n\tprivate @Nullable Class<?> trimStackTraceTarget;\n\n\tprivate int retainStackTraceElements = DEFAULT_RETAIN_STACKTRACE_ELEMENTS;\n\n\t/**\n\t * Create a new {@code AssertionFailureBuilder}.\n\t */\n\tpublic static AssertionFailureBuilder assertionFailure() {\n\t\treturn new AssertionFailureBuilder();\n\t}\n\n\tprivate AssertionFailureBuilder() {\n\t}\n\n\t/**\n\t * Set the user-defined message of the assertion.\n\t *\n\t * <p>The {@code message} may be passed as a {@link Supplier} or plain\n\t * {@link String}. If any other type is passed, it is converted to\n\t * {@code String} as per {@link StringUtils#nullSafeToString(Object)}.\n\t *\n\t * @param message the user-defined failure message; may be {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder message(@Nullable Object message) {\n\t\tthis.message = message;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the reason why the assertion failed.\n\t *\n\t * @param reason the failure reason; may be {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder reason(@Nullable String reason) {\n\t\tthis.reason = reason;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the cause of the assertion failure.\n\t *\n\t * @param cause the failure cause; may be {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder cause(@Nullable Throwable cause) {\n\t\tthis.cause = cause;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the expected value of the assertion.\n\t *\n\t * @param expected the expected value; may be {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder expected(@Nullable Object expected) {\n\t\tthis.mismatch = true;\n\t\tthis.expected = expected;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the actual value of the assertion.\n\t *\n\t * @param actual the actual value; may be {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder actual(@Nullable Object actual) {\n\t\tthis.mismatch = true;\n\t\tthis.actual = actual;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set whether to include the actual and expected values in the generated\n\t * failure message.\n\t *\n\t * @param includeValuesInMessage whether to include the actual and expected\n\t * values\n\t * @return this builder for method chaining\n\t */\n\tpublic AssertionFailureBuilder includeValuesInMessage(boolean includeValuesInMessage) {\n\t\tthis.includeValuesInMessage = includeValuesInMessage;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set target to trim the stacktrace to.\n\t *\n\t * <p>Unless {@link #retainStackTraceElements(int)} is set all stacktrace\n\t * elements before the last element from {@code target} are trimmed.\n\t *\n\t * @param target class to trim from the stacktrace\n\t * @return this builder for method chaining\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic AssertionFailureBuilder trimStacktrace(@Nullable Class<?> target) {\n\t\tthis.trimStackTraceTarget = target;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set depth to trim the stacktrace to. Defaults to\n\t * {@value #DEFAULT_RETAIN_STACKTRACE_ELEMENTS}.\n\t *\n\t * <p>If {@link #trimStacktrace(Class)} was set, all but\n\t * {@code retainStackTraceElements - 1} stacktrace elements before the last\n\t * element from {@code target} are removed. If\n\t * {@code retainStackTraceElements} is zero, all elements including those\n\t * from {@code target} are trimmed.\n\t *\n\t * @param retainStackTraceElements depth of trimming, must be non-negative\n\t * @return this builder for method chaining\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic AssertionFailureBuilder retainStackTraceElements(int retainStackTraceElements) {\n\t\tPreconditions.condition(retainStackTraceElements >= 0,\n\t\t\t\"retainStackTraceElements must have a non-negative value\");\n\t\tthis.retainStackTraceElements = retainStackTraceElements;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Build the {@link AssertionFailedError AssertionFailedError} and throw it.\n\t *\n\t * @throws AssertionFailedError always\n\t */\n\t@Contract(\" -> fail\")\n\tpublic void buildAndThrow() throws AssertionFailedError {\n\t\tthrow build();\n\t}\n\n\t/**\n\t * Build the {@link AssertionFailedError AssertionFailedError} without\n\t * throwing it.\n\t *\n\t * @return the built assertion failure\n\t */\n\tpublic AssertionFailedError build() {\n\t\tString reason = nullSafeGet(this.reason);\n\t\tif (mismatch && includeValuesInMessage) {\n\t\t\treason = (reason == null ? \"\" : reason + \", \") + formatValues(expected, actual);\n\t\t}\n\t\tString message = nullSafeGet(this.message);\n\t\tif (reason != null) {\n\t\t\tmessage = buildPrefix(message) + reason;\n\t\t}\n\n\t\tvar assertionFailedError = mismatch //\n\t\t\t\t? new AssertionFailedError(message, expected, actual, cause) //\n\t\t\t\t: new AssertionFailedError(message, cause);\n\n\t\tmaybeTrimStackTrace(assertionFailedError);\n\t\treturn assertionFailedError;\n\t}\n\n\tprivate void maybeTrimStackTrace(Throwable throwable) {\n\t\tif (trimStackTraceTarget == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar pruneTargetClassName = trimStackTraceTarget.getName();\n\t\tvar stackTrace = throwable.getStackTrace();\n\n\t\tint lastIndexOf = -1;\n\t\tfor (int i = 0; i < stackTrace.length; i++) {\n\t\t\tvar element = stackTrace[i];\n\t\t\tvar className = element.getClassName();\n\t\t\tif (className.equals(pruneTargetClassName)) {\n\t\t\t\tlastIndexOf = i;\n\t\t\t}\n\t\t}\n\n\t\tif (lastIndexOf != -1) {\n\t\t\tint from = clamp0(lastIndexOf + 1 - retainStackTraceElements, stackTrace.length);\n\t\t\tvar trimmed = Arrays.copyOfRange(stackTrace, from, stackTrace.length);\n\t\t\tthrowable.setStackTrace(trimmed);\n\t\t}\n\t}\n\n\tprivate static int clamp0(int value, int max) {\n\t\treturn Math.max(0, Math.min(value, max));\n\t}\n\n\tprivate static @Nullable String nullSafeGet(@Nullable Object messageOrSupplier) {\n\t\tif (messageOrSupplier == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (messageOrSupplier instanceof Supplier<?> supplier) {\n\t\t\tObject message = supplier.get();\n\t\t\treturn StringUtils.nullSafeToString(message);\n\t\t}\n\t\treturn StringUtils.nullSafeToString(messageOrSupplier);\n\t}\n\n\tprivate static String buildPrefix(@Nullable String message) {\n\t\treturn (StringUtils.isNotBlank(message) ? message + \" ==> \" : \"\");\n\t}\n\n\tprivate static String formatValues(@Nullable Object expected, @Nullable Object actual) {\n\t\tString expectedString = toString(expected);\n\t\tString actualString = toString(actual);\n\t\tif (expectedString.equals(actualString)) {\n\t\t\treturn \"expected: %s but was: %s\".formatted(formatClassAndValue(expected, expectedString),\n\t\t\t\tformatClassAndValue(actual, actualString));\n\t\t}\n\t\treturn \"expected: <%s> but was: <%s>\".formatted(expectedString, actualString);\n\t}\n\n\tprivate static String formatClassAndValue(@Nullable Object value, String valueString) {\n\t\t// If the value is null, return <null> instead of null<null>.\n\t\tif (value == null) {\n\t\t\treturn \"<null>\";\n\t\t}\n\t\tString classAndHash = getClassName(value) + toHash(value);\n\t\t// if it's a class, there's no need to repeat the class name contained in the valueString.\n\t\treturn (value instanceof Class ? \"<\" + classAndHash + \">\" : classAndHash + \"<\" + valueString + \">\");\n\t}\n\n\tprivate static String toString(@Nullable Object obj) {\n\t\tif (obj instanceof Class<?> clazz) {\n\t\t\treturn getCanonicalName(clazz);\n\t\t}\n\t\treturn StringUtils.nullSafeToString(obj);\n\t}\n\n\tprivate static String toHash(@Nullable Object obj) {\n\t\treturn (obj == null ? \"\" : \"@\" + Integer.toHexString(System.identityHashCode(obj)));\n\t}\n\n\tprivate static String getClassName(@Nullable Object obj) {\n\t\treturn (obj == null ? \"null\"\n\t\t\t\t: obj instanceof Class<?> clazz ? getCanonicalName(clazz) : obj.getClass().getName());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\n\nimport java.util.Deque;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code AssertionUtils} is a collection of utility methods that are common to\n * all assertion implementations.\n *\n * @since 5.0\n */\nclass AssertionUtils {\n\n\tprivate AssertionUtils() {\n\t\t/* no-op */\n\t}\n\n\tstatic AssertionFailedError failure() {\n\t\tthrow assertionFailure() //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tstatic AssertionFailedError failure(@Nullable String message) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(message) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tstatic AssertionFailedError failure(@Nullable String message, @Nullable Throwable cause) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(message) //\n\t\t\t\t.cause(cause) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tstatic AssertionFailedError failure(@Nullable Throwable cause) {\n\t\tthrow assertionFailure() //\n\t\t\t\t.cause(cause) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tstatic AssertionFailedError failure(Supplier<@Nullable String> messageSupplier) {\n\t\treturn assertionFailure() //\n\t\t\t\t.message(nullSafeGet(messageSupplier)) //\n\t\t\t\t.trimStacktrace(Assertions.class) //\n\t\t\t\t.build();\n\t}\n\n\tstatic @Nullable String nullSafeGet(@Nullable Supplier<@Nullable String> messageSupplier) {\n\t\treturn (messageSupplier != null ? messageSupplier.get() : null);\n\t}\n\n\tstatic String getCanonicalName(Class<?> clazz) {\n\t\ttry {\n\t\t\tString canonicalName = clazz.getCanonicalName();\n\t\t\treturn (canonicalName != null ? canonicalName : clazz.getTypeName());\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\treturn clazz.getTypeName();\n\t\t}\n\t}\n\n\tstatic String formatIndexes(@Nullable Deque<Integer> indexes) {\n\t\tif (indexes == null || indexes.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\tString indexesString = indexes.stream().map(Object::toString).collect(joining(\"][\", \"[\", \"]\"));\n\t\treturn \" at index \" + indexesString;\n\t}\n\n\tstatic boolean floatsAreEqual(float value1, float value2, float delta) {\n\t\tassertValidDelta(delta);\n\t\treturn floatsAreEqual(value1, value2) || Math.abs(value1 - value2) <= delta;\n\t}\n\n\tstatic void assertValidDelta(float delta) {\n\t\tif (Float.isNaN(delta) || delta < 0.0) {\n\t\t\tfailIllegalDelta(String.valueOf(delta));\n\t\t}\n\t}\n\n\tstatic void assertValidDelta(double delta) {\n\t\tif (Double.isNaN(delta) || delta < 0.0) {\n\t\t\tfailIllegalDelta(String.valueOf(delta));\n\t\t}\n\t}\n\n\tstatic boolean floatsAreEqual(float value1, float value2) {\n\t\treturn Float.floatToIntBits(value1) == Float.floatToIntBits(value2);\n\t}\n\n\tstatic boolean doublesAreEqual(double value1, double value2, double delta) {\n\t\tassertValidDelta(delta);\n\t\treturn doublesAreEqual(value1, value2) || Math.abs(value1 - value2) <= delta;\n\t}\n\n\tstatic boolean doublesAreEqual(double value1, double value2) {\n\t\treturn Double.doubleToLongBits(value1) == Double.doubleToLongBits(value2);\n\t}\n\n\tstatic boolean objectsAreEqual(@Nullable Object obj1, @Nullable Object obj2) {\n\t\tif (obj1 == null) {\n\t\t\treturn (obj2 == null);\n\t\t}\n\t\treturn obj1.equals(obj2);\n\t}\n\n\tprivate static void failIllegalDelta(String delta) {\n\t\tthrow failure(\"positive delta expected but was: <\" + delta + \">\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.time.Duration;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.function.BooleanSupplier;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\nimport org.junit.platform.commons.annotation.Contract;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * {@code Assertions} is a collection of utility methods that support asserting\n * conditions in tests.\n *\n * <p>Unless otherwise noted, a <em>failed</em> assertion will throw an\n * {@link org.opentest4j.AssertionFailedError} or a subclass thereof.\n *\n * <h2>Object Equality</h2>\n *\n * <p>Assertion methods comparing two objects for <em>equality</em>, such as the\n * {@code assertEquals(expected, actual)} and {@code assertNotEquals(unexpected, actual)}\n * variants, are <em>only</em> intended to test equality for an (un-)expected value\n * and an actual value. They are not designed for testing whether a class correctly\n * implements {@link Object#equals(Object)}. For example, {@code assertEquals()}\n * might immediately return {@code true} when provided the same object for the\n * expected and actual values, without calling {@code equals(Object)} at all.\n * Tests that aim to verify the {@code equals(Object)} implementation should instead\n * be written to explicitly verify the {@link Object#equals(Object)} contract by\n * using {@link #assertTrue(boolean) assertTrue()} or {@link #assertFalse(boolean)\n * assertFalse()} &mdash; for example, {@code assertTrue(expected.equals(actual))},\n * {@code assertTrue(actual.equals(expected))}, {@code assertFalse(expected.equals(null))},\n * etc.\n *\n * <h2>Kotlin Support</h2>\n *\n * <p>Additional <a href=\"https://kotlinlang.org/\">Kotlin</a> assertions can be\n * found as <em>top-level functions</em> in the {@link org.junit.jupiter.api}\n * package.\n *\n * <h2>Preemptive Timeouts</h2>\n *\n * <p>The various {@code assertTimeoutPreemptively()} methods in this class\n * execute the provided callback ({@code executable} or {@code supplier}) in a\n * different thread than that of the calling code. If the timeout is exceeded,\n * an attempt will be made to preemptively abort execution of the callback by\n * {@linkplain Thread#interrupt() interrupting} the callback's thread. If the\n * callback's thread does not return when interrupted, the thread will continue\n * to run in the background after the {@code assertTimeoutPreemptively()} method\n * has returned.\n *\n * <p>Furthermore, the behavior of {@code assertTimeoutPreemptively()} methods\n * can lead to undesirable side effects if the code that is executed within the\n * callback relies on {@link ThreadLocal} storage. One common example of this is\n * the transactional testing support in the Spring Framework. Specifically, Spring's\n * testing support binds transaction state to the current thread (via a\n * {@code ThreadLocal}) before a test method is invoked. Consequently, if a\n * callback provided to {@code assertTimeoutPreemptively()} invokes Spring-managed\n * components that participate in transactions, any actions taken by those\n * components will not be rolled back with the test-managed transaction. On the\n * contrary, such actions will be committed to the persistent store (e.g.,\n * relational database) even though the test-managed transaction is rolled back.\n * Similar side effects may be encountered with other frameworks that rely on\n * {@code ThreadLocal} storage.\n *\n * <h2>Extensibility</h2>\n *\n * <p>Although it is technically possible to extend this class, extension is\n * strongly discouraged. The JUnit Team highly recommends that the methods\n * defined in this class be used via <em>static imports</em>.\n *\n * @since 5.0\n * @see org.opentest4j.AssertionFailedError\n * @see Assumptions\n */\n@API(status = STABLE, since = \"5.0\")\npublic class Assertions {\n\n\t/**\n\t * Protected constructor allowing subclassing but not direct instantiation.\n\t *\n\t * @since 5.3\n\t */\n\t@API(status = STABLE, since = \"5.3\")\n\tprotected Assertions() {\n\t\t/* no-op */\n\t}\n\n\t// --- fail ----------------------------------------------------------------\n\n\t/**\n\t * <em>Fail</em> the test <em>without</em> a failure message.\n\t *\n\t * <p>Although failing <em>with</em> an explicit failure message is recommended,\n\t * this method may be useful when maintaining legacy code.\n\t *\n\t * <p>See Javadoc for {@link #fail(String)} for an explanation of this method's\n\t * generic return type {@code V}.\n\t */\n\t@Contract(\" -> fail\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V fail() {\n\t\tthrow AssertionUtils.failure();\n\t}\n\n\t/**\n\t * <em>Fail</em> the test with the given failure {@code message}.\n\t *\n\t * <p>The generic return type {@code V} allows this method to be used\n\t * directly as a single-statement lambda expression, thereby avoiding the\n\t * need to implement a code block with an explicit return value. Since this\n\t * method throws an {@link org.opentest4j.AssertionFailedError} before its\n\t * return statement, this method never actually returns a value to its caller.\n\t * The following example demonstrates how this may be used in practice.\n\t *\n\t * <pre>{@code\n\t * Stream.of().map(entry -> fail(\"should not be called\"));\n\t * }</pre>\n\t */\n\t@Contract(\"_ -> fail\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V fail(@Nullable String message) {\n\t\tthrow AssertionUtils.failure(message);\n\t}\n\n\t/**\n\t * <em>Fail</em> the test with the given failure {@code message} as well\n\t * as the underlying {@code cause}.\n\t *\n\t * <p>See Javadoc for {@link #fail(String)} for an explanation of this method's\n\t * generic return type {@code V}.\n\t */\n\t@Contract(\"_, _ -> fail\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V fail(@Nullable String message, @Nullable Throwable cause) {\n\t\tthrow AssertionUtils.failure(message, cause);\n\t}\n\n\t/**\n\t * <em>Fail</em> the test with the given underlying {@code cause}.\n\t *\n\t * <p>See Javadoc for {@link #fail(String)} for an explanation of this method's\n\t * generic return type {@code V}.\n\t */\n\t@Contract(\"_ -> fail\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V fail(@Nullable Throwable cause) {\n\t\tthrow AssertionUtils.failure(cause);\n\t}\n\n\t/**\n\t * <em>Fail</em> the test with the failure message retrieved from the\n\t * given {@code messageSupplier}.\n\t *\n\t * <p>See Javadoc for {@link #fail(String)} for an explanation of this method's\n\t * generic return type {@code V}.\n\t */\n\t@Contract(\"_ -> fail\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V fail(Supplier<@Nullable String> messageSupplier) {\n\t\tthrow AssertionUtils.failure(messageSupplier);\n\t}\n\n\t// --- assertTrue ----------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code true}.\n\t */\n\t@Contract(\"false -> fail\")\n\tpublic static void assertTrue(boolean condition) {\n\t\tAssertTrue.assertTrue(condition);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code true}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void assertTrue(boolean condition, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertTrue.assertTrue(condition, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by\n\t * {@code booleanSupplier} is {@code true}.\n\t *\n\t * <p>The supplier will be called exactly once so calling this method is\n\t * equivalent to calling {@code assertTrue(booleanSupplier.get())}.\n\t */\n\tpublic static void assertTrue(BooleanSupplier booleanSupplier) {\n\t\tassertTrue(booleanSupplier.getAsBoolean());\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by\n\t * {@code booleanSupplier} is {@code true}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * <p>The supplier will be called exactly once so calling this method is\n\t * equivalent to calling {@code assertTrue(booleanSupplier.get(), message)}.\n\t */\n\tpublic static void assertTrue(BooleanSupplier booleanSupplier, @Nullable String message) {\n\t\tassertTrue(booleanSupplier.getAsBoolean(), message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code true}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void assertTrue(boolean condition, @Nullable String message) {\n\t\tAssertTrue.assertTrue(condition, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by\n\t * {@code booleanSupplier} is {@code true}.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * <p>The {@code booleanSupplier} will be called exactly once so calling\n\t * this method is equivalent to calling\n\t * {@code assertTrue(booleanSupplier.get(), messageSupplier)}.\n\t */\n\tpublic static void assertTrue(BooleanSupplier booleanSupplier, Supplier<@Nullable String> messageSupplier) {\n\t\tassertTrue(booleanSupplier.getAsBoolean(), messageSupplier);\n\t}\n\n\t// --- assertFalse ---------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code false}.\n\t */\n\t@Contract(\"true -> fail\")\n\tpublic static void assertFalse(boolean condition) {\n\t\tAssertFalse.assertFalse(condition);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code false}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\t@Contract(\"true, _ -> fail\")\n\tpublic static void assertFalse(boolean condition, @Nullable String message) {\n\t\tAssertFalse.assertFalse(condition, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code condition} is {@code false}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\t@Contract(\"true, _ -> fail\")\n\tpublic static void assertFalse(boolean condition, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertFalse.assertFalse(condition, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by {@code booleanSupplier} is {@code false}.\n\t */\n\tpublic static void assertFalse(BooleanSupplier booleanSupplier) {\n\t\tAssertFalse.assertFalse(booleanSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by {@code booleanSupplier} is {@code false}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertFalse(BooleanSupplier booleanSupplier, @Nullable String message) {\n\t\tAssertFalse.assertFalse(booleanSupplier, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the boolean condition supplied by {@code booleanSupplier} is {@code false}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertFalse(BooleanSupplier booleanSupplier, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertFalse.assertFalse(booleanSupplier, messageSupplier);\n\t}\n\n\t// --- assertNull ----------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is {@code null}.\n\t */\n\t@Contract(\"!null -> fail\")\n\tpublic static void assertNull(@Nullable Object actual) {\n\t\tAssertNull.assertNull(actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is {@code null}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\t@Contract(\"!null, _ -> fail\")\n\tpublic static void assertNull(@Nullable Object actual, @Nullable String message) {\n\t\tAssertNull.assertNull(actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is {@code null}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\t@Contract(\"!null, _ -> fail\")\n\tpublic static void assertNull(@Nullable Object actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNull.assertNull(actual, messageSupplier);\n\t}\n\n\t// --- assertNotNull -------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is not {@code null}.\n\t */\n\t@Contract(\"null -> fail\")\n\tpublic static void assertNotNull(@Nullable Object actual) {\n\t\tAssertNotNull.assertNotNull(actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is not {@code null}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static void assertNotNull(@Nullable Object actual, @Nullable String message) {\n\t\tAssertNotNull.assertNotNull(actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code actual} is not {@code null}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static void assertNotNull(@Nullable Object actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotNull.assertNotNull(actual, messageSupplier);\n\t}\n\n\t// --- assertEquals --------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(short expected, short actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(short expected, @Nullable Short actual) {\n\t\tAssertEquals.assertEquals((Short) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(@Nullable Short expected, short actual) {\n\t\tAssertEquals.assertEquals(expected, (Short) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Short expected, @Nullable Short actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(short expected, short actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(short expected, @Nullable Short actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Short) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Short expected, short actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Short) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Short expected, @Nullable Short actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(short expected, short actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(short expected, @Nullable Short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Short) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Short expected, short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Short) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Short expected, @Nullable Short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(byte expected, byte actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(byte expected, @Nullable Byte actual) {\n\t\tAssertEquals.assertEquals((Byte) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(@Nullable Byte expected, byte actual) {\n\t\tAssertEquals.assertEquals(expected, (Byte) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Byte expected, @Nullable Byte actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(byte expected, byte actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(byte expected, @Nullable Byte actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Byte) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Byte expected, byte actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Byte) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Byte expected, @Nullable Byte actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(byte expected, byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(byte expected, @Nullable Byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Byte) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Byte expected, byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Byte) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Byte expected, @Nullable Byte actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(int expected, int actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(int expected, @Nullable Integer actual) {\n\t\tAssertEquals.assertEquals((Integer) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(@Nullable Integer expected, int actual) {\n\t\tAssertEquals.assertEquals(expected, (Integer) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Integer expected, @Nullable Integer actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(int expected, int actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(int expected, @Nullable Integer actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Integer) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Integer expected, int actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Integer) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Integer expected, @Nullable Integer actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(int expected, int actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(int expected, @Nullable Integer actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Integer) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Integer expected, int actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Integer) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Integer expected, @Nullable Integer actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(long expected, long actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(long expected, @Nullable Long actual) {\n\t\tAssertEquals.assertEquals((Long) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(@Nullable Long expected, long actual) {\n\t\tAssertEquals.assertEquals(expected, (Long) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Long expected, @Nullable Long actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(long expected, long actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(long expected, @Nullable Long actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Long) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Long expected, long actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Long) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Long expected, @Nullable Long actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(long expected, long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(long expected, @Nullable Long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Long) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Long expected, long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Long) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Long expected, @Nullable Long actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertEquals(float expected, float actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertEquals(float expected, @Nullable Float actual) {\n\t\tAssertEquals.assertEquals((Float) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertEquals(@Nullable Float expected, float actual) {\n\t\tAssertEquals.assertEquals(expected, (Float) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Float expected, @Nullable Float actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(float expected, float actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(float expected, @Nullable Float actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Float) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Float expected, float actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Float) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Float expected, @Nullable Float actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(float expected, float actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(float expected, @Nullable Float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Float) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Float expected, float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Float) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Float expected, @Nullable Float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertEquals(float expected, float actual, float delta) {\n\t\tAssertEquals.assertEquals(expected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(float expected, float actual, float delta, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(float expected, float actual, float delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertEquals(double expected, double actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertEquals(double expected, @Nullable Double actual) {\n\t\tAssertEquals.assertEquals((Double) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertEquals(@Nullable Double expected, double actual) {\n\t\tAssertEquals.assertEquals(expected, (Double) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Double expected, @Nullable Double actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(double expected, double actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(double expected, @Nullable Double actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Double) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Double expected, double actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Double) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Double expected, @Nullable Double actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(double expected, double actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(double expected, @Nullable Double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Double) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Double expected, double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Double) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Double expected, @Nullable Double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertEquals(double expected, double actual, double delta) {\n\t\tAssertEquals.assertEquals(expected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(double expected, double actual, double delta, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(double expected, double actual, double delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(char expected, char actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(char expected, @Nullable Character actual) {\n\t\tAssertEquals.assertEquals((Character) expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t */\n\tpublic static void assertEquals(@Nullable Character expected, char actual) {\n\t\tAssertEquals.assertEquals(expected, (Character) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Character expected, @Nullable Character actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(char expected, char actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(char expected, @Nullable Character actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals((Character) expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertEquals(@Nullable Character expected, char actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, (Character) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Character expected, @Nullable Character actual,\n\t\t\t@Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(char expected, char actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(char expected, @Nullable Character actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals((Character) expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertEquals(@Nullable Character expected, char actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, (Character) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertEquals(@Nullable Character expected, @Nullable Character actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertEquals(@Nullable Object expected, @Nullable Object actual) {\n\t\tAssertEquals.assertEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertEquals(@Nullable Object expected, @Nullable Object actual, @Nullable String message) {\n\t\tAssertEquals.assertEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertEquals(@Nullable Object expected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertEquals.assertEquals(expected, actual, messageSupplier);\n\t}\n\n\t// --- assertArrayEquals ---------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} boolean arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} boolean arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} boolean arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(boolean @Nullable [] expected, boolean @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} char arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} char arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} char arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(char @Nullable [] expected, char @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} byte arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} byte arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} byte arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(byte @Nullable [] expected, byte @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} short arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} short arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} short arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(short @Nullable [] expected, short @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} int arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} int arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual, @Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} int arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(int @Nullable [] expected, int @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} long arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t */\n\tpublic static void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} long arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} long arrays are equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(long @Nullable [] expected, long @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} float arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Float#equals(Object)} and\n\t * {@link Float#compare(float, float)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(float @Nullable [] expected, float @Nullable [] actual, float delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} double arrays are equal within the given non-negative {@code delta}.\n\t * <p>Equality imposed by this method is consistent with {@link Double#equals(Object)} and\n\t * {@link Double#compare(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t */\n\tpublic static void assertArrayEquals(double @Nullable [] expected, double @Nullable [] actual, double delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} object arrays are deeply equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Nested float arrays are checked as in {@link #assertEquals(float, float)}.\n\t * <p>Nested double arrays are checked as in {@link #assertEquals(double, double)}.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t */\n\tpublic static void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} object arrays are deeply equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Nested float arrays are checked as in {@link #assertEquals(float, float)}.\n\t * <p>Nested double arrays are checked as in {@link #assertEquals(double, double)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t */\n\tpublic static void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual,\n\t\t\t@Nullable String message) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} object arrays are deeply equal.\n\t * <p>If both are {@code null}, they are considered equal.\n\t * <p>Nested float arrays are checked as in {@link #assertEquals(float, float)}.\n\t * <p>Nested double arrays are checked as in {@link #assertEquals(double, double)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t */\n\tpublic static void assertArrayEquals(@Nullable Object @Nullable [] expected, @Nullable Object @Nullable [] actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertArrayEquals.assertArrayEquals(expected, actual, messageSupplier);\n\t}\n\n\t// --- assertIterableEquals --------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} iterables are deeply equal.\n\t * <p>Similarly to the check for deep equality in {@link #assertArrayEquals(Object[], Object[])},\n\t * if two iterables are encountered (including {@code expected} and {@code actual}) then their\n\t * iterators must return equal elements in the same order as each other. <strong>Note:</strong>\n\t * this means that the iterables <em>do not</em> need to be of the same type. Example: <pre>{@code\n\t * import static java.util.Arrays.asList;\n\t *  ...\n\t * Iterable<Integer> i0 = new ArrayList<>(asList(1, 2, 3));\n\t * Iterable<Integer> i1 = new LinkedList<>(asList(1, 2, 3));\n\t * assertIterableEquals(i0, i1); // Passes\n\t * }</pre>\n\t * <p>If both {@code expected} and {@code actual} are {@code null}, they are considered equal.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t * @see #assertArrayEquals(Object[], Object[])\n\t */\n\tpublic static void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual) {\n\t\tAssertIterableEquals.assertIterableEquals(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} iterables are deeply equal.\n\t * <p>Similarly to the check for deep equality in\n\t * {@link #assertArrayEquals(Object[], Object[], String)}, if two iterables are encountered\n\t * (including {@code expected} and {@code actual}) then their iterators must return equal\n\t * elements in the same order as each other. <strong>Note:</strong> this means that the iterables\n\t * <em>do not</em> need to be of the same type. Example: <pre>{@code\n\t * import static java.util.Arrays.asList;\n\t *  ...\n\t * Iterable<Integer> i0 = new ArrayList<>(asList(1, 2, 3));\n\t * Iterable<Integer> i1 = new LinkedList<>(asList(1, 2, 3));\n\t * assertIterableEquals(i0, i1); // Passes\n\t * }</pre>\n\t * <p>If both {@code expected} and {@code actual} are {@code null}, they are considered equal.\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t * @see #assertArrayEquals(Object[], Object[], String)\n\t */\n\tpublic static void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\t@Nullable String message) {\n\t\tAssertIterableEquals.assertIterableEquals(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} and {@code actual} iterables are deeply equal.\n\t * <p>Similarly to the check for deep equality in\n\t * {@link #assertArrayEquals(Object[], Object[], Supplier)}, if two iterables are encountered\n\t * (including {@code expected} and {@code actual}) then their iterators must return equal\n\t * elements in the same order as each other. <strong>Note:</strong> this means that the iterables\n\t * <em>do not</em> need to be of the same type. Example: <pre>{@code\n\t * import static java.util.Arrays.asList;\n\t *  ...\n\t * Iterable<Integer> i0 = new ArrayList<>(asList(1, 2, 3));\n\t * Iterable<Integer> i1 = new LinkedList<>(asList(1, 2, 3));\n\t * assertIterableEquals(i0, i1); // Passes\n\t * }</pre>\n\t * <p>If both {@code expected} and {@code actual} are {@code null}, they are considered equal.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied {@code messageSupplier}.\n\t *\n\t * @see Objects#equals(Object, Object)\n\t * @see Arrays#deepEquals(Object[], Object[])\n\t * @see #assertArrayEquals(Object[], Object[], Supplier)\n\t */\n\tpublic static void assertIterableEquals(@Nullable Iterable<?> expected, @Nullable Iterable<?> actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertIterableEquals.assertIterableEquals(expected, actual, messageSupplier);\n\t}\n\n\t// --- assertLinesMatch ----------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code expected} list of {@linkplain String}s matches {@code actual}\n\t * list.\n\t *\n\t * <p>This method differs from other assertions that effectively only check {@link String#equals(Object)},\n\t * in that it uses the following staged matching algorithm:\n\t *\n\t * <p>For each pair of expected and actual lines do\n\t * <ol>\n\t *   <li>check if {@code expected.equals(actual)} - if yes, continue with next pair</li>\n\t *   <li>otherwise treat {@code expected} as a regular expression and check via\n\t *   {@link String#matches(String)} - if yes, continue with next pair</li>\n\t *   <li>otherwise check if {@code expected} line is a fast-forward marker, if yes apply\n\t *   fast-forward actual lines accordingly (see below) and goto 1.</li>\n\t * </ol>\n\t *\n\t * <p>A valid fast-forward marker is an expected line that starts and ends with the literal\n\t * {@code >>} and contains at least 4 characters. Examples:\n\t * <ul>\n\t *   <li>{@code >>>>}<br>{@code >> stacktrace >>}<br>{@code >> single line, non Integer.parse()-able comment >>}\n\t *   <br>Skip arbitrary number of actual lines, until first matching subsequent expected line is found. Any\n\t *   character between the fast-forward literals are discarded.</li>\n\t *   <li>{@code \">> 21 >>\"}\n\t *   <br>Skip strictly 21 lines. If they can't be skipped for any reason, an assertion error is raised.</li>\n\t * </ul>\n\t *\n\t * <p>Here is an example showing all three kinds of expected line formats:\n\t * <pre>{@code\n\t * ls -la /\n\t * total [\\d]+\n\t * drwxr-xr-x  0 root root   512 Jan  1  1970 .\n\t * drwxr-xr-x  0 root root   512 Jan  1  1970 ..\n\t * drwxr-xr-x  0 root root   512 Apr  5 07:45 bin\n\t * >> 4 >>\n\t * -rwxr-xr-x  1 root root [\\d]+ Jan  1  1970 init\n\t * >> M A N Y  M O R E  E N T R I E S >>\n\t * drwxr-xr-x  0 root root   512 Sep 22  2017 var\n\t * }</pre>\n\t * <p>Fails with a generated failure message describing the difference.\n\t */\n\tpublic static void assertLinesMatch(List<String> expectedLines, List<String> actualLines) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} list of {@linkplain String}s matches {@code actual}\n\t * list.\n\t *\n\t * <p>Find a detailed description of the matching algorithm in {@link #assertLinesMatch(List, List)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message} and the generated message.\n\t *\n\t * @see #assertLinesMatch(List, List)\n\t */\n\tpublic static void assertLinesMatch(List<String> expectedLines, List<String> actualLines,\n\t\t\t@Nullable String message) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} list of {@linkplain String}s matches {@code actual}\n\t * list.\n\t *\n\t * <p>Find a detailed description of the matching algorithm in {@link #assertLinesMatch(List, List)}.\n\t *\n\t * <p>If necessary, a custom failure message will be retrieved lazily from the supplied\n\t * {@code messageSupplier}. Fails with the custom failure message prepended to\n\t * a generated failure message describing the difference.\n\t *\n\t * @see #assertLinesMatch(List, List)\n\t */\n\tpublic static void assertLinesMatch(List<String> expectedLines, List<String> actualLines,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} stream of {@linkplain String}s matches {@code actual}\n\t * stream.\n\t *\n\t * <p>Find a detailed description of the matching algorithm in {@link #assertLinesMatch(List, List)}.\n\t *\n\t * <p>Note: An implementation of this method may consume all lines of both streams eagerly and\n\t * delegate the evaluation to {@link #assertLinesMatch(List, List)}.\n\t *\n\t * @since 5.7\n\t * @see #assertLinesMatch(List, List)\n\t */\n\tpublic static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} stream of {@linkplain String}s matches {@code actual}\n\t * stream.\n\t *\n\t * <p>Find a detailed description of the matching algorithm in {@link #assertLinesMatch(List, List)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message} and the generated message.\n\t *\n\t * <p>Note: An implementation of this method may consume all lines of both streams eagerly and\n\t * delegate the evaluation to {@link #assertLinesMatch(List, List)}.\n\t *\n\t * @since 5.7\n\t * @see #assertLinesMatch(List, List)\n\t */\n\tpublic static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines,\n\t\t\t@Nullable String message) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code expected} stream of {@linkplain String}s matches {@code actual}\n\t * stream.\n\t *\n\t * <p>Find a detailed description of the matching algorithm in {@link #assertLinesMatch(List, List)}.\n\t *\n\t * <p>If necessary, a custom failure message will be retrieved lazily from the supplied\n\t * {@code messageSupplier}. Fails with the custom failure message prepended to\n\t * a generated failure message describing the difference.\n\t *\n\t * <p>Note: An implementation of this method may consume all lines of both streams eagerly and\n\t * delegate the evaluation to {@link #assertLinesMatch(List, List)}.\n\t *\n\t * @since 5.7\n\t * @see #assertLinesMatch(List, List)\n\t */\n\tpublic static void assertLinesMatch(Stream<String> expectedLines, Stream<String> actualLines,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertLinesMatch.assertLinesMatch(expectedLines, actualLines, messageSupplier);\n\t}\n\n\t// --- assertNotEquals -----------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, byte actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, @Nullable Byte actual) {\n\t\tAssertNotEquals.assertNotEquals((Byte) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, byte actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Byte) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, @Nullable Byte actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, byte actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, @Nullable Byte actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Byte) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, byte actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Byte) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, @Nullable Byte actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, byte actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(byte unexpected, @Nullable Byte actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Byte) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, byte actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Byte) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Byte unexpected, @Nullable Byte actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, short actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, @Nullable Short actual) {\n\t\tAssertNotEquals.assertNotEquals((Short) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, short actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Short) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, @Nullable Short actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, short actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, @Nullable Short actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Short) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, short actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Short) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, @Nullable Short actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, short actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(short unexpected, @Nullable Short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Short) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Short) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Short unexpected, @Nullable Short actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, int actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, @Nullable Integer actual) {\n\t\tAssertNotEquals.assertNotEquals((Integer) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, int actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Integer) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, @Nullable Integer actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, int actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, @Nullable Integer actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Integer) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, int actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Integer) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, @Nullable Integer actual,\n\t\t\t@Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, int actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(int unexpected, @Nullable Integer actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Integer) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, int actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Integer) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Integer unexpected, @Nullable Integer actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, long actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, @Nullable Long actual) {\n\t\tAssertNotEquals.assertNotEquals((Long) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, long actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Long) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, @Nullable Long actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, long actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, @Nullable Long actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Long) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, long actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Long) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, @Nullable Long actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, long actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(long unexpected, @Nullable Long actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Long) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, long actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Long) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Long unexpected, @Nullable Long actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, @Nullable Float actual) {\n\t\tAssertNotEquals.assertNotEquals((Float) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, float actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Float) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, @Nullable Float actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, @Nullable Float actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Float) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, float actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Float) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, @Nullable Float actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, @Nullable Float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Float) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Float) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Float unexpected, @Nullable Float actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual, float delta) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual, float delta, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Float#equals(Object)} and {@link Float#compare(float, float)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(float unexpected, float actual, float delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, @Nullable Double actual) {\n\t\tAssertNotEquals.assertNotEquals((Double) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, double actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Double) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, @Nullable Double actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, @Nullable Double actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Double) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, double actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Double) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, @Nullable Double actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, @Nullable Double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Double) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Double) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Double unexpected, @Nullable Double actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual, double delta) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual, double delta, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal\n\t * within the given {@code delta}.\n\t *\n\t * <p>Inequality imposed by this method is consistent with\n\t * {@link Double#equals(Object)} and {@link Double#compare(double, double)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(double unexpected, double actual, double delta,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, delta, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, char actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, @Nullable Character actual) {\n\t\tAssertNotEquals.assertNotEquals((Character) unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, char actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Character) actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, @Nullable Character actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, char actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, @Nullable Character actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals((Character) unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, char actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Character) actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, @Nullable Character actual,\n\t\t\t@Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, char actual, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(char unexpected, @Nullable Character actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals((Character) unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, char actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, (Character) actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.4\")\n\tpublic static void assertNotEquals(@Nullable Character unexpected, @Nullable Character actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails if both are {@code null}.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails if both are {@code null}.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual, @Nullable String message) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that {@code unexpected} and {@code actual} are not equal.\n\t *\n\t * <p>Fails if both are {@code null}.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @see Object#equals(Object)\n\t */\n\tpublic static void assertNotEquals(@Nullable Object unexpected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotEquals.assertNotEquals(unexpected, actual, messageSupplier);\n\t}\n\n\t// --- assertSame ----------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that the {@code expected} object and the {@code actual} object\n\t * are the same object.\n\t * <p>This method should only be used to assert <em>identity</em> between objects.\n\t * To assert <em>equality</em> between two objects or two primitive values,\n\t * use one of the {@code assertEquals(...)} methods instead &mdash; for example,\n\t * use {@code assertEquals(999, 999)} instead of {@code assertSame(999, 999)}.\n\t */\n\tpublic static void assertSame(@Nullable Object expected, @Nullable Object actual) {\n\t\tAssertSame.assertSame(expected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the {@code expected} object and the {@code actual} object\n\t * are the same object.\n\t * <p>This method should only be used to assert <em>identity</em> between objects.\n\t * To assert <em>equality</em> between two objects or two primitive values,\n\t * use one of the {@code assertEquals(...)} methods instead &mdash; for example,\n\t * use {@code assertEquals(999, 999)} instead of {@code assertSame(999, 999)}.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertSame(@Nullable Object expected, @Nullable Object actual, @Nullable String message) {\n\t\tAssertSame.assertSame(expected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the {@code expected} object and the {@code actual} object\n\t * are the same object.\n\t * <p>This method should only be used to assert <em>identity</em> between objects.\n\t * To assert <em>equality</em> between two objects or two primitive values,\n\t * use one of the {@code assertEquals(...)} methods instead &mdash; for example,\n\t * use {@code assertEquals(999, 999)} instead of {@code assertSame(999, 999)}.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied\n\t * {@code messageSupplier}.\n\t */\n\tpublic static void assertSame(@Nullable Object expected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertSame.assertSame(expected, actual, messageSupplier);\n\t}\n\n\t// --- assertNotSame -------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that the {@code unexpected} object and the {@code actual}\n\t * object are not the same object.\n\t * <p>This method should only be used to compare the <em>identity</em> of two\n\t * objects. To assert that two objects or two primitive values are not\n\t * <em>equal</em>, use one of the {@code assertNotEquals(...)} methods instead.\n\t */\n\tpublic static void assertNotSame(@Nullable Object unexpected, @Nullable Object actual) {\n\t\tAssertNotSame.assertNotSame(unexpected, actual);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the {@code unexpected} object and the {@code actual}\n\t * object are not the same object.\n\t * <p>This method should only be used to compare the <em>identity</em> of two\n\t * objects. To assert that two objects or two primitive values are not\n\t * <em>equal</em>, use one of the {@code assertNotEquals(...)} methods instead.\n\t * <p>Fails with the supplied failure {@code message}.\n\t */\n\tpublic static void assertNotSame(@Nullable Object unexpected, @Nullable Object actual, @Nullable String message) {\n\t\tAssertNotSame.assertNotSame(unexpected, actual, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the {@code unexpected} object and the {@code actual}\n\t * object are not the same object.\n\t * <p>This method should only be used to compare the <em>identity</em> of two\n\t * objects. To assert that two objects or two primitive values are not\n\t * <em>equal</em>, use one of the {@code assertNotEquals(...)} methods instead.\n\t * <p>If necessary, the failure message will be retrieved lazily from the supplied\n\t * {@code messageSupplier}.\n\t */\n\tpublic static void assertNotSame(@Nullable Object unexpected, @Nullable Object actual,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertNotSame.assertNotSame(unexpected, actual, messageSupplier);\n\t}\n\n\t// --- assertAll -----------------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>See Javadoc for {@link #assertAll(String, Stream)} for an explanation of this\n\t * method's exception handling semantics.\n\t *\n\t * @see #assertAll(String, Executable...)\n\t * @see #assertAll(Collection)\n\t * @see #assertAll(String, Collection)\n\t * @see #assertAll(Stream)\n\t * @see #assertAll(String, Stream)\n\t */\n\tpublic static void assertAll(Executable... executables) throws MultipleFailuresError {\n\t\tAssertAll.assertAll(executables);\n\t}\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>See Javadoc for {@link #assertAll(String, Stream)} for an explanation of this\n\t * method's exception handling semantics.\n\t *\n\t * @see #assertAll(Executable...)\n\t * @see #assertAll(Collection)\n\t * @see #assertAll(Stream)\n\t * @see #assertAll(String, Collection)\n\t * @see #assertAll(String, Stream)\n\t */\n\tpublic static void assertAll(@Nullable String heading, Executable... executables) throws MultipleFailuresError {\n\t\tAssertAll.assertAll(heading, executables);\n\t}\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>See Javadoc for {@link #assertAll(String, Stream)} for an explanation of this\n\t * method's exception handling semantics.\n\t *\n\t * @see #assertAll(Executable...)\n\t * @see #assertAll(String, Executable...)\n\t * @see #assertAll(String, Collection)\n\t * @see #assertAll(Stream)\n\t * @see #assertAll(String, Stream)\n\t */\n\tpublic static void assertAll(Collection<Executable> executables) throws MultipleFailuresError {\n\t\tAssertAll.assertAll(executables);\n\t}\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>See Javadoc for {@link #assertAll(String, Stream)} for an explanation of this\n\t * method's exception handling semantics.\n\t *\n\t * @see #assertAll(Executable...)\n\t * @see #assertAll(String, Executable...)\n\t * @see #assertAll(Collection)\n\t * @see #assertAll(Stream)\n\t * @see #assertAll(String, Stream)\n\t */\n\tpublic static void assertAll(@Nullable String heading, Collection<Executable> executables)\n\t\t\tthrows MultipleFailuresError {\n\t\tAssertAll.assertAll(heading, executables);\n\t}\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>See Javadoc for {@link #assertAll(String, Stream)} for an explanation of this\n\t * method's exception handling semantics.\n\t *\n\t * @see #assertAll(Executable...)\n\t * @see #assertAll(String, Executable...)\n\t * @see #assertAll(Collection)\n\t * @see #assertAll(String, Collection)\n\t * @see #assertAll(String, Stream)\n\t */\n\tpublic static void assertAll(Stream<Executable> executables) throws MultipleFailuresError {\n\t\tAssertAll.assertAll(executables);\n\t}\n\n\t/**\n\t * <em>Assert</em> that <em>all</em> supplied {@code executables} do not throw\n\t * exceptions.\n\t *\n\t * <p>If any supplied {@link Executable} throws an exception (i.e., a {@link Throwable}\n\t * or any subclass thereof), all remaining {@code executables} will still be executed,\n\t * and all exceptions will be aggregated and reported in a {@link MultipleFailuresError}.\n\t * In addition, all aggregated exceptions will be added as {@linkplain\n\t * Throwable#addSuppressed(Throwable) suppressed exceptions} to the\n\t * {@code MultipleFailuresError}. However, if one of the {@code executables} throws an\n\t * <em>unrecoverable</em> exception &mdash; for example, an {@link OutOfMemoryError}\n\t * &mdash; execution will halt immediately, and the unrecoverable exception will be\n\t * rethrown <em>as is</em> but <em>masked</em> as an unchecked exception.\n\t *\n\t * <p>The supplied {@code heading} will be included in the message string for the\n\t * {@code MultipleFailuresError}.\n\t *\n\t * @see #assertAll(Executable...)\n\t * @see #assertAll(String, Executable...)\n\t * @see #assertAll(Collection)\n\t * @see #assertAll(String, Collection)\n\t * @see #assertAll(Stream)\n\t */\n\tpublic static void assertAll(@Nullable String heading, Stream<Executable> executables)\n\t\t\tthrows MultipleFailuresError {\n\t\tAssertAll.assertAll(heading, executables);\n\t}\n\n\t// --- assert exceptions ---------------------------------------------------\n\n\t// --- executable ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of exactly the {@code expectedType} and return the exception.\n\t *\n\t * <p>If no exception is thrown, or if an exception of a different type is\n\t * thrown, this method will fail.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable) {\n\t\treturn AssertThrowsExactly.assertThrowsExactly(expectedType, executable);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of exactly the {@code expectedType} and return the exception.\n\t *\n\t * <p>If no exception is thrown, or if an exception of a different type is\n\t * thrown, this method will fail.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * <p>Fails with the supplied failure {@code message}. Note that the supplied\n\t * {@code message} is <strong>not</strong> the expected message of the thrown\n\t * exception. To assert the expected message of the thrown exception, you must\n\t * use a separate, subsequent assertion against the exception returned from\n\t * this method.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable String message) {\n\t\treturn AssertThrowsExactly.assertThrowsExactly(expectedType, executable, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of exactly the {@code expectedType} and return the exception.\n\t *\n\t * <p>If no exception is thrown, or if an exception of a different type is\n\t * thrown, this method will fail.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}. Note that the failure message is\n\t * <strong>not</strong> the expected message of the thrown exception. To\n\t * assert the expected message of the thrown exception, you must use a\n\t * separate, subsequent assertion against the exception returned from this\n\t * method.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static <T extends Throwable> T assertThrowsExactly(Class<T> expectedType, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn AssertThrowsExactly.assertThrowsExactly(expectedType, executable, messageSupplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of the {@code expectedType} and return the exception.\n\t *\n\t * <p>The assertion passes if the thrown exception type is the same as\n\t * {@code expectedType} or a subtype thereof. To check for the exact thrown\n\t * type use {@link #assertThrowsExactly(Class, Executable) assertThrowsExactly}.\n\t * If no exception is thrown, or if an exception of a different type is thrown,\n\t * this method will fail.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * @see #assertThrowsExactly(Class, Executable)\n\t */\n\tpublic static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {\n\t\treturn AssertThrows.assertThrows(expectedType, executable);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of the {@code expectedType} and return the exception.\n\t *\n\t * <p>The assertion passes if the thrown exception type is the same as\n\t * {@code expectedType} or a subtype thereof. To check for the exact thrown\n\t * type use {@link #assertThrowsExactly(Class, Executable, String) assertThrowsExactly}.\n\t * If no exception is thrown, or if an exception of a different type is thrown,\n\t * this method will fail.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * <p>Fails with the supplied failure {@code message}. Note that the supplied\n\t * {@code message} is <strong>not</strong> the expected message of the thrown\n\t * exception. To assert the expected message of the thrown exception, you must\n\t * use a separate, subsequent assertion against the exception returned from\n\t * this method.\n\t *\n\t * @see #assertThrowsExactly(Class, Executable, String)\n\t */\n\tpublic static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,\n\t\t\t@Nullable String message) {\n\t\treturn AssertThrows.assertThrows(expectedType, executable, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} throws\n\t * an exception of the {@code expectedType} and return the exception.\n\t *\n\t * <p>The assertion passes if the thrown exception type is the same as\n\t * {@code expectedType} or a subtype thereof. To check for the exact thrown\n\t * type use {@link #assertThrowsExactly(Class, Executable, Supplier) assertThrowsExactly}.\n\t * If no exception is thrown, or if an exception of a different type is thrown,\n\t * this method will fail.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}. Note that the failure message is\n\t * <strong>not</strong> the expected message of the thrown exception. To\n\t * assert the expected message of the thrown exception, you must use a\n\t * separate, subsequent assertion against the exception returned from this\n\t * method.\n\t *\n\t * <p>If you do not want to perform additional checks on the exception instance,\n\t * ignore the return value.\n\t *\n\t * @see #assertThrowsExactly(Class, Executable, Supplier)\n\t */\n\tpublic static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn AssertThrows.assertThrows(expectedType, executable, messageSupplier);\n\t}\n\n\t// --- executable ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static void assertDoesNotThrow(Executable executable) {\n\t\tAssertDoesNotThrow.assertDoesNotThrow(executable);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static void assertDoesNotThrow(Executable executable, @Nullable String message) {\n\t\tAssertDoesNotThrow.assertDoesNotThrow(executable, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static void assertDoesNotThrow(Executable executable, Supplier<@Nullable String> messageSupplier) {\n\t\tAssertDoesNotThrow.assertDoesNotThrow(executable, messageSupplier);\n\t}\n\n\t// --- supplier ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <p>If the assertion passes, the {@code supplier}'s result will be returned.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier) {\n\t\treturn AssertDoesNotThrow.assertDoesNotThrow(supplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <p>If the assertion passes, the {@code supplier}'s result will be returned.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier,\n\t\t\t@Nullable String message) {\n\t\treturn AssertDoesNotThrow.assertDoesNotThrow(supplier, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier} does\n\t * <em>not</em> throw any kind of {@linkplain Throwable exception}.\n\t *\n\t * <p>If the assertion passes, the {@code supplier}'s result will be returned.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * <h4>Usage Note</h4>\n\t * <p>Although any exception thrown from a test method will cause the test\n\t * to <em>fail</em>, there are certain use cases where it can be beneficial\n\t * to explicitly assert that an exception is not thrown for a given code\n\t * block within a test method.\n\t *\n\t * @since 5.2\n\t */\n\t@API(status = STABLE, since = \"5.2\")\n\tpublic static <T extends @Nullable Object> T assertDoesNotThrow(ThrowingSupplier<T> supplier,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn AssertDoesNotThrow.assertDoesNotThrow(supplier, messageSupplier);\n\t}\n\n\t// --- assertTimeout -------------------------------------------------------\n\n\t// --- executable ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>Note: the {@code executable} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code executable} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * @see #assertTimeout(Duration, Executable, String)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t */\n\tpublic static void assertTimeout(Duration timeout, Executable executable) {\n\t\tAssertTimeout.assertTimeout(timeout, executable);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>Note: the {@code executable} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code executable} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see #assertTimeout(Duration, Executable)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t */\n\tpublic static void assertTimeout(Duration timeout, Executable executable, @Nullable String message) {\n\t\tAssertTimeout.assertTimeout(timeout, executable, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>Note: the {@code executable} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code executable} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @see #assertTimeout(Duration, Executable)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t * @see #assertTimeout(Duration, ThrowingSupplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t */\n\tpublic static void assertTimeout(Duration timeout, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertTimeout.assertTimeout(timeout, executable, messageSupplier);\n\t}\n\n\t// --- supplier ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>Note: the {@code supplier} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code supplier} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * @see #assertTimeout(Duration, Executable)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier) {\n\t\treturn AssertTimeout.assertTimeout(timeout, supplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>Note: the {@code supplier} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code supplier} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see #assertTimeout(Duration, Executable)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\t@Nullable String message) {\n\t\treturn AssertTimeout.assertTimeout(timeout, supplier, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>Note: the {@code supplier} will be executed in the same thread as that\n\t * of the calling code. Consequently, execution of the {@code supplier} will\n\t * not be preemptively aborted if the timeout is exceeded.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @see #assertTimeout(Duration, Executable)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier)\n\t * @see #assertTimeout(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeout(Duration timeout, ThrowingSupplier<T> supplier,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn AssertTimeout.assertTimeout(timeout, supplier, messageSupplier);\n\t}\n\n\t// --- executable - preemptively ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeout(Duration, Executable)\n\t */\n\tpublic static void assertTimeoutPreemptively(Duration timeout, Executable executable) {\n\t\tAssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t */\n\tpublic static void assertTimeoutPreemptively(Duration timeout, Executable executable, @Nullable String message) {\n\t\tAssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code executable}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t */\n\tpublic static void assertTimeoutPreemptively(Duration timeout, Executable executable,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\tAssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, executable, messageSupplier);\n\t}\n\n\t// --- supplier - preemptively ---\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeout(Duration, Executable)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout,\n\t\t\tThrowingSupplier<T> supplier) {\n\t\treturn AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, Supplier)\n\t * @see #assertTimeout(Duration, Executable, String)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout,\n\t\t\tThrowingSupplier<T> supplier, @Nullable String message) {\n\t\treturn AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @see #assertTimeoutPreemptively(Duration, Executable)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, String)\n\t * @see #assertTimeoutPreemptively(Duration, Executable, Supplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier)\n\t * @see #assertTimeoutPreemptively(Duration, ThrowingSupplier, String)\n\t * @see #assertTimeout(Duration, Executable, Supplier)\n\t */\n\tpublic static <T extends @Nullable Object> T assertTimeoutPreemptively(Duration timeout,\n\t\t\tThrowingSupplier<T> supplier, Supplier<@Nullable String> messageSupplier) {\n\t\treturn AssertTimeoutPreemptively.assertTimeoutPreemptively(timeout, supplier, messageSupplier);\n\t}\n\n\t// --- assertInstanceOf ----------------------------------------------------\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code actualValue} is an instance of the\n\t * {@code expectedType}.\n\t *\n\t * <p>Like the {@code instanceof} operator a {@code null} value is not\n\t * considered to be of the {@code expectedType} and does not pass the assertion.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t@Contract(\"_, null -> fail\")\n\tpublic static <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue) {\n\t\treturn AssertInstanceOf.assertInstanceOf(expectedType, actualValue);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code actualValue} is an instance of the\n\t * {@code expectedType}.\n\t *\n\t * <p>Like the {@code instanceof} operator a {@code null} value is not\n\t * considered to be of the {@code expectedType} and does not pass the assertion.\n\t *\n\t * <p>Fails with the supplied failure {@code message}.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t@Contract(\"_, null, _ -> fail\")\n\tpublic static <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue,\n\t\t\t@Nullable String message) {\n\t\treturn AssertInstanceOf.assertInstanceOf(expectedType, actualValue, message);\n\t}\n\n\t/**\n\t * <em>Assert</em> that the supplied {@code actualValue} is an instance of the\n\t * {@code expectedType}.\n\t *\n\t * <p>Like the {@code instanceof} operator a {@code null} value is not\n\t * considered to be of the {@code expectedType} and does not pass the assertion.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t *\n\t * @since 5.8\n\t */\n\t@Contract(\"_, null, _ -> fail\")\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static <T> T assertInstanceOf(Class<T> expectedType, @Nullable Object actualValue,\n\t\t\tSupplier<@Nullable String> messageSupplier) {\n\t\treturn AssertInstanceOf.assertInstanceOf(expectedType, actualValue, messageSupplier);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Assumptions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.function.BooleanSupplier;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.annotation.Contract;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * {@code Assumptions} is a collection of utility methods that support\n * conditional test execution based on <em>assumptions</em>.\n *\n * <p>In direct contrast to failed {@linkplain Assertions assertions},\n * failed assumptions do not result in a test <em>failure</em>; rather,\n * a failed assumption results in a test being <em>aborted</em>. However,\n * failed assertions and other exceptions thrown by tests take precedence over\n * failed assumptions when both are thrown during the execution of a test\n * (for example, by different lifecycle methods), regardless of the order they\n * are thrown in. In such cases, the test will be reported as <em>failed</em>\n * rather than <em>aborted</em>.\n *\n * <p>Assumptions are typically used whenever it does not make sense to\n * continue execution of a given test method &mdash; for example, if the\n * test depends on something that does not exist in the current runtime\n * environment.\n *\n * <p>Although it is technically possible to extend this class, extension is\n * strongly discouraged. The JUnit Team highly recommends that the methods\n * defined in this class be used via <em>static imports</em>.\n *\n * @since 5.0\n * @see TestAbortedException\n * @see Assertions\n */\n@API(status = STABLE, since = \"5.0\")\npublic class Assumptions {\n\n\t/**\n\t * Protected constructor allowing subclassing but not direct instantiation.\n\t *\n\t * @since 5.3\n\t */\n\tprotected Assumptions() {\n\t\t/* no-op */\n\t}\n\n\t// --- assumeTrue ----------------------------------------------------------\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\t@Contract(\"false -> fail\")\n\tpublic static void assumeTrue(boolean assumption) throws TestAbortedException {\n\t\tassumeTrue(assumption, \"assumption is not true\");\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\tpublic static void assumeTrue(BooleanSupplier assumptionSupplier) throws TestAbortedException {\n\t\tassumeTrue(assumptionSupplier.getAsBoolean(), \"assumption is not true\");\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @param message the message to be included in the {@code TestAbortedException}\n\t * if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\tpublic static void assumeTrue(BooleanSupplier assumptionSupplier, @Nullable String message)\n\t\t\tthrows TestAbortedException {\n\t\tassumeTrue(assumptionSupplier.getAsBoolean(), message);\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @param messageSupplier the supplier of the message to be included in\n\t * the {@code TestAbortedException} if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void assumeTrue(boolean assumption, Supplier<@Nullable String> messageSupplier)\n\t\t\tthrows TestAbortedException {\n\t\tif (!assumption) {\n\t\t\tthrowAssumptionFailed(messageSupplier.get());\n\t\t}\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @param message the message to be included in the {@code TestAbortedException}\n\t * if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void assumeTrue(boolean assumption, @Nullable String message) throws TestAbortedException {\n\t\tif (!assumption) {\n\t\t\tthrowAssumptionFailed(message);\n\t\t}\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @param messageSupplier the supplier of the message to be included in\n\t * the {@code TestAbortedException} if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code true}\n\t */\n\tpublic static void assumeTrue(BooleanSupplier assumptionSupplier, Supplier<@Nullable String> messageSupplier)\n\t\t\tthrows TestAbortedException {\n\n\t\tassumeTrue(assumptionSupplier.getAsBoolean(), messageSupplier);\n\t}\n\n\t// --- assumeFalse ---------------------------------------------------------\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\t@Contract(\"true -> fail\")\n\tpublic static void assumeFalse(boolean assumption) throws TestAbortedException {\n\t\tassumeFalse(assumption, \"assumption is not false\");\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\tpublic static void assumeFalse(BooleanSupplier assumptionSupplier) throws TestAbortedException {\n\t\tassumeFalse(assumptionSupplier.getAsBoolean(), \"assumption is not false\");\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @param message the message to be included in the {@code TestAbortedException}\n\t * if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\tpublic static void assumeFalse(BooleanSupplier assumptionSupplier, @Nullable String message)\n\t\t\tthrows TestAbortedException {\n\t\tassumeFalse(assumptionSupplier.getAsBoolean(), message);\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @param messageSupplier the supplier of the message to be included in\n\t * the {@code TestAbortedException} if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\t@Contract(\"true, _ -> fail\")\n\tpublic static void assumeFalse(boolean assumption, Supplier<@Nullable String> messageSupplier)\n\t\t\tthrows TestAbortedException {\n\t\tif (assumption) {\n\t\t\tthrowAssumptionFailed(messageSupplier.get());\n\t\t}\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumption the assumption to validate\n\t * @param message the message to be included in the {@code TestAbortedException}\n\t * if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\t@Contract(\"true, _ -> fail\")\n\tpublic static void assumeFalse(boolean assumption, @Nullable String message) throws TestAbortedException {\n\t\tif (assumption) {\n\t\t\tthrowAssumptionFailed(message);\n\t\t}\n\t}\n\n\t/**\n\t * Validate the given assumption.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @param messageSupplier the supplier of the message to be included in\n\t * the {@code TestAbortedException} if the assumption is invalid\n\t * @throws TestAbortedException if the assumption is not {@code false}\n\t */\n\tpublic static void assumeFalse(BooleanSupplier assumptionSupplier, Supplier<@Nullable String> messageSupplier)\n\t\t\tthrows TestAbortedException {\n\n\t\tassumeFalse(assumptionSupplier.getAsBoolean(), messageSupplier);\n\t}\n\n\t// --- assumingThat --------------------------------------------------------\n\n\t/**\n\t * Execute the supplied {@link Executable}, but only if the supplied\n\t * assumption is valid.\n\t *\n\t * <p>Unlike the other assumption methods, this method will not abort the test.\n\t * If the assumption is invalid, this method does nothing. If the assumption is\n\t * valid and the {@code executable} throws an exception, it will be treated like\n\t * a regular test <em>failure</em>. That exception will be rethrown <em>as is</em>\n\t * but {@link ExceptionUtils#throwAsUncheckedException masked} as an unchecked\n\t * exception.\n\t *\n\t * @param assumptionSupplier the supplier of the assumption to validate\n\t * @param executable the block of code to execute if the assumption is valid\n\t * @see #assumingThat(boolean, Executable)\n\t */\n\tpublic static void assumingThat(BooleanSupplier assumptionSupplier, Executable executable) {\n\t\tassumingThat(assumptionSupplier.getAsBoolean(), executable);\n\t}\n\n\t/**\n\t * Execute the supplied {@link Executable}, but only if the supplied\n\t * assumption is valid.\n\t *\n\t * <p>Unlike the other assumption methods, this method will not abort the test.\n\t * If the assumption is invalid, this method does nothing. If the assumption is\n\t * valid and the {@code executable} throws an exception, it will be treated like\n\t * a regular test <em>failure</em>. That exception will be rethrown <em>as is</em>\n\t * but {@link ExceptionUtils#throwAsUncheckedException masked} as an unchecked\n\t * exception.\n\t *\n\t * @param assumption the assumption to validate\n\t * @param executable the block of code to execute if the assumption is valid\n\t * @see #assumingThat(BooleanSupplier, Executable)\n\t */\n\tpublic static void assumingThat(boolean assumption, Executable executable) {\n\t\tif (assumption) {\n\t\t\ttry {\n\t\t\t\texecutable.execute();\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- abort ---------------------------------------------------------------\n\n\t/**\n\t * <em>Abort</em> the test <em>without</em> a message.\n\t *\n\t * <p>Although aborting with an explicit message is recommended, this may be\n\t * useful when maintaining legacy code.\n\t *\n\t * <p>See Javadoc for {@link #abort(String)} for an explanation of this\n\t * method's generic return type {@code V}.\n\t *\n\t * @throws TestAbortedException always\n\t * @since 5.9\n\t */\n\t@Contract(\" -> fail\")\n\t@API(status = STABLE, since = \"5.9\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V abort() {\n\t\tthrow new TestAbortedException();\n\t}\n\n\t/**\n\t * <em>Abort</em> the test with the given {@code message}.\n\t *\n\t * <p>The generic return type {@code V} allows this method to be used\n\t * directly as a single-statement lambda expression, thereby avoiding the\n\t * need to implement a code block with an explicit return value. Since this\n\t * method throws a {@link TestAbortedException} before its return statement,\n\t * this method never actually returns a value to its caller. The following\n\t * example demonstrates how this may be used in practice.\n\t *\n\t * <pre>{@code\n\t * Stream.of().map(entry -> abort(\"assumption not met\"));\n\t * }</pre>\n\t *\n\t * @param message the message to be included in the {@code TestAbortedException}\n\t * @throws TestAbortedException always\n\t * @since 5.9\n\t */\n\t@Contract(\"_ -> fail\")\n\t@API(status = STABLE, since = \"5.9\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V abort(String message) {\n\t\tthrow new TestAbortedException(message);\n\t}\n\n\t/**\n\t * <em>Abort</em> the test with the supplied message.\n\t *\n\t * <p>See Javadoc for {@link #abort(String)} for an explanation of this\n\t * method's generic return type {@code V}.\n\t *\n\t * @param messageSupplier the supplier of the message to be included in the\n\t * {@code TestAbortedException}\n\t * @throws TestAbortedException always\n\t * @since 5.9\n\t */\n\t@Contract(\"_ -> fail\")\n\t@API(status = STABLE, since = \"5.9\")\n\t@SuppressWarnings(\"TypeParameterUnusedInFormals\")\n\tpublic static <V> V abort(Supplier<String> messageSupplier) {\n\t\tthrow new TestAbortedException(messageSupplier.get());\n\t}\n\n\t@Contract(\"_ -> fail\")\n\tprivate static void throwAssumptionFailed(@Nullable String message) {\n\t\tthrow new TestAbortedException(\n\t\t\tStringUtils.isNotBlank(message) ? \"Assumption failed: \" + message : \"Assumption failed\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/AutoClose.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @AutoClose} is used to indicate that an annotated field will be\n * automatically closed after test execution.\n *\n * <p>{@code @AutoClose} fields may be either {@code static} or non-static. If\n * the value of an {@code @AutoClose} field is {@code null} when it is evaluated\n * the field will be ignored, but a warning message will be logged to inform you.\n *\n * <p>By default, {@code @AutoClose} expects the value of the annotated field to\n * implement a {@code close()} method that will be invoked to close the resource.\n * However, developers can customize the name of the {@code close} method via the\n * {@link #value} attribute. For example, {@code @AutoClose(\"shutdown\")} instructs\n * JUnit to look for a {@code shutdown()} method to close the resource.\n *\n * <p>{@code @AutoClose} may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of\n * {@code @AutoClose}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @AutoClose} fields are inherited from superclasses. Furthermore,\n * {@code @AutoClose} fields from subclasses will be closed before\n * {@code @AutoClose} fields in superclasses.\n *\n * <h2>Evaluation Order</h2>\n *\n * <p>When multiple {@code @AutoClose} fields exist within a given test class,\n * the order in which the resources are closed depends on an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite close resources in the same order, thereby allowing for\n * repeatable builds.\n *\n * <h2>Scope and Lifecycle</h2>\n *\n * <p>The extension that closes {@code @AutoClose} fields implements the\n * {@link org.junit.jupiter.api.extension.AfterAllCallback AfterAllCallback} and\n * {@link org.junit.jupiter.api.extension.TestInstancePreDestroyCallback\n * TestInstancePreDestroyCallback} extension APIs. Consequently, a {@code static}\n * {@code @AutoClose} field will be closed after all tests in the current test\n * class have completed, effectively after {@code @AfterAll} methods have executed\n * for the test class. A non-static {@code @AutoClose} field will be closed before\n * the current test class instance is destroyed. Specifically, if the test class\n * is configured with\n * {@link TestInstance.Lifecycle#PER_METHOD @TestInstance(Lifecycle.PER_METHOD)}\n * semantics, a non-static {@code @AutoClose} field will be closed after the\n * execution of each test method, test factory method, or test template method.\n * However, if the test class is configured with\n * {@link TestInstance.Lifecycle#PER_CLASS @TestInstance(Lifecycle.PER_CLASS)}\n * semantics, a non-static {@code @AutoClose} field will not be closed until the\n * current test class instance is no longer needed, which means after\n * {@code @AfterAll} methods and after all {@code static} {@code @AutoClose} fields\n * have been closed.\n *\n * @since 5.11\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic @interface AutoClose {\n\n\t/**\n\t * Specify the name of the method to invoke to close the resource.\n\t *\n\t * <p>The default value is {@code \"close\"} which works with any type that\n\t * implements {@link AutoCloseable} or has a {@code close()} method.\n\t *\n\t * @return the name of the method to invoke to close the resource\n\t */\n\tString value() default \"close\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeAll.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @BeforeAll} is used to signal that the annotated method should be\n * executed <em>before</em> <strong>all</strong> tests in the current test class.\n *\n * <p>In contrast to {@link BeforeEach @BeforeEach} methods, {@code @BeforeAll}\n * methods are only executed once per execution of a given test class. If the\n * test class is annotated with {@link ClassTemplate @ClassTemplate}, the\n * {@code @BeforeAll} methods are executed once before the first invocation of\n * the class template. If a {@link Nested @Nested} test class is declared in a\n * {@link ClassTemplate @ClassTemplate}, its {@code @BeforeAll} methods are\n * called once per execution of the nested test class, namely, once per\n * invocation of the outer class template.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @BeforeAll} methods must have a {@code void} return type and must\n * be {@code static} unless the test class is annotated with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}. In addition,\n * {@code @BeforeAll} methods may optionally declare parameters to be resolved by\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>Using {@code private} visibility for {@code @BeforeAll} methods is strongly\n * discouraged and will be disallowed in a future release.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @BeforeAll} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @BeforeAll} methods from superclasses will be\n * executed before {@code @BeforeAll} methods in subclasses.\n *\n * <p>Similarly, {@code @BeforeAll} methods declared in an interface are inherited\n * as long as they are not overridden, and {@code @BeforeAll} methods from an\n * interface will be executed before {@code @BeforeAll} methods in the class that\n * implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @BeforeAll} methods that are declared within a single test class or\n * test interface. While it may at times appear that these methods are invoked\n * in alphabetical order, they are in fact sorted using an algorithm that is\n * deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @BeforeAll} methods are in no way linked to\n * {@code @AfterAll} methods. Consequently, there are no guarantees with regard\n * to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeAll} methods {@code createA()} and {@code createB()} as well as\n * two {@code @AfterAll} methods {@code destroyA()} and {@code destroyB()}, the\n * order in which the {@code @BeforeAll} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterAll} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeAll} method and at most one\n * {@code @AfterAll} method per test class or test interface unless there are no\n * dependencies between the {@code @BeforeAll} methods or between the\n * {@code @AfterAll} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @BeforeAll} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @BeforeAll}.\n *\n * @since 5.0\n * @see AfterAll\n * @see BeforeEach\n * @see AfterEach\n * @see Test\n * @see TestFactory\n * @see TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface BeforeAll {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/BeforeEach.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @BeforeEach} is used to signal that the annotated method should be\n * executed <em>before</em> <strong>each</strong> {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory},\n * and {@code @TestTemplate} method in the current test class.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @BeforeEach} methods must have a {@code void} return type and must\n * not be {@code static}. In addition, {@code @BeforeEach} methods may optionally\n * declare parameters to be resolved by\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>Using {@code private} visibility for {@code @BeforeEach} methods is strongly\n * discouraged and will be disallowed in a future release.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @BeforeEach} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @BeforeEach} methods from superclasses will be\n * executed before {@code @BeforeEach} methods in subclasses.\n *\n * <p>Similarly, {@code @BeforeEach} methods declared as <em>interface default\n * methods</em> are inherited as long as they are not overridden, and\n * {@code @BeforeEach} default methods will be executed before {@code @BeforeEach}\n * methods in the class that implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @BeforeEach} methods that are declared within a single test class or\n * test interface. While it may at times appear that these methods are invoked\n * in alphabetical order, they are in fact sorted using an algorithm that is\n * deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @BeforeEach} methods are in no way linked to\n * {@code @AfterEach} methods. Consequently, there are no guarantees with regard\n * to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeEach} methods {@code createA()} and {@code createB()} as well\n * as two {@code @AfterEach} methods {@code destroyA()} and {@code destroyB()},\n * the order in which the {@code @BeforeEach} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterEach} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeEach} method and at most one\n * {@code @AfterEach} method per test class or test interface unless there are\n * no dependencies between the {@code @BeforeEach} methods or between the\n * {@code @AfterEach} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @BeforeEach} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @BeforeEach}.\n *\n * @since 5.0\n * @see AfterEach\n * @see BeforeAll\n * @see AfterAll\n * @see Test\n * @see RepeatedTest\n * @see TestFactory\n * @see TestTemplate\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface BeforeEach {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Annotation;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ClassDescriptor} encapsulates functionality for a given {@link Class}.\n *\n * @since 5.8\n * @see ClassOrdererContext\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface ClassDescriptor {\n\n\t/**\n\t * Get the class for this descriptor.\n\t *\n\t * @return the class; never {@code null}\n\t */\n\tClass<?> getTestClass();\n\n\t/**\n\t * Get the display name for this descriptor's {@link #getTestClass() class}.\n\t *\n\t * @return the display name for this descriptor's class; never {@code null}\n\t * or blank\n\t */\n\tString getDisplayName();\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Class} for\n\t * this descriptor.\n\t *\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @see #findAnnotation(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\tboolean isAnnotated(Class<? extends Annotation> annotationType);\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Class} for\n\t * this descriptor.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\t<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of\n\t * {@code annotationType} that are either <em>present</em> or\n\t * <em>meta-present</em> on the {@link Class} for this descriptor.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the repeatable annotation type to search for; never\n\t * {@code null}\n\t * @return the list of all such annotations found; neither {@code null} nor\n\t * mutable, but potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findAnnotation(Class)\n\t * @see java.lang.annotation.Repeatable\n\t */\n\t<A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrderer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Comparator.comparingInt;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Collections;\nimport java.util.Comparator;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\n\n/**\n * {@code ClassOrderer} defines the API for ordering top-level test classes and\n * {@link Nested @Nested} test classes.\n *\n * <p>In this context, the term \"test class\" refers to any class containing methods\n * annotated with {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest},\n * {@code @TestFactory}, or {@code @TestTemplate}.\n *\n * <p>Top-level test classes will be ordered relative to each other; whereas,\n * {@code @Nested} test classes will be ordered relative to other {@code @Nested}\n * test classes sharing the same {@linkplain Class#getEnclosingClass() enclosing\n * class}.\n *\n * <p>A {@link ClassOrderer} can be configured <em>globally</em> for the entire\n * test suite via the {@value #DEFAULT_ORDER_PROPERTY_NAME} configuration\n * parameter (see the User Guide for details) or <em>locally</em> for\n * {@link Nested @Nested} test classes via the {@link TestClassOrder @TestClassOrder}\n * annotation.\n *\n * <h2>Built-in Implementations</h2>\n *\n * <p>JUnit Jupiter provides the following built-in {@code ClassOrderer}\n * implementations.\n *\n * <ul>\n * <li>{@link ClassOrderer.ClassName}</li>\n * <li>{@link ClassOrderer.Default}</li>\n * <li>{@link ClassOrderer.DisplayName}</li>\n * <li>{@link ClassOrderer.OrderAnnotation}</li>\n * <li>{@link ClassOrderer.Random}</li>\n * </ul>\n *\n * @since 5.8\n * @see TestClassOrder\n * @see ClassOrdererContext\n * @see #orderClasses(ClassOrdererContext)\n * @see MethodOrderer\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface ClassOrderer {\n\n\t/**\n\t * Property name used to set the default class orderer class name: {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include fully qualified class names for types that\n\t * implement {@link org.junit.jupiter.api.ClassOrderer}.\n\t *\n\t * <p>If not specified, test classes are not ordered unless test classes are\n\t * annotated with {@link TestClassOrder @TestClassOrder}.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_ORDER_PROPERTY_NAME = \"junit.jupiter.testclass.order.default\";\n\n\t/**\n\t * Order the classes encapsulated in the supplied {@link ClassOrdererContext}.\n\t *\n\t * <p>The classes to order or sort are made indirectly available via\n\t * {@link ClassOrdererContext#getClassDescriptors()}. Since this method\n\t * has a {@code void} return type, the list of class descriptors must be\n\t * modified directly.\n\t *\n\t * <p>For example, a simplified implementation of the {@link ClassOrderer.Random}\n\t * {@code ClassOrderer} might look like the following.\n\t *\n\t * <pre class=\"code\">\n\t * public void orderClasses(ClassOrdererContext context) {\n\t *     Collections.shuffle(context.getClassDescriptors());\n\t * }</pre>\n\t *\n\t * @param context the {@code ClassOrdererContext} containing the\n\t * {@linkplain ClassDescriptor class descriptors} to order; never {@code null}\n\t */\n\tvoid orderClasses(ClassOrdererContext context);\n\n\t/**\n\t * {@code ClassOrderer} that allows to explicitly specify that the default\n\t * ordering should be applied.\n\t *\n\t * <p>If the {@value #DEFAULT_ORDER_PROPERTY_NAME} is set, specifying this\n\t * {@code ClassOrderer} has the same effect as referencing the configured\n\t * class directly. Otherwise, it has the same effect as not specifying any\n\t * {@code ClassOrderer}.\n\t *\n\t * <p>This class can be used to reset the {@code ClassOrderer} for a\n\t * {@link Nested @Nested} class and its {@code @Nested} inner classes,\n\t * recursively, when a {@code ClassOrderer} is configured using\n\t * {@link TestClassOrder @TestClassOrder} on an enclosing class.\n\t *\n\t * @since 6.0\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tfinal class Default implements ClassOrderer {\n\n\t\tprivate Default() {\n\t\t\tthrow new JUnitException(\"This class must not be instantiated\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void orderClasses(ClassOrdererContext context) {\n\t\t\t// never called\n\t\t}\n\t}\n\n\t/**\n\t * {@code ClassOrderer} that sorts classes alphanumerically based on their\n\t * fully qualified names using {@link String#compareTo(String)}.\n\t */\n\tclass ClassName implements ClassOrderer {\n\n\t\tpublic ClassName() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the classes encapsulated in the supplied\n\t\t * {@link ClassOrdererContext} alphanumerically based on their fully\n\t\t * qualified names.\n\t\t */\n\t\t@Override\n\t\tpublic void orderClasses(ClassOrdererContext context) {\n\t\t\tcontext.getClassDescriptors().sort(comparator);\n\t\t}\n\n\t\tprivate static final Comparator<ClassDescriptor> comparator = Comparator.comparing(\n\t\t\tdescriptor -> descriptor.getTestClass().getName());\n\t}\n\n\t/**\n\t * {@code ClassOrderer} that sorts classes alphanumerically based on their\n\t * display names using {@link String#compareTo(String)}\n\t */\n\tclass DisplayName implements ClassOrderer {\n\n\t\tpublic DisplayName() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the classes encapsulated in the supplied\n\t\t * {@link ClassOrdererContext} alphanumerically based on their display\n\t\t * names.\n\t\t */\n\t\t@Override\n\t\tpublic void orderClasses(ClassOrdererContext context) {\n\t\t\tcontext.getClassDescriptors().sort(comparator);\n\t\t}\n\n\t\tprivate static final Comparator<ClassDescriptor> comparator = Comparator.comparing(\n\t\t\tClassDescriptor::getDisplayName);\n\t}\n\n\t/**\n\t * {@code ClassOrderer} that sorts classes based on the {@link Order @Order}\n\t * annotation.\n\t *\n\t * <p>Any classes that are assigned the same order value will be sorted\n\t * arbitrarily adjacent to each other.\n\t *\n\t * <p>Any classes not annotated with {@code @Order} will be assigned the\n\t * {@linkplain Order#DEFAULT default order} value which will effectively cause them\n\t * to appear at the end of the sorted list, unless certain classes are assigned\n\t * an explicit order value greater than the default order value. Any classes\n\t * assigned an explicit order value greater than the default order value will\n\t * appear after non-annotated classes in the sorted list.\n\t */\n\tclass OrderAnnotation implements ClassOrderer {\n\n\t\tpublic OrderAnnotation() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the classes encapsulated in the supplied\n\t\t * {@link ClassOrdererContext} based on the {@link Order @Order}\n\t\t * annotation.\n\t\t */\n\t\t@Override\n\t\tpublic void orderClasses(ClassOrdererContext context) {\n\t\t\tcontext.getClassDescriptors().sort(comparingInt(OrderAnnotation::getOrder));\n\t\t}\n\n\t\tprivate static int getOrder(ClassDescriptor descriptor) {\n\t\t\treturn descriptor.findAnnotation(Order.class).map(Order::value).orElse(Order.DEFAULT);\n\t\t}\n\t}\n\n\t/**\n\t * {@code ClassOrderer} that orders classes pseudo-randomly.\n\t *\n\t * <h2>Custom Seed</h2>\n\t *\n\t * <p>By default, the random <em>seed</em> used for ordering classes is the\n\t * value returned by {@link System#nanoTime()} during static class\n\t * initialization. In order to support repeatable builds, the value of the\n\t * default random seed is logged at {@code CONFIG} level. In addition, a\n\t * custom seed (potentially the default seed from the previous test plan\n\t * execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME}\n\t * <em>configuration parameter</em> which can be supplied via the {@code Launcher}\n\t * API, build tools (e.g., Gradle and Maven), a JVM system property, or the JUnit\n\t * Platform configuration file (i.e., a file named {@code junit-platform.properties}\n\t * in the root of the class path). Consult the User Guide for further information.\n\t *\n\t * @see Random#RANDOM_SEED_PROPERTY_NAME\n\t * @see java.util.Random\n\t */\n\tclass Random implements ClassOrderer {\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(Random.class);\n\n\t\tstatic {\n\t\t\tlogger.config(() -> \"ClassOrderer.Random default seed: \" + RandomOrdererUtils.DEFAULT_SEED);\n\t\t}\n\n\t\t/**\n\t\t * Property name used to set the random seed used by this\n\t\t * {@code ClassOrderer}: {@value}\n\t\t *\n\t\t * <p>The same property is used by {@link MethodOrderer.Random} for\n\t\t * consistency between the two random orderers.\n\t\t *\n\t\t * <h4>Supported Values</h4>\n\t\t *\n\t\t * <p>Supported values include any string that can be converted to a\n\t\t * {@link Long} via {@link Long#valueOf(String)}.\n\t\t *\n\t\t * <p>If not specified or if the specified value cannot be converted to\n\t\t * a {@link Long}, the default random seed will be used (see the\n\t\t * {@linkplain Random class-level Javadoc} for details).\n\t\t *\n\t\t * @see MethodOrderer.Random\n\t\t */\n\t\tpublic static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME;\n\n\t\tpublic Random() {\n\t\t}\n\n\t\t/**\n\t\t * Order the classes encapsulated in the supplied\n\t\t * {@link ClassOrdererContext} pseudo-randomly.\n\t\t */\n\t\t@Override\n\t\tpublic void orderClasses(ClassOrdererContext context) {\n\t\t\tCollections.shuffle(context.getClassDescriptors(),\n\t\t\t\tnew java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger)));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassOrdererContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ClassOrdererContext} encapsulates the <em>context</em> in which\n * a {@link ClassOrderer} will be invoked.\n *\n * @since 5.8\n * @see ClassOrderer\n * @see ClassDescriptor\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface ClassOrdererContext {\n\n\t/**\n\t * Get the list of {@linkplain ClassDescriptor class descriptors} to\n\t * order.\n\t *\n\t * @return the list of class descriptors; never {@code null}\n\t */\n\tList<? extends ClassDescriptor> getClassDescriptors();\n\n\t/**\n\t * Get the configuration parameter stored under the specified {@code key}.\n\t *\n\t * <p>If no such key is present in the {@code ConfigurationParameters} for\n\t * the JUnit Platform, an attempt will be made to look up the value as a\n\t * JVM system property. If no such system property exists, an attempt will\n\t * be made to look up the value in the JUnit Platform properties file.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @see System#getProperty(String)\n\t * @see org.junit.platform.engine.ConfigurationParameters\n\t */\n\tOptional<String> getConfigurationParameter(String key);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/ClassTemplate.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Testable;\n\n/**\n * {@code @ClassTemplate} is used to signal that the annotated class is a\n * <em>class template</em>.\n *\n * <p>In contrast to regular test classes, a class template is not directly\n * a test class but rather a template for a set of test cases. As such, it is\n * designed to be invoked multiple times depending on the number of {@linkplain\n * org.junit.jupiter.api.extension.ClassTemplateInvocationContext invocation contexts}\n * returned by the registered {@linkplain\n * org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider providers}.\n * Must be used together with at least one provider. Otherwise, execution will fail.\n *\n * <p>Each invocation of a class template behaves like the execution of a regular\n * test class with full support for the same lifecycle callbacks and extensions.\n *\n * <p>{@code @ClassTemplate} may be combined with {@link Nested @Nested}, and a\n * class template may contain regular nested test classes or nested class templates.\n *\n * <p>{@code @ClassTemplate} may also be used as a meta-annotation in order\n * to create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @ClassTemplate}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.13\n * @see TestTemplate @TestTemplate\n * @see org.junit.jupiter.api.extension.ClassTemplateInvocationContext ClassTemplateInvocationContext\n * @see org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider ClassTemplateInvocationContextProvider\n * @see org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback BeforeClassTemplateInvocationCallback\n * @see org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback AfterClassTemplateInvocationCallback\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = EXPERIMENTAL, since = \"6.0\")\n@Testable\npublic @interface ClassTemplate {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Constants.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.PreInterruptCallback;\nimport org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.parallel.Execution;\n\n/**\n * Collection of configuration constants for the Jupiter test engine.\n *\n * @since 6.1\n * @see <a href=\"https://docs.junit.org/current/running-tests/configuration-parameters.html\">User Guide</a>\n * section about configuration parameters\n */\n@API(status = STABLE, since = \"6.1\")\npublic final class Constants {\n\n\t/**\n\t * Property name used to include patterns for auto-detecting extensions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * extensions will be included. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each extension.\n\t * Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: includes all extensions.\n\t * <li>{@code org.junit.*}: includes every extension under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyExtension}: includes every extension whose simple class name is\n\t * exactly {@code MyExtension}.\n\t * <li>{@code *System*}: includes every extension whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: includes every extension whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyExtension, org.example.TheirExtension}: includes\n\t * extensions whose FQCN is exactly {@code org.example.MyExtension} or\n\t * {@code org.example.TheirExtension}.\n\t * </ul>\n\t *\n\t * <p>Note: A class that matches both an inclusion and exclusion pattern will be excluded.\n\t */\n\tpublic static final String EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME = \"junit.jupiter.extensions.autodetection.include\";\n\n\t/**\n\t * Property name used to exclude patterns for auto-detecting extensions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * extensions will be excluded. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each extension.\n\t * Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: excludes all extensions.\n\t * <li>{@code org.junit.*}: excludes every extension under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyExtension}: excludes every extension whose simple class name is\n\t * exactly {@code MyExtension}.\n\t * <li>{@code *System*}: excludes every extension whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: excludes every extension whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyExtension, org.example.TheirExtension}: excludes\n\t * extensions whose FQCN is exactly {@code org.example.MyExtension} or\n\t * {@code org.example.TheirExtension}.\n\t * </ul>\n\t *\n\t * <p>Note: A class that matches both an inclusion and exclusion pattern will be excluded.\n\t */\n\tpublic static final String EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME = \"junit.jupiter.extensions.autodetection.exclude\";\n\n\t/**\n\t * Property name used to enable auto-detection and registration of extensions via\n\t * Java's {@link java.util.ServiceLoader} mechanism: {@value}\n\t *\n\t * <p>The default behavior is not to perform auto-detection.\n\t */\n\tpublic static final String EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME = \"junit.jupiter.extensions.autodetection.enabled\";\n\n\t/**\n\t * Property name used to enable auto-closing of {@link AutoCloseable} instances: {@value}\n\t *\n\t * <p>By default, auto-closing is enabled.\n\t *\n\t */\n\tpublic static final String CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME = \"junit.jupiter.extensions.store.close.autocloseable.enabled\";\n\n\t/**\n\t * Property name used to provide patterns for deactivating conditions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * conditions will be deactivated. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each registered\n\t * condition. Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: deactivates all conditions.\n\t * <li>{@code org.junit.*}: deactivates every condition under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyCondition}: deactivates every condition whose simple class name is\n\t * exactly {@code MyCondition}.\n\t * <li>{@code *System*}: deactivates every condition whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: deactivates every condition whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyCondition, org.example.TheirCondition}: deactivates\n\t * conditions whose FQCN is exactly {@code org.example.MyCondition} or\n\t * {@code org.example.TheirCondition}.\n\t * </ul>\n\t *\n\t * @see #DEACTIVATE_ALL_CONDITIONS_PATTERN\n\t * @see org.junit.jupiter.api.extension.ExecutionCondition\n\t */\n\tpublic static final String DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME = \"junit.jupiter.conditions.deactivate\";\n\n\t/**\n\t * Wildcard pattern which signals that all conditions should be deactivated: {@value}\n\t *\n\t * @see #DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME\n\t * @see org.junit.jupiter.api.extension.ExecutionCondition\n\t */\n\tpublic static final String DEACTIVATE_ALL_CONDITIONS_PATTERN = \"*\";\n\n\t/**\n\t * Property name used to set the default display name generator class name: {@value}\n\t *\n\t * @see DisplayNameGenerator#DEFAULT_GENERATOR_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME = DisplayNameGenerator.DEFAULT_GENERATOR_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable dumping the stack of all\n\t * {@linkplain Thread threads} to {@code System.out} when a timeout has occurred: {@value}\n\t *\n\t * <p>This behavior is disabled by default.\n\t */\n\tpublic static final String EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME = PreInterruptCallback.THREAD_DUMP_ENABLED_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default test instance lifecycle mode: {@value}\n\t *\n\t * @see TestInstance.Lifecycle#DEFAULT_LIFECYCLE_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME = TestInstance.Lifecycle.DEFAULT_LIFECYCLE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable parallel test execution: {@value}\n\t *\n\t * <p>By default, tests are executed sequentially in a single thread.\n\t *\n\t */\n\tpublic static final String PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME = \"junit.jupiter.execution.parallel.enabled\";\n\n\t/**\n\t * Property name used to set the default test execution mode: {@value}\n\t *\n\t * @see Execution#DEFAULT_EXECUTION_MODE_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_EXECUTION_MODE_PROPERTY_NAME = Execution.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default test execution mode for top-level\n\t * classes: {@value}\n\t *\n\t * @see Execution#DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME = Execution.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Internal prefix for all configuration parameters concerning parallel test\n\t * execution.\n\t */\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_PREFIX = \"junit.jupiter.execution.parallel.config.\";\n\n\t/**\n\t * Property name used to determine the desired parallel executor service\n\t * type: {@value}\n\t *\n\t * <p>Value must be {@code FORK_JOIN_POOL} or {@code WORKER_THREAD_POOL},\n\t * ignoring case.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX\n\t\t\t+ \"executor-service\";\n\n\t/**\n\t * Property name used to select the parallel execution configuration\n\t * strategy: {@value}\n\t *\n\t * <p>Potential values: {@code dynamic} (default), {@code fixed}, or\n\t * {@code custom}.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX + \"strategy\";\n\n\t/**\n\t * Property name used to set the desired parallelism for the {@code fixed}\n\t * configuration strategy: {@value}\n\t *\n\t * <p>No default value; must be a positive integer.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX\n\t\t\t+ \"fixed.parallelism\";\n\n\t/**\n\t * Property name used to configure the maximum pool size of the underlying\n\t * fork-join pool for the {@code fixed} configuration strategy: {@value}\n\t *\n\t * <p>Value must be an integer and greater than or equal to\n\t * {@value #PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME}; defaults to\n\t * {@code 256 + fixed.parallelism}.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX\n\t\t\t+ \"fixed.max-pool-size\";\n\n\t/**\n\t * Property name used to disable saturation of the underlying fork-join pool\n\t * for the {@code fixed} configuration strategy: {@value}\n\t *\n\t * <p>When set to {@code false} the underlying fork-join pool will reject\n\t * additional tasks if all available workers are busy and the maximum\n\t * pool-size would be exceeded.\n\t *\n\t * <p>Value must either {@code true} or {@code false}; defaults to {@code true}.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX + \"fixed.saturate\";\n\n\t/**\n\t * Property name used to set the factor to be multiplied with the number of\n\t * available processors/cores to determine the desired parallelism for the\n\t * {@code dynamic} configuration strategy: {@value}\n\t *\n\t * <p>Value must be a positive decimal number; defaults to {@code 1}.\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX + \"dynamic.factor\";\n\n\t/**\n\t * Property name used to specify the fully qualified class name of the\n\t * {@code custom} parallel execution configuration strategy to be used:\n\t * {@value}\n\t *\n\t */\n\tpublic static final String PARALLEL_CONFIG_CUSTOM_CLASS_PROPERTY_NAME = PARALLEL_CONFIG_PREFIX + \"custom.class\";\n\n\t/**\n\t * Property name used to set the default timeout for all testable and\n\t * lifecycle methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all testable methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link Test @Test} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestTemplate @TestTemplate} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestFactory @TestFactory} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all lifecycle methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeAll @BeforeAll} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeEach @BeforeEach} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterEach @AfterEach} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterAll @AfterAll} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME = Timeout.DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to configure whether timeouts are applied to tests: {@value}\n\t *\n\t * @see Timeout#TIMEOUT_MODE_PROPERTY_NAME\n\t */\n\tpublic static final String TIMEOUT_MODE_PROPERTY_NAME = Timeout.TIMEOUT_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default method orderer class name: {@value}\n\t *\n\t * @see MethodOrderer#DEFAULT_ORDER_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME = MethodOrderer.DEFAULT_ORDER_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default class orderer class name: {@value}\n\t *\n\t * @see ClassOrderer#DEFAULT_ORDER_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME = ClassOrderer.DEFAULT_ORDER_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout thread mode: {@value}\n\t *\n\t * @see Timeout\n\t * @see Timeout.ThreadMode\n\t */\n\tpublic static final String DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME = Timeout.DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default factory for temporary directories\n\t * created via the {@link TempDir @TempDir} annotation: {@value}\n\t *\n\t * @see TempDir#DEFAULT_FACTORY_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME = TempDir.DEFAULT_FACTORY_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to configure the default {@link CleanupMode} for\n\t * temporary directories created via the {@link TempDir @TempDir}\n\t * annotation: {@value}\n\t *\n\t * @see TempDir#DEFAULT_CLEANUP_MODE_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME = TempDir.DEFAULT_CLEANUP_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default deletion strategy class name for\n\t * temporary directories created via the {@link TempDir @TempDir}\n\t * annotation: {@value}\n\t *\n\t * @see TempDir#DEFAULT_DELETION_STRATEGY_PROPERTY_NAME\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static final String DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME = TempDir.DEFAULT_DELETION_STRATEGY_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default extension context scope for\n\t * extensions that participate in test instantiation: {@value}\n\t *\n\t * @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension\n\t */\n\tpublic static final String DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME = ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME;\n\n\tprivate Constants() {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Disabled.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Disabled} is used to signal that the annotated test class or\n * test method is currently <em>disabled</em> and should not be executed.\n *\n * <p>{@code @Disabled} may optionally be declared with a {@linkplain #value\n * reason} to document why the annotated test class or test method is disabled.\n *\n * <p>When applied at the class level, all test methods within that class\n * are automatically disabled as well.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * @since 5.0\n * @see #value\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.extension.ExecutionCondition\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface Disabled {\n\n\t/**\n\t * The reason this annotated test class or test method is disabled.\n\t */\n\tString value() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayName.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @DisplayName} is used to declare a {@linkplain #value custom display\n * name} for the annotated test class or test method.\n *\n * <p>Display names are typically used for test reporting in IDEs and build\n * tools and may contain spaces, special characters, and even emoji.\n *\n * @since 5.0\n * @see Test\n * @see Tag\n * @see TestInfo\n * @see DisplayNameGeneration\n * @see DisplayNameGenerator\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface DisplayName {\n\n\t/**\n\t * Custom display name for the annotated class or method.\n\t *\n\t * @return a custom display name; never blank or consisting solely of\n\t * whitespace\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGeneration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @DisplayNameGeneration} is used to declare a custom display name\n * generator for the annotated test class.\n *\n * <p>This annotation is <em>inherited</em> from superclasses and implemented\n * interfaces. It is also inherited from {@linkplain Class#getEnclosingClass()\n * enclosing classes} for {@link Nested @Nested} test classes.\n *\n * <p>As an alternative to {@code @DisplayNameGeneration}, a global\n * {@link DisplayNameGenerator} can be configured for the entire test suite via\n * the {@value DisplayNameGenerator#DEFAULT_GENERATOR_PROPERTY_NAME} configuration parameter. See\n * the User Guide for details. Note, however, that a {@code @DisplayNameGeneration}\n * declaration always overrides a global {@code DisplayNameGenerator}.\n *\n * @since 5.4\n * @see DisplayName\n * @see DisplayNameGenerator\n * @see IndicativeSentencesGeneration\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\npublic @interface DisplayNameGeneration {\n\n\t/**\n\t * Custom display name generator.\n\t *\n\t * @return custom display name generator class\n\t */\n\tClass<? extends DisplayNameGenerator> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DisplayNameGenerator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.ModifierSupport.isStatic;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.getKotlinSuspendingFunctionParameterTypes;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinSuspendingFunction;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code DisplayNameGenerator} defines the SPI for generating display names\n * programmatically.\n *\n * <p>Display names are typically used for test reporting in IDEs and build\n * tools and may contain spaces, special characters, and even emoji.\n *\n * <p>Concrete implementations must have a <em>default constructor</em>.\n *\n * <p>A {@link DisplayNameGenerator} can be configured <em>globally</em> for the\n * entire test suite via the {@value #DEFAULT_GENERATOR_PROPERTY_NAME}\n * configuration parameter (see the User Guide for details) or <em>locally</em>\n * for a test class via the {@link DisplayNameGeneration @DisplayNameGeneration}\n * annotation.\n *\n * <h2>Built-in Implementations</h2>\n * <ul>\n * <li>{@link Standard}</li>\n * <li>{@link Simple}</li>\n * <li>{@link ReplaceUnderscores}</li>\n * <li>{@link IndicativeSentences}</li>\n * </ul>\n *\n * @since 5.4\n * @see DisplayName @DisplayName\n * @see DisplayNameGeneration @DisplayNameGeneration\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface DisplayNameGenerator {\n\n\t/**\n\t * Property name used to set the default display name generator class name:\n\t * {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include fully qualified class names for types that\n\t * implement {@link DisplayNameGenerator}.\n\t *\n\t * <p>If not specified, the default is\n\t * {@link DisplayNameGenerator.Standard}.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_GENERATOR_PROPERTY_NAME = \"junit.jupiter.displayname.generator.default\";\n\n\t/**\n\t * Generate a display name for the given top-level or {@code static} nested test class.\n\t *\n\t * <p>If this method returns {@code null}, the default display name\n\t * generator will be used instead.\n\t *\n\t * @param testClass the class to generate a name for; never {@code null}\n\t * @return the display name for the class; never blank\n\t */\n\tString generateDisplayNameForClass(Class<?> testClass);\n\n\t/**\n\t * Generate a display name for the given {@link Nested @Nested} inner test\n\t * class.\n\t *\n\t * <p>If this method returns {@code null}, the default display name\n\t * generator will be used instead.\n\t *\n\t * @param nestedClass the class to generate a name for; never {@code null}\n\t * @return the display name for the nested class; never blank\n\t * @deprecated in favor of {@link #generateDisplayNameForNestedClass(List, Class)}\n\t */\n\t@API(status = DEPRECATED, since = \"5.12\")\n\t@Deprecated(since = \"5.12\")\n\tdefault String generateDisplayNameForNestedClass(Class<?> nestedClass) {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\"Implement generateDisplayNameForNestedClass(List<Class<?>>, Class<?>) instead\");\n\t}\n\n\t/**\n\t * Generate a display name for the given {@link Nested @Nested} inner test\n\t * class.\n\t *\n\t * <p>If this method returns {@code null}, the default display name\n\t * generator will be used instead.\n\t *\n\t * @implNote The classes supplied as {@code enclosingInstanceTypes} may\n\t * differ from the classes returned from invocations of\n\t * {@link Class#getEnclosingClass()} &mdash; for example, when a nested test\n\t * class is inherited from a superclass.\n\t *\n\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t * instances for the test class, ordered from outermost to innermost,\n\t * excluding {@code nestedClass}; never {@code null}\n\t * @param nestedClass the class to generate a name for; never {@code null}\n\t * @return the display name for the nested class; never blank\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\treturn generateDisplayNameForNestedClass(nestedClass);\n\t}\n\n\t/**\n\t * Generate a display name for the given method.\n\t *\n\t * <p>If this method returns {@code null}, the default display name\n\t * generator will be used instead.\n\t *\n\t * @implNote The class instance supplied as {@code testClass} may differ from\n\t * the class returned by {@code testMethod.getDeclaringClass()} &mdash; for\n\t * example, when a test method is inherited from a superclass.\n\t *\n\t * @param testClass the class the test method is invoked on; never {@code null}\n\t * @param testMethod method to generate a display name for; never {@code null}\n\t * @return the display name for the test; never blank\n\t * @deprecated in favor of {@link #generateDisplayNameForMethod(List, Class, Method)}\n\t */\n\t@API(status = DEPRECATED, since = \"5.12\")\n\t@Deprecated(since = \"5.12\")\n\tdefault String generateDisplayNameForMethod(Class<?> testClass, Method testMethod) {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\"Implement generateDisplayNameForMethod(List<Class<?>>, Class<?>, Method) instead\");\n\t}\n\n\t/**\n\t * Generate a display name for the given method.\n\t *\n\t * <p>If this method returns {@code null}, the default display name\n\t * generator will be used instead.\n\t *\n\t * @implNote The classes supplied as {@code enclosingInstanceTypes} may\n\t * differ from the classes returned from invocations of\n\t * {@link Class#getEnclosingClass()} &mdash; for example, when a nested test\n\t * class is inherited from a superclass. Similarly, the class instance\n\t * supplied as {@code testClass} may differ from the class returned by\n\t * {@code testMethod.getDeclaringClass()} &mdash; for example, when a test\n\t * method is inherited from a superclass.\n\t *\n\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t * instances for the test class, ordered from outermost to innermost,\n\t * excluding {@code testClass}; never {@code null}\n\t * @param testClass the class the test method is invoked on; never {@code null}\n\t * @param testMethod method to generate a display name for; never {@code null}\n\t * @return the display name for the test; never blank\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\tMethod testMethod) {\n\t\treturn generateDisplayNameForMethod(testClass, testMethod);\n\t}\n\n\t/**\n\t * Generate a string representation of the formal parameters of the supplied\n\t * method, consisting of the {@linkplain Class#getSimpleName() simple names}\n\t * of the parameter types, separated by commas, and enclosed in parentheses.\n\t *\n\t * @param method the method from to extract the parameter types from; never\n\t * {@code null}\n\t * @return a string representation of all parameter types of the supplied\n\t * method or {@code \"()\"} if the method declares no parameters\n\t */\n\tstatic String parameterTypesAsString(Method method) {\n\t\tPreconditions.notNull(method, \"Method must not be null\");\n\t\tvar parameterTypes = isKotlinSuspendingFunction(method) //\n\t\t\t\t? getKotlinSuspendingFunctionParameterTypes(method) //\n\t\t\t\t: method.getParameterTypes();\n\t\treturn '(' + ClassUtils.nullSafeToString(Class::getSimpleName, parameterTypes) + ')';\n\t}\n\n\t/**\n\t * Standard {@code DisplayNameGenerator}.\n\t *\n\t * <p>This implementation matches the standard display name generation\n\t * behavior in place since JUnit Jupiter was introduced.\n\t */\n\tclass Standard implements DisplayNameGenerator {\n\n\t\tstatic final DisplayNameGenerator INSTANCE = new Standard();\n\n\t\tpublic Standard() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\t\tString name = testClass.getName();\n\t\t\tint lastDot = name.lastIndexOf('.');\n\t\t\treturn name.substring(lastDot + 1);\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\t\treturn nestedClass.getSimpleName();\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\treturn testMethod.getName() + parameterTypesAsString(testMethod);\n\t\t}\n\t}\n\n\t/**\n\t * Simple {@code DisplayNameGenerator} that removes trailing parentheses\n\t * for methods with no parameters.\n\t *\n\t * <p>This generator extends the functionality of {@link Standard} by\n\t * removing parentheses ({@code '()'}) found at the end of method names\n\t * with no parameters.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tclass Simple extends Standard {\n\n\t\tstatic final DisplayNameGenerator INSTANCE = new Simple();\n\n\t\tpublic Simple() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\tString displayName = testMethod.getName();\n\t\t\tif (hasParameters(testMethod)) {\n\t\t\t\tdisplayName += ' ' + parameterTypesAsString(testMethod);\n\t\t\t}\n\t\t\treturn displayName;\n\t\t}\n\n\t\tprivate static boolean hasParameters(Method method) {\n\t\t\treturn method.getParameterCount() > 0;\n\t\t}\n\n\t}\n\n\t/**\n\t * {@code DisplayNameGenerator} that replaces underscores with spaces.\n\t *\n\t * <p>This generator extends the functionality of {@link Simple} by\n\t * replacing all underscores ({@code '_'}) found in class and method names\n\t * with spaces ({@code ' '}).\n\t */\n\tclass ReplaceUnderscores extends Simple {\n\n\t\tstatic final DisplayNameGenerator INSTANCE = new ReplaceUnderscores();\n\n\t\tpublic ReplaceUnderscores() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\t\treturn replaceUnderscores(super.generateDisplayNameForClass(testClass));\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\t\treturn replaceUnderscores(super.generateDisplayNameForNestedClass(enclosingInstanceTypes, nestedClass));\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\treturn replaceUnderscores(\n\t\t\t\tsuper.generateDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod));\n\t\t}\n\n\t\tprivate static String replaceUnderscores(String name) {\n\t\t\treturn name.replace('_', ' ');\n\t\t}\n\n\t}\n\n\t/**\n\t * {@code DisplayNameGenerator} that generates complete sentences.\n\t *\n\t * <p>This generator generates display names that build up complete sentences\n\t * by concatenating the names of the test and the enclosing classes. The\n\t * sentence fragments are concatenated using a separator. The separator and\n\t * the display name generator for individual sentence fragments can be configured\n\t * via the {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration}\n\t * annotation.\n\t *\n\t * <p>If you do not want to rely on a display name generator for individual\n\t * sentence fragments, you can supply custom text for individual fragments\n\t * via the {@link SentenceFragment @SentenceFragment} annotation.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tclass IndicativeSentences implements DisplayNameGenerator {\n\n\t\t/**\n\t\t * {@code @SentenceFragment} is used to configure a custom sentence fragment\n\t\t * for a sentence generated by the {@link IndicativeSentences IndicativeSentences}\n\t\t * {@code DisplayNameGenerator}.\n\t\t *\n\t\t * <p>Note that {@link DisplayName @DisplayName} always takes precedence\n\t\t * over {@code @SentenceFragment}.\n\t\t *\n\t\t * @since 5.13\n\t\t */\n\t\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tpublic @interface SentenceFragment {\n\n\t\t\t/**\n\t\t\t * Custom sentence fragment for the annotated class or method.\n\t\t\t *\n\t\t\t * @return a custom sentence fragment; never blank or consisting solely\n\t\t\t * of whitespace\n\t\t\t */\n\t\t\tString value();\n\n\t\t}\n\n\t\tstatic final DisplayNameGenerator INSTANCE = new IndicativeSentences();\n\n\t\tprivate static final Predicate<Class<?>> notIndicativeSentences = clazz -> clazz != IndicativeSentences.class;\n\n\t\tpublic IndicativeSentences() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\t\tString sentenceFragment = getSentenceFragment(testClass);\n\t\t\treturn (sentenceFragment != null ? sentenceFragment\n\t\t\t\t\t: getGeneratorFor(testClass, emptyList()).generateDisplayNameForClass(testClass));\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\t\treturn getSentenceBeginning(nestedClass, enclosingInstanceTypes);\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\n\t\t\tString displayName = getSentenceBeginning(testClass, enclosingInstanceTypes)\n\t\t\t\t\t+ getFragmentSeparator(testClass, enclosingInstanceTypes);\n\n\t\t\tString sentenceFragment = getSentenceFragment(testMethod);\n\t\t\tdisplayName += (sentenceFragment != null ? sentenceFragment\n\t\t\t\t\t: getGeneratorFor(testClass, enclosingInstanceTypes).generateDisplayNameForMethod(\n\t\t\t\t\t\tenclosingInstanceTypes, testClass, testMethod));\n\t\t\treturn displayName;\n\t\t}\n\n\t\tprivate String getSentenceBeginning(Class<?> testClass, List<Class<?>> enclosingInstanceTypes) {\n\t\t\tClass<?> enclosingClass = enclosingInstanceTypes.isEmpty() ? null\n\t\t\t\t\t: enclosingInstanceTypes.get(enclosingInstanceTypes.size() - 1);\n\n\t\t\tString sentenceFragment = findAnnotation(testClass, DisplayName.class)//\n\t\t\t\t\t.map(DisplayName::value)//\n\t\t\t\t\t.map(String::strip)//\n\t\t\t\t\t.orElseGet(() -> getSentenceFragment(testClass));\n\n\t\t\tif (enclosingClass == null || isStatic(testClass)) { // top-level class\n\t\t\t\tif (sentenceFragment != null) {\n\t\t\t\t\treturn sentenceFragment;\n\t\t\t\t}\n\t\t\t\tClass<? extends DisplayNameGenerator> generatorClass = findDisplayNameGeneration(testClass,\n\t\t\t\t\tenclosingInstanceTypes)//\n\t\t\t\t\t\t\t.map(DisplayNameGeneration::value)//\n\t\t\t\t\t\t\t.filter(notIndicativeSentences)//\n\t\t\t\t\t\t\t.orElse(null);\n\t\t\t\tif (generatorClass != null) {\n\t\t\t\t\treturn getDisplayNameGenerator(generatorClass).generateDisplayNameForClass(testClass);\n\t\t\t\t}\n\t\t\t\treturn generateDisplayNameForClass(testClass);\n\t\t\t}\n\n\t\t\tList<Class<?>> remainingEnclosingInstanceTypes = enclosingInstanceTypes.isEmpty() ? emptyList()\n\t\t\t\t\t: enclosingInstanceTypes.subList(0, enclosingInstanceTypes.size() - 1);\n\n\t\t\t// Only build prefix based on the enclosing class if the enclosing\n\t\t\t// class is also configured to use the IndicativeSentences generator.\n\t\t\tboolean buildPrefix = findDisplayNameGeneration(enclosingClass, remainingEnclosingInstanceTypes)//\n\t\t\t\t\t.map(DisplayNameGeneration::value)//\n\t\t\t\t\t.filter(IndicativeSentences.class::equals)//\n\t\t\t\t\t.isPresent();\n\n\t\t\tString prefix = (buildPrefix\n\t\t\t\t\t? getSentenceBeginning(enclosingClass, remainingEnclosingInstanceTypes)\n\t\t\t\t\t\t\t+ getFragmentSeparator(testClass, enclosingInstanceTypes)\n\t\t\t\t\t: \"\");\n\n\t\t\treturn prefix + (sentenceFragment != null ? sentenceFragment\n\t\t\t\t\t: getGeneratorFor(testClass, enclosingInstanceTypes).generateDisplayNameForNestedClass(\n\t\t\t\t\t\tremainingEnclosingInstanceTypes, testClass));\n\t\t}\n\n\t\t/**\n\t\t * Get the sentence fragment separator.\n\t\t *\n\t\t * <p>If {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration}\n\t\t * is present (searching enclosing classes if not found locally), the\n\t\t * configured {@link IndicativeSentencesGeneration#separator() separator}\n\t\t * will be used. Otherwise, {@link IndicativeSentencesGeneration#DEFAULT_SEPARATOR}\n\t\t * will be used.\n\t\t *\n\t\t * @param testClass the test class to search on for {@code @IndicativeSentencesGeneration}\n\t\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t\t * instances; never {@code null}\n\t\t * @return the sentence fragment separator\n\t\t */\n\t\tprivate static String getFragmentSeparator(Class<?> testClass, List<Class<?>> enclosingInstanceTypes) {\n\t\t\treturn findIndicativeSentencesGeneration(testClass, enclosingInstanceTypes)//\n\t\t\t\t\t.map(IndicativeSentencesGeneration::separator)//\n\t\t\t\t\t.orElse(IndicativeSentencesGeneration.DEFAULT_SEPARATOR);\n\t\t}\n\n\t\t/**\n\t\t * Get the display name generator to use for the supplied test class.\n\t\t *\n\t\t * <p>If {@link IndicativeSentencesGeneration @IndicativeSentencesGeneration}\n\t\t * is present (searching enclosing classes if not found locally), the\n\t\t * configured {@link IndicativeSentencesGeneration#generator() generator}\n\t\t * will be used. Otherwise, {@link IndicativeSentencesGeneration#DEFAULT_GENERATOR}\n\t\t * will be used.\n\t\t *\n\t\t * @param testClass the test class to search on for {@code @IndicativeSentencesGeneration}\n\t\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t\t * instances; never {@code null}\n\t\t * @return the {@code DisplayNameGenerator} instance to use\n\t\t */\n\t\tprivate static DisplayNameGenerator getGeneratorFor(Class<?> testClass, List<Class<?>> enclosingInstanceTypes) {\n\t\t\treturn findIndicativeSentencesGeneration(testClass, enclosingInstanceTypes)//\n\t\t\t\t\t.map(IndicativeSentencesGeneration::generator)//\n\t\t\t\t\t.filter(notIndicativeSentences)//\n\t\t\t\t\t.map(DisplayNameGenerator::getDisplayNameGenerator)//\n\t\t\t\t\t.orElseGet(() -> getDisplayNameGenerator(IndicativeSentencesGeneration.DEFAULT_GENERATOR));\n\t\t}\n\n\t\t/**\n\t\t * Find the first {@code DisplayNameGeneration} annotation that is either\n\t\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly present</em>\n\t\t * on the supplied {@code testClass} or on an enclosing instance type.\n\t\t *\n\t\t * @param testClass the test class on which to find the annotation; never {@code null}\n\t\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t\t * instances; never {@code null}\n\t\t * @return an {@code Optional} containing the annotation, potentially empty if not found\n\t\t */\n\t\t@API(status = INTERNAL, since = \"5.12\")\n\t\tprivate static Optional<DisplayNameGeneration> findDisplayNameGeneration(Class<?> testClass,\n\t\t\t\tList<Class<?>> enclosingInstanceTypes) {\n\t\t\treturn findAnnotation(testClass, DisplayNameGeneration.class, enclosingInstanceTypes);\n\t\t}\n\n\t\t/**\n\t\t * Find the first {@code IndicativeSentencesGeneration} annotation that is either\n\t\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly present</em>\n\t\t * on the supplied {@code testClass} or on an enclosing instance type.\n\t\t *\n\t\t * @param testClass the test class on which to find the annotation; never {@code null}\n\t\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t\t * instances; never {@code null}\n\t\t * @return an {@code Optional} containing the annotation, potentially empty if not found\n\t\t */\n\t\tprivate static Optional<IndicativeSentencesGeneration> findIndicativeSentencesGeneration(Class<?> testClass,\n\t\t\t\tList<Class<?>> enclosingInstanceTypes) {\n\t\t\treturn findAnnotation(testClass, IndicativeSentencesGeneration.class, enclosingInstanceTypes);\n\t\t}\n\n\t\tprivate static @Nullable String getSentenceFragment(AnnotatedElement element) {\n\t\t\treturn findAnnotation(element, SentenceFragment.class) //\n\t\t\t\t\t.map(SentenceFragment::value) //\n\t\t\t\t\t.map(sentenceFragment -> {\n\t\t\t\t\t\tPreconditions.notBlank(sentenceFragment,\n\t\t\t\t\t\t\t\"@SentenceFragment on [%s] must be declared with a non-blank value.\".formatted(element));\n\t\t\t\t\t\treturn sentenceFragment.strip();\n\t\t\t\t\t}) //\n\t\t\t\t\t.orElse(null);\n\t\t}\n\n\t}\n\n\t/**\n\t * Return the {@code DisplayNameGenerator} instance corresponding to the\n\t * given {@code Class}.\n\t *\n\t * @param generatorClass the generator's {@code Class}; never {@code null},\n\t * has to be a {@code DisplayNameGenerator} implementation\n\t * @return a {@code DisplayNameGenerator} implementation instance\n\t */\n\tstatic DisplayNameGenerator getDisplayNameGenerator(Class<?> generatorClass) {\n\t\tPreconditions.notNull(generatorClass, \"Class must not be null\");\n\t\tPreconditions.condition(DisplayNameGenerator.class.isAssignableFrom(generatorClass),\n\t\t\t\"Class must be a DisplayNameGenerator implementation\");\n\t\tif (generatorClass == Standard.class) {\n\t\t\treturn Standard.INSTANCE;\n\t\t}\n\t\tif (generatorClass == Simple.class) {\n\t\t\treturn Simple.INSTANCE;\n\t\t}\n\t\tif (generatorClass == ReplaceUnderscores.class) {\n\t\t\treturn ReplaceUnderscores.INSTANCE;\n\t\t}\n\t\tif (generatorClass == IndicativeSentences.class) {\n\t\t\treturn IndicativeSentences.INSTANCE;\n\t\t}\n\t\treturn (DisplayNameGenerator) ReflectionSupport.newInstance(generatorClass);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicContainer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.net.URI;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * A {@code DynamicContainer} is a container generated at runtime.\n *\n * <p>It is composed of a {@linkplain DynamicNode#getDisplayName display name}\n * and an {@link Iterable} or {@link Stream} of {@link DynamicNode DynamicNodes}.\n *\n * <p>Instances of {@code DynamicContainer} must be generated by factory methods\n * annotated with {@link TestFactory @TestFactory}.\n *\n * @since 5.0\n * @see #dynamicContainer(String, Iterable)\n * @see #dynamicContainer(String, Stream)\n * @see TestFactory\n * @see DynamicTest\n */\n@API(status = MAINTAINED, since = \"5.3\")\npublic class DynamicContainer extends DynamicNode {\n\n\tprivate final @Nullable ExecutionMode childExecutionMode;\n\n\t/**\n\t * Factory for creating a new {@code DynamicContainer} for the supplied display\n\t * name and collection of dynamic nodes.\n\t *\n\t * <p>The collection of dynamic nodes must not contain {@code null} elements.\n\t *\n\t * @param displayName the display name for the dynamic container; never\n\t * {@code null} or blank\n\t * @param dynamicNodes collection of dynamic nodes to execute;\n\t * never {@code null}\n\t * @see #dynamicContainer(String, Stream)\n\t */\n\tpublic static DynamicContainer dynamicContainer(String displayName, Iterable<? extends DynamicNode> dynamicNodes) {\n\t\treturn dynamicContainer(config -> config.displayName(displayName).children(dynamicNodes));\n\t}\n\n\t/**\n\t * Factory for creating a new {@code DynamicContainer} for the supplied display\n\t * name and stream of dynamic nodes.\n\t *\n\t * <p>The stream of dynamic nodes must not contain {@code null} elements.\n\t *\n\t * @param displayName the display name for the dynamic container; never\n\t * {@code null} or blank\n\t * @param dynamicNodes stream of dynamic nodes to execute;\n\t * never {@code null}\n\t * @see #dynamicContainer(String, Iterable)\n\t */\n\tpublic static DynamicContainer dynamicContainer(String displayName, Stream<? extends DynamicNode> dynamicNodes) {\n\t\treturn dynamicContainer(config -> config.displayName(displayName).children(dynamicNodes));\n\t}\n\n\t/**\n\t * Factory for creating a new {@code DynamicContainer} for the supplied display\n\t * name, custom test source {@link URI}, and stream of dynamic nodes.\n\t *\n\t * <p>The stream of dynamic nodes must not contain {@code null} elements.\n\t *\n\t * @param displayName the display name for the dynamic container; never\n\t * {@code null} or blank\n\t * @param testSourceUri a custom test source URI for the dynamic container;\n\t * may be {@code null} if the framework should generate the test source based\n\t * on the {@code @TestFactory} method\n\t * @param dynamicNodes stream of dynamic nodes to execute; never {@code null}\n\t * @since 5.3\n\t * @see #dynamicContainer(String, Iterable)\n\t */\n\tpublic static DynamicContainer dynamicContainer(String displayName, @Nullable URI testSourceUri,\n\t\t\tStream<? extends DynamicNode> dynamicNodes) {\n\n\t\treturn dynamicContainer(\n\t\t\tconfig -> config.displayName(displayName).testSourceUri(testSourceUri).children(dynamicNodes));\n\t}\n\n\t/**\n\t * Factory for creating a new {@code DynamicTest} that is configured via the\n\t * supplied {@link Consumer} of {@link DynamicTest.Configuration}.\n\t *\n\t * @param configurer callback for configuring the resulting\n\t * {@code DynamicTest}; never {@code null}.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static DynamicContainer dynamicContainer(Consumer<? super Configuration> configurer) {\n\t\tvar configuration = new DefaultConfiguration();\n\t\tconfigurer.accept(configuration);\n\t\treturn new DynamicContainer(configuration);\n\t}\n\n\tprivate final Stream<? extends DynamicNode> children;\n\n\tprivate DynamicContainer(DefaultConfiguration configuration) {\n\t\tsuper(configuration);\n\t\tthis.children = Preconditions.notNull(configuration.children, \"children must not be null\");\n\t\tthis.childExecutionMode = configuration.childExecutionMode;\n\t}\n\n\t/**\n\t * Get the {@link Stream} of {@link DynamicNode DynamicNodes} associated\n\t * with this {@code DynamicContainer}.\n\t */\n\tpublic Stream<? extends DynamicNode> getChildren() {\n\t\treturn children;\n\t}\n\n\t/**\n\t * {@return the {@link ExecutionMode} for\n\t * {@linkplain #getChildren() children} of this {@code DynamicContainer}\n\t * that is used unless they are\n\t * {@linkplain DynamicTest#getExecutionMode() configured} differently}.\n\t *\n\t * @since 6.1\n\t * @see DynamicTest#getExecutionMode()\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic Optional<ExecutionMode> getChildExecutionMode() {\n\t\treturn Optional.ofNullable(childExecutionMode);\n\t}\n\n\t/**\n\t * {@code Configuration} of a {@link DynamicContainer}.\n\t *\n\t * @since 6.1\n\t * @see DynamicContainer#dynamicContainer(Consumer)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic sealed interface Configuration extends DynamicNode.Configuration<Configuration> {\n\n\t\t/**\n\t\t * Set the\n\t\t * {@linkplain DynamicContainer#getChildExecutionMode() child execution mode}\n\t\t * to use for the configured {@link DynamicContainer}.\n\t\t *\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tConfiguration childExecutionMode(ExecutionMode executionMode);\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicContainer#getChildren() children} of the\n\t\t * configured {@link DynamicContainer}.\n\t\t *\n\t\t * <p>Any previously configured value is overridden.\n\t\t *\n\t\t * @param children the children; never {@code null} or containing\n\t\t * {@code null} elements\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tdefault Configuration children(Iterable<? extends DynamicNode> children) {\n\t\t\tPreconditions.notNull(children, \"children must not be null\");\n\t\t\treturn children(StreamSupport.stream(children.spliterator(), false));\n\t\t}\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicContainer#getChildren() children} of the\n\t\t * configured {@link DynamicContainer}.\n\t\t *\n\t\t * <p>Any previously configured value is overridden.\n\t\t *\n\t\t * @param children the children; never {@code null} or containing\n\t\t * {@code null} elements\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tdefault Configuration children(DynamicNode... children) {\n\t\t\tPreconditions.notNull(children, \"children must not be null\");\n\t\t\tPreconditions.containsNoNullElements(children, \"children must not contain null elements\");\n\t\t\treturn children(List.of(children));\n\t\t}\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicContainer#getChildren() children} of the\n\t\t * configured {@link DynamicContainer}.\n\t\t *\n\t\t * <p>Any previously configured value is overridden.\n\t\t *\n\t\t * @param children the children; never {@code null} or containing\n\t\t * {@code null} elements\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tConfiguration children(Stream<? extends DynamicNode> children);\n\n\t}\n\n\tstatic final class DefaultConfiguration extends AbstractConfiguration<Configuration> implements Configuration {\n\n\t\tprivate @Nullable Stream<? extends DynamicNode> children;\n\t\tprivate @Nullable ExecutionMode childExecutionMode;\n\n\t\t@Override\n\t\tpublic Configuration childExecutionMode(ExecutionMode executionMode) {\n\t\t\tthis.childExecutionMode = Preconditions.notNull(executionMode, \"executionMode must not be null\");\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Configuration children(Stream<? extends DynamicNode> children) {\n\t\t\tPreconditions.notNull(children, \"children must not be null\");\n\t\t\tPreconditions.condition(this.children == null, \"children can only be set once\");\n\t\t\tthis.children = children;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tprotected Configuration self() {\n\t\t\treturn this;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicNode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.net.URI;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code DynamicNode} serves as the abstract base class for a container or a\n * test case generated at runtime.\n *\n * @since 5.0\n * @see DynamicTest\n * @see DynamicContainer\n */\n@API(status = MAINTAINED, since = \"5.3\")\npublic abstract class DynamicNode {\n\n\tprivate final String displayName;\n\n\t/** Custom test source {@link URI} associated with this node; potentially {@code null}. */\n\tprivate final @Nullable URI testSourceUri;\n\n\tprivate final @Nullable ExecutionMode executionMode;\n\n\tDynamicNode(AbstractConfiguration<?> configuration) {\n\t\tthis.displayName = Preconditions.notBlank(configuration.displayName, \"displayName must not be null or blank\");\n\t\tthis.testSourceUri = configuration.testSourceUri;\n\t\tthis.executionMode = configuration.executionMode;\n\t}\n\n\t/**\n\t * Get the display name of this {@code DynamicNode}.\n\t *\n\t * @return the display name\n\t */\n\tpublic String getDisplayName() {\n\t\treturn this.displayName;\n\t}\n\n\t/**\n\t * Get the custom test source {@link URI} of this {@code DynamicNode}.\n\t *\n\t * @return an {@code Optional} containing the custom test source {@link URI};\n\t * never {@code null} but potentially empty\n\t * @since 5.3\n\t */\n\tpublic Optional<URI> getTestSourceUri() {\n\t\treturn Optional.ofNullable(testSourceUri);\n\t}\n\n\t/**\n\t * {@return the {@link ExecutionMode} of this {@code DynamicNode}}\n\t *\n\t * @since 6.1\n\t * @see DynamicContainer#getChildExecutionMode()\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic Optional<ExecutionMode> getExecutionMode() {\n\t\treturn Optional.ofNullable(executionMode);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"displayName\", displayName) //\n\t\t\t\t.append(\"testSourceUri\", testSourceUri) //\n\t\t\t\t.toString();\n\t}\n\n\t/**\n\t * {@code Configuration} of a {@link DynamicNode} or one of its\n\t * subinterfaces.\n\t *\n\t * @since 6.1\n\t * @see DynamicTest.Configuration\n\t * @see DynamicContainer.Configuration\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic sealed interface Configuration<T extends Configuration<T>>\n\t\t\tpermits AbstractConfiguration, DynamicContainer.Configuration, DynamicTest.Configuration {\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicNode#getDisplayName() display name} to use\n\t\t * for the configured {@link DynamicNode}.\n\t\t *\n\t\t * @param displayName the display name; never {@code null} or blank\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tT displayName(String displayName);\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicNode#getTestSourceUri() test source URI}\n\t\t * to use for the configured {@link DynamicNode}.\n\t\t *\n\t\t * @param testSourceUri the test source URI; may be {@code null}\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tT testSourceUri(@Nullable URI testSourceUri);\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicNode#getExecutionMode() execution mode} to\n\t\t * use for the configured {@link DynamicNode}.\n\t\t *\n\t\t * @param executionMode the execution mode; never {@code null}\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tT executionMode(ExecutionMode executionMode);\n\n\t}\n\n\tabstract static sealed class AbstractConfiguration<T extends Configuration<T>> implements Configuration<T>\n\t\t\tpermits DynamicContainer.DefaultConfiguration, DynamicTest.DefaultConfiguration {\n\n\t\tprivate @Nullable String displayName;\n\t\tprivate @Nullable URI testSourceUri;\n\t\tprivate @Nullable ExecutionMode executionMode;\n\n\t\t@Override\n\t\tpublic T displayName(String displayName) {\n\t\t\tthis.displayName = Preconditions.notBlank(displayName, \"displayName must not be null or blank\");\n\t\t\treturn self();\n\t\t}\n\n\t\t@Override\n\t\tpublic T testSourceUri(@Nullable URI testSourceUri) {\n\t\t\tthis.testSourceUri = testSourceUri;\n\t\t\treturn self();\n\t\t}\n\n\t\t@Override\n\t\tpublic T executionMode(ExecutionMode executionMode) {\n\t\t\tthis.executionMode = Preconditions.notNull(executionMode, \"executionMode must not be null\");\n\t\t\treturn self();\n\t\t}\n\n\t\tprotected abstract T self();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/DynamicTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Spliterator.ORDERED;\nimport static java.util.Spliterators.spliteratorUnknownSize;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.net.URI;\nimport java.util.Iterator;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * A {@code DynamicTest} is a test case generated at runtime.\n *\n * <p>It is composed of a {@linkplain DynamicNode#getDisplayName display name}\n * and an {@link #getExecutable Executable}.\n *\n * <p>Instances of {@code DynamicTest} must be generated by factory methods\n * annotated with {@link TestFactory @TestFactory}.\n *\n * <p>Note that dynamic tests are quite different from standard {@link Test @Test}\n * cases since callbacks such as {@link BeforeEach @BeforeEach} and\n * {@link AfterEach @AfterEach} methods are not executed for dynamic tests.\n *\n * @since 5.0\n * @see #dynamicTest(String, Executable)\n * @see #stream(Iterator, Function, ThrowingConsumer)\n * @see Test\n * @see TestFactory\n * @see DynamicContainer\n * @see Executable\n */\n@API(status = MAINTAINED, since = \"5.3\")\npublic class DynamicTest extends DynamicNode {\n\n\t/**\n\t * Factory for creating a new {@code DynamicTest} for the supplied display\n\t * name and executable code block.\n\t *\n\t * @param displayName the display name for the dynamic test; never\n\t * {@code null} or blank\n\t * @param executable the executable code block for the dynamic test;\n\t * never {@code null}\n\t * @see #stream(Iterator, Function, ThrowingConsumer)\n\t */\n\tpublic static DynamicTest dynamicTest(String displayName, Executable executable) {\n\t\treturn dynamicTest(config -> config.displayName(displayName).executable(executable));\n\t}\n\n\t/**\n\t * Factory for creating a new {@code DynamicTest} for the supplied display\n\t * name, custom test source {@link URI}, and executable code block.\n\t *\n\t * @param displayName the display name for the dynamic test; never\n\t * {@code null} or blank\n\t * @param testSourceUri a custom test source URI for the dynamic test; may\n\t * be {@code null} if the framework should generate the test source based on\n\t * the {@code @TestFactory} method\n\t * @param executable the executable code block for the dynamic test;\n\t * never {@code null}\n\t * @since 5.3\n\t * @see #stream(Iterator, Function, ThrowingConsumer)\n\t */\n\tpublic static DynamicTest dynamicTest(String displayName, @Nullable URI testSourceUri, Executable executable) {\n\t\treturn dynamicTest(\n\t\t\tconfig -> config.displayName(displayName).testSourceUri(testSourceUri).executable(executable));\n\t}\n\n\t/**\n\t * Factory for creating a new {@code DynamicTest} that is configured via the\n\t * supplied {@link Consumer} of {@link Configuration}.\n\t *\n\t * @param configurer callback for configuring the resulting\n\t * {@code DynamicTest}; never {@code null}.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static DynamicTest dynamicTest(Consumer<? super Configuration> configurer) {\n\t\tvar configuration = new DefaultConfiguration();\n\t\tconfigurer.accept(configuration);\n\t\treturn new DynamicTest(configuration);\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given generator and test\n\t * executor.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Iterator}. See\n\t * {@link #stream(Stream, Function, ThrowingConsumer)} as an alternative.\n\t *\n\t * <p>The given {@code inputGenerator} is responsible for generating\n\t * input values. A {@link DynamicTest} will be added to the resulting\n\t * stream for each dynamically generated input value, using the given\n\t * {@code displayNameGenerator} and {@code testExecutor}.\n\t *\n\t * @param inputGenerator an {@code Iterator} that serves as a dynamic\n\t * <em>input generator</em>; never {@code null}\n\t * @param displayNameGenerator a function that generates a display name\n\t * based on an input value; never {@code null}\n\t * @param testExecutor a consumer that executes a test based on an input\n\t * value; never {@code null}\n\t * @param <T> the type of <em>input</em> generated by the {@code inputGenerator}\n\t * and used by the {@code displayNameGenerator} and {@code testExecutor}\n\t * @return a stream of dynamic tests based on the given generator and\n\t * executor; never {@code null}\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Stream, Function, ThrowingConsumer)\n\t */\n\tpublic static <T> Stream<DynamicTest> stream(Iterator<T> inputGenerator,\n\t\t\tFunction<? super T, String> displayNameGenerator, ThrowingConsumer<? super T> testExecutor) {\n\n\t\tPreconditions.notNull(inputGenerator, \"inputGenerator must not be null\");\n\n\t\treturn stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false),\n\t\t\tdisplayNameGenerator, testExecutor);\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given input stream and\n\t * test executor.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Stream}. See\n\t * {@link #stream(Iterator, Function, ThrowingConsumer)} as an alternative.\n\t *\n\t * <p>The given {@code inputStream} is responsible for supplying input values.\n\t * A {@link DynamicTest} will be added to the resulting stream for each\n\t * dynamically supplied input value, using the given {@code displayNameGenerator}\n\t * and {@code testExecutor}.\n\t *\n\t * @param inputStream a {@code Stream} that supplies dynamic input values;\n\t * never {@code null}\n\t * @param displayNameGenerator a function that generates a display name\n\t * based on an input value; never {@code null}\n\t * @param testExecutor a consumer that executes a test based on an input\n\t * value; never {@code null}\n\t * @param <T> the type of <em>input</em> supplied by the {@code inputStream}\n\t * and used by the {@code displayNameGenerator} and {@code testExecutor}\n\t * @return a stream of dynamic tests based on the given generator and\n\t * executor; never {@code null}\n\t * @since 5.7\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Iterator, Function, ThrowingConsumer)\n\t */\n\t@API(status = MAINTAINED, since = \"5.7\")\n\tpublic static <T> Stream<DynamicTest> stream(Stream<T> inputStream,\n\t\t\tFunction<? super T, String> displayNameGenerator, ThrowingConsumer<? super T> testExecutor) {\n\n\t\tPreconditions.notNull(inputStream, \"inputStream must not be null\");\n\t\tPreconditions.notNull(displayNameGenerator, \"displayNameGenerator must not be null\");\n\t\tPreconditions.notNull(testExecutor, \"testExecutor must not be null\");\n\n\t\treturn inputStream //\n\t\t\t\t.map(input -> dynamicTest(displayNameGenerator.apply(input), () -> testExecutor.accept(input)));\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given generator and test\n\t * executor.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Iterator}. See\n\t * {@link #stream(Stream, ThrowingConsumer)} as an alternative.\n\t *\n\t * <p>The given {@code inputGenerator} is responsible for generating\n\t * input values and display names. A {@link DynamicTest} will be added to\n\t * the resulting stream for each dynamically generated input value,\n\t * using the given {@code testExecutor}.\n\t *\n\t * @param inputGenerator an {@code Iterator} with {@code Named} values\n\t * that serves as a dynamic <em>input generator</em>; never {@code null}\n\t * @param testExecutor a consumer that executes a test based on an input\n\t * value; never {@code null}\n\t * @param <T> the type of <em>input</em> generated by the {@code inputGenerator}\n\t * and used by the {@code testExecutor}\n\t * @return a stream of dynamic tests based on the given generator and\n\t * executor; never {@code null}\n\t * @since 5.8\n\t *\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Stream, ThrowingConsumer)\n\t * @see Named\n\t */\n\t@API(status = MAINTAINED, since = \"5.8\")\n\tpublic static <T> Stream<DynamicTest> stream(Iterator<? extends Named<T>> inputGenerator,\n\t\t\tThrowingConsumer<? super T> testExecutor) {\n\t\tPreconditions.notNull(inputGenerator, \"inputGenerator must not be null\");\n\n\t\treturn stream(StreamSupport.stream(spliteratorUnknownSize(inputGenerator, ORDERED), false), testExecutor);\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given input stream and\n\t * test executor.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Stream}. See\n\t * {@link #stream(Iterator, ThrowingConsumer)} as an alternative.\n\t *\n\t * <p>The given {@code inputStream} is responsible for supplying input values\n\t * and display names. A {@link DynamicTest} will be added to the resulting stream for\n\t * each dynamically supplied input value, using the given {@code testExecutor}.\n\t *\n\t * @param inputStream a {@code Stream} that supplies dynamic {@code Named}\n\t * input values; never {@code null}\n\t * @param testExecutor a consumer that executes a test based on an input\n\t * value; never {@code null}\n\t * @param <T> the type of <em>input</em> supplied by the {@code inputStream}\n\t * and used by the {@code displayNameGenerator} and {@code testExecutor}\n\t * @return a stream of dynamic tests based on the given generator and\n\t * executor; never {@code null}\n\t * @since 5.8\n\t *\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Iterator, ThrowingConsumer)\n\t * @see Named\n\t */\n\t@API(status = MAINTAINED, since = \"5.8\")\n\tpublic static <T> Stream<DynamicTest> stream(Stream<? extends Named<T>> inputStream,\n\t\t\tThrowingConsumer<? super T> testExecutor) {\n\t\tPreconditions.notNull(inputStream, \"inputStream must not be null\");\n\t\tPreconditions.notNull(testExecutor, \"testExecutor must not be null\");\n\n\t\treturn inputStream //\n\t\t\t\t.map(input -> dynamicTest(input.getName(), () -> testExecutor.accept(input.getPayload())));\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given iterator.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Iterator}. See\n\t * {@link #stream(Stream)} as an alternative.\n\t *\n\t * <p>The given {@code iterator} is responsible for supplying\n\t * {@link Named} input values that provide an {@link Executable} code block.\n\t * A {@link DynamicTest} comprised of both parts will be added to the\n\t * resulting stream for each dynamically supplied input value.\n\t *\n\t * @param iterator an {@code Iterator} that supplies named executables;\n\t * never {@code null}\n\t * @param <T> the type of <em>input</em> supplied by the {@code inputStream}\n\t * @return a stream of dynamic tests based on the given iterator; never\n\t * {@code null}\n\t * @since 5.11\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Stream)\n\t * @see NamedExecutable\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static <T extends Named<E>, E extends Executable> Stream<DynamicTest> stream(\n\t\t\tIterator<? extends T> iterator) {\n\t\tPreconditions.notNull(iterator, \"iterator must not be null\");\n\n\t\treturn stream(StreamSupport.stream(spliteratorUnknownSize(iterator, ORDERED), false));\n\t}\n\n\t/**\n\t * Generate a stream of dynamic tests based on the given input stream.\n\t *\n\t * <p>Use this method when the set of dynamic tests is nondeterministic in\n\t * nature or when the input comes from an existing {@link Stream}. See\n\t * {@link #stream(Iterator)} as an alternative.\n\t *\n\t * <p>The given {@code inputStream} is responsible for supplying\n\t * {@link Named} input values that provide an {@link Executable} code block.\n\t * A {@link DynamicTest} comprised of both parts will be added to the\n\t * resulting stream for each dynamically supplied input value.\n\t *\n\t * @param inputStream a {@code Stream} that supplies named executables;\n\t * never {@code null}\n\t * @param <T> the type of <em>input</em> supplied by the {@code inputStream}\n\t * @return a stream of dynamic tests based on the given stream; never\n\t * {@code null}\n\t * @since 5.11\n\t * @see #dynamicTest(String, Executable)\n\t * @see #stream(Iterator)\n\t * @see NamedExecutable\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static <T extends Named<E>, E extends Executable> Stream<DynamicTest> stream(\n\t\t\tStream<? extends T> inputStream) {\n\t\tPreconditions.notNull(inputStream, \"inputStream must not be null\");\n\n\t\treturn inputStream. //\n\t\t\t\tmap(input -> dynamicTest(input.getName(), input.getPayload()));\n\t}\n\n\tprivate final Executable executable;\n\n\tprivate DynamicTest(DefaultConfiguration configuration) {\n\t\tsuper(configuration);\n\t\tthis.executable = Preconditions.notNull(configuration.executable, \"executable must not be null\");\n\t}\n\n\t/**\n\t * Get the {@code executable} code block associated with this {@code DynamicTest}.\n\t */\n\tpublic Executable getExecutable() {\n\t\treturn this.executable;\n\t}\n\n\t/**\n\t * {@code Configuration} of a {@link DynamicTest}.\n\t *\n\t * @since 6.1\n\t * @see DynamicTest#dynamicTest(Consumer)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic sealed interface Configuration extends DynamicNode.Configuration<Configuration> {\n\n\t\t/**\n\t\t * Set the {@linkplain DynamicTest#getExecutable() executable} to use\n\t\t * for the configured {@link DynamicTest}.\n\t\t *\n\t\t * @param executable the executable; never {@code null} or blank\n\t\t * @return this configuration for method chaining\n\t\t */\n\t\tConfiguration executable(Executable executable);\n\n\t}\n\n\tstatic final class DefaultConfiguration extends AbstractConfiguration<Configuration> implements Configuration {\n\n\t\tprivate @Nullable Executable executable;\n\n\t\t@Override\n\t\tpublic Configuration executable(Executable executable) {\n\t\t\tthis.executable = Preconditions.notNull(executable, \"executable must not be null\");\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tprotected Configuration self() {\n\t\t\treturn this;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/IndicativeSentencesGeneration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences;\n\n/**\n * {@code @IndicativeSentencesGeneration} is used to register the\n * {@link IndicativeSentences} display name generator and configure it.\n *\n * <p>The {@link #separator} for sentence fragments and the display name\n * {@link #generator} for sentence fragments are configurable. If this annotation\n * is declared without any attributes &mdash; for example,\n * {@code @IndicativeSentencesGeneration} or {@code @IndicativeSentencesGeneration()}\n * &mdash; the default configuration will be used.\n *\n * <p>This annotation is <em>inherited</em> from superclasses and implemented\n * interfaces. It is also inherited from {@linkplain Class#getEnclosingClass()\n * enclosing classes} for {@link Nested @Nested} test classes.\n *\n * @since 5.7\n * @see DisplayName\n * @see DisplayNameGenerator\n * @see DisplayNameGenerator.IndicativeSentences\n * @see DisplayNameGeneration\n */\n@DisplayNameGeneration(IndicativeSentences.class)\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.10\")\npublic @interface IndicativeSentencesGeneration {\n\n\tString DEFAULT_SEPARATOR = \", \";\n\n\tClass<? extends DisplayNameGenerator> DEFAULT_GENERATOR = DisplayNameGenerator.Standard.class;\n\n\t/**\n\t * Custom separator for sentence fragments.\n\t *\n\t * <p>Defaults to {@value #DEFAULT_SEPARATOR}.\n\t */\n\tString separator() default DEFAULT_SEPARATOR;\n\n\t/**\n\t * Custom display name generator to use for sentence fragments.\n\t *\n\t * <p>Defaults to {@link DisplayNameGenerator.Standard}.\n\t */\n\tClass<? extends DisplayNameGenerator> generator() default DisplayNameGenerator.Standard.class;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/MediaType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.nio.charset.Charset;\nimport java.nio.file.Path;\nimport java.util.Objects;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Represents a media type as defined by\n * <a href=\"https://tools.ietf.org/html/rfc2045\">RFC 2045</a>.\n *\n * <p><strong>WARNING</strong>: This type should not be extended by third parties.\n *\n * @since 5.14\n * @see TestReporter#publishFile(Path, MediaType)\n * @see TestReporter#publishFile(String, MediaType, org.junit.jupiter.api.function.ThrowingConsumer)\n * @see org.junit.jupiter.api.extension.ExtensionContext#publishFile(String, MediaType, org.junit.jupiter.api.function.ThrowingConsumer)\n */\n@SuppressWarnings(\"removal\")\n@API(status = MAINTAINED, since = \"5.14\")\npublic sealed class MediaType permits org.junit.jupiter.api.extension.MediaType {\n\n\tprivate static final Pattern PATTERN;\n\n\tstatic {\n\t\t// https://datatracker.ietf.org/doc/html/rfc2045#section-5.1\n\t\tString whitespace = \"[ \\t]*\";\n\t\tString token = \"[0-9A-Za-z!#$%&'*+.^_`|~-]+\";\n\t\tString quotedString = \"\\\"(?:[^\\\"\\\\\\\\]|\\\\.)*\\\"\";\n\t\tString parameter = \";\" + whitespace + token + \"=\" + \"(?:\" + token + \"|\" + quotedString + \")\";\n\t\tPATTERN = Pattern.compile(token + \"/\" + token + \"(?:\" + whitespace + parameter + \")*\");\n\t}\n\n\t/**\n\t * The {@code text/plain} media type.\n\t */\n\tpublic static final MediaType TEXT_PLAIN = create(\"text\", \"plain\");\n\n\t/**\n\t * The {@code text/plain; charset=UTF-8} media type.\n\t */\n\tpublic static final MediaType TEXT_PLAIN_UTF_8 = create(\"text\", \"plain\", UTF_8);\n\n\t/**\n\t * The {@code application/json} media type.\n\t */\n\tpublic static final MediaType APPLICATION_JSON = create(\"application\", \"json\");\n\n\t/**\n\t * The {@code application/octet-stream} media type.\n\t */\n\tpublic static final MediaType APPLICATION_OCTET_STREAM = create(\"application\", \"octet-stream\");\n\n\t/**\n\t * The {@code image/jpeg} media type.\n\t */\n\tpublic static final MediaType IMAGE_JPEG = create(\"image\", \"jpeg\");\n\n\t/**\n\t * The {@code image/png} media type.\n\t */\n\tpublic static final MediaType IMAGE_PNG = create(\"image\", \"png\");\n\n\tprivate final String value;\n\n\t/**\n\t * Parse the given media type value.\n\t *\n\t * <p>Must be valid according to\n\t * <a href=\"https://tools.ietf.org/html/rfc2045\">RFC 2045</a>.\n\t *\n\t * @param value the media type value to parse; never {@code null} or blank\n\t * @return the parsed media type\n\t */\n\tpublic static MediaType parse(String value) {\n\t\treturn new MediaType(value);\n\t}\n\n\t/**\n\t * Create a media type with the given type and subtype.\n\t *\n\t * @param type the type; never {@code null} or blank\n\t * @param subtype the subtype; never {@code null} or blank\n\t * @return the media type\n\t */\n\tpublic static MediaType create(String type, String subtype) {\n\t\treturn new MediaType(type, subtype, null);\n\t}\n\n\t/**\n\t * Create a media type with the given type, subtype, and charset.\n\t *\n\t * @param type the type; never {@code null} or blank\n\t * @param subtype the subtype; never {@code null} or blank\n\t * @param charset the charset; never {@code null}\n\t * @return the media type\n\t */\n\tpublic static MediaType create(String type, String subtype, Charset charset) {\n\t\tPreconditions.notNull(charset, \"charset must not be null\");\n\t\treturn new MediaType(type, subtype, charset);\n\t}\n\n\tprotected MediaType(String type, String subtype, @Nullable Charset charset) {\n\t\tthis(\"%s/%s%s\".formatted(//\n\t\t\tPreconditions.notBlank(type, \"type must not be null or blank\").strip(),\n\t\t\tPreconditions.notBlank(subtype, \"subtype must not be null or blank\").strip(),\n\t\t\t(charset != null ? (\"; charset=\" + charset.name()) : \"\")));\n\t}\n\n\tprotected MediaType(String value) {\n\t\tString strippedValue = Preconditions.notBlank(value, \"value must not be null or blank\").strip();\n\t\tMatcher matcher = PATTERN.matcher(strippedValue);\n\t\tPreconditions.condition(matcher.matches(), () -> \"Invalid media type: '\" + strippedValue + \"'\");\n\t\tthis.value = strippedValue;\n\t}\n\n\t/**\n\t * {@return a string representation of this media type}\n\t */\n\t@Override\n\tpublic final String toString() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic final boolean equals(Object obj) {\n\t\treturn this == obj || (obj instanceof MediaType that && this.value.equals(that.value));\n\t}\n\n\t@Override\n\tpublic final int hashCode() {\n\t\treturn Objects.hashCode(this.value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code MethodDescriptor} encapsulates functionality for a given {@link Method}.\n *\n * @since 5.4\n * @see MethodOrdererContext\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface MethodDescriptor {\n\n\t/**\n\t * Get the method for this descriptor.\n\t *\n\t * @return the method; never {@code null}\n\t */\n\tMethod getMethod();\n\n\t/**\n\t * Get the display name for this descriptor's {@link #getMethod() method}.\n\t *\n\t * @return the display name for this descriptor's method; never {@code null}\n\t * or blank\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tString getDisplayName();\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Method} for\n\t * this descriptor.\n\t *\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @see #findAnnotation(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\tboolean isAnnotated(Class<? extends Annotation> annotationType);\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Method} for\n\t * this descriptor.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\t<A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType);\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of\n\t * {@code annotationType} that are either <em>present</em> or\n\t * <em>meta-present</em> on the {@link Method} for this descriptor.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the repeatable annotation type to search for; never\n\t * {@code null}\n\t * @return the list of all such annotations found; neither {@code null} nor\n\t * mutable, but potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findAnnotation(Class)\n\t * @see java.lang.annotation.Repeatable\n\t */\n\t<A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrderer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Comparator.comparingInt;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassUtils;\n\n/**\n * {@code MethodOrderer} defines the API for ordering the <em>test methods</em>\n * in a given test class.\n *\n * <p>In this context, the term \"test method\" refers to any method annotated with\n * {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest},\n * {@code @TestFactory}, or {@code @TestTemplate}.\n *\n * <p>A {@link MethodOrderer} can be configured <em>globally</em> for the entire\n * test suite via the {@value #DEFAULT_ORDER_PROPERTY_NAME} configuration\n * parameter (see the User Guide for details) or <em>locally</em> for a test\n * class via the {@link TestMethodOrder @TestMethodOrder} annotation.\n *\n * <h2>Built-in Implementations</h2>\n *\n * <p>JUnit Jupiter provides the following built-in {@code MethodOrderer}\n * implementations.\n *\n * <ul>\n * <li>{@link Default}</li>\n * <li>{@link MethodName}</li>\n * <li>{@link OrderAnnotation}</li>\n * <li>{@link Random}</li>\n * </ul>\n *\n * @since 5.4\n * @see TestMethodOrder\n * @see MethodOrdererContext\n * @see #orderMethods(MethodOrdererContext)\n * @see ClassOrderer\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface MethodOrderer {\n\n\t/**\n\t * Property name used to set the default method orderer class name: {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include fully qualified class names for types that\n\t * implement {@link org.junit.jupiter.api.MethodOrderer}.\n\t *\n\t * <p>If not specified, test methods will be ordered using an algorithm that\n\t * is deterministic but intentionally non-obvious.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_ORDER_PROPERTY_NAME = \"junit.jupiter.testmethod.order.default\";\n\n\t/**\n\t * Order the methods encapsulated in the supplied {@link MethodOrdererContext}.\n\t *\n\t * <p>The methods to order or sort are made indirectly available via\n\t * {@link MethodOrdererContext#getMethodDescriptors()}. Since this method\n\t * has a {@code void} return type, the list of method descriptors must be\n\t * modified directly.\n\t *\n\t * <p>For example, a simplified implementation of the {@link Random}\n\t * {@code MethodOrderer} might look like the following.\n\t *\n\t * <pre class=\"code\">\n\t * public void orderMethods(MethodOrdererContext context) {\n\t *     Collections.shuffle(context.getMethodDescriptors());\n\t * }</pre>\n\t *\n\t * @param context the {@code MethodOrdererContext} containing the\n\t * {@linkplain MethodDescriptor method descriptors} to order; never {@code null}\n\t * @see #getDefaultExecutionMode()\n\t */\n\tvoid orderMethods(MethodOrdererContext context);\n\n\t/**\n\t * Get the <em>default</em> {@link ExecutionMode} for the test class\n\t * configured with this {@link MethodOrderer}.\n\t *\n\t * <p>This method is guaranteed to be invoked after\n\t * {@link #orderMethods(MethodOrdererContext)} which allows implementations\n\t * of this method to determine the appropriate return value programmatically,\n\t * potentially based on actions that were taken in {@code orderMethods()}.\n\t *\n\t * <p>Defaults to {@link ExecutionMode#SAME_THREAD SAME_THREAD}, since\n\t * ordered methods are typically sorted in a fashion that would conflict\n\t * with concurrent execution.\n\t *\n\t * <p>In case the ordering does not conflict with concurrent execution,\n\t * implementations should return an empty {@link Optional} to signal that\n\t * the engine should decide which execution mode to use.\n\t *\n\t * <p>Can be overridden via an explicit\n\t * {@link org.junit.jupiter.api.parallel.Execution @Execution} declaration\n\t * on the test class or in concrete implementations of the\n\t * {@code MethodOrderer} API.\n\t *\n\t * @return the default {@code ExecutionMode}; never {@code null} but\n\t * potentially empty\n\t * @see #orderMethods(MethodOrdererContext)\n\t */\n\tdefault Optional<ExecutionMode> getDefaultExecutionMode() {\n\t\treturn Optional.of(ExecutionMode.SAME_THREAD);\n\t}\n\n\t/**\n\t * {@code MethodOrderer} that allows to explicitly specify that the default\n\t * ordering should be applied.\n\t *\n\t * <p>If the {@value #DEFAULT_ORDER_PROPERTY_NAME} is set, specifying this\n\t * {@code MethodOrderer} has the same effect as referencing the configured\n\t * class directly. Otherwise, it has the same effect as not specifying any\n\t * {@code MethodOrderer}.\n\t *\n\t * <p>This class can be used to reset the {@code MethodOrderer} for a\n\t * {@link Nested @Nested} class and its {@code @Nested} inner classes,\n\t * recursively, when a {@code MethodOrderer} is configured using\n\t * {@link TestMethodOrder @TestMethodOrder} on an enclosing class.\n\t *\n\t * @since 6.0\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tfinal class Default implements MethodOrderer {\n\n\t\tprivate Default() {\n\t\t\tthrow new JUnitException(\"This class must not be instantiated\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\t// never called\n\t\t}\n\t}\n\n\t/**\n\t * {@code MethodOrderer} that sorts methods alphanumerically based on their\n\t * names using {@link String#compareTo(String)}.\n\t *\n\t * <p>If two methods have the same name, {@code String} representations of\n\t * their formal parameter lists will be used as a fallback for comparing the\n\t * methods.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tclass MethodName implements MethodOrderer {\n\n\t\tpublic MethodName() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the methods encapsulated in the supplied\n\t\t * {@link MethodOrdererContext} alphanumerically based on their names\n\t\t * and formal parameter lists.\n\t\t */\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparator);\n\t\t}\n\n\t\tprivate static final Comparator<MethodDescriptor> comparator = Comparator.<MethodDescriptor, String> //\n\t\t\t\tcomparing(descriptor -> descriptor.getMethod().getName())//\n\t\t\t\t.thenComparing(descriptor -> parameterList(descriptor.getMethod()));\n\n\t\tprivate static String parameterList(Method method) {\n\t\t\treturn ClassUtils.nullSafeToString(method.getParameterTypes());\n\t\t}\n\t}\n\n\t/**\n\t * {@code MethodOrderer} that sorts methods alphanumerically based on their\n\t * display names using {@link String#compareTo(String)}\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tclass DisplayName implements MethodOrderer {\n\n\t\tpublic DisplayName() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the methods encapsulated in the supplied\n\t\t * {@link MethodOrdererContext} alphanumerically based on their display\n\t\t * names.\n\t\t */\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparator);\n\t\t}\n\n\t\tprivate static final Comparator<MethodDescriptor> comparator = Comparator.comparing(\n\t\t\tMethodDescriptor::getDisplayName);\n\t}\n\n\t/**\n\t * {@code MethodOrderer} that sorts methods based on the {@link Order @Order}\n\t * annotation.\n\t *\n\t * <p>Any methods that are assigned the same order value will be sorted\n\t * arbitrarily adjacent to each other.\n\t *\n\t * <p>Any methods not annotated with {@code @Order} will be assigned the\n\t * {@linkplain Order#DEFAULT default order} value which will effectively cause them\n\t * to appear at the end of the sorted list, unless certain methods are assigned\n\t * an explicit order value greater than the default order value. Any methods\n\t * assigned an explicit order value greater than the default order value will\n\t * appear after non-annotated methods in the sorted list.\n\t */\n\tclass OrderAnnotation implements MethodOrderer {\n\n\t\tpublic OrderAnnotation() {\n\t\t}\n\n\t\t/**\n\t\t * Sort the methods encapsulated in the supplied\n\t\t * {@link MethodOrdererContext} based on the {@link Order @Order}\n\t\t * annotation.\n\t\t */\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparingInt(OrderAnnotation::getOrder));\n\t\t}\n\n\t\tprivate static int getOrder(MethodDescriptor descriptor) {\n\t\t\treturn descriptor.findAnnotation(Order.class).map(Order::value).orElse(Order.DEFAULT);\n\t\t}\n\t}\n\n\t/**\n\t * {@code MethodOrderer} that orders methods pseudo-randomly.\n\t *\n\t * <h2>Custom Seed</h2>\n\t *\n\t * <p>By default, the random <em>seed</em> used for ordering methods is the\n\t * value returned by {@link System#nanoTime()} during static class\n\t * initialization. In order to support repeatable builds, the value of the\n\t * default random seed is logged at {@code CONFIG} level. In addition, a\n\t * custom seed (potentially the default seed from the previous test plan\n\t * execution) may be specified via the {@value Random#RANDOM_SEED_PROPERTY_NAME}\n\t * <em>configuration parameter</em> which can be supplied via the {@code Launcher}\n\t * API, build tools (e.g., Gradle and Maven), a JVM system property, or the JUnit\n\t * Platform configuration file (i.e., a file named {@code junit-platform.properties}\n\t * in the root of the class path). Consult the User Guide for further information.\n\t *\n\t * @see Random#RANDOM_SEED_PROPERTY_NAME\n\t * @see java.util.Random\n\t */\n\tclass Random implements MethodOrderer {\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(Random.class);\n\n\t\tstatic {\n\t\t\tlogger.config(() -> \"MethodOrderer.Random default seed: \" + RandomOrdererUtils.DEFAULT_SEED);\n\t\t}\n\n\t\t/**\n\t\t * Property name used to set the random seed used by this\n\t\t * {@code MethodOrderer}: {@value}\n\t\t *\n\t\t * <p>The same property is used by {@link ClassOrderer.Random} for\n\t\t * consistency between the two random orderers.\n\t\t *\n\t\t * <h4>Supported Values</h4>\n\t\t *\n\t\t * <p>Supported values include any string that can be converted to a\n\t\t * {@link Long} via {@link Long#valueOf(String)}.\n\t\t *\n\t\t * <p>If not specified or if the specified value cannot be converted to\n\t\t * a {@link Long}, the default random seed will be used (see the\n\t\t * {@linkplain Random class-level Javadoc} for details).\n\t\t *\n\t\t * @see ClassOrderer.Random\n\t\t */\n\t\tpublic static final String RANDOM_SEED_PROPERTY_NAME = RandomOrdererUtils.RANDOM_SEED_PROPERTY_NAME;\n\n\t\tpublic Random() {\n\t\t}\n\n\t\t/**\n\t\t * Order the methods encapsulated in the supplied\n\t\t * {@link MethodOrdererContext} pseudo-randomly.\n\t\t */\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tCollections.shuffle(context.getMethodDescriptors(),\n\t\t\t\tnew java.util.Random(RandomOrdererUtils.getSeed(context::getConfigurationParameter, logger)));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/MethodOrdererContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code MethodOrdererContext} encapsulates the <em>context</em> in which\n * a {@link MethodOrderer} will be invoked.\n *\n * @since 5.4\n * @see MethodOrderer\n * @see MethodDescriptor\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface MethodOrdererContext {\n\n\t/**\n\t * Get the test class for this context.\n\t *\n\t * @return the test class; never {@code null}\n\t */\n\tClass<?> getTestClass();\n\n\t/**\n\t * Get the list of {@linkplain MethodDescriptor method descriptors} to\n\t * order.\n\t *\n\t * @return the list of method descriptors; never {@code null}\n\t */\n\tList<? extends MethodDescriptor> getMethodDescriptors();\n\n\t/**\n\t * Get the configuration parameter stored under the specified {@code key}.\n\t *\n\t * <p>If no such key is present in the {@code ConfigurationParameters} for\n\t * the JUnit Platform, an attempt will be made to look up the value as a\n\t * JVM system property. If no such system property exists, an attempt will\n\t * be made to look up the value in the JUnit Platform properties file.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @see System#getProperty(String)\n\t * @see org.junit.platform.engine.ConfigurationParameters\n\t */\n\tOptional<String> getConfigurationParameter(String key);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Named.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code Named} is a container that associates a name with a given payload.\n *\n * @param <T> the type of the payload\n *\n * @since 5.8\n */\n@API(status = STABLE, since = \"5.8\")\npublic interface Named<T extends @Nullable Object> {\n\n\t/**\n\t * Factory method for creating an instance of {@code Named} based on a\n\t * {@code name} and a {@code payload}.\n\t *\n\t * @param name the name associated with the payload; never {@code null} or\n\t * blank\n\t * @param payload the object that serves as the payload; may be {@code null}\n\t * depending on the use case\n\t * @param <T> the type of the payload\n\t * @return an instance of {@code Named}; never {@code null}\n\t * @see #named(String, java.lang.Object)\n\t */\n\tstatic <T extends @Nullable Object> Named<T> of(String name, T payload) {\n\t\tPreconditions.notBlank(name, \"name must not be null or blank\");\n\n\t\treturn new Named<>() {\n\t\t\t@Override\n\t\t\tpublic String getName() {\n\t\t\t\treturn name;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic T getPayload() {\n\t\t\t\treturn payload;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Factory method for creating an instance of {@code Named} based on a\n\t * {@code name} and a {@code payload}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link Named#of} and is\n\t * intended to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.jupiter.api.Named.named;}\n\t *\n\t * @param name the name associated with the payload; never {@code null} or\n\t * blank\n\t * @param payload the object that serves as the payload; may be {@code null}\n\t * depending on the use case\n\t * @param <T> the type of the payload\n\t * @return an instance of {@code Named}; never {@code null}\n\t */\n\tstatic <T extends @Nullable Object> Named<T> named(String name, T payload) {\n\t\treturn of(name, payload);\n\t}\n\n\t/**\n\t * Get the name of the payload.\n\t *\n\t * @return the name of the payload; never {@code null} or blank\n\t */\n\tString getName();\n\n\t/**\n\t * Get the payload.\n\t *\n\t * @return the payload; may be {@code null} depending on the use case\n\t */\n\tT getPayload();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/NamedExecutable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Iterator;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.function.Executable;\n\n/**\n * {@code NamedExecutable} joins {@code Executable} and {@code Named} in a\n * one self-typed functional interface.\n *\n * <p>The default implementation of {@link #getName()} returns the result of\n * calling {@link Object#toString()} on the implementing instance but may be\n * overridden by concrete implementations to provide a more meaningful name.\n *\n * <p>It is recommended to implement this interface using a record type.\n *\n * @since 5.11\n * @see DynamicTest#stream(Stream)\n * @see DynamicTest#stream(Iterator)\n */\n@FunctionalInterface\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface NamedExecutable extends Named<Executable>, Executable {\n\t@Override\n\tdefault String getName() {\n\t\treturn toString();\n\t}\n\n\t@Override\n\tdefault Executable getPayload() {\n\t\treturn this;\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Nested.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n/**\n * {@code @Nested} is used to signal that the annotated class is a nested,\n * non-static test class (i.e., an <em>inner class</em>) that can share\n * setup and state with an instance of its {@linkplain Class#getEnclosingClass()\n * enclosing class}. The enclosing class may be a top-level test class or\n * another {@code @Nested} test class, and nesting can be arbitrarily deep.\n *\n * <p>{@code @Nested} test classes may be ordered via\n * {@link TestClassOrder @TestClassOrder} or a global {@link ClassOrderer}.\n *\n * <p>{@code @Nested} may be combined with {@link ClassTemplate @ClassTemplate}.\n *\n * <h2>Test Instance Lifecycle</h2>\n *\n * <ul>\n * <li>A {@code @Nested} test class <em>can</em> be configured with its own\n * {@link Lifecycle} mode which may differ from that of an enclosing test\n * class.</li>\n * <li>A {@code @Nested} test class <em>cannot</em> change the {@link Lifecycle}\n * mode of an enclosing test class.</li>\n * </ul>\n *\n * @since 5.0\n * @see ClassTemplate\n * @see Test\n * @see TestInstance\n * @see TestClassOrder\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface Nested {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Order.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Order} is an annotation that is used to configure the\n * {@linkplain #value order} in which the annotated element (i.e., field,\n * method, or class) should be evaluated or executed relative to other elements\n * of the same category.\n *\n * <p>When used with\n * {@link org.junit.jupiter.api.extension.RegisterExtension @RegisterExtension} or\n * {@link org.junit.jupiter.api.extension.ExtendWith @ExtendWith},\n * the category applies to <em>extension fields</em>. When used with\n * {@link MethodOrderer.OrderAnnotation}, the category applies to <em>test methods</em>.\n * When used with {@link ClassOrderer.OrderAnnotation}, the category applies to\n * <em>test classes</em>.\n *\n * <p>If {@code @Order} is not explicitly declared on an element, the\n * {@link #DEFAULT} order value will be assigned to the element.\n *\n * @since 5.4\n * @see MethodOrderer.OrderAnnotation\n * @see ClassOrderer.OrderAnnotation\n * @see org.junit.jupiter.api.extension.RegisterExtension @RegisterExtension\n * @see org.junit.jupiter.api.extension.ExtendWith @ExtendWith\n */\n@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.9\")\npublic @interface Order {\n\n\t/**\n\t * Default order value for elements not explicitly annotated with {@code @Order},\n\t * equal to the value of {@code Integer.MAX_VALUE / 2}.\n\t *\n\t * @since 5.6\n\t * @see Order#value\n\t */\n\tint DEFAULT = Integer.MAX_VALUE / 2;\n\n\t/**\n\t * The order value for the annotated element (i.e., field, method, or class).\n\t *\n\t * <p>Elements are ordered based on priority where a lower value has greater\n\t * priority than a higher value. For example, {@link Integer#MAX_VALUE} has\n\t * the lowest priority.\n\t *\n\t * @see #DEFAULT\n\t */\n\tint value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/RandomOrdererUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.logging.Logger;\n\n/**\n * Shared utility methods for ordering test classes and test methods randomly.\n *\n * @since 5.11\n * @see ClassOrderer.Random\n * @see MethodOrderer.Random\n */\nclass RandomOrdererUtils {\n\n\tstatic final String RANDOM_SEED_PROPERTY_NAME = \"junit.jupiter.execution.order.random.seed\";\n\n\tstatic final long DEFAULT_SEED = System.nanoTime();\n\n\tstatic Long getSeed(Function<String, Optional<String>> configurationParameterLookup, Logger logger) {\n\t\treturn getCustomSeed(configurationParameterLookup, logger).orElse(DEFAULT_SEED);\n\t}\n\n\tprivate static Optional<Long> getCustomSeed(Function<String, Optional<String>> configurationParameterLookup,\n\t\t\tLogger logger) {\n\t\treturn configurationParameterLookup.apply(RANDOM_SEED_PROPERTY_NAME).map(configurationParameter -> {\n\t\t\ttry {\n\t\t\t\tlogger.config(() -> \"Using custom seed for configuration parameter [%s] with value [%s].\".formatted(\n\t\t\t\t\tRANDOM_SEED_PROPERTY_NAME, configurationParameter));\n\t\t\t\treturn Long.valueOf(configurationParameter);\n\t\t\t}\n\t\t\tcatch (NumberFormatException ex) {\n\t\t\t\tlogger.warn(ex, () -> \"\"\"\n\t\t\t\t\t\tFailed to convert configuration parameter [%s] with value [%s] to a long. \\\n\t\t\t\t\t\tUsing default seed [%s] as fallback.\"\"\".formatted(RANDOM_SEED_PROPERTY_NAME,\n\t\t\t\t\tconfigurationParameter, DEFAULT_SEED));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate RandomOrdererUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepeatedTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @RepeatedTest} is used to signal that the annotated method is a\n * <em>test template</em> method that should be repeated a {@linkplain #value\n * specified number of times} with a configurable {@linkplain #name display\n * name} and an optional {@linkplain #failureThreshold() failure threshold}.\n *\n * <p>Each invocation of the repeated test behaves like the execution of a\n * regular {@link Test @Test} method with full support for the same lifecycle\n * callbacks and extensions. In addition, the current repetition and total\n * number of repetitions can be accessed by having the {@link RepetitionInfo}\n * injected.\n *\n * <p>{@code @RepeatedTest} methods must not be {@code private} or {@code static}\n * and must return {@code void}.\n *\n * <p>{@code @RepeatedTest} methods may optionally declare parameters to be\n * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver\n * ParameterResolvers}.\n *\n * <p>{@code @RepeatedTest} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @RepeatedTest}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @RepeatedTest} methods are inherited from superclasses as long as\n * they are not <em>overridden</em> according to the visibility rules of the Java\n * language. Similarly, {@code @RepeatedTest} methods declared as <em>interface\n * default methods</em> are inherited as long as they are not overridden.\n *\n * <h2>Test Execution Order</h2>\n *\n * <p>By default, test methods will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute test methods in the same order, thereby allowing for\n * repeatable builds. In this context, a <em>test method</em> is any instance\n * method that is directly annotated or meta-annotated with {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or\n * {@code @TestTemplate}.\n *\n * <p>Although true <em>unit tests</em> typically should not rely on the order\n * in which they are executed, there are times when it is necessary to enforce\n * a specific test method execution order &mdash; for example, when writing\n * <em>integration tests</em> or <em>functional tests</em> where the sequence of\n * the tests is important, especially in conjunction with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <p>To control the order in which test methods are executed, annotate your\n * test class or test interface with {@link TestMethodOrder @TestMethodOrder}\n * and specify the desired {@link MethodOrderer} implementation.\n *\n * @since 5.0\n * @see DisplayName\n * @see RepetitionInfo\n * @see TestTemplate\n * @see TestInfo\n * @see Test\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\n@TestTemplate\npublic @interface RepeatedTest {\n\n\t/**\n\t * Placeholder for the {@linkplain TestInfo#getDisplayName display name} of\n\t * a {@code @RepeatedTest} method: <code>{displayName}</code>\n\t */\n\tString DISPLAY_NAME_PLACEHOLDER = \"{displayName}\";\n\n\t/**\n\t * Placeholder for the current repetition count of a {@code @RepeatedTest}\n\t * method: <code>{currentRepetition}</code>\n\t */\n\tString CURRENT_REPETITION_PLACEHOLDER = \"{currentRepetition}\";\n\n\t/**\n\t * Placeholder for the total number of repetitions of a {@code @RepeatedTest}\n\t * method: <code>{totalRepetitions}</code>\n\t */\n\tString TOTAL_REPETITIONS_PLACEHOLDER = \"{totalRepetitions}\";\n\n\t/**\n\t * <em>Short</em> display name pattern for a repeated test: {@value}\n\t *\n\t * @see #CURRENT_REPETITION_PLACEHOLDER\n\t * @see #TOTAL_REPETITIONS_PLACEHOLDER\n\t * @see #LONG_DISPLAY_NAME\n\t */\n\tString SHORT_DISPLAY_NAME = \"repetition \" + CURRENT_REPETITION_PLACEHOLDER + \" of \" + TOTAL_REPETITIONS_PLACEHOLDER;\n\n\t/**\n\t * <em>Long</em> display name pattern for a repeated test: {@value}\n\t *\n\t * @see #DISPLAY_NAME_PLACEHOLDER\n\t * @see #SHORT_DISPLAY_NAME\n\t */\n\tString LONG_DISPLAY_NAME = DISPLAY_NAME_PLACEHOLDER + \" :: \" + SHORT_DISPLAY_NAME;\n\n\t/**\n\t * The number of repetitions.\n\t *\n\t * @return the number of repetitions; must be greater than zero\n\t */\n\tint value();\n\n\t/**\n\t * The display name for each repetition of the repeated test.\n\t *\n\t * <h4>Supported placeholders</h4>\n\t * <ul>\n\t * <li>{@link #DISPLAY_NAME_PLACEHOLDER}</li>\n\t * <li>{@link #CURRENT_REPETITION_PLACEHOLDER}</li>\n\t * <li>{@link #TOTAL_REPETITIONS_PLACEHOLDER}</li>\n\t * </ul>\n\t *\n\t * <p>Defaults to {@link #SHORT_DISPLAY_NAME}, resulting in\n\t * names such as {@code \"repetition 1 of 2\"}, {@code \"repetition 2 of 2\"},\n\t * etc.\n\t *\n\t * <p>Can be set to <code>{@link #LONG_DISPLAY_NAME}</code>, resulting in\n\t * names such as {@code \"myRepeatedTest() :: repetition 1 of 2\"},\n\t * {@code \"myRepeatedTest() :: repetition 2 of 2\"}, etc.\n\t *\n\t * <p>Alternatively, you can provide a custom display name, optionally\n\t * using the aforementioned placeholders.\n\t *\n\t * @return a custom display name; never blank or consisting solely of\n\t * whitespace\n\t * @see #SHORT_DISPLAY_NAME\n\t * @see #LONG_DISPLAY_NAME\n\t * @see #DISPLAY_NAME_PLACEHOLDER\n\t * @see #CURRENT_REPETITION_PLACEHOLDER\n\t * @see #TOTAL_REPETITIONS_PLACEHOLDER\n\t * @see TestInfo#getDisplayName()\n\t */\n\tString name() default SHORT_DISPLAY_NAME;\n\n\t/**\n\t * Configures the number of failures after which remaining repetitions will\n\t * be automatically skipped.\n\t *\n\t * <p>Set this to a positive number less than the total {@linkplain #value()\n\t * number of repetitions} in order to skip the invocations of remaining\n\t * repetitions after the specified number of failures has been encountered.\n\t *\n\t * <p>For example, if you are using {@code @RepeatedTest} to repeatedly invoke\n\t * a test that you suspect to be <em>flaky</em>, a single failure is sufficient\n\t * to demonstrate that the test is flaky, and there is no need to invoke the\n\t * remaining repetitions. To support that specific use case, set\n\t * {@code failureThreshold = 1}. You can alternatively set the threshold to\n\t * a number greater than {@code 1} depending on your use case.\n\t *\n\t * <p>Defaults to {@link Integer#MAX_VALUE}, signaling that no failure\n\t * threshold will be applied, which effectively means that the specified\n\t * {@linkplain #value() number of repetitions} will be invoked regardless of\n\t * whether any repetitions fail.\n\t *\n\t * <p><strong>WARNING</strong>: if the repetitions of a {@code @RepeatedTest}\n\t * method are executed in parallel, no guarantees can be made regarding the\n\t * failure threshold. It is therefore recommended that a {@code @RepeatedTest}\n\t * method be annotated with\n\t * {@link org.junit.jupiter.api.parallel.Execution @Execution(SAME_THREAD)}\n\t * when parallel execution is configured.\n\t *\n\t * @since 5.10\n\t * @return the failure threshold; must be greater than zero and less than the\n\t * total number of repetitions\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint failureThreshold() default Integer.MAX_VALUE;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/RepetitionInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code RepetitionInfo} is used to inject information about the current\n * repetition of a repeated test into {@code @RepeatedTest}, {@code @BeforeEach},\n * and {@code @AfterEach} methods.\n *\n * <p>If a method parameter is of type {@code RepetitionInfo}, JUnit will\n * supply an instance of {@code RepetitionInfo} corresponding to the current\n * repeated test as the value for the parameter.\n *\n * <p><strong>WARNING</strong>: {@code RepetitionInfo} cannot be injected into\n * a {@code @BeforeEach} or {@code @AfterEach} method if the corresponding test\n * method is not a {@code @RepeatedTest}. Any attempt to do so will result in a\n * {@link org.junit.jupiter.api.extension.ParameterResolutionException\n * ParameterResolutionException}.\n *\n * @since 5.0\n * @see RepeatedTest\n * @see TestInfo\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface RepetitionInfo {\n\n\t/**\n\t * Get the current repetition of the corresponding\n\t * {@link RepeatedTest @RepeatedTest} method.\n\t */\n\tint getCurrentRepetition();\n\n\t/**\n\t * Get the total number of repetitions of the corresponding\n\t * {@link RepeatedTest @RepeatedTest} method.\n\t *\n\t * @see RepeatedTest#value\n\t */\n\tint getTotalRepetitions();\n\n\t/**\n\t * Get the current number of repetitions of the corresponding\n\t * {@link RepeatedTest @RepeatedTest} method that have ended in a failure.\n\t *\n\t * @since 5.10\n\t * @see #getFailureThreshold()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint getFailureCount();\n\n\t/**\n\t * Get the configured failure threshold of the corresponding\n\t * {@link RepeatedTest @RepeatedTest} method.\n\t *\n\t * @since 5.10\n\t * @see RepeatedTest#failureThreshold()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint getFailureThreshold();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tag.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Tag} is a {@linkplain Repeatable repeatable} annotation that is\n * used to declare a <em>tag</em> for the annotated test class or test method.\n *\n * <p>Tags are used to filter which tests are executed for a given test\n * plan. For example, a development team may tag tests with values such as\n * {@code \"fast\"}, {@code \"slow\"}, {@code \"ci-server\"}, etc. and then supply a\n * list of tags to be included in or excluded from the current test plan,\n * potentially dependent on the current environment.\n *\n * <h2>Syntax Rules for Tags</h2>\n * <ul>\n * <li>A tag must not be blank.</li>\n * <li>A <em>trimmed</em> tag must not contain whitespace.</li>\n * <li>A <em>trimmed</em> tag must not contain ISO control characters.</li>\n * <li>A <em>trimmed</em> tag must not contain any of the following\n * <em>reserved characters</em>.\n * <ul>\n * <li>{@code ,}: <em>comma</em></li>\n * <li>{@code (}: <em>left parenthesis</em></li>\n * <li>{@code )}: <em>right parenthesis</em></li>\n * <li>{@code &}: <em>ampersand</em></li>\n * <li>{@code |}: <em>vertical bar</em></li>\n * <li>{@code !}: <em>exclamation point</em></li>\n * </ul>\n * </li>\n * </ul>\n *\n * @since 5.0\n * @see Tags\n * @see Test\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(Tags.class)\n@API(status = STABLE, since = \"5.0\")\npublic @interface Tag {\n\n\t/**\n\t * The <em>tag</em>.\n\t *\n\t * <p>Note: the tag will first be {@linkplain String#strip() stripped}. If the\n\t * supplied tag is syntactically invalid after trimming, the error will be\n\t * logged as a warning, and the invalid tag will be effectively ignored. See\n\t * {@linkplain Tag Syntax Rules for Tags}.\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Tags.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Tags} is a container for one or more {@link Tag @Tag} declarations.\n *\n * <p>Note, however, that use of the {@code @Tags} container is completely\n * optional since {@code @Tag} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * @since 5.0\n * @see Tag\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.0\")\npublic @interface Tags {\n\n\t/**\n\t * An array of one or more {@link Tag Tags}.\n\t */\n\tTag[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Test.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Testable;\n\n/**\n * {@code @Test} is used to signal that the annotated method is a <em>test</em>\n * method.\n *\n * <p>{@code @Test} methods must not be {@code private} or {@code static} and\n * must not return a value.\n *\n * <p>{@code @Test} methods may optionally declare parameters to be resolved by\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>{@code @Test} may also be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of {@code @Test}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @Test} methods are inherited from superclasses as long as they are\n * not <em>overridden</em> according to the visibility rules of the Java language.\n * Similarly, {@code @Test} methods declared as <em>interface default methods</em>\n * are inherited as long as they are not overridden.\n *\n * <h2>Test Execution Order</h2>\n *\n * <p>By default, test methods will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute test methods in the same order, thereby allowing for\n * repeatable builds. In this context, a <em>test method</em> is any instance\n * method that is directly annotated or meta-annotated with {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or\n * {@code @TestTemplate}.\n *\n * <p>Although true <em>unit tests</em> typically should not rely on the order\n * in which they are executed, there are times when it is necessary to enforce\n * a specific test method execution order &mdash; for example, when writing\n * <em>integration tests</em> or <em>functional tests</em> where the sequence of\n * the tests is important, especially in conjunction with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <p>To control the order in which test methods are executed, annotate your\n * test class or test interface with {@link TestMethodOrder @TestMethodOrder}\n * and specify the desired {@link MethodOrderer} implementation.\n *\n * @since 5.0\n * @see RepeatedTest\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see TestTemplate\n * @see TestFactory\n * @see TestInfo\n * @see DisplayName\n * @see Tag\n * @see BeforeAll\n * @see AfterAll\n * @see BeforeEach\n * @see AfterEach\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\n@Testable\npublic @interface Test {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestClassOrder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @TestClassOrder} is a type-level annotation that is used to configure\n * a {@link #value ClassOrderer} for the {@link Nested @Nested} test classes of\n * the annotated test class.\n *\n * <p>If {@code @TestClassOrder} is not explicitly declared on a test class,\n * inherited from a parent class, declared on a test interface implemented by\n * a test class, or inherited from an {@linkplain Class#getEnclosingClass() enclosing\n * class}, {@code @Nested} test classes will be ordered using a default\n * algorithm that is deterministic but intentionally nonobvious.\n *\n * <p>As an alternative to {@code @TestClassOrder}, a global {@link ClassOrderer}\n * can be configured for the entire test suite via the\n * {@value ClassOrderer#DEFAULT_ORDER_PROPERTY_NAME} configuration parameter. See\n * the User Guide for details. Note, however, that a {@code @TestClassOrder}\n * declaration always overrides a global {@code ClassOrderer}.\n *\n * <h2>Example Usage</h2>\n *\n * <p>The following demonstrates how to guarantee that {@code @Nested} test classes\n * are executed in the order specified via the {@link Order @Order} annotation.\n *\n * <pre class=\"code\">\n * {@literal @}TestClassOrder(ClassOrderer.OrderAnnotation.class)\n * class OrderedNestedTests {\n *\n *     {@literal @}Nested\n *     {@literal @}Order(1)\n *     class PrimaryTests {\n *         // {@literal @}Test methods ...\n *     }\n *\n *     {@literal @}Nested\n *     {@literal @}Order(2)\n *     class SecondaryTests {\n *         // {@literal @}Test methods ...\n *     }\n * }</pre>\n *\n * @since 5.8\n * @see ClassOrderer\n * @see TestMethodOrder\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.10\")\npublic @interface TestClassOrder {\n\n\t/**\n\t * The {@link ClassOrderer} to use.\n\t *\n\t * @see ClassOrderer\n\t * @see ClassOrderer.ClassName\n\t * @see ClassOrderer.DisplayName\n\t * @see ClassOrderer.OrderAnnotation\n\t * @see ClassOrderer.Random\n\t */\n\tClass<? extends ClassOrderer> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Testable;\n\n/**\n * {@code @TestFactory} is used to signal that the annotated method is a\n * <em>test factory</em> method.\n *\n * <p>In contrast to {@link Test @Test} methods, a test factory is not itself\n * a test case but rather a factory for test cases.\n *\n * <p>{@code @TestFactory} methods must not be {@code private} or {@code static}\n * and must return a {@code Stream}, {@code Collection}, {@code Iterable},\n * {@code Iterator}, array of {@link DynamicNode} instances, or any type that\n * provides an {@link java.util.Iterator Iterator}-returning {@code iterator()}\n * method (such as, for example, a {@code kotlin.sequences.Sequence}). Supported\n * subclasses of {@code DynamicNode} include {@link DynamicContainer} and\n * {@link DynamicTest}. <em>Dynamic tests</em> will be executed lazily,\n * enabling dynamic and even non-deterministic generation of test cases.\n *\n * <p>Any {@code Stream} returned by a {@code @TestFactory} will be properly\n * closed by calling {@code stream.close()}, making it safe to use a resource\n * such as {@code Files.lines()} as the initial source of the stream.\n *\n * <p>{@code @TestFactory} methods may optionally declare parameters to be\n * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver\n * ParameterResolvers}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @TestFactory} methods are inherited from superclasses as long as\n * they are not <em>overridden</em> according to the visibility rules of the Java\n * language. Similarly, {@code @TestFactory} methods declared as <em>interface\n * default methods</em> are inherited as long as they are not overridden.\n *\n * <h2>Test Execution Order</h2>\n *\n * <p>By default, test methods will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute test methods in the same order, thereby allowing for\n * repeatable builds. In this context, a <em>test method</em> is any instance\n * method that is directly annotated or meta-annotated with {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or\n * {@code @TestTemplate}.\n *\n * <p>Although true <em>unit tests</em> typically should not rely on the order\n * in which they are executed, there are times when it is necessary to enforce\n * a specific test method execution order &mdash; for example, when writing\n * <em>integration tests</em> or <em>functional tests</em> where the sequence of\n * the tests is important, especially in conjunction with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <p>To control the order in which test methods are executed, annotate your\n * test class or test interface with {@link TestMethodOrder @TestMethodOrder}\n * and specify the desired {@link MethodOrderer} implementation.\n *\n * @since 5.0\n * @see Test\n * @see DynamicNode\n * @see DynamicTest\n * @see DynamicContainer\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = MAINTAINED, since = \"5.3\")\n@Testable\npublic @interface TestFactory {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestInfo} is used to inject information about the current test or\n * container into to {@code @Test}, {@code @RepeatedTest},\n * {@code @ParameterizedTest}, {@code @TestFactory}, {@code @BeforeEach},\n * {@code @AfterEach}, {@code @BeforeAll}, and {@code @AfterAll} methods.\n *\n * <p>If a method parameter is of type {@link TestInfo}, JUnit will supply\n * an instance of {@code TestInfo} corresponding to the current test or\n * container as the value for the parameter.\n *\n * @since 5.0\n * @see Test\n * @see RepeatedTest\n * @see TestFactory\n * @see BeforeEach\n * @see AfterEach\n * @see BeforeAll\n * @see AfterAll\n * @see DisplayName\n * @see Tag\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface TestInfo {\n\n\t/**\n\t * Get the display name of the current test or container.\n\t *\n\t * <p>The display name is either a default name or a custom name configured\n\t * via {@link DisplayName @DisplayName}.\n\t *\n\t * <h4>Default Display Names</h4>\n\t *\n\t * <p>If the context in which {@code TestInfo} is used is at the container\n\t * level, the default display name is generated based on the name of the\n\t * test class. For top-level and {@link Nested @Nested} test classes, the\n\t * default display name is the {@linkplain Class#getSimpleName simple name}\n\t * of the class. For {@code static} nested test classes, the default display\n\t * name is the default display name for the enclosing class concatenated with\n\t * the {@linkplain Class#getSimpleName simple name} of the {@code static}\n\t * nested class, separated by a dollar sign ({@code $}). For example, the\n\t * default display names for the following test classes are\n\t * {@code TopLevelTests}, {@code NestedTests}, and {@code TopLevelTests$StaticTests}.\n\t *\n\t * <pre class=\"code\">\n\t *   class TopLevelTests {\n\t *\n\t *      {@literal @}Nested\n\t *      class NestedTests {}\n\t *\n\t *      static class StaticTests {}\n\t *   }</pre>\n\t *\n\t * <p>If the context in which {@code TestInfo} is used is at the test level,\n\t * the default display name is the name of the test method concatenated with\n\t * a comma-separated list of {@linkplain Class#getSimpleName simple names}\n\t * of the parameter types in parentheses. For example, the default display\n\t * name for the following test method is {@code testUser(TestInfo, User)}.\n\t *\n\t * <pre class=\"code\">\n\t *   {@literal @}Test\n\t *   void testUser(TestInfo testInfo, {@literal @}Mock User user) {}</pre>\n\t *\n\t * <p>Note that display names are typically used for test reporting in IDEs\n\t * and build tools and may contain spaces, special characters, and even emoji.\n\t *\n\t * @return the display name of the test or container; never {@code null} or blank\n\t */\n\tString getDisplayName();\n\n\t/**\n\t * Get the set of all tags for the current test or container.\n\t *\n\t * <p>Tags may be declared directly on the test element or <em>inherited</em>\n\t * from an outer context.\n\t */\n\tSet<String> getTags();\n\n\t/**\n\t * Get the {@link Class} associated with the current test or container, if available.\n\t */\n\tOptional<Class<?>> getTestClass();\n\n\t/**\n\t * Get the {@link Method} associated with the current test or container, if available.\n\t */\n\tOptional<Method> getTestMethod();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestInstance.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.Execution;\n\n/**\n * {@code @TestInstance} is a type-level annotation that is used to configure\n * the {@linkplain Lifecycle lifecycle} of test instances for the annotated\n * test class or test interface.\n *\n * <p>If {@code @TestInstance} is not explicitly declared on a test class or\n * on a test interface implemented by a test class, the lifecycle mode will\n * implicitly default to {@link Lifecycle#PER_METHOD PER_METHOD}. Note, however,\n * that an explicit lifecycle mode is <em>inherited</em> within a test class\n * hierarchy. In addition, the <em>default</em> lifecycle mode may be overridden\n * via the {@value Lifecycle#DEFAULT_LIFECYCLE_PROPERTY_NAME} <em>configuration\n * parameter</em> which can be supplied via the {@code Launcher} API, build tools\n * (e.g., Gradle and Maven), a JVM system property, or the JUnit Platform\n * configuration file (i.e., a file named {@code junit-platform.properties} in\n * the root of the class path). Consult the User Guide for further information.\n *\n * <h2>Use Cases</h2>\n * <p>Setting the test instance lifecycle mode to {@link Lifecycle#PER_CLASS\n * PER_CLASS} enables the following features.\n * <ul>\n * <li>Shared test instance state between test methods in a given test class\n * as well as between non-static {@link BeforeAll @BeforeAll} and\n * {@link AfterAll @AfterAll} methods in the test class.</li>\n * <li>Declaration of non-static {@code @BeforeAll} and {@code @AfterAll} methods\n * in top-level or {@link Nested @Nested} test classes.</li>\n * <li>Declaration of {@code @BeforeAll} and {@code @AfterAll} on interface\n * {@code default} methods.</li>\n * <li>Simplified declaration of non-static {@code @BeforeAll} and {@code @AfterAll}\n * lifecycle methods as well as {@code @MethodSource} factory methods in test classes\n * implemented with the Kotlin programming language.</li>\n * </ul>\n *\n * <p>{@code @TestInstance} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @TestInstance}.\n *\n * <h2>Parallel Execution</h2>\n * <p>Using the {@link Lifecycle#PER_CLASS PER_CLASS} lifecycle mode disables\n * parallel execution unless the test class or test method is annotated with\n * {@link Execution @Execution(CONCURRENT)}.\n *\n * @since 5.0\n * @see Nested @Nested\n * @see Execution @Execution\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"5.0\")\npublic @interface TestInstance {\n\n\t/**\n\t * Enumeration of test instance lifecycle <em>modes</em>.\n\t *\n\t * @see #PER_METHOD\n\t * @see #PER_CLASS\n\t */\n\tenum Lifecycle {\n\n\t\t/**\n\t\t * When using this mode, a new test instance will be created once per\n\t\t * test class or class template.\n\t\t *\n\t\t * <p>For {@link Nested @Nested}</p> test classes declared inside an\n\t\t * enclosing {@link ClassTemplate @ClassTemplate}, an instance of the\n\t\t * {@code @Nested} class will be created for each invocation of the\n\t\t * {@code @ClassTemplate}.\n\t\t *\n\t\t * @see #PER_METHOD\n\t\t */\n\t\tPER_CLASS,\n\n\t\t/**\n\t\t * When using this mode, a new test instance will be created for each\n\t\t * test method, test factory method, or test template method.\n\t\t *\n\t\t * <p>This mode is analogous to the behavior found in JUnit versions 1\n\t\t * through 4.\n\t\t *\n\t\t * @see #PER_CLASS\n\t\t */\n\t\tPER_METHOD;\n\n\t\t/**\n\t\t * Property name used to set the default test instance lifecycle mode:\n\t\t * {@value}\n\t\t *\n\t\t * <h4>Supported Values</h4>\n\t\t *\n\t\t * <p>Supported values include names of enum constants defined in\n\t\t * {@link org.junit.jupiter.api.TestInstance.Lifecycle}, ignoring case.\n\t\t *\n\t\t * <p>If not specified, the default is \"per_method\" which corresponds to\n\t\t * {@code @TestInstance(Lifecycle.PER_METHOD)}.\n\t\t *\n\t\t * @since 5.0\n\t\t * @see org.junit.jupiter.api.TestInstance\n\t\t */\n\t\t@API(status = STABLE, since = \"5.9\")\n\t\tpublic static final String DEFAULT_LIFECYCLE_PROPERTY_NAME = \"junit.jupiter.testinstance.lifecycle.default\";\n\n\t}\n\n\t/**\n\t * The test instance lifecycle <em>mode</em> to use.\n\t */\n\tLifecycle value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestMethodOrder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.Execution;\n\n/**\n * {@code @TestMethodOrder} is a type-level annotation that is used to configure\n * a {@link #value MethodOrderer} for the <em>test methods</em> of the annotated\n * test class or test interface.\n *\n * <p>In this context, the term \"test method\" refers to any method annotated with\n * {@code @Test}, {@code @RepeatedTest}, {@code @ParameterizedTest},\n * {@code @TestFactory}, or {@code @TestTemplate}.\n *\n * <p>If {@code @TestMethodOrder} is not explicitly declared on a test class,\n * inherited from a parent class, declared on a test interface implemented by\n * a test class, or inherited from an {@linkplain Class#getEnclosingClass() enclosing\n * class}, test methods will be ordered using a default algorithm that is\n * deterministic but intentionally nonobvious.\n *\n * <p>As an alternative to {@code @TestMethodOrder}, a global {@link MethodOrderer}\n * can be configured for the entire test suite via the\n * {@value MethodOrderer#DEFAULT_ORDER_PROPERTY_NAME} configuration parameter. See\n * the User Guide for details. Note, however, that a {@code @TestClassOrder}\n * declaration always overrides a global {@code ClassOrderer}.\n *\n * <h2>Example Usage</h2>\n *\n * <p>The following demonstrates how to guarantee that test methods are executed\n * in the order specified via the {@link Order @Order} annotation.\n *\n * <pre class=\"code\">\n * {@literal @}TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n * class OrderedTests {\n *\n *     {@literal @}Test\n *     {@literal @}Order(1)\n *     void nullValues() {}\n *\n *     {@literal @}Test\n *     {@literal @}Order(2)\n *     void emptyValues() {}\n *\n *     {@literal @}Test\n *     {@literal @}Order(3)\n *     void validValues() {}\n * }</pre>\n *\n * <h2>Parallel Execution</h2>\n * <p>Using a {@link MethodOrderer} disables parallel execution unless the test\n * class or test method is annotated with\n * {@link Execution @Execution(CONCURRENT)}.\n *\n * @since 5.4\n * @see MethodOrderer\n * @see TestClassOrder\n */\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\npublic @interface TestMethodOrder {\n\n\t/**\n\t * The {@link MethodOrderer} to use.\n\t *\n\t * @see MethodOrderer\n\t * @see MethodOrderer.MethodName\n\t * @see MethodOrderer.DisplayName\n\t * @see MethodOrderer.OrderAnnotation\n\t * @see MethodOrderer.Random\n\t */\n\tClass<? extends MethodOrderer> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestReporter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.nio.file.StandardCopyOption.REPLACE_EXISTING;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Parameters of type {@code TestReporter} can be injected into\n * {@link BeforeEach @BeforeEach} and {@link AfterEach @AfterEach} lifecycle\n * methods as well as methods annotated with {@link Test @Test},\n * {@link RepeatedTest @RepeatedTest},\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest},\n * {@link TestFactory @TestFactory}, etc.\n *\n * <p>Within such methods the injected {@code TestReporter} can be used to\n * publish <em>report entries</em> for the current container or test to the\n * reporting infrastructure.\n *\n * @since 5.0\n * @see #publishEntry(Map)\n * @see #publishEntry(String, String)\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface TestReporter {\n\n\t/**\n\t * Publish the supplied map of key-value pairs as a <em>report entry</em>.\n\t *\n\t * @param map the key-value pairs to be published; never {@code null};\n\t * keys and values within entries in the map also must not be\n\t * {@code null} or blank\n\t * @see #publishEntry(String, String)\n\t * @see #publishEntry(String)\n\t */\n\tvoid publishEntry(Map<String, String> map);\n\n\t/**\n\t * Publish the supplied key-value pair as a <em>report entry</em>.\n\t *\n\t * @param key the key of the entry to publish; never {@code null} or blank\n\t * @param value the value of the entry to publish; never {@code null} or blank\n\t * @see #publishEntry(Map)\n\t * @see #publishEntry(String)\n\t */\n\tdefault void publishEntry(String key, String value) {\n\t\tPreconditions.notBlank(key, \"key must not be null or blank\");\n\t\tPreconditions.notBlank(value, \"value must not be null or blank\");\n\t\tpublishEntry(Map.of(key, value));\n\t}\n\n\t/**\n\t * Publish the supplied value as a <em>report entry</em>.\n\t *\n\t * <p>This method delegates to {@link #publishEntry(String, String)},\n\t * supplying {@code \"value\"} as the key and the supplied {@code value}\n\t * argument as the value.\n\t *\n\t * @param value the value to be published; never {@code null} or blank\n\t * @since 5.3\n\t * @see #publishEntry(Map)\n\t * @see #publishEntry(String, String)\n\t */\n\t@API(status = STABLE, since = \"5.3\")\n\tdefault void publishEntry(String value) {\n\t\tpublishEntry(\"value\", value);\n\t}\n\n\t/**\n\t * Publish the supplied file and attach it to the current test or container.\n\t *\n\t * <p>The file will be copied to the report output directory replacing any\n\t * potentially existing file with the same name.\n\t *\n\t * @param file the file to be published; never {@code null}\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link org.junit.jupiter.api.extension.MediaType#APPLICATION_OCTET_STREAM}\n\t * if unknown\n\t * @since 5.12\n\t * @deprecated Use {@link #publishFile(Path, MediaType)} instead.\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\t@SuppressWarnings(\"removal\")\n\tdefault void publishFile(Path file, org.junit.jupiter.api.extension.MediaType mediaType) {\n\t\tPreconditions.notNull(mediaType, \"mediaType must not be null\");\n\t\tpublishFile(file, MediaType.parse(mediaType.toString()));\n\t}\n\n\t/**\n\t * Publish the supplied file and attach it to the current test or container.\n\t *\n\t * <p>The file will be copied to the report output directory replacing any\n\t * potentially existing file with the same name.\n\t *\n\t * @param file the file to be published; never {@code null}\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link MediaType#APPLICATION_OCTET_STREAM} if unknown\n\t * @since 5.14\n\t */\n\t@API(status = MAINTAINED, since = \"5.14\")\n\tdefault void publishFile(Path file, MediaType mediaType) {\n\t\tPreconditions.notNull(file, \"file must not be null\");\n\t\tPreconditions.notNull(mediaType, \"mediaType must not be null\");\n\t\tPreconditions.condition(Files.exists(file), () -> \"file must exist: \" + file);\n\t\tPreconditions.condition(Files.isRegularFile(file), () -> \"file must be a regular file: \" + file);\n\t\tpublishFile(file.getFileName().toString(), mediaType, path -> Files.copy(file, path, REPLACE_EXISTING));\n\t}\n\n\t/**\n\t * Publish the supplied directory and attach it to the current test or\n\t * container.\n\t *\n\t * <p>The entire directory will be copied to the report output directory\n\t * replacing any potentially existing files with the same name.\n\t *\n\t * @param directory the directory to be published; never {@code null}\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault void publishDirectory(Path directory) {\n\t\tPreconditions.notNull(directory, \"directory must not be null\");\n\t\tPreconditions.condition(Files.exists(directory), () -> \"directory must exist: \" + directory);\n\t\tPreconditions.condition(Files.isDirectory(directory), () -> \"path must represent a directory: \" + directory);\n\t\tpublishDirectory(directory.getFileName().toString(), path -> {\n\t\t\ttry (Stream<Path> stream = Files.walk(directory)) {\n\t\t\t\tstream.forEach(source -> {\n\t\t\t\t\tPath destination = path.resolve(directory.relativize(source));\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (Files.isDirectory(source)) {\n\t\t\t\t\t\t\tFiles.createDirectories(destination);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tFiles.copy(source, destination, REPLACE_EXISTING);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (IOException e) {\n\t\t\t\t\t\tthrow new UncheckedIOException(\"Failed to copy files to the output directory\", e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Publish a file with the supplied name and media type written by the supplied\n\t * action and attach it to the current test or container.\n\t *\n\t * <p>The {@link Path} passed to the supplied action will be relative to the\n\t * report output directory, but it is up to the action to write the file.\n\t *\n\t * @param name the name of the file to be published; never {@code null} or\n\t * blank and must not contain any path separators\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link org.junit.jupiter.api.extension.MediaType#APPLICATION_OCTET_STREAM}\n\t * if unknown\n\t * @param action the action to be executed to write the file; never {@code null}\n\t * @since 5.12\n\t * @deprecated Use {@link #publishFile(String, MediaType, ThrowingConsumer)} instead.\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\t@SuppressWarnings(\"removal\")\n\tdefault void publishFile(String name, org.junit.jupiter.api.extension.MediaType mediaType,\n\t\t\tThrowingConsumer<Path> action) {\n\n\t\tPreconditions.notNull(mediaType, \"mediaType must not be null\");\n\t\tpublishFile(name, MediaType.parse(mediaType.toString()), action);\n\t}\n\n\t/**\n\t * Publish a file with the supplied name and media type written by the supplied\n\t * action and attach it to the current test or container.\n\t *\n\t * <p>The {@link Path} passed to the supplied action will be relative to the\n\t * report output directory, but it is up to the action to write the file.\n\t *\n\t * @param name the name of the file to be published; never {@code null} or\n\t * blank and must not contain any path separators\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link MediaType#APPLICATION_OCTET_STREAM} if unknown\n\t * @param action the action to be executed to write the file; never {@code null}\n\t * @since 5.14\n\t */\n\t@API(status = MAINTAINED, since = \"5.14\")\n\tdefault void publishFile(String name, MediaType mediaType, ThrowingConsumer<Path> action) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t/**\n\t * Publish a directory with the supplied name written by the supplied action\n\t * and attach it to the current test or container.\n\t *\n\t * <p>The {@link Path} passed to the supplied action will be relative to the\n\t * report output directory and will point to an existing directory, but it is\n\t * up to the action to write files to the directory.\n\t *\n\t * @param name the name of the directory to be published; never {@code null}\n\t * or blank and must not contain any path separators\n\t * @param action the action to be executed to write to the directory; never\n\t * {@code null}\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault void publishDirectory(String name, ThrowingConsumer<Path> action) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/TestTemplate.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Testable;\n\n/**\n * {@code @TestTemplate} is used to signal that the annotated method is a\n * <em>test template</em> method.\n *\n * <p>In contrast to {@link Test @Test} methods, a test template is not itself\n * a test case but rather a template for test cases. As such, it is designed to\n * be invoked multiple times depending on the number of {@linkplain\n * org.junit.jupiter.api.extension.TestTemplateInvocationContext invocation\n * contexts} returned by the registered {@linkplain\n * org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider\n * providers}. Must be used together with at least one provider. Otherwise,\n * execution will fail.\n *\n * <p>Each invocation of a test template method behaves like the execution of\n * a regular {@link Test @Test} method with full support for the same lifecycle\n * callbacks and extensions.\n *\n * <p>{@code @TestTemplate} methods must not be {@code private} or {@code static}\n * and must return {@code void}.\n *\n * <p>{@code @TestTemplate} methods may optionally declare parameters to be\n * resolved by {@link org.junit.jupiter.api.extension.ParameterResolver\n * ParameterResolvers}.\n *\n * <p>{@code @TestTemplate} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @TestTemplate}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @TestTemplate} methods are inherited from superclasses as long as\n * they are not <em>overridden</em> according to the visibility rules of the Java\n * language. Similarly, {@code @TestTemplate} methods declared as <em>interface\n * default methods</em> are inherited as long as they are not overridden.\n *\n * <h2>Test Execution Order</h2>\n *\n * <p>By default, test methods will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute test methods in the same order, thereby allowing for\n * repeatable builds. In this context, a <em>test method</em> is any instance\n * method that is directly annotated or meta-annotated with {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or\n * {@code @TestTemplate}.\n *\n * <p>Although true <em>unit tests</em> typically should not rely on the order\n * in which they are executed, there are times when it is necessary to enforce\n * a specific test method execution order &mdash; for example, when writing\n * <em>integration tests</em> or <em>functional tests</em> where the sequence of\n * the tests is important, especially in conjunction with\n * {@link TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <p>To control the order in which test methods are executed, annotate your\n * test class or test interface with {@link TestMethodOrder @TestMethodOrder}\n * and specify the desired {@link MethodOrderer} implementation.\n *\n * @since 5.0\n * @see Test\n * @see ClassTemplate\n * @see org.junit.jupiter.api.extension.TestTemplateInvocationContext\n * @see org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.0\")\n@Testable\npublic @interface TestTemplate {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/Timeout.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Timeout} is used to define a timeout for a method or all testable\n * methods within one class and its {@link Nested @Nested} classes.\n *\n * <p>This annotation may also be used on lifecycle methods annotated with\n * {@link BeforeAll @BeforeAll}, {@link BeforeEach @BeforeEach},\n * {@link AfterEach @AfterEach}, or {@link AfterAll @AfterAll}.\n *\n * <p>Applying this annotation to a test class has the same effect as applying\n * it to all testable methods, i.e. all methods annotated or meta-annotated with\n * {@link Test @Test}, {@link TestFactory @TestFactory}, or\n * {@link TestTemplate @TestTemplate}, but not to its lifecycle methods.\n *\n * <h2>Default Timeouts</h2>\n *\n * <p>If this annotation is not present, no timeout will be used unless a\n * default timeout is defined via one of the following configuration parameters:\n *\n * <dl>\n *     <dt>{@value #DEFAULT_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for all testable and lifecycle methods</dd>\n *     <dt>{@value #DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for all testable methods</dd>\n *     <dt>{@value #DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link Test @Test} methods</dd>\n *     <dt>{@value #DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link TestTemplate @TestTemplate} methods</dd>\n *     <dt>{@value #DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link TestFactory @TestFactory} methods</dd>\n *     <dt>{@value #DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for all lifecycle methods</dd>\n *     <dt>{@value #DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link BeforeAll @BeforeAll} methods</dd>\n *     <dt>{@value #DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link BeforeEach @BeforeEach} methods</dd>\n *     <dt>{@value #DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link AfterEach @AfterEach} methods</dd>\n *     <dt>{@value #DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME}</dt>\n *     <dd>Default timeout for {@link AfterAll @AfterAll} methods</dd>\n * </dl>\n *\n * <p>More specific configuration parameters override less specific ones. For\n * example, {@value #DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME}\n * overrides {@value #DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME}\n * which overrides {@value #DEFAULT_TIMEOUT_PROPERTY_NAME}.\n *\n * <h3 id=\"supported-values\">Supported Values</h3>\n *\n * <p>Values for timeouts must be in the following, case-insensitive format:\n * {@code <number> [ns|μs|ms|s|m|h|d]}. The space between the number and the\n * unit may be omitted. Specifying no unit is equivalent to using seconds.\n *\n * <table class=\"plain\">\n * <caption>Timeout configuration via configuration parameter vs. annotation</caption>\n * <tr><th> Value         </th><th> Equivalent annotation                             </th></tr>\n * <tr><td> {@code 42}    </td><td> {@code @Timeout(42)}                              </td></tr>\n * <tr><td> {@code 42 ns} </td><td> {@code @Timeout(value = 42, unit = NANOSECONDS)}  </td></tr>\n * <tr><td> {@code 42 μs} </td><td> {@code @Timeout(value = 42, unit = MICROSECONDS)} </td></tr>\n * <tr><td> {@code 42 ms} </td><td> {@code @Timeout(value = 42, unit = MILLISECONDS)} </td></tr>\n * <tr><td> {@code 42 s}  </td><td> {@code @Timeout(value = 42, unit = SECONDS)}      </td></tr>\n * <tr><td> {@code 42 m}  </td><td> {@code @Timeout(value = 42, unit = MINUTES)}      </td></tr>\n * <tr><td> {@code 42 h}  </td><td> {@code @Timeout(value = 42, unit = HOURS)}        </td></tr>\n * <tr><td> {@code 42 d}  </td><td> {@code @Timeout(value = 42, unit = DAYS)}         </td></tr>\n * </table>\n *\n * <h2>Disabling Timeouts</h2>\n *\n * <p>You may use the {@value #TIMEOUT_MODE_PROPERTY_NAME} configuration\n * parameter to explicitly enable or disable timeouts.\n *\n * <p>Supported values:\n * <ul>\n * <li>{@code enabled}: enables timeouts\n * <li>{@code disabled}: disables timeouts\n * <li>{@code disabled_on_debug}: disables timeouts while debugging\n * </ul>\n *\n * @since 5.5\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\npublic @interface Timeout {\n\n\t/**\n\t * Property name used to set the default timeout for all testable and\n\t * lifecycle methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a more\n\t * specific property or a {@link Timeout @Timeout}\n\t * annotation present on the method or on an enclosing test class (for\n\t * testable methods).\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all testable methods:\n\t * {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a more\n\t * specific property or a {@link Timeout @Timeout}\n\t * annotation present on the testable method or on an enclosing test class.\n\t *\n\t * <p>This property overrides the {@value #DEFAULT_TIMEOUT_PROPERTY_NAME}\n\t * property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.testable.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all {@link Test @Test}\n\t * methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the {@link Test @Test}\n\t * method or on an enclosing test class.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.test.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestTemplate @TestTemplate} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link TestTemplate @TestTemplate} method or on an enclosing test class.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.testtemplate.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestFactory @TestFactory} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link TestFactory @TestFactory} method or on an enclosing test class.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.testfactory.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all lifecycle methods:\n\t * {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a more\n\t * specific property or a {@link Timeout @Timeout} annotation present on the\n\t * lifecycle method.\n\t *\n\t * <p>This property overrides the {@value #DEFAULT_TIMEOUT_PROPERTY_NAME}\n\t * property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.lifecycle.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeAll @BeforeAll} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link BeforeAll @BeforeAll} method.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.beforeall.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeEach @BeforeEach} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link BeforeEach @BeforeEach} method.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.beforeeach.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterEach @AfterEach} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link AfterEach @AfterEach} method.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.aftereach.method.default\";\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterAll @AfterAll} methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the\n\t * {@link AfterAll @AfterAll} method.\n\t *\n\t * <p>This property overrides the\n\t * {@value #DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME} property.\n\t *\n\t * <p>Please refer to the <a href=\"#supported-values\">class\n\t * description</a> for the definition of supported values.\n\t *\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME = \"junit.jupiter.execution.timeout.afterall.method.default\";\n\n\t/**\n\t * Property name used to configure whether timeouts are applied to tests:\n\t * {@value}.\n\t *\n\t * <p>The value of this property will be used to toggle whether\n\t * {@link Timeout @Timeout} is applied to tests.</p>\n\t *\n\t * <h4>Supported timeout mode values (case insensitive):</h4>\n\t * <ul>\n\t * <li>{@code ENABLED}: enables timeouts\n\t * <li>{@code DISABLED}: disables timeouts\n\t * <li>{@code DISABLED_ON_DEBUG}: disables timeouts while debugging\n\t * </ul>\n\t *\n\t * <p>If not specified, the default is {@code ENABLED}.\n\t *\n\t * @since 5.6\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString TIMEOUT_MODE_PROPERTY_NAME = \"junit.jupiter.execution.timeout.mode\";\n\n\t/**\n\t * Property name used to set the default thread mode for all testable and\n\t * lifecycle methods: {@value}.\n\t *\n\t * <p>The value of this property will be used unless overridden by a\n\t * {@link Timeout @Timeout} annotation present on the method or on an\n\t * enclosing test class (for testable methods).\n\t *\n\t * <p>The supported values are {@code SAME_THREAD} or\n\t * {@code SEPARATE_THREAD}, ignoring case. If none is provided,\n\t * {@code SAME_THREAD} is used as default.\n\t *\n\t * @since 5.9\n\t * @see #threadMode()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME = \"junit.jupiter.execution.timeout.thread.mode.default\";\n\n\t/**\n\t * The duration of this timeout.\n\t *\n\t * @return timeout duration; must be a positive number\n\t */\n\tlong value();\n\n\t/**\n\t * The time unit of this timeout.\n\t *\n\t * @return time unit\n\t * @see TimeUnit\n\t */\n\tTimeUnit unit() default TimeUnit.SECONDS;\n\n\t/**\n\t * The thread mode of this timeout.\n\t *\n\t * @return thread mode\n\t * @since 5.9\n\t * @see ThreadMode\n\t * @see #DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME\n\t */\n\t@API(status = STABLE, since = \"5.11\")\n\tThreadMode threadMode() default ThreadMode.INFERRED;\n\n\t/**\n\t * {@code ThreadMode} is used to define whether test code should be executed\n\t * in the thread of the calling code or in a separate thread.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.11\")\n\tenum ThreadMode {\n\t\t/**\n\t\t * The thread mode is determined using the parameter configured in property\n\t\t * {@value Timeout#DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME}.\n\t\t */\n\t\tINFERRED,\n\n\t\t/**\n\t\t * The test code is executed in the thread of the calling code.\n\t\t */\n\t\tSAME_THREAD,\n\n\t\t/**\n\t\t * The test code is executed in a different thread than that of the calling code. Furthermore,\n\t\t * execution of the test code will be preemptively aborted if the timeout is exceeded. See the\n\t\t * {@linkplain Assertions Preemptive Timeouts} section of the class-level\n\t\t * Javadoc for a discussion of possible undesirable side effects.\n\t\t */\n\t\tSEPARATE_THREAD,\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static java.util.function.Predicate.isEqual;\n\nimport java.lang.annotation.Annotation;\nimport java.util.Arrays;\nimport java.util.function.Function;\nimport java.util.stream.IntStream;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Abstract base class for {@link EnabledOnJreCondition} and\n * {@link DisabledOnJreCondition}.\n *\n * @since 5.12\n */\nabstract class AbstractJreCondition<A extends Annotation> extends BooleanExecutionCondition<A> {\n\n\tstatic final String ENABLED_ON_CURRENT_JRE = //\n\t\t\"Enabled on JRE version: \" + System.getProperty(\"java.version\");\n\n\tstatic final String DISABLED_ON_CURRENT_JRE = //\n\t\t\"Disabled on JRE version: \" + System.getProperty(\"java.version\");\n\n\tAbstractJreCondition(Class<A> annotationType, Function<A, String> customDisabledReason) {\n\t\tsuper(annotationType, ENABLED_ON_CURRENT_JRE, DISABLED_ON_CURRENT_JRE, customDisabledReason);\n\t}\n\n\tprotected final IntStream validatedVersions(JRE[] jres, int[] versions) {\n\t\tString annotationName = super.annotationType.getSimpleName();\n\n\t\tPreconditions.condition(jres.length > 0 || versions.length > 0,\n\t\t\t() -> \"You must declare at least one JRE or version in @\" + annotationName);\n\n\t\tPreconditions.condition(Arrays.stream(jres).noneMatch(isEqual(JRE.UNDEFINED)),\n\t\t\t() -> \"JRE.UNDEFINED is not supported in @\" + annotationName);\n\t\tArrays.stream(versions).min().ifPresent(version -> Preconditions.condition(version >= JRE.MINIMUM_VERSION,\n\t\t\t() -> \"Version [%d] in @%s must be greater than or equal to %d\".formatted(version, annotationName,\n\t\t\t\tJRE.MINIMUM_VERSION)));\n\n\t\treturn IntStream.concat(//\n\t\t\tArrays.stream(jres).mapToInt(JRE::version), //\n\t\t\tArrays.stream(versions) //\n\t\t).distinct();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractJreRangeCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.AbstractJreCondition.DISABLED_ON_CURRENT_JRE;\nimport static org.junit.jupiter.api.condition.AbstractJreCondition.ENABLED_ON_CURRENT_JRE;\n\nimport java.lang.annotation.Annotation;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Abstract base class for {@link EnabledForJreRangeCondition} and\n * {@link DisabledForJreRangeCondition}.\n *\n * @since 5.12\n */\nabstract class AbstractJreRangeCondition<A extends Annotation> extends BooleanExecutionCondition<A> {\n\n\tprivate static final JRE DEFAULT_MINIMUM_JRE = JRE.JAVA_17;\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static final JRE DEFAULT_MAXIMUM_JRE = JRE.OTHER;\n\n\tAbstractJreRangeCondition(Class<A> annotationType, Function<A, String> customDisabledReason) {\n\t\tsuper(annotationType, ENABLED_ON_CURRENT_JRE, DISABLED_ON_CURRENT_JRE, customDisabledReason);\n\t}\n\n\tprotected final boolean isCurrentVersionWithinRange(JRE minJre, JRE maxJre, int minVersion, int maxVersion) {\n\t\tString annotationName = super.annotationType.getSimpleName();\n\n\t\tboolean minJreSet = minJre != JRE.UNDEFINED;\n\t\tboolean maxJreSet = maxJre != JRE.UNDEFINED;\n\t\tboolean minVersionSet = minVersion != JRE.UNDEFINED_VERSION;\n\t\tboolean maxVersionSet = maxVersion != JRE.UNDEFINED_VERSION;\n\n\t\t// Users must choose between JRE enum constants and version numbers.\n\t\tPreconditions.condition(!minJreSet || !minVersionSet,\n\t\t\t() -> \"@%s's minimum value must be configured with either a JRE enum constant or numeric version, but not both\".formatted(\n\t\t\t\tannotationName));\n\t\tPreconditions.condition(!maxJreSet || !maxVersionSet,\n\t\t\t() -> \"@%s's maximum value must be configured with either a JRE enum constant or numeric version, but not both\".formatted(\n\t\t\t\tannotationName));\n\n\t\t// Users must supply valid values for minVersion and maxVersion.\n\t\tPreconditions.condition(!minVersionSet || (minVersion >= JRE.MINIMUM_VERSION),\n\t\t\t() -> \"@%s's minVersion [%d] must be greater than or equal to %d\".formatted(annotationName, minVersion,\n\t\t\t\tJRE.MINIMUM_VERSION));\n\t\tPreconditions.condition(!maxVersionSet || (maxVersion >= JRE.MINIMUM_VERSION),\n\t\t\t() -> \"@%s's maxVersion [%d] must be greater than or equal to %d\".formatted(annotationName, maxVersion,\n\t\t\t\tJRE.MINIMUM_VERSION));\n\n\t\t// Now that we have checked the basic preconditions, we need to ensure that we are\n\t\t// using valid JRE enum constants.\n\t\tif (!minJreSet) {\n\t\t\tminJre = DEFAULT_MINIMUM_JRE;\n\t\t}\n\t\tif (!maxJreSet) {\n\t\t\tmaxJre = DEFAULT_MAXIMUM_JRE;\n\t\t}\n\n\t\tint min = (minVersionSet ? minVersion : minJre.version());\n\t\tint max = (maxVersionSet ? maxVersion : maxJre.version());\n\n\t\t// Finally, we need to validate the effective minimum and maximum values.\n\t\tPreconditions.condition((min != DEFAULT_MINIMUM_JRE.version() || max != DEFAULT_MAXIMUM_JRE.version()),\n\t\t\t() -> \"You must declare a non-default value for the minimum or maximum value in @\" + annotationName);\n\t\tPreconditions.condition(min <= max,\n\t\t\t() -> \"@%s's minimum value [%d] must be less than or equal to its maximum value [%d]\".formatted(\n\t\t\t\tannotationName, min, max));\n\n\t\treturn JRE.isCurrentVersionWithinRange(min, max);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractOsBasedExecutionCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.annotation.Annotation;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Base class for OS-based {@link ExecutionCondition} implementations.\n *\n * @since 5.9\n */\nabstract class AbstractOsBasedExecutionCondition<A extends Annotation> implements ExecutionCondition {\n\n\tstatic final String CURRENT_ARCHITECTURE = System.getProperty(\"os.arch\");\n\tstatic final String CURRENT_OS = System.getProperty(\"os.name\");\n\n\tprivate final Class<A> annotationType;\n\n\tAbstractOsBasedExecutionCondition(Class<A> annotationType) {\n\t\tthis.annotationType = annotationType;\n\t}\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\treturn findAnnotation(context.getElement(), this.annotationType) //\n\t\t\t\t.map(this::evaluateExecutionCondition) //\n\t\t\t\t.orElseGet(this::enabledByDefault);\n\t}\n\n\tabstract ConditionEvaluationResult evaluateExecutionCondition(A annotation);\n\n\tString createReason(boolean enabled, boolean osSpecified, boolean archSpecified) {\n\t\tStringBuilder reason = new StringBuilder() //\n\t\t\t\t.append(enabled ? \"Enabled\" : \"Disabled\") //\n\t\t\t\t.append(osSpecified ? \" on operating system: \" : \" on architecture: \");\n\n\t\tif (osSpecified && archSpecified) {\n\t\t\treason.append(\"%s (%s)\".formatted(CURRENT_OS, CURRENT_ARCHITECTURE));\n\t\t}\n\t\telse if (osSpecified) {\n\t\t\treason.append(CURRENT_OS);\n\t\t}\n\t\telse {\n\t\t\treason.append(CURRENT_ARCHITECTURE);\n\t\t}\n\n\t\treturn reason.toString();\n\t}\n\n\tprivate ConditionEvaluationResult enabledByDefault() {\n\t\tString reason = \"@%s is not present\".formatted(this.annotationType.getSimpleName());\n\t\treturn enabled(reason);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/AbstractRepeatableAnnotationCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Repeatable;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\n\n/**\n * Abstract base class for {@link ExecutionCondition} implementations that support\n * {@linkplain Repeatable repeatable} annotations.\n *\n * @param <A> the type of repeatable annotation supported by this {@code ExecutionCondition}\n * @since 5.6\n */\nabstract class AbstractRepeatableAnnotationCondition<A extends Annotation> implements ExecutionCondition {\n\n\tprivate final Logger logger = LoggerFactory.getLogger(getClass());\n\n\tprivate final Class<A> annotationType;\n\n\tAbstractRepeatableAnnotationCondition(Class<A> annotationType) {\n\t\tthis.annotationType = annotationType;\n\t}\n\n\t@Override\n\tpublic final ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tOptional<AnnotatedElement> optionalElement = context.getElement();\n\t\tif (optionalElement.isPresent()) {\n\t\t\tAnnotatedElement annotatedElement = optionalElement.get();\n\t\t\t// @formatter:off\n\t\t\treturn findRepeatableAnnotations(annotatedElement, this.annotationType).stream()\n\t\t\t\t\t.map(annotation -> {\n\t\t\t\t\t\tConditionEvaluationResult result = evaluate(annotation);\n\t\t\t\t\t\tlogResult(annotation, annotatedElement, result);\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t})\n\t\t\t\t\t.filter(ConditionEvaluationResult::isDisabled)\n\t\t\t\t\t.findFirst()\n\t\t\t\t\t.orElse(getNoDisabledConditionsEncounteredResult());\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn getNoDisabledConditionsEncounteredResult();\n\t}\n\n\tprotected abstract ConditionEvaluationResult evaluate(A annotation);\n\n\tprotected abstract ConditionEvaluationResult getNoDisabledConditionsEncounteredResult();\n\n\tprivate void logResult(A annotation, AnnotatedElement annotatedElement, ConditionEvaluationResult result) {\n\t\tlogger.trace(() -> \"Evaluation of %s on [%s] resulted in: %s\".formatted(annotation, annotatedElement, result));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/BooleanExecutionCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.annotation.Annotation;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\nabstract class BooleanExecutionCondition<A extends Annotation> implements ExecutionCondition {\n\n\tprotected final Class<A> annotationType;\n\tprivate final String enabledReason;\n\tprivate final String disabledReason;\n\tprivate final Function<A, String> customDisabledReason;\n\n\tBooleanExecutionCondition(Class<A> annotationType, String enabledReason, String disabledReason,\n\t\t\tFunction<A, String> customDisabledReason) {\n\n\t\tthis.annotationType = annotationType;\n\t\tthis.enabledReason = enabledReason;\n\t\tthis.disabledReason = disabledReason;\n\t\tthis.customDisabledReason = customDisabledReason;\n\t}\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\treturn findAnnotation(context.getElement(), this.annotationType) //\n\t\t\t\t.map(annotation -> isEnabled(annotation) ? enabled(this.enabledReason)\n\t\t\t\t\t\t: disabled(this.disabledReason, this.customDisabledReason.apply(annotation))) //\n\t\t\t\t.orElseGet(this::enabledByDefault);\n\t}\n\n\tabstract boolean isEnabled(A annotation);\n\n\tprivate ConditionEvaluationResult enabledByDefault() {\n\t\tString reason = \"@%s is not present\".formatted(this.annotationType.getSimpleName());\n\t\treturn enabled(reason);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRange.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledForJreRange} is used to signal that the annotated test class\n * or test method is <em>disabled</em> for a specific range of Java Runtime\n * Environment (JRE) versions.\n *\n * <p>Version ranges can be specified as {@link JRE} enum constants via\n * {@link #min min} and {@link #max max} or as integers via\n * {@link #minVersion minVersion} and {@link #maxVersion maxVersion}.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be disabled on the same specified JRE versions.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} (i.e., test\n * interface, test class, or test method). If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.6\n * @see JRE\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(DisabledForJreRangeCondition.class)\n@API(status = STABLE, since = \"5.6\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledForJreRange {\n\n\t/**\n\t * Java Runtime Environment version which is used as the lower boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be disabled, specified as a {@link JRE} enum constant.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the minimum version via\n\t * {@link #minVersion() minVersion} instead.\n\t *\n\t * <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted\n\t * as {@link JRE#JAVA_17 JAVA_17} if the {@link #minVersion() minVersion} is\n\t * not set.\n\t *\n\t * @see JRE\n\t * @see #minVersion()\n\t */\n\tJRE min() default JRE.UNDEFINED;\n\n\t/**\n\t * Java Runtime Environment version which is used as the upper boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be disabled, specified as a {@link JRE} enum constant.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the maximum version via\n\t * {@link #maxVersion() maxVersion} instead.\n\t *\n\t * <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted\n\t * as {@link JRE#OTHER OTHER} if the {@link #maxVersion() maxVersion} is not\n\t * set.\n\t *\n\t * @see JRE\n\t * @see #maxVersion()\n\t */\n\tJRE max() default JRE.UNDEFINED;\n\n\t/**\n\t * Java Runtime Environment version which is used as the lower boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be disabled, specified as an integer.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for the particular JRE version,\n\t * you can specify the minimum version via {@link #min() min} instead.\n\t *\n\t * <p>Defaults to {@code -1} to signal that {@link #min() min} should be used\n\t * instead.\n\t *\n\t * @since 5.12\n\t * @see #min()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint minVersion() default -1;\n\n\t/**\n\t * Java Runtime Environment version which is used as the upper boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be disabled, specified as an integer.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for the particular JRE version,\n\t * you can specify the maximum version via {@link #max() max} instead.\n\t *\n\t * <p>Defaults to {@code -1} to signal that {@link #max() max} should be used\n\t * instead.\n\t *\n\t * @since 5.12\n\t * @see #max()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint maxVersion() default -1;\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledForJreRangeCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledForJreRange @DisabledForJreRange}.\n *\n * @since 5.6\n * @see DisabledForJreRange\n */\nclass DisabledForJreRangeCondition extends AbstractJreRangeCondition<DisabledForJreRange> {\n\n\tDisabledForJreRangeCondition() {\n\t\tsuper(DisabledForJreRange.class, DisabledForJreRange::disabledReason);\n\t}\n\n\t@Override\n\tboolean isEnabled(DisabledForJreRange range) {\n\t\treturn !isCurrentVersionWithinRange(range.min(), range.max(), range.minVersion(), range.maxVersion());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIf.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledIf} is used to signal that the annotated test class or test\n * method is <em>disabled</em> if the provided {@linkplain #value() condition}\n * evaluates to {@code true}.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be disabled on the same condition.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} (i.e., test\n * interface, test class, or test method). If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.7\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(DisabledIfCondition.class)\n@API(status = STABLE, since = \"5.7\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledIf {\n\n\t/**\n\t * The name of a method within the test class or in an external class to use\n\t * as a condition for the test's or container's execution.\n\t *\n\t * <p>Condition methods must be static if located outside the test class or\n\t * if {@code @DisabledIf} is used at the class level.\n\t *\n\t * <p>A condition method in an external class must be referenced by its\n\t * <em>fully qualified method name</em> &mdash; for example,\n\t * {@code com.example.Conditions#isEncryptionSupported}.\n\t */\n\tString value();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t */\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledIf @DisabledIf}.\n *\n * @since 5.7\n * @see DisabledIf\n */\nclass DisabledIfCondition extends MethodBasedCondition<DisabledIf> {\n\n\tDisabledIfCondition() {\n\t\tsuper(DisabledIf.class, DisabledIf::value, DisabledIf::disabledReason);\n\t}\n\n\t@Override\n\tprotected boolean isEnabled(boolean methodResult) {\n\t\treturn !methodResult;\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledIfEnvironmentVariable} is used to signal that the annotated test\n * class or test method is <em>disabled</em> if the value of the specified\n * {@linkplain #named environment variable} matches the specified\n * {@linkplain #matches regular expression}.\n *\n * <p>When declared at the class level, the result will apply to all test methods\n * within that class as well.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>If the specified environment variable is undefined, the presence of this\n * annotation will have no effect on whether or not the class or method\n * is disabled.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <p>This annotation is a {@linkplain Repeatable repeatable} annotation and may\n * be declared multiple times on an {@link java.lang.reflect.AnnotatedElement\n * AnnotatedElement} such as a test interface, test class, or test method.\n * Specifically, this annotation will be found if it is directly present,\n * indirectly present, or meta-present on a given element.\n *\n * @since 5.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Repeatable(DisabledIfEnvironmentVariables.class)\n@ExtendWith(DisabledIfEnvironmentVariableCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledIfEnvironmentVariable {\n\n\t/**\n\t * The name of the environment variable to retrieve.\n\t *\n\t * @return the environment variable name; never <em>blank</em>\n\t * @see System#getenv(String)\n\t */\n\tString named();\n\n\t/**\n\t * A regular expression that will be used to match against the retrieved\n\t * value of the {@link #named} environment variable.\n\t *\n\t * @return the regular expression; never <em>blank</em>\n\t * @see String#matches(String)\n\t * @see java.util.regex.Pattern\n\t */\n\tString matches();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledIfEnvironmentVariable @DisabledIfEnvironmentVariable}.\n *\n * @since 5.1\n * @see DisabledIfEnvironmentVariable\n */\nclass DisabledIfEnvironmentVariableCondition\n\t\textends AbstractRepeatableAnnotationCondition<DisabledIfEnvironmentVariable> {\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"No @DisabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\n\tDisabledIfEnvironmentVariableCondition() {\n\t\tsuper(DisabledIfEnvironmentVariable.class);\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult getNoDisabledConditionsEncounteredResult() {\n\t\treturn ENABLED;\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult evaluate(DisabledIfEnvironmentVariable annotation) {\n\t\tString name = annotation.named().strip();\n\t\tString regex = annotation.matches();\n\t\tPreconditions.notBlank(name, () -> \"The 'named' attribute must not be blank in \" + annotation);\n\t\tPreconditions.notBlank(regex, () -> \"The 'matches' attribute must not be blank in \" + annotation);\n\t\tString actual = getEnvironmentVariable(name);\n\n\t\t// Nothing to match against?\n\t\tif (actual == null) {\n\t\t\treturn enabled(\"Environment variable [%s] does not exist\".formatted(name));\n\t\t}\n\n\t\tif (actual.matches(regex)) {\n\t\t\treturn disabled(\"Environment variable [%s] with value [%s] matches regular expression [%s]\".formatted(name,\n\t\t\t\tactual, regex), annotation.disabledReason());\n\t\t}\n\t\t// else\n\t\treturn enabled(\"Environment variable [%s] with value [%s] does not match regular expression [%s]\".formatted(\n\t\t\tname, actual, regex));\n\t}\n\n\t/**\n\t * Get the value of the named environment variable.\n\t *\n\t * <p>The default implementation delegates to\n\t * {@link System#getenv(String)}. Can be overridden in a subclass for\n\t * testing purposes.\n\t */\n\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\treturn System.getenv(name);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariables.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @DisabledIfEnvironmentVariables} is a container for one or more\n * {@link DisabledIfEnvironmentVariable @DisabledIfEnvironmentVariable} declarations.\n *\n * <p>Note, however, that use of the {@code @DisabledIfEnvironmentVariables} container\n * is completely optional since {@code @DisabledIfEnvironmentVariable} is a {@linkplain\n * java.lang.annotation.Repeatable repeatable} annotation.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * @since 5.6\n * @see DisabledIfEnvironmentVariable\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.6\")\npublic @interface DisabledIfEnvironmentVariables {\n\n\t/**\n\t * An array of one or more {@link DisabledIfEnvironmentVariable @DisabledIfEnvironmentVariable}\n\t * declarations.\n\t */\n\tDisabledIfEnvironmentVariable[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperties.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @DisabledIfSystemProperties} is a container for one or more\n * {@link DisabledIfSystemProperty @DisabledIfSystemProperty} declarations.\n *\n * <p>Note, however, that use of the {@code @DisabledIfSystemProperties} container\n * is completely optional since {@code @DisabledIfSystemProperty} is a {@linkplain\n * java.lang.annotation.Repeatable repeatable} annotation.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * @since 5.6\n * @see DisabledIfSystemProperty\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.6\")\npublic @interface DisabledIfSystemProperties {\n\n\t/**\n\t * An array of one or more {@link DisabledIfSystemProperty @DisabledIfSystemProperty}\n\t * declarations.\n\t */\n\tDisabledIfSystemProperty[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledIfSystemProperty} is used to signal that the annotated test\n * class or test method is <em>disabled</em> if the value of the specified\n * {@linkplain #named system property} matches the specified\n * {@linkplain #matches regular expression}.\n *\n * <p>When declared at the class level, the result will apply to all test methods\n * within that class as well.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>If the specified system property is undefined, the presence of this\n * annotation will have no effect on whether or not the class or method\n * is disabled.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <p>This annotation is a {@linkplain Repeatable repeatable} annotation and may\n * be declared multiple times on an {@link java.lang.reflect.AnnotatedElement\n * AnnotatedElement} such as a test interface, test class, or test method.\n * Specifically, this annotation will be found if it is directly present,\n * indirectly present, or meta-present on a given element.\n *\n * @since 5.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Repeatable(DisabledIfSystemProperties.class)\n@ExtendWith(DisabledIfSystemPropertyCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledIfSystemProperty {\n\n\t/**\n\t * The name of the JVM system property to retrieve.\n\t *\n\t * @return the system property name; never <em>blank</em>\n\t * @see System#getProperty(String)\n\t */\n\tString named();\n\n\t/**\n\t * A regular expression that will be used to match against the retrieved\n\t * value of the {@link #named} JVM system property.\n\t *\n\t * @return the regular expression; never <em>blank</em>\n\t * @see String#matches(String)\n\t * @see java.util.regex.Pattern\n\t */\n\tString matches();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledIfSystemProperty @DisabledIfSystemProperty}.\n *\n * @since 5.1\n * @see DisabledIfSystemProperty\n */\nclass DisabledIfSystemPropertyCondition extends AbstractRepeatableAnnotationCondition<DisabledIfSystemProperty> {\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"No @DisabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\n\tDisabledIfSystemPropertyCondition() {\n\t\tsuper(DisabledIfSystemProperty.class);\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult getNoDisabledConditionsEncounteredResult() {\n\t\treturn ENABLED;\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult evaluate(DisabledIfSystemProperty annotation) {\n\t\tString name = annotation.named().strip();\n\t\tString regex = annotation.matches();\n\t\tPreconditions.notBlank(name, () -> \"The 'named' attribute must not be blank in \" + annotation);\n\t\tPreconditions.notBlank(regex, () -> \"The 'matches' attribute must not be blank in \" + annotation);\n\t\tString actual = System.getProperty(name);\n\n\t\t// Nothing to match against?\n\t\tif (actual == null) {\n\t\t\treturn enabled(\"System property [%s] does not exist\".formatted(name));\n\t\t}\n\n\t\tif (actual.matches(regex)) {\n\t\t\treturn disabled(\n\t\t\t\t\"System property [%s] with value [%s] matches regular expression [%s]\".formatted(name, actual, regex),\n\t\t\t\tannotation.disabledReason());\n\t\t}\n\t\t// else\n\t\treturn enabled(\"System property [%s] with value [%s] does not match regular expression [%s]\".formatted(name,\n\t\t\tactual, regex));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledInNativeImage.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @DisabledInNativeImage} is used to signal that the annotated test class\n * or test method is <em>disabled</em> when executing within a GraalVM native\n * image.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be disabled within a native image.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Technical Details</h2>\n *\n * <p>JUnit detects whether tests are executing within a GraalVM native image by\n * checking for the presence of the {@code org.graalvm.nativeimage.imagecode}\n * system property (see\n * <a href=\"https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java\">org.graalvm.nativeimage.ImageInfo</a>\n * for details). The GraalVM compiler sets the property to {@code buildtime} while\n * compiling a native image; the property is set to {@code runtime} while a native\n * image is executing; and the Gradle and Maven plug-ins in the GraalVM\n * <a href=\"https://graalvm.github.io/native-build-tools/latest/\">Native Build Tools</a>\n * project set the property to {@code agent} while executing tests with the GraalVM\n * <a href=\"https://www.graalvm.org/reference-manual/native-image/metadata/AutomaticMetadataCollection/\">tracing agent</a>.\n *\n * @since 5.9.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@DisabledIfSystemProperty(named = \"org.graalvm.nativeimage.imagecode\", matches = \".+\", //\n\t\tdisabledReason = \"Currently executing within a GraalVM native image\")\n@API(status = STABLE, since = \"5.9.1\")\npublic @interface DisabledInNativeImage {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJre.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledOnJre} is used to signal that the annotated test class or\n * test method is <em>disabled</em> on one or more specified Java Runtime\n * Environment (JRE) versions.\n *\n * <p>Versions can be specified as {@link JRE} enum constants via\n * {@link #value() value} or as integers via {@link #versions() versions}.\n *\n * <p>When applied at the class level, all test methods within that class\n * will be disabled on the same specified JRE versions.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} such as a test\n * interface, test class, or test method. If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.1\n * @see JRE\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(DisabledOnJreCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledOnJre {\n\n\t/**\n\t * Java Runtime Environment versions on which the annotated class or method\n\t * should be disabled, specified as {@link JRE} enum constants.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the version via {@link #versions() versions}\n\t * instead.\n\t *\n\t * @see JRE\n\t * @see #versions()\n\t */\n\tJRE[] value() default {};\n\n\t/**\n\t * Java Runtime Environment versions on which the annotated class or method\n\t * should be disabled, specified as integers.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for a particular JRE version, you\n\t * can specify the version via {@link #value() value} instead.\n\t *\n\t * @since 5.12\n\t * @see #value()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint[] versions() default {};\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnJreCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledOnJre @DisabledOnJre}.\n *\n * @since 5.1\n * @see DisabledOnJre\n */\nclass DisabledOnJreCondition extends AbstractJreCondition<DisabledOnJre> {\n\n\tDisabledOnJreCondition() {\n\t\tsuper(DisabledOnJre.class, DisabledOnJre::disabledReason);\n\t}\n\n\t@Override\n\tboolean isEnabled(DisabledOnJre annotation) {\n\t\treturn validatedVersions(annotation.value(), annotation.versions()).noneMatch(JRE::isCurrentVersion);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOs.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DisabledOnOs} is used to signal that the annotated test class or\n * test method is <em>disabled</em> on one or more specified\n * {@linkplain #value operating systems} or on one or more specified\n * {@linkplain #architectures architectures}\n *\n * <p>If operating systems <em>and</em> architectures are specified, the annotated\n * test class or test method is disabled if both conditions apply.\n *\n * <p>When applied at the class level, all test methods within that class\n * will be disabled on the same specified operating systems, architectures, or\n * the specified combinations of both.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} such as a test\n * interface, test class, or test method. If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.1\n * @see OS\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(DisabledOnOsCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface DisabledOnOs {\n\n\t/**\n\t * Operating systems on which the annotated class or method should be\n\t * disabled.\n\t *\n\t * @see OS\n\t */\n\tOS[] value() default {};\n\n\t/**\n\t * Architectures on which the annotated class or method should be disabled.\n\t *\n\t * <p>Each architecture will be compared to the value returned from\n\t * {@code System.getProperty(\"os.arch\")}, ignoring case.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString[] architectures() default {};\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/DisabledOnOsCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link DisabledOnOs @DisabledOnOs}.\n *\n * @since 5.1\n * @see DisabledOnOs\n */\nclass DisabledOnOsCondition extends AbstractOsBasedExecutionCondition<DisabledOnOs> {\n\n\tDisabledOnOsCondition() {\n\t\tsuper(DisabledOnOs.class);\n\t}\n\n\t@Override\n\tConditionEvaluationResult evaluateExecutionCondition(DisabledOnOs annotation) {\n\t\tboolean osSpecified = annotation.value().length > 0;\n\t\tboolean archSpecified = annotation.architectures().length > 0;\n\t\tPreconditions.condition(osSpecified || archSpecified,\n\t\t\t\"You must declare at least one OS or architecture in @DisabledOnOs\");\n\n\t\tboolean enabled = isEnabledBasedOnOs(annotation) || isEnabledBasedOnArchitecture(annotation);\n\t\tString reason = createReason(enabled, osSpecified, archSpecified);\n\n\t\treturn enabled ? ConditionEvaluationResult.enabled(reason)\n\t\t\t\t: ConditionEvaluationResult.disabled(reason, annotation.disabledReason());\n\t}\n\n\tprivate boolean isEnabledBasedOnOs(DisabledOnOs annotation) {\n\t\tOS[] operatingSystems = annotation.value();\n\t\tif (operatingSystems.length == 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Arrays.stream(operatingSystems).noneMatch(OS::isCurrentOs);\n\t}\n\n\tprivate boolean isEnabledBasedOnArchitecture(DisabledOnOs annotation) {\n\t\tString[] architectures = annotation.architectures();\n\t\tif (architectures.length == 0) {\n\t\t\treturn false;\n\t\t}\n\t\treturn Arrays.stream(architectures).noneMatch(CURRENT_ARCHITECTURE::equalsIgnoreCase);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRange.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledForJreRange} is used to signal that the annotated test class or\n * test method is only <em>enabled</em> for a specific range of Java Runtime\n * Environment (JRE) versions.\n *\n * <p>Version ranges can be specified as {@link JRE} enum constants via\n * {@link #min min} and {@link #max max} or as integers via\n * {@link #minVersion minVersion} and {@link #maxVersion maxVersion}.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be enabled on the same specified JRE versions.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} (i.e., test\n * interface, test class, or test method). If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.6\n * @see JRE\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(EnabledForJreRangeCondition.class)\n@API(status = STABLE, since = \"5.6\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledForJreRange {\n\n\t/**\n\t * Java Runtime Environment version which is used as the lower boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be enabled, specified as a {@link JRE} enum constant.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the minimum version via\n\t * {@link #minVersion() minVersion} instead.\n\t *\n\t * <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted\n\t * as {@link JRE#JAVA_17 JAVA_17} if the {@link #minVersion() minVersion} is\n\t * not set.\n\t *\n\t * @see JRE\n\t * @see #minVersion()\n\t */\n\tJRE min() default JRE.UNDEFINED;\n\n\t/**\n\t * Java Runtime Environment version which is used as the upper boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be enabled, specified as a {@link JRE} enum constant.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the maximum version via\n\t * {@link #maxVersion() maxVersion} instead.\n\t *\n\t * <p>Defaults to {@link JRE#UNDEFINED UNDEFINED}, which will be interpreted\n\t * as {@link JRE#OTHER OTHER} if the {@link #maxVersion() maxVersion} is not\n\t * set.\n\t *\n\t * @see JRE\n\t * @see #maxVersion()\n\t */\n\tJRE max() default JRE.UNDEFINED;\n\n\t/**\n\t * Java Runtime Environment version which is used as the lower boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be enabled, specified as an integer.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for the particular JRE version,\n\t * you can specify the minimum version via {@link #min() min} instead.\n\t *\n\t * <p>Defaults to {@code -1} to signal that {@link #min() min} should be used\n\t * instead.\n\t *\n\t * @since 5.12\n\t * @see #min()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint minVersion() default -1;\n\n\t/**\n\t * Java Runtime Environment version which is used as the upper boundary for\n\t * the version range that determines if the annotated class or method should\n\t * be enabled, specified as an integer.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for the particular JRE version,\n\t * you can specify the maximum version via {@link #max() max} instead.\n\t *\n\t * <p>Defaults to {@code -1} to signal that {@link #max() max} should be used\n\t * instead.\n\t *\n\t * @since 5.12\n\t * @see #max()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint maxVersion() default -1;\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledForJreRangeCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledForJreRange @EnabledForJreRange}.\n *\n * @since 5.6\n * @see EnabledForJreRange\n */\nclass EnabledForJreRangeCondition extends AbstractJreRangeCondition<EnabledForJreRange> {\n\n\tEnabledForJreRangeCondition() {\n\t\tsuper(EnabledForJreRange.class, EnabledForJreRange::disabledReason);\n\t}\n\n\t@Override\n\tboolean isEnabled(EnabledForJreRange range) {\n\t\treturn isCurrentVersionWithinRange(range.min(), range.max(), range.minVersion(), range.maxVersion());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIf.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledIf} is used to signal that the annotated test class or test\n * method is only <em>enabled</em> if the provided {@linkplain #value() condition}\n * evaluates to {@code true}.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be enabled on the same condition.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} (i.e., test\n * interface, test class, or test method). If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.7\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(EnabledIfCondition.class)\n@API(status = STABLE, since = \"5.7\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledIf {\n\n\t/**\n\t * The name of a method within the test class or in an external class to use\n\t * as a condition for the test's or container's execution.\n\t *\n\t * <p>Condition methods must be static if located outside the test class or\n\t * if {@code @EnabledIf} is used at the class level.\n\t *\n\t * <p>A condition method in an external class must be referenced by its\n\t * <em>fully qualified method name</em> &mdash; for example,\n\t * {@code com.example.Conditions#isEncryptionSupported}.\n\t */\n\tString value();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t */\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledIf @EnabledIf}.\n *\n * @since 5.7\n * @see EnabledIf\n */\nclass EnabledIfCondition extends MethodBasedCondition<EnabledIf> {\n\n\tEnabledIfCondition() {\n\t\tsuper(EnabledIf.class, EnabledIf::value, EnabledIf::disabledReason);\n\t}\n\n\t@Override\n\tprotected boolean isEnabled(boolean methodResult) {\n\t\treturn methodResult;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledIfEnvironmentVariable} is used to signal that the annotated test\n * class or test method is only <em>enabled</em> if the value of the specified\n * {@linkplain #named environment variable} matches the specified\n * {@linkplain #matches regular expression}.\n *\n * <p>When declared at the class level, the result will apply to all test methods\n * within that class as well.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>If the specified environment variable is undefined, the annotated class or\n * method will be disabled.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <p>This annotation is a {@linkplain Repeatable repeatable} annotation and may\n * be declared multiple times on an {@link java.lang.reflect.AnnotatedElement\n * AnnotatedElement} such as a test interface, test class, or test method.\n * Specifically, this annotation will be found if it is directly present,\n * indirectly present, or meta-present on a given element.\n *\n * @since 5.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Repeatable(EnabledIfEnvironmentVariables.class)\n@ExtendWith(EnabledIfEnvironmentVariableCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledIfEnvironmentVariable {\n\n\t/**\n\t * The name of the environment variable to retrieve.\n\t *\n\t * @return the environment variable name; never <em>blank</em>\n\t * @see System#getenv(String)\n\t */\n\tString named();\n\n\t/**\n\t * A regular expression that will be used to match against the retrieved\n\t * value of the {@link #named} environment variable.\n\t *\n\t * @return the regular expression; never <em>blank</em>\n\t * @see String#matches(String)\n\t * @see java.util.regex.Pattern\n\t */\n\tString matches();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledIfEnvironmentVariable @EnabledIfEnvironmentVariable}.\n *\n * @since 5.1\n * @see EnabledIfEnvironmentVariable\n */\nclass EnabledIfEnvironmentVariableCondition\n\t\textends AbstractRepeatableAnnotationCondition<EnabledIfEnvironmentVariable> {\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"No @EnabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\n\tEnabledIfEnvironmentVariableCondition() {\n\t\tsuper(EnabledIfEnvironmentVariable.class);\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult getNoDisabledConditionsEncounteredResult() {\n\t\treturn ENABLED;\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult evaluate(EnabledIfEnvironmentVariable annotation) {\n\n\t\tString name = annotation.named().strip();\n\t\tString regex = annotation.matches();\n\t\tPreconditions.notBlank(name, () -> \"The 'named' attribute must not be blank in \" + annotation);\n\t\tPreconditions.notBlank(regex, () -> \"The 'matches' attribute must not be blank in \" + annotation);\n\t\tString actual = getEnvironmentVariable(name);\n\n\t\t// Nothing to match against?\n\t\tif (actual == null) {\n\t\t\treturn disabled(\"Environment variable [%s] does not exist\".formatted(name), annotation.disabledReason());\n\t\t}\n\t\tif (actual.matches(regex)) {\n\t\t\treturn enabled(\"Environment variable [%s] with value [%s] matches regular expression [%s]\".formatted(name,\n\t\t\t\tactual, regex));\n\t\t}\n\t\treturn disabled(\"Environment variable [%s] with value [%s] does not match regular expression [%s]\".formatted(\n\t\t\tname, actual, regex), annotation.disabledReason());\n\t}\n\n\t/**\n\t * Get the value of the named environment variable.\n\t *\n\t * <p>The default implementation delegates to\n\t * {@link System#getenv(String)}. Can be overridden in a subclass for\n\t * testing purposes.\n\t */\n\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\treturn System.getenv(name);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariables.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @EnabledIfEnvironmentVariables} is a container for one or more\n * {@link EnabledIfEnvironmentVariable @EnabledIfEnvironmentVariable} declarations.\n *\n * <p>Note, however, that use of the {@code @EnabledIfEnvironmentVariables} container\n * is completely optional since {@code @EnabledIfEnvironmentVariable} is a {@linkplain\n * java.lang.annotation.Repeatable repeatable} annotation.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * @since 5.6\n * @see EnabledIfEnvironmentVariable\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.6\")\npublic @interface EnabledIfEnvironmentVariables {\n\n\t/**\n\t * An array of one or more {@link EnabledIfEnvironmentVariable @EnabledIfEnvironmentVariable}\n\t * declarations.\n\t */\n\tEnabledIfEnvironmentVariable[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperties.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @EnabledIfSystemProperties} is a container for one or more\n * {@link EnabledIfSystemProperty @EnabledIfSystemProperty} declarations.\n *\n * <p>Note, however, that use of the {@code @EnabledIfSystemProperties} container\n * is completely optional since {@code @EnabledIfSystemProperty} is a {@linkplain\n * java.lang.annotation.Repeatable repeatable} annotation.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * @since 5.6\n * @see EnabledIfSystemProperty\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.6\")\npublic @interface EnabledIfSystemProperties {\n\n\t/**\n\t * An array of one or more {@link EnabledIfSystemProperty @EnabledIfSystemProperty}\n\t * declarations.\n\t */\n\tEnabledIfSystemProperty[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledIfSystemProperty} is used to signal that the annotated test\n * class or test method is only <em>enabled</em> if the value of the specified\n * {@linkplain #named system property} matches the specified\n * {@linkplain #matches regular expression}.\n *\n * <p>When declared at the class level, the result will apply to all test methods\n * within that class as well.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>If the specified system property is undefined, the annotated class or\n * method will be disabled.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <p>This annotation is a {@linkplain Repeatable repeatable} annotation and may\n * be declared multiple times on an {@link java.lang.reflect.AnnotatedElement\n * AnnotatedElement} such as a test interface, test class, or test method.\n * Specifically, this annotation will be found if it is directly present,\n * indirectly present, or meta-present on a given element.\n *\n * @since 5.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Repeatable(EnabledIfSystemProperties.class)\n@ExtendWith(EnabledIfSystemPropertyCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledIfSystemProperty {\n\n\t/**\n\t * The name of the JVM system property to retrieve.\n\t *\n\t * @return the system property name; never <em>blank</em>\n\t * @see System#getProperty(String)\n\t */\n\tString named();\n\n\t/**\n\t * A regular expression that will be used to match against the retrieved\n\t * value of the {@link #named} JVM system property.\n\t *\n\t * @return the regular expression; never <em>blank</em>\n\t * @see String#matches(String)\n\t * @see java.util.regex.Pattern\n\t */\n\tString matches();\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledIfSystemProperty @EnabledIfSystemProperty}.\n *\n * @since 5.1\n * @see EnabledIfSystemProperty\n */\nclass EnabledIfSystemPropertyCondition extends AbstractRepeatableAnnotationCondition<EnabledIfSystemProperty> {\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"No @EnabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\n\tEnabledIfSystemPropertyCondition() {\n\t\tsuper(EnabledIfSystemProperty.class);\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult getNoDisabledConditionsEncounteredResult() {\n\t\treturn ENABLED;\n\t}\n\n\t@Override\n\tprotected ConditionEvaluationResult evaluate(EnabledIfSystemProperty annotation) {\n\n\t\tString name = annotation.named().strip();\n\t\tString regex = annotation.matches();\n\t\tPreconditions.notBlank(name, () -> \"The 'named' attribute must not be blank in \" + annotation);\n\t\tPreconditions.notBlank(regex, () -> \"The 'matches' attribute must not be blank in \" + annotation);\n\t\tString actual = System.getProperty(name);\n\n\t\t// Nothing to match against?\n\t\tif (actual == null) {\n\t\t\treturn disabled(\"System property [%s] does not exist\".formatted(name), annotation.disabledReason());\n\t\t}\n\t\tif (actual.matches(regex)) {\n\t\t\treturn enabled(\n\t\t\t\t\"System property [%s] with value [%s] matches regular expression [%s]\".formatted(name, actual, regex));\n\t\t}\n\t\treturn disabled(\"System property [%s] with value [%s] does not match regular expression [%s]\".formatted(name,\n\t\t\tactual, regex), annotation.disabledReason());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledInNativeImage.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @EnabledInNativeImage} is used to signal that the annotated test class\n * or test method is only <em>enabled</em> when executing within a GraalVM native\n * image.\n *\n * <p>When applied at the class level, all test methods within that class will\n * be enabled within a native image.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Technical Details</h2>\n *\n * <p>JUnit detects whether tests are executing within a GraalVM native image by\n * checking for the presence of the {@code org.graalvm.nativeimage.imagecode}\n * system property (see\n * <a href=\"https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java\">org.graalvm.nativeimage.ImageInfo</a>\n * for details). The GraalVM compiler sets the property to {@code buildtime} while\n * compiling a native image; the property is set to {@code runtime} while a native\n * image is executing; and the Gradle and Maven plug-ins in the GraalVM\n * <a href=\"https://graalvm.github.io/native-build-tools/latest/\">Native Build Tools</a>\n * project set the property to {@code agent} while executing tests with the GraalVM\n * <a href=\"https://www.graalvm.org/reference-manual/native-image/metadata/AutomaticMetadataCollection/\">tracing agent</a>.\n *\n * @since 5.9.1\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@EnabledIfSystemProperty(named = \"org.graalvm.nativeimage.imagecode\", matches = \".+\", //\n\t\tdisabledReason = \"Not currently executing within a GraalVM native image\")\n@API(status = STABLE, since = \"5.9.1\")\npublic @interface EnabledInNativeImage {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJre.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledOnJre} is used to signal that the annotated test class or\n * test method is only <em>enabled</em> on one or more specified Java Runtime\n * Environment (JRE) versions.\n *\n * <p>Versions can be specified as {@link JRE} enum constants via\n * {@link #value() value} or as integers via {@link #versions() versions}.\n *\n * <p>When applied at the class level, all test methods within that class\n * will be enabled on the same specified JRE versions.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} such as a test\n * interface, test class, or test method. If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.1\n * @see JRE\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(EnabledOnJreCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledOnJre {\n\n\t/**\n\t * Java Runtime Environment versions on which the annotated class or method\n\t * should be enabled, specified as {@link JRE} enum constants.\n\t *\n\t * <p>If a {@code JRE} enum constant does not exist for a particular JRE\n\t * version, you can specify the version via {@link #versions() versions}\n\t * instead.\n\t *\n\t * @see JRE\n\t * @see #versions()\n\t */\n\tJRE[] value() default {};\n\n\t/**\n\t * Java Runtime Environment versions on which the annotated class or method\n\t * should be enabled, specified as integers.\n\t *\n\t * <p>If a {@code JRE} enum constant exists for a particular JRE version, you\n\t * can specify the version via {@link #value() value} instead.\n\t *\n\t * @since 5.12\n\t * @see #value()\n\t * @see JRE#version()\n\t * @see Runtime.Version#feature()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tint[] versions() default {};\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnJreCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledOnJre @EnabledOnJre}.\n *\n * @since 5.1\n * @see EnabledOnJre\n */\nclass EnabledOnJreCondition extends AbstractJreCondition<EnabledOnJre> {\n\n\tEnabledOnJreCondition() {\n\t\tsuper(EnabledOnJre.class, EnabledOnJre::disabledReason);\n\t}\n\n\t@Override\n\tboolean isEnabled(EnabledOnJre annotation) {\n\t\treturn validatedVersions(annotation.value(), annotation.versions()).anyMatch(JRE::isCurrentVersion);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOs.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @EnabledOnOs} is used to signal that the annotated test class or\n * test method is only <em>enabled</em> on one or more specified\n * {@linkplain #value operating systems} or one or more specified\n * {@linkplain #architectures architectures}.\n *\n * <p>If operating systems <em>and</em> architectures are specified, the annotated\n * test class or test method is enabled if both conditions apply.\n *\n * <p>When applied at the class level, all test methods within that class\n * will be enabled on the same specified operating systems, architectures, or\n * the specified combinations of both.\n *\n * <p>This annotation is not {@link java.lang.annotation.Inherited @Inherited}.\n * Consequently, if you wish to apply the same semantics to a subclass, this\n * annotation must be redeclared on the subclass.\n *\n * <p>If a test method is disabled via this annotation, that prevents execution\n * of the test method and method-level lifecycle callbacks such as\n * {@code @BeforeEach} methods, {@code @AfterEach} methods, and corresponding\n * extension APIs. However, that does not prevent the test class from being\n * instantiated, and it does not prevent the execution of class-level lifecycle\n * callbacks such as {@code @BeforeAll} methods, {@code @AfterAll} methods, and\n * corresponding extension APIs.\n *\n * <p>This annotation may be used as a meta-annotation in order to create a\n * custom <em>composed annotation</em> that inherits the semantics of this\n * annotation.\n *\n * <h2>Warning</h2>\n *\n * <p>This annotation can only be declared once on an\n * {@link java.lang.reflect.AnnotatedElement AnnotatedElement} such as a test\n * interface, test class, or test method. If this annotation is directly\n * present, indirectly present, or meta-present multiple times on a given\n * element, only the first such annotation discovered by JUnit will be used;\n * any additional declarations will be silently ignored. Note, however, that\n * this annotation may be used in conjunction with other {@code @Enabled*} or\n * {@code @Disabled*} annotations in this package.\n *\n * @since 5.1\n * @see OS\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.Disabled\n */\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@ExtendWith(EnabledOnOsCondition.class)\n@API(status = STABLE, since = \"5.1\")\n@SuppressWarnings(\"exports\")\npublic @interface EnabledOnOs {\n\n\t/**\n\t * Operating systems on which the annotated class or method should be\n\t * enabled.\n\t *\n\t * @see OS\n\t */\n\tOS[] value() default {};\n\n\t/**\n\t * Architectures on which the annotated class or method should be enabled.\n\t *\n\t * <p>Each architecture will be compared to the value returned from\n\t * {@code System.getProperty(\"os.arch\")}, ignoring case.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tString[] architectures() default {};\n\n\t/**\n\t * Custom reason to provide if the test or container is disabled.\n\t *\n\t * <p>If a custom reason is supplied, it will be combined with the default\n\t * reason for this annotation. If a custom reason is not supplied, the default\n\t * reason will be used.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tString disabledReason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/EnabledOnOsCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ExecutionCondition} for {@link EnabledOnOs @EnabledOnOs}.\n *\n * @since 5.1\n * @see EnabledOnOs\n */\nclass EnabledOnOsCondition extends AbstractOsBasedExecutionCondition<EnabledOnOs> {\n\n\tEnabledOnOsCondition() {\n\t\tsuper(EnabledOnOs.class);\n\t}\n\n\t@Override\n\tConditionEvaluationResult evaluateExecutionCondition(EnabledOnOs annotation) {\n\t\tboolean osSpecified = annotation.value().length > 0;\n\t\tboolean archSpecified = annotation.architectures().length > 0;\n\t\tPreconditions.condition(osSpecified || archSpecified,\n\t\t\t\"You must declare at least one OS or architecture in @EnabledOnOs\");\n\n\t\tboolean enabled = isEnabledBasedOnOs(annotation) && isEnabledBasedOnArchitecture(annotation);\n\t\tString reason = createReason(enabled, osSpecified, archSpecified);\n\n\t\treturn enabled ? ConditionEvaluationResult.enabled(reason)\n\t\t\t\t: ConditionEvaluationResult.disabled(reason, annotation.disabledReason());\n\t}\n\n\tprivate boolean isEnabledBasedOnOs(EnabledOnOs annotation) {\n\t\tOS[] operatingSystems = annotation.value();\n\t\tif (operatingSystems.length == 0) {\n\t\t\treturn true;\n\t\t}\n\t\treturn Arrays.stream(operatingSystems).anyMatch(OS::isCurrentOs);\n\t}\n\n\tprivate boolean isEnabledBasedOnArchitecture(EnabledOnOs annotation) {\n\t\tString[] architectures = annotation.architectures();\n\t\tif (architectures.length == 0) {\n\t\t\treturn true;\n\t\t}\n\t\treturn Arrays.stream(architectures).anyMatch(CURRENT_ARCHITECTURE::equalsIgnoreCase);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/MethodBasedCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * @since 5.7\n */\nabstract class MethodBasedCondition<A extends Annotation> implements ExecutionCondition {\n\n\tprivate final Class<A> annotationType;\n\tprivate final Function<A, String> methodName;\n\tprivate final Function<A, String> customDisabledReason;\n\n\tMethodBasedCondition(Class<A> annotationType, Function<A, String> methodName,\n\t\t\tFunction<A, String> customDisabledReason) {\n\t\tthis.annotationType = annotationType;\n\t\tthis.methodName = methodName;\n\t\tthis.customDisabledReason = customDisabledReason;\n\t}\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tOptional<A> annotation = findAnnotation(context.getElement(), this.annotationType);\n\t\treturn annotation //\n\t\t\t\t.map(this.methodName) //\n\t\t\t\t.map(methodName -> getConditionMethod(methodName, context)) //\n\t\t\t\t.map(method -> invokeConditionMethod(method, context)) //\n\t\t\t\t.map(methodResult -> buildConditionEvaluationResult(methodResult, annotation.get())) //\n\t\t\t\t.orElseGet(this::enabledByDefault);\n\t}\n\n\t// package-private for testing\n\tMethod getConditionMethod(String fullyQualifiedMethodName, ExtensionContext context) {\n\t\tClass<?> testClass = context.getRequiredTestClass();\n\t\tif (!fullyQualifiedMethodName.contains(\"#\")) {\n\t\t\treturn findMethod(testClass, fullyQualifiedMethodName);\n\t\t}\n\t\tString[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName);\n\t\tString className = methodParts[0];\n\t\tString methodName = methodParts[1];\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass);\n\t\tClass<?> clazz = ReflectionSupport.tryToLoadClass(className, classLoader).getNonNullOrThrow(\n\t\t\tcause -> new JUnitException(\"Could not load class [%s]\".formatted(className), cause));\n\t\treturn findMethod(clazz, methodName);\n\t}\n\n\tprivate Method findMethod(Class<?> clazz, String methodName) {\n\t\treturn ReflectionSupport.findMethod(clazz, methodName) //\n\t\t\t\t.orElseGet(() -> ReflectionUtils.getRequiredMethod(clazz, methodName, ExtensionContext.class));\n\t}\n\n\tprivate boolean invokeConditionMethod(Method method, ExtensionContext context) {\n\t\tPreconditions.condition(method.getReturnType() == boolean.class,\n\t\t\t() -> \"Method [%s] must return a boolean\".formatted(method));\n\t\tPreconditions.condition(acceptsExtensionContextOrNoArguments(method),\n\t\t\t() -> \"Method [%s] must accept either an ExtensionContext or no arguments\".formatted(method));\n\n\t\tObject testInstance = context.getTestInstance().orElse(null);\n\t\treturn invokeMethod(method, context, testInstance);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tprivate static boolean invokeMethod(Method method, ExtensionContext context, @Nullable Object testInstance) {\n\t\tif (method.getParameterCount() == 0) {\n\t\t\treturn (boolean) ReflectionSupport.invokeMethod(method, testInstance);\n\t\t}\n\t\treturn (boolean) ReflectionSupport.invokeMethod(method, testInstance, context);\n\t}\n\n\tprivate boolean acceptsExtensionContextOrNoArguments(Method method) {\n\t\tint parameterCount = method.getParameterCount();\n\t\treturn parameterCount == 0 || (parameterCount == 1 && method.getParameterTypes()[0] == ExtensionContext.class);\n\t}\n\n\tprivate ConditionEvaluationResult buildConditionEvaluationResult(boolean methodResult, A annotation) {\n\t\tSupplier<String> defaultReason = () -> \"@%s(\\\"%s\\\") evaluated to %s\".formatted(\n\t\t\tthis.annotationType.getSimpleName(), this.methodName.apply(annotation), methodResult);\n\t\tif (isEnabled(methodResult)) {\n\t\t\treturn enabled(defaultReason.get());\n\t\t}\n\t\tString customReason = this.customDisabledReason.apply(annotation);\n\t\treturn StringUtils.isNotBlank(customReason) ? disabled(customReason) : disabled(defaultReason.get());\n\t}\n\n\tprotected abstract boolean isEnabled(boolean methodResult);\n\n\tprivate ConditionEvaluationResult enabledByDefault() {\n\t\treturn enabled(\"@%s is not present\".formatted(this.annotationType.getSimpleName()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/OS.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * Enumeration of common operating systems used for testing Java applications.\n *\n * <p>If the current operating system cannot be detected &mdash; for example,\n * if the {@code os.name} JVM system property is undefined &mdash; then none\n * of the constants defined in this enum will be considered to be the\n * {@linkplain #isCurrentOs current operating system}.\n *\n * @since 5.1\n * @see #AIX\n * @see #FREEBSD\n * @see #LINUX\n * @see #MAC\n * @see #OPENBSD\n * @see #SOLARIS\n * @see #WINDOWS\n * @see #OTHER\n * @see EnabledOnOs\n * @see DisabledOnOs\n */\n@API(status = STABLE, since = \"5.1\")\npublic enum OS {\n\n\t/**\n\t * IBM AIX operating system.\n\t *\n\t * @since 5.3\n\t */\n\t@API(status = STABLE, since = \"5.3\")\n\tAIX,\n\n\t/**\n\t * FreeBSD operating system.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tFREEBSD,\n\n\t/**\n\t * Linux-based operating system.\n\t */\n\tLINUX,\n\n\t/**\n\t * Apple Macintosh operating system (e.g., macOS).\n\t */\n\tMAC,\n\n\t/**\n\t * OpenBSD operating system.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.9\")\n\tOPENBSD,\n\n\t/**\n\t * Oracle Solaris operating system.\n\t */\n\tSOLARIS,\n\n\t/**\n\t * Microsoft Windows operating system.\n\t */\n\tWINDOWS,\n\n\t/**\n\t * An operating system other than {@link #AIX}, {@link #FREEBSD}, {@link #LINUX},\n\t * {@link #MAC}, {@link #OPENBSD}, {@link #SOLARIS}, or {@link #WINDOWS}.\n\t */\n\tOTHER;\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(OS.class);\n\n\tprivate static final @Nullable OS CURRENT_OS = determineCurrentOs();\n\n\t/**\n\t * {@return the current operating system, if known; otherwise, {@code null}}\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static @Nullable OS current() {\n\t\treturn CURRENT_OS;\n\t}\n\n\tprivate static @Nullable OS determineCurrentOs() {\n\t\treturn parse(System.getProperty(\"os.name\"));\n\t}\n\n\tstatic @Nullable OS parse(String osName) {\n\t\tif (StringUtils.isBlank(osName)) {\n\t\t\tlogger.debug(\n\t\t\t\t() -> \"JVM system property 'os.name' is undefined. It is therefore not possible to detect the current OS.\");\n\n\t\t\t// null signals that the current OS is \"unknown\"\n\t\t\treturn null;\n\t\t}\n\n\t\tosName = osName.toLowerCase(Locale.ENGLISH);\n\n\t\tif (osName.contains(\"aix\")) {\n\t\t\treturn AIX;\n\t\t}\n\t\tif (osName.contains(\"freebsd\")) {\n\t\t\treturn FREEBSD;\n\t\t}\n\t\tif (osName.contains(\"linux\")) {\n\t\t\treturn LINUX;\n\t\t}\n\t\tif (osName.contains(\"mac\")) {\n\t\t\treturn MAC;\n\t\t}\n\t\tif (osName.contains(\"openbsd\")) {\n\t\t\treturn OPENBSD;\n\t\t}\n\t\tif (osName.contains(\"sunos\") || osName.contains(\"solaris\")) {\n\t\t\treturn SOLARIS;\n\t\t}\n\t\tif (osName.contains(\"win\")) {\n\t\t\treturn WINDOWS;\n\t\t}\n\t\treturn OTHER;\n\t}\n\n\t/**\n\t * {@return {@code true} if <em>this</em> {@code OS} is known to be the\n\t * operating system on which the current JVM is executing}\n\t */\n\tpublic boolean isCurrentOs() {\n\t\treturn this == CURRENT_OS;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/condition/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Annotation-based conditions for enabling or disabling tests in JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.condition;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterAllCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code AfterAllCallback} defines the API for {@link Extension Extensions}\n * that wish to provide additional behavior to test containers <strong>once</strong>\n * after all tests in the container have been executed.\n *\n * <p>Concrete implementations often implement {@link BeforeAllCallback} as well.\n *\n * <p>Extensions that implement {@code AfterAllCallback} must be registered at\n * the class level.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.AfterAll\n * @see BeforeAllCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface AfterAllCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked once <em>after</em> all tests in the current\n\t * container.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid afterAll(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterClassTemplateInvocationCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\n\n/**\n * {@code AfterClassTemplateInvocationCallback} defines the API for\n * {@link Extension Extensions} that wish to provide additional behavior\n * <strong>once</strong> after each invocation of a\n * {@link ClassTemplate @ClassTemplate}.\n *\n * <p>Concrete implementations often implement\n * {@link BeforeClassTemplateInvocationCallback} as well.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.13\n * @see ClassTemplate\n * @see BeforeClassTemplateInvocationCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n */\n@FunctionalInterface\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface AfterClassTemplateInvocationCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>after</em> each invocation of a class\n\t * template.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid afterClassTemplateInvocation(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterEachCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code AfterEachCallback} defines the API for {@link Extension Extensions}\n * that wish to provide additional behavior to tests after an individual test\n * and any user-defined teardown methods (e.g.,\n * {@link org.junit.jupiter.api.AfterEach @AfterEach} methods) for that test\n * have been executed.\n *\n * <p>Concrete implementations often implement {@link BeforeEachCallback} as well.\n * If you do not wish to have your callbacks <em>wrapped</em> around user-defined\n * setup and teardown methods, implement {@link BeforeTestExecutionCallback} and\n * {@link AfterTestExecutionCallback} instead of {@link BeforeEachCallback} and\n * {@link AfterEachCallback}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.AfterEach\n * @see BeforeEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface AfterEachCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>after</em> an individual test and any\n\t * user-defined teardown methods for that test have been executed.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid afterEach(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AfterTestExecutionCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code AfterTestExecutionCallback} defines the API for {@link Extension\n * Extensions} that wish to provide additional behavior to tests\n * <strong>immediately</strong> after an individual test has been executed but\n * before any user-defined teardown methods (e.g.,\n * {@link org.junit.jupiter.api.AfterEach @AfterEach} methods) have been executed\n * for that test.\n *\n * <p>Concrete implementations often implement {@link BeforeTestExecutionCallback}\n * as well. If you wish to have your callbacks <em>wrapped</em> around user-defined\n * setup and teardown methods, implement {@link BeforeEachCallback} and\n * {@link AfterEachCallback} instead of {@link BeforeTestExecutionCallback} and\n * {@link AfterTestExecutionCallback}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.Test\n * @see BeforeTestExecutionCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface AfterTestExecutionCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>immediately after</em> an individual test has\n\t * been executed but before any user-defined teardown methods have been\n\t * executed for that test.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid afterTestExecution(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/AnnotatedElementContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.support.AnnotationSupport;\n\n/**\n * {@code AnnotatedElementContext} encapsulates the <em>context</em> in which an\n * {@link #getAnnotatedElement() AnnotatedElement} is declared.\n *\n * <p>For example, an {@code AnnotatedElementContext} is used in\n * {@link org.junit.jupiter.api.io.TempDirFactory TempDirFactory} to allow inspecting\n * the field or parameter the {@link org.junit.jupiter.api.io.TempDir TempDir}\n * annotation is declared on.\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 5.10\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface AnnotatedElementContext {\n\n\t/**\n\t * Get the {@link AnnotatedElement} for this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>When searching for annotations on the annotated element in this context,\n\t * favor {@link #isAnnotated(Class)}, {@link #findAnnotation(Class)}, and\n\t * {@link #findRepeatableAnnotations(Class)} over methods in the\n\t * {@link AnnotatedElement} API due to a bug in {@code javac} on JDK versions prior\n\t * to JDK 9.\n\t *\n\t * @return the annotated element; never {@code null}\n\t */\n\tAnnotatedElement getAnnotatedElement();\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link AnnotatedElement} for\n\t * this context.\n\t *\n\t * <p><strong>Note:</strong> This method does not find repeatable annotations.\n\t * To check for repeatable annotations, use {@link #findRepeatableAnnotations(Class)}\n\t * and verify that the returned list is not empty.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking\n\t * {@link AnnotatedElement#isAnnotationPresent(Class)} due to a bug in {@code javac}\n\t * on JDK versions prior to JDK 9.\n\t *\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @see #findAnnotation(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\tdefault boolean isAnnotated(Class<? extends Annotation> annotationType) {\n\t\treturn AnnotationSupport.isAnnotated(getAnnotatedElement(), annotationType);\n\t}\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link AnnotatedElement} for\n\t * this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking annotation lookup\n\t * methods in the {@link AnnotatedElement} API due to a bug in {@code javac} on JDK\n\t * versions prior to JDK 9.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\tdefault <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {\n\t\treturn AnnotationSupport.findAnnotation(getAnnotatedElement(), annotationType);\n\t}\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of\n\t * {@code annotationType} that are either <em>present</em> or\n\t * <em>meta-present</em> on the {@link AnnotatedElement} for this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking annotation lookup\n\t * methods in the {@link AnnotatedElement} API due to a bug in {@code javac} on JDK\n\t * versions prior to JDK 9.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the repeatable annotation type to search for; never\n\t * {@code null}\n\t * @return the list of all such annotations found; neither {@code null} nor\n\t * mutable, but potentially empty\n\t * @see #isAnnotated(Class)\n\t * @see #findAnnotation(Class)\n\t * @see java.lang.annotation.Repeatable\n\t */\n\tdefault <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {\n\t\treturn AnnotationSupport.findRepeatableAnnotations(getAnnotatedElement(), annotationType);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeAllCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code BeforeAllCallback} defines the API for {@link Extension Extensions}\n * that wish to provide additional behavior to test containers <strong>once</strong>\n * before all tests in the container have been executed.\n *\n * <p>Concrete implementations often implement {@link AfterAllCallback} as well.\n *\n * <p>Extensions that implement {@code BeforeAllCallback} must be registered at\n * the class level.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.BeforeAll\n * @see AfterAllCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface BeforeAllCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked once <em>before</em> all tests in the current\n\t * container.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid beforeAll(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeClassTemplateInvocationCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\n\n/**\n * {@code BeforeClassTemplateInvocationCallback} defines the API for\n * {@link Extension Extensions} that wish to provide additional behavior\n * <strong>once</strong> before each invocation of a\n * {@link ClassTemplate @ClassTemplate}.\n *\n * <p>Concrete implementations often implement\n * {@link AfterClassTemplateInvocationCallback} as well.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.13\n * @see ClassTemplate\n * @see AfterClassTemplateInvocationCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n */\n@FunctionalInterface\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface BeforeClassTemplateInvocationCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>before</em> each invocation of a class\n\t * template.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid beforeClassTemplateInvocation(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeEachCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code BeforeEachCallback} defines the API for {@link Extension Extensions}\n * that wish to provide additional behavior to tests before an individual test\n * and any user-defined setup methods (e.g.,\n * {@link org.junit.jupiter.api.BeforeEach @BeforeEach} methods) for that test\n * have been executed.\n *\n * <p>Concrete implementations often implement {@link AfterEachCallback} as well.\n * If you do not wish to have your callbacks <em>wrapped</em> around user-defined\n * setup and teardown methods, implement {@link BeforeTestExecutionCallback} and\n * {@link AfterTestExecutionCallback} instead of {@link BeforeEachCallback} and\n * {@link AfterEachCallback}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.BeforeEach\n * @see AfterEachCallback\n * @see BeforeTestExecutionCallback\n * @see AfterTestExecutionCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface BeforeEachCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>before</em> an individual test and any\n\t * user-defined setup methods for that test have been executed.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid beforeEach(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/BeforeTestExecutionCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code BeforeTestExecutionCallback} defines the API for {@link Extension\n * Extensions} that wish to provide additional behavior to tests\n * <strong>immediately</strong> before an individual test is executed but after\n * any user-defined setup methods (e.g.,\n * {@link org.junit.jupiter.api.BeforeEach @BeforeEach} methods) have been\n * executed for that test.\n *\n * <p>Concrete implementations often implement {@link AfterTestExecutionCallback}\n * as well. If you wish to have your callbacks <em>wrapped</em> around user-defined\n * setup and teardown methods, implement {@link BeforeEachCallback} and\n * {@link AfterEachCallback} instead of {@link BeforeTestExecutionCallback} and\n * {@link AfterTestExecutionCallback}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>Wrapping Behavior</h2>\n *\n * <p>JUnit Jupiter guarantees <em>wrapping behavior</em> for multiple\n * registered extensions that implement lifecycle callbacks such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link BeforeClassTemplateInvocationCallback},\n * {@link AfterClassTemplateInvocationCallback}, {@link BeforeEachCallback},\n * {@link AfterEachCallback}, {@link BeforeTestExecutionCallback}, and\n * {@link AfterTestExecutionCallback}.\n *\n * <p>That means that, given two extensions {@code Extension1} and\n * {@code Extension2} with {@code Extension1} registered before\n * {@code Extension2}, any \"before\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute before any \"before\" callbacks implemented by\n * {@code Extension2}. Similarly, given the two same two extensions registered\n * in the same order, any \"after\" callbacks implemented by {@code Extension1}\n * are guaranteed to execute after any \"after\" callbacks implemented by\n * {@code Extension2}. {@code Extension1} is therefore said to <em>wrap</em>\n * {@code Extension2}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.Test\n * @see AfterTestExecutionCallback\n * @see BeforeEachCallback\n * @see AfterEachCallback\n * @see BeforeAllCallback\n * @see AfterAllCallback\n * @see BeforeClassTemplateInvocationCallback\n * @see AfterClassTemplateInvocationCallback\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface BeforeTestExecutionCallback extends Extension {\n\n\t/**\n\t * Callback that is invoked <em>immediately before</em> an individual test is\n\t * executed but after any user-defined setup methods have been executed\n\t * for that test.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid beforeTestExecution(ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ClassTemplateInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\n\n/**\n * {@code ClassTemplateInvocationContext} represents the <em>context</em> of\n * a single invocation of a {@link ClassTemplate @ClassTemplate}.\n *\n * <p>Each context is provided by a {@link ClassTemplateInvocationContextProvider}.\n *\n * @since 5.13\n * @see ClassTemplate\n * @see ClassTemplateInvocationContextProvider\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface ClassTemplateInvocationContext {\n\n\t/**\n\t * Get the display name for this invocation.\n\t *\n\t * <p>The supplied {@code invocationIndex} is incremented by the framework\n\t * with each class template invocation. Thus, in the case of multiple active\n\t * {@linkplain ClassTemplateInvocationContextProvider providers}, only the\n\t * first active provider receives indices starting with {@code 1}.\n\t *\n\t * <p>The default implementation returns the supplied {@code invocationIndex}\n\t * wrapped in brackets &mdash; for example, {@code [1]}, {@code [42]}, etc.\n\t *\n\t * @param invocationIndex the index of this invocation (1-based).\n\t * @return the display name for this invocation; never {@code null} or blank\n\t */\n\tdefault String getDisplayName(int invocationIndex) {\n\t\treturn \"[\" + invocationIndex + \"]\";\n\t}\n\n\t/**\n\t * Get additional {@linkplain Extension extensions} for this invocation.\n\t *\n\t * <p>The extensions provided by this method will only be used for this\n\t * invocation of the class template. Thus, it does not make sense to return\n\t * an extension that needs to perform some action at the container level,\n\t * such as an implementation of {@link BeforeAllCallback}.\n\t *\n\t * <p>The default implementation returns an empty list.\n\t *\n\t * @return additional extensions for this invocation; never {@code null}\n\t * or containing {@code null} elements, but potentially empty\n\t */\n\tdefault List<Extension> getAdditionalExtensions() {\n\t\treturn emptyList();\n\t}\n\n\t/**\n\t * Prepare the imminent invocation of the class template.\n\t *\n\t * <p>This may be used, for example, to store entries in the\n\t * {@link ExtensionContext.Store Store} to benefit from its cleanup support\n\t * or for retrieval by other extensions.\n\t *\n\t * @param context the invocation-level extension context\n\t */\n\tdefault void prepareInvocation(ExtensionContext context) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ClassTemplateInvocationContextProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\n\n/**\n * {@code ClassTemplateInvocationContextProvider} defines the API for\n * {@link Extension Extensions} that wish to provide one or multiple contexts\n * for the invocation of a {@link ClassTemplate @ClassTemplate}.\n *\n * <p>This extension API makes it possible to execute a class template in\n * different contexts &mdash; for example, with different parameters, by\n * preparing the test class instance differently, or multiple times without\n * modifying the context.\n *\n * <p>This interface defines two main methods: {@link #supportsClassTemplate} and\n * {@link #provideClassTemplateInvocationContexts}. The former is called by the\n * framework to determine whether this extension wants to act on a class\n * template that is about to be executed. If so, the latter is called and must\n * return a {@link Stream} of {@link ClassTemplateInvocationContext} instances.\n * Otherwise, this provider is ignored for the execution of the current class\n * template.\n *\n * <p>A provider that has returned {@code true} from its {@link #supportsClassTemplate}\n * method is called <em>active</em>. When multiple providers are active for a class\n * template, the {@code Streams} returned by their\n * {@link #provideClassTemplateInvocationContexts} methods will be chained, and\n * the class template method will be invoked using the contexts of all active\n * providers.\n *\n * <p>An active provider may return zero invocation contexts from its\n * {@link #provideClassTemplateInvocationContexts} method if it overrides\n * {@link #mayReturnZeroClassTemplateInvocationContexts} to return {@code true}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on constructor\n * requirements.\n *\n * @since 5.13\n * @see ClassTemplate\n * @see ClassTemplateInvocationContext\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface ClassTemplateInvocationContextProvider extends Extension {\n\n\t/**\n\t * Determine if this provider supports providing invocation contexts for the\n\t * class template represented by the supplied {@code context}.\n\t *\n\t * @param context the extension context for the class template\n\t * about to be invoked; never {@code null}\n\t * @return {@code true} if this provider can provide invocation contexts\n\t * @see #provideClassTemplateInvocationContexts\n\t * @see ExtensionContext\n\t */\n\tboolean supportsClassTemplate(ExtensionContext context);\n\n\t/**\n\t * Provide {@linkplain ClassTemplateInvocationContext invocation contexts}\n\t * for the class template represented by the supplied {@code context}.\n\t *\n\t * <p>This method is only called by the framework if\n\t * {@link #supportsClassTemplate} previously returned {@code true} for the\n\t * same {@link ExtensionContext}; this method is allowed to return an empty\n\t * {@code Stream} but not {@code null}.\n\t *\n\t * <p>The returned {@code Stream} will be properly closed by calling\n\t * {@link Stream#close()}, making it safe to use a resource such as\n\t * {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.\n\t *\n\t * @param context the extension context for the class template about to be\n\t * invoked; never {@code null}\n\t * @return a {@code Stream} of {@code ClassTemplateInvocationContext}\n\t * instances for the invocation of the class template; never {@code null}\n\t * @throws TemplateInvocationValidationException if validation fails while\n\t * providing or closing the {@link Stream}\n\t * @see #supportsClassTemplate\n\t * @see ExtensionContext\n\t */\n\tStream<? extends ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context);\n\n\t/**\n\t * Signal that this provider may provide zero\n\t * {@linkplain ClassTemplateInvocationContext invocation contexts} for\n\t * the class template represented by the supplied {@code context}.\n\t *\n\t * <p>If this method returns {@code false} (which is the default) and the\n\t * provider returns an empty stream from\n\t * {@link #provideClassTemplateInvocationContexts}, this will be considered\n\t * an execution error. Override this method to return {@code true} to ignore\n\t * the absence of invocation contexts for this provider.\n\t *\n\t * @param context the extension context for the class template\n\t * about to be invoked; never {@code null}\n\t * @return {@code true} to allow zero contexts, {@code false} to fail\n\t * execution in case of zero contexts\n\t */\n\tdefault boolean mayReturnZeroClassTemplateInvocationContexts(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ClassTemplateInvocationLifecycleMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * Internal marker annotation for lifecycle methods specific to implementations\n * of {@link ClassTemplateInvocationContextProvider}.\n *\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.ANNOTATION_TYPE)\npublic @interface ClassTemplateInvocationLifecycleMethod {\n\n\t/**\n\t * The corresponding {@link org.junit.jupiter.api.ClassTemplate}-derived\n\t * annotation class.\n\t */\n\tClass<? extends Annotation> classTemplateAnnotation();\n\n\t/**\n\t * The actual lifecycle method annotation class.\n\t */\n\tClass<? extends Annotation> lifecycleMethodAnnotation();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ConditionEvaluationResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * The result of evaluating an {@link ExecutionCondition}.\n *\n * @since 5.0\n */\n@API(status = STABLE, since = \"5.0\")\npublic class ConditionEvaluationResult {\n\n\t/**\n\t * Factory for creating <em>enabled</em> results.\n\t *\n\t * @param reason the reason why the container or test should be enabled; may\n\t * be {@code null} or <em>blank</em> if the reason is unknown\n\t * @return an enabled {@code ConditionEvaluationResult} with the given reason\n\t * or an <em>empty</em> reason if the reason is unknown\n\t * @see StringUtils#isBlank(String)\n\t */\n\tpublic static ConditionEvaluationResult enabled(@Nullable String reason) {\n\t\treturn new ConditionEvaluationResult(true, reason);\n\t}\n\n\t/**\n\t * Factory for creating <em>disabled</em> results.\n\t *\n\t * @param reason the reason why the container or test should be disabled; may\n\t * be {@code null} or <em>blank</em> if the reason is unknown\n\t * @return a disabled {@code ConditionEvaluationResult} with the given reason\n\t * or an <em>empty</em> reason if the reason is unknown\n\t * @see StringUtils#isBlank(String)\n\t */\n\tpublic static ConditionEvaluationResult disabled(@Nullable String reason) {\n\t\treturn new ConditionEvaluationResult(false, reason);\n\t}\n\n\t/**\n\t * Factory for creating <em>disabled</em> results with custom reasons\n\t * added by the user.\n\t *\n\t * <p>If non-blank default and custom reasons are provided, they will be\n\t * concatenated using the format: <code>\"reason&nbsp;==&gt;&nbsp;customReason\"</code>.\n\t *\n\t * @param reason the default reason why the container or test should be disabled;\n\t * may be {@code null} or <em>blank</em> if the default reason is unknown\n\t * @param customReason the custom reason why the container or test should be\n\t * disabled; may be {@code null} or <em>blank</em> if the custom reason is unknown\n\t * @return a disabled {@code ConditionEvaluationResult} with the given reason(s)\n\t * or an <em>empty</em> reason if the reasons are unknown\n\t * @since 5.7\n\t * @see StringUtils#isBlank(String)\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tpublic static ConditionEvaluationResult disabled(@Nullable String reason, @Nullable String customReason) {\n\t\tif (StringUtils.isBlank(reason)) {\n\t\t\treturn disabled(customReason);\n\t\t}\n\t\tif (StringUtils.isBlank(customReason)) {\n\t\t\treturn disabled(reason);\n\t\t}\n\t\treturn disabled(\"%s ==> %s\".formatted(reason.strip(), customReason.strip()));\n\t}\n\n\tprivate final boolean enabled;\n\n\tprivate final Optional<String> reason;\n\n\tprivate ConditionEvaluationResult(boolean enabled, @Nullable String reason) {\n\t\tthis.enabled = enabled;\n\t\tthis.reason = StringUtils.isNotBlank(reason) ? Optional.of(reason.strip()) : Optional.empty();\n\t}\n\n\t/**\n\t * Whether the container or test should be disabled.\n\t *\n\t * @return {@code true} if the container or test should be disabled\n\t */\n\tpublic boolean isDisabled() {\n\t\treturn !this.enabled;\n\t}\n\n\t/**\n\t * Get the reason why the container or test should be enabled or disabled,\n\t * if available.\n\t */\n\tpublic Optional<String> getReason() {\n\t\treturn this.reason;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"enabled\", this.enabled)\n\t\t\t\t.append(\"reason\", this.reason.orElse(\"<unknown>\"))\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/DynamicTestInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.function.Executable;\n\n/**\n * {@code DynamicTestInvocationContext} represents the <em>context</em> of a\n * single invocation of a {@linkplain org.junit.jupiter.api.DynamicTest\n * dynamic test}.\n *\n * @since 5.8\n * @see org.junit.jupiter.api.DynamicTest\n */\n@API(status = STABLE, since = \"5.11\")\npublic interface DynamicTestInvocationContext {\n\n\t/**\n\t * Get the {@code Executable} of this dynamic test invocation context.\n\t *\n\t * @return the executable of the dynamic test invocation, never {@code null}\n\t */\n\tExecutable getExecutable();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutableInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code ExecutableInvoker} allows invoking methods and constructors\n * with support for dynamic resolution of parameters via\n * {@link ParameterResolver ParameterResolvers}.\n *\n * @since 5.9\n */\n@API(status = STABLE, since = \"5.11\")\npublic interface ExecutableInvoker {\n\n\t/**\n\t * Invoke the supplied {@code static} method with dynamic parameter resolution.\n\t *\n\t * @param method the method to invoke and resolve parameters for\n\t * @see #invoke(Method, Object)\n\t */\n\tdefault @Nullable Object invoke(Method method) {\n\t\treturn invoke(method, null);\n\t}\n\n\t/**\n\t * Invoke the supplied method with dynamic parameter resolution.\n\t *\n\t * @param method the method to invoke and resolve parameters for\n\t * @param target the target on which the executable will be invoked;\n\t * can be {@code null} for {@code static} methods\n\t */\n\t@Nullable\n\tObject invoke(Method method, @Nullable Object target);\n\n\t/**\n\t * Invoke the supplied top-level constructor with dynamic parameter resolution.\n\t *\n\t * @param constructor the constructor to invoke and resolve parameters for\n\t * @see #invoke(Constructor, Object)\n\t */\n\tdefault <T> T invoke(Constructor<T> constructor) {\n\t\treturn invoke(constructor, null);\n\t}\n\n\t/**\n\t * Invoke the supplied constructor with the supplied outer instance and\n\t * dynamic parameter resolution.\n\t *\n\t * <p>Use this method when invoking the constructor for an <em>inner</em> class.\n\t *\n\t * @param constructor the constructor to invoke and resolve parameters for\n\t * @param outerInstance the outer instance to supply as the first argument\n\t * to the constructor; must be {@code null} for top-level classes\n\t * or {@code static} nested classes\n\t */\n\t<T> T invoke(Constructor<T> constructor, @Nullable Object outerInstance);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExecutionCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ExecutionCondition} defines the {@link Extension} API for\n * programmatic, <em>conditional test execution</em>.\n *\n * <p>An {@code ExecutionCondition} is\n * {@linkplain #evaluateExecutionCondition(ExtensionContext) evaluated}\n * to determine if a given container or test should be executed based on the\n * supplied {@link ExtensionContext}.\n *\n * <p>If an {@code ExecutionCondition} {@linkplain ConditionEvaluationResult#disabled\n * disables} a test method, that prevents execution of the test method and\n * method-level lifecycle callbacks such as {@code @BeforeEach} methods,\n * {@code @AfterEach} methods, and corresponding extension APIs. However, that\n * does not prevent the test class from being instantiated, and it does not prevent\n * the execution of class-level lifecycle callbacks such as {@code @BeforeAll}\n * methods, {@code @AfterAll} methods, and corresponding extension APIs.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.Disabled\n * @see org.junit.jupiter.api.condition.EnabledIf\n * @see org.junit.jupiter.api.condition.DisabledIf\n * @see org.junit.jupiter.api.condition.EnabledOnOs\n * @see org.junit.jupiter.api.condition.DisabledOnOs\n * @see org.junit.jupiter.api.condition.EnabledOnJre\n * @see org.junit.jupiter.api.condition.DisabledOnJre\n * @see org.junit.jupiter.api.condition.EnabledForJreRange\n * @see org.junit.jupiter.api.condition.DisabledForJreRange\n * @see org.junit.jupiter.api.condition.EnabledInNativeImage\n * @see org.junit.jupiter.api.condition.DisabledInNativeImage\n * @see org.junit.jupiter.api.condition.EnabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.DisabledIfSystemProperty\n * @see org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable\n * @see org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface ExecutionCondition extends Extension {\n\n\t/**\n\t * Evaluate this condition for the supplied {@link ExtensionContext}.\n\t *\n\t * <p>An {@linkplain ConditionEvaluationResult#enabled enabled} result\n\t * indicates that the container or test should be executed; whereas, a\n\t * {@linkplain ConditionEvaluationResult#disabled disabled} result\n\t * indicates that the container or test should not be executed.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @return the result of evaluating this condition; never {@code null}\n\t */\n\tConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtendWith.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ExtendWith} is a {@linkplain Repeatable repeatable} annotation that\n * is used to register {@linkplain Extension extensions} for the annotated test\n * class, test interface, test method, parameter, or field.\n *\n * <p>Annotated parameters are supported in test class constructors, in test\n * methods, and in {@code @BeforeAll}, {@code @AfterAll}, {@code @BeforeEach},\n * and {@code @AfterEach} lifecycle methods.\n *\n * <p>{@code @ExtendWith} fields may be either {@code static} or non-static.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @ExtendWith} fields are inherited from superclasses. Furthermore,\n * {@code @ExtendWith} fields from superclasses will be registered before\n * {@code @ExtendWith} fields in subclasses unless {@code @Order} is used to\n * alter that behavior (see below).\n *\n * <h2>Registration Order</h2>\n *\n * <p>When {@code @ExtendWith} is present on a test class, test interface, or\n * test method or on a parameter in a test method or lifecycle method, the\n * corresponding extensions will be registered in the order in which the\n * {@code @ExtendWith} annotations are discovered. For example, if a test class\n * is annotated with {@code @ExtendWith(A.class)} and then with\n * {@code @ExtendWith(B.class)}, extension {@code A} will be registered before\n * extension {@code B}.\n *\n * <p>By default, if multiple extensions are registered on fields via\n * {@code @ExtendWith}, they will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute extensions in the same order, thereby allowing for\n * repeatable builds. However, there are times when extensions need to be\n * registered in an explicit order. To achieve that, you can annotate\n * {@code @ExtendWith} fields with {@link org.junit.jupiter.api.Order @Order}.\n * Any {@code @ExtendWith} field not annotated with {@code @Order} will be\n * ordered using the {@link org.junit.jupiter.api.Order#DEFAULT default} order\n * value. Note that {@code @RegisterExtension} fields can also be ordered with\n * {@code @Order}, relative to {@code @ExtendWith} fields and other\n * {@code @RegisterExtension} fields.\n *\n * <h2>Supported Extension APIs</h2>\n *\n * <ul>\n * <li>{@link ExecutionCondition}</li>\n * <li>{@link InvocationInterceptor}</li>\n * <li>{@link BeforeAllCallback}</li>\n * <li>{@link AfterAllCallback}</li>\n * <li>{@link BeforeEachCallback}</li>\n * <li>{@link AfterEachCallback}</li>\n * <li>{@link BeforeTestExecutionCallback}</li>\n * <li>{@link AfterTestExecutionCallback}</li>\n * <li>{@link TestInstanceFactory}</li>\n * <li>{@link TestInstancePostProcessor}</li>\n * <li>{@link TestInstancePreConstructCallback}</li>\n * <li>{@link TestInstancePreDestroyCallback}</li>\n * <li>{@link ParameterResolver}</li>\n * <li>{@link LifecycleMethodExecutionExceptionHandler}</li>\n * <li>{@link TestExecutionExceptionHandler}</li>\n * <li>{@link TestTemplateInvocationContextProvider}</li>\n * <li>{@link TestWatcher}</li>\n * </ul>\n *\n * @since 5.0\n * @see RegisterExtension\n * @see Extension\n */\n@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(Extensions.class)\n@API(status = STABLE, since = \"5.0\")\npublic @interface ExtendWith {\n\n\t/**\n\t * An array of one or more {@link Extension} classes to register.\n\t */\n\tClass<? extends Extension>[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * Marker interface for all extensions.\n *\n * <p>An {@code Extension} can be registered <em>declaratively</em> via\n * {@link ExtendWith @ExtendWith}, <em>programmatically</em> via\n * {@link RegisterExtension @RegisterExtension}, or <em>automatically</em> via\n * the {@link java.util.ServiceLoader} mechanism. For details on the latter,\n * consult the User Guide.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Extension implementations must have a <em>default constructor</em> if\n * registered via {@code @ExtendWith} or the {@code ServiceLoader}. When\n * registered via {@code @ExtendWith} the default constructor is not required\n * to be {@code public}. When registered via the {@code ServiceLoader} the\n * default constructor must be {@code public}. When registered via\n * {@code @RegisterExtension} the extension's constructors typically must be\n * {@code public} unless the extension provides {@code static} factory methods\n * or a builder API as an alternative to constructors.\n *\n * @since 5.0\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface Extension {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionConfigurationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Thrown if an error is encountered regarding the configuration of an\n * extension.\n *\n * @since 5.0\n */\n@API(status = STABLE, since = \"5.0\")\npublic class ExtensionConfigurationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ExtensionConfigurationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ExtensionConfigurationException(String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code ExtensionContext} encapsulates the <em>context</em> in which the\n * current test or container is being executed.\n *\n * <p>{@link Extension Extensions} are provided an instance of\n * {@code ExtensionContext} to perform their work.\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 5.0\n * @see Store\n * @see Namespace\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface ExtensionContext {\n\n\t/**\n\t * Get the parent extension context, if available.\n\t *\n\t * @return an {@code Optional} containing the parent; never {@code null} but\n\t * potentially empty\n\t * @see #getRoot()\n\t */\n\tOptional<ExtensionContext> getParent();\n\n\t/**\n\t * Get the <em>root</em> {@code ExtensionContext}.\n\t *\n\t * @return the root extension context; never {@code null} but potentially\n\t * <em>this</em> {@code ExtensionContext}\n\t * @see #getParent()\n\t */\n\tExtensionContext getRoot();\n\n\t/**\n\t * Get the unique ID of the current test or container.\n\t *\n\t * @return the unique ID of the test or container; never {@code null} or blank\n\t */\n\tString getUniqueId();\n\n\t/**\n\t * Get the display name for the current test or container.\n\t *\n\t * <p>The display name is either a default name or a custom name configured\n\t * via {@link org.junit.jupiter.api.DisplayName @DisplayName}.\n\t *\n\t * <p>For details on default display names consult the Javadoc for\n\t * {@link org.junit.jupiter.api.TestInfo#getDisplayName()}.\n\t *\n\t * <p>Note that display names are typically used for test reporting in IDEs\n\t * and build tools and may contain spaces, special characters, and even emoji.\n\t *\n\t * @return the display name of the test or container; never {@code null} or blank\n\t */\n\tString getDisplayName();\n\n\t/**\n\t * Get the set of all tags for the current test or container.\n\t *\n\t * <p>Tags may be declared directly on the test element or <em>inherited</em>\n\t * from an outer context.\n\t *\n\t * @return the set of tags for the test or container; never {@code null} but\n\t * potentially empty\n\t */\n\tSet<String> getTags();\n\n\t/**\n\t * Get the {@link AnnotatedElement} corresponding to the current extension\n\t * context, if available.\n\t *\n\t * <p>For example, if the current extension context encapsulates a test\n\t * class, test method, test factory method, or test template method, the\n\t * annotated element will be the corresponding {@link Class} or {@link Method}\n\t * reference.\n\t *\n\t * <p>Favor this method over more specific methods whenever the\n\t * {@code AnnotatedElement} API suits the task at hand &mdash; for example,\n\t * when looking up annotations regardless of concrete element type.\n\t *\n\t * @return an {@code Optional} containing the {@code AnnotatedElement};\n\t * never {@code null} but potentially empty\n\t * @see #getTestClass()\n\t * @see #getTestMethod()\n\t */\n\tOptional<AnnotatedElement> getElement();\n\n\t/**\n\t * Get the {@link Class} associated with the current test or container,\n\t * if available.\n\t *\n\t * @return an {@code Optional} containing the class; never {@code null} but\n\t * potentially empty\n\t * @see #getRequiredTestClass()\n\t */\n\tOptional<Class<?>> getTestClass();\n\n\t/**\n\t * Get the <em>required</em> {@link Class} associated with the current test\n\t * or container.\n\t *\n\t * <p>Use this method as an alternative to {@link #getTestClass()} for use\n\t * cases in which the test class is required to be present.\n\t *\n\t * @return the test class; never {@code null}\n\t * @throws PreconditionViolationException if the test class is not present\n\t * in this {@code ExtensionContext}\n\t */\n\tdefault Class<?> getRequiredTestClass() {\n\t\treturn Preconditions.notNull(getTestClass().orElse(null),\n\t\t\t\"Illegal state: required test class is not present in the current ExtensionContext\");\n\t}\n\n\t/**\n\t * Get the enclosing test classes of the current test or container.\n\t *\n\t * <p>This method is useful to look up annotations on nested test classes\n\t * and their enclosing <em>runtime</em> types:\n\t *\n\t * <pre>{@code\n\t * AnnotationSupport.findAnnotation(\n\t *     extensionContext.getRequiredTestClass(),\n\t *     MyAnnotation.class,\n\t *     extensionContext.getEnclosingTestClasses()\n\t * );\n\t * }</pre>\n\t *\n\t * @return an empty list if there is no class associated with the current\n\t * test or container or when it is not nested; otherwise, a list containing\n\t * the enclosing test classes in order from outermost to innermost; never\n\t * {@code null}\n\t *\n\t * @since 5.12.1\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findAnnotation(Class, Class, List)\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tList<Class<?>> getEnclosingTestClasses();\n\n\t/**\n\t * Get the {@link Lifecycle} of the {@linkplain #getTestInstance() test\n\t * instance} associated with the current test or container, if available.\n\t *\n\t * @return an {@code Optional} containing the test instance {@code Lifecycle};\n\t * never {@code null} but potentially empty\n\t * @since 5.1\n\t * @see org.junit.jupiter.api.TestInstance {@code @TestInstance}\n\t */\n\t@API(status = STABLE, since = \"5.1\")\n\tOptional<Lifecycle> getTestInstanceLifecycle();\n\n\t/**\n\t * Get the test instance associated with the current test or container,\n\t * if available.\n\t *\n\t * @return an {@code Optional} containing the test instance; never\n\t * {@code null} but potentially empty\n\t * @see #getRequiredTestInstance()\n\t * @see #getTestInstances()\n\t */\n\tOptional<Object> getTestInstance();\n\n\t/**\n\t * Get the <em>required</em> test instance associated with the current test\n\t * or container.\n\t *\n\t * <p>Use this method as an alternative to {@link #getTestInstance()} for use\n\t * cases in which the test instance is required to be present.\n\t *\n\t * @return the test instance; never {@code null}\n\t * @throws PreconditionViolationException if the test instance is not present\n\t * in this {@code ExtensionContext}\n\t *\n\t * @see #getRequiredTestInstances()\n\t */\n\tdefault Object getRequiredTestInstance() {\n\t\treturn Preconditions.notNull(getTestInstance().orElse(null),\n\t\t\t\"Illegal state: required test instance is not present in the current ExtensionContext\");\n\t}\n\n\t/**\n\t * Get the test instances associated with the current test or container,\n\t * if available.\n\t *\n\t * <p>While top-level tests only have a single test instance, nested tests\n\t * have one additional instance for each enclosing test class.\n\t *\n\t * @return an {@code Optional} containing the test instances; never\n\t * {@code null} but potentially empty\n\t * @since 5.4\n\t * @see #getRequiredTestInstances()\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tOptional<TestInstances> getTestInstances();\n\n\t/**\n\t * Get the <em>required</em> test instances associated with the current test\n\t * or container.\n\t *\n\t * <p>Use this method as an alternative to {@link #getTestInstances()} for use\n\t * cases in which the test instances are required to be present.\n\t *\n\t * @return the test instances; never {@code null}\n\t * @throws PreconditionViolationException if the test instances are not present\n\t * in this {@code ExtensionContext}\n\t * @since 5.4\n\t */\n\t@API(status = STABLE, since = \"5.7\")\n\tdefault TestInstances getRequiredTestInstances() {\n\t\treturn Preconditions.notNull(getTestInstances().orElse(null),\n\t\t\t\"Illegal state: required test instances are not present in the current ExtensionContext\");\n\t}\n\n\t/**\n\t * Get the {@link Method} associated with the current test, if available.\n\t *\n\t * @return an {@code Optional} containing the method; never {@code null} but\n\t * potentially empty\n\t * @see #getRequiredTestMethod()\n\t */\n\tOptional<Method> getTestMethod();\n\n\t/**\n\t * Get the <em>required</em> {@link Method} associated with the current test\n\t * or container.\n\t *\n\t * <p>Use this method as an alternative to {@link #getTestMethod()} for use\n\t * cases in which the test method is required to be present.\n\t *\n\t * @return the test method; never {@code null}\n\t * @throws PreconditionViolationException if the test method is not present\n\t * in this {@code ExtensionContext}\n\t */\n\tdefault Method getRequiredTestMethod() {\n\t\treturn Preconditions.notNull(getTestMethod().orElse(null),\n\t\t\t\"Illegal state: required test method is not present in the current ExtensionContext\");\n\t}\n\n\t/**\n\t * Get the exception that was thrown during execution of the test or container\n\t * associated with this {@code ExtensionContext}, if available.\n\t *\n\t * <p>This method is typically used for logging and tracing purposes. If you\n\t * wish to actually <em>handle</em> an exception thrown during test execution,\n\t * implement the {@link TestExecutionExceptionHandler} API.\n\t *\n\t * <p>Unlike the exception passed to a {@code TestExecutionExceptionHandler},\n\t * an <em>execution exception</em> returned by this method can be any\n\t * exception thrown during the invocation of a {@code @Test} method, its\n\t * surrounding {@code @BeforeEach} and {@code @AfterEach} methods, or a\n\t * test-level {@link Extension}. Similarly, if this {@code ExtensionContext}\n\t * represents a test class, the <em>execution exception</em> returned by\n\t * this method can be any exception thrown in a {@code @BeforeAll} or\n\t * {@code AfterAll} method or a class-level {@link Extension}.\n\t *\n\t * <p>Note, however, that this method will never return an exception\n\t * swallowed by a {@code TestExecutionExceptionHandler}. Furthermore, if\n\t * multiple exceptions have been thrown during test execution, the exception\n\t * returned by this method will be the first such exception with all\n\t * additional exceptions {@linkplain Throwable#addSuppressed(Throwable)\n\t * suppressed} in the first one.\n\t *\n\t * @return an {@code Optional} containing the exception thrown; never\n\t * {@code null} but potentially empty if test execution has not (yet)\n\t * resulted in an exception\n\t */\n\tOptional<Throwable> getExecutionException();\n\n\t/**\n\t * Get the configuration parameter stored under the specified {@code key}.\n\t *\n\t * <p>If no such key is present in the {@code ConfigurationParameters} for\n\t * the JUnit Platform, an attempt will be made to look up the value as a\n\t * JVM system property. If no such system property exists, an attempt will\n\t * be made to look up the value in the JUnit Platform properties file.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @since 5.1\n\t * @see System#getProperty(String)\n\t * @see org.junit.platform.engine.ConfigurationParameters\n\t */\n\t@API(status = STABLE, since = \"5.1\")\n\tOptional<String> getConfigurationParameter(String key);\n\n\t/**\n\t * Get and transform the configuration parameter stored under the specified\n\t * {@code key} using the specified {@code transformer}.\n\t *\n\t * <p>If no such key is present in the {@code ConfigurationParameters} for\n\t * the JUnit Platform, an attempt will be made to look up the value as a\n\t * JVM system property. If no such system property exists, an attempt will\n\t * be made to look up the value in the JUnit Platform properties file.\n\t *\n\t * <p>In case the transformer throws an exception, it will be wrapped in a\n\t * {@link org.junit.platform.commons.JUnitException} with a helpful message.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @param transformer the transformer to apply in case a value is found;\n\t * never {@code null}\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @since 5.7\n\t * @see System#getProperty(String)\n\t * @see org.junit.platform.engine.ConfigurationParameters\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t<T> Optional<T> getConfigurationParameter(String key, Function<? super String, ? extends @Nullable T> transformer);\n\n\t/**\n\t * Publish a map of key-value pairs to be consumed by an\n\t * {@code org.junit.platform.engine.EngineExecutionListener} in order to\n\t * supply additional information to the reporting infrastructure.\n\t *\n\t * @param map the key-value pairs to be published; never {@code null};\n\t * keys and values within entries in the map also must not be\n\t * {@code null} or blank\n\t * @see #publishReportEntry(String, String)\n\t * @see #publishReportEntry(String)\n\t * @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished\n\t */\n\tvoid publishReportEntry(Map<String, String> map);\n\n\t/**\n\t * Publish the specified key-value pair to be consumed by an\n\t * {@code org.junit.platform.engine.EngineExecutionListener} in order to\n\t * supply additional information to the reporting infrastructure.\n\t *\n\t * @param key the key of the published pair; never {@code null} or blank\n\t * @param value the value of the published pair; never {@code null} or blank\n\t * @see #publishReportEntry(Map)\n\t * @see #publishReportEntry(String)\n\t * @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished\n\t */\n\tdefault void publishReportEntry(String key, String value) {\n\t\tPreconditions.notBlank(key, \"key must not be null or blank\");\n\t\tPreconditions.notBlank(value, \"value must not be null or blank\");\n\t\tpublishReportEntry(Map.of(key, value));\n\t}\n\n\t/**\n\t * Publish the specified value to be consumed by an\n\t * {@code org.junit.platform.engine.EngineExecutionListener} in order to\n\t * supply additional information to the reporting infrastructure.\n\t *\n\t * <p>This method delegates to {@link #publishReportEntry(String, String)},\n\t * supplying {@code \"value\"} as the key and the supplied {@code value}\n\t * argument as the value.\n\t *\n\t * @param value the value to be published; never {@code null} or blank\n\t * @since 5.3\n\t * @see #publishReportEntry(Map)\n\t * @see #publishReportEntry(String, String)\n\t * @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished\n\t */\n\t@API(status = STABLE, since = \"5.3\")\n\tdefault void publishReportEntry(String value) {\n\t\tpublishReportEntry(\"value\", value);\n\t}\n\n\t/**\n\t * Publish a file with the supplied name written by the supplied action and\n\t * attach it to the current test or container.\n\t *\n\t * <p>The file will be resolved in the report output directory prior to\n\t * invoking the supplied action.\n\t *\n\t * @param name the name of the file to be published; never {@code null} or\n\t * blank and must not contain any path separators\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link org.junit.jupiter.api.extension.MediaType#APPLICATION_OCTET_STREAM}\n\t * if unknown\n\t * @param action the action to be executed to write the file; never {@code null}\n\t * @since 5.12\n\t * @see org.junit.platform.engine.EngineExecutionListener#fileEntryPublished\n\t * @deprecated Use\n\t * {@link #publishFile(String, MediaType, ThrowingConsumer)}\n\t * instead.\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\t@SuppressWarnings(\"removal\")\n\tdefault void publishFile(String name, org.junit.jupiter.api.extension.MediaType mediaType,\n\t\t\tThrowingConsumer<Path> action) {\n\n\t\tPreconditions.notNull(mediaType, \"mediaType must not be null\");\n\t\tpublishFile(name, MediaType.parse(mediaType.toString()), action);\n\t}\n\n\t/**\n\t * Publish a file with the supplied name written by the supplied action and\n\t * attach it to the current test or container.\n\t *\n\t * <p>The file will be resolved in the report output directory prior to\n\t * invoking the supplied action.\n\t *\n\t * @param name the name of the file to be attached; never {@code null} or\n\t * blank and must not contain any path separators\n\t * @param mediaType the media type of the file; never {@code null}; use\n\t * {@link MediaType#APPLICATION_OCTET_STREAM} if unknown\n\t * @param action the action to be executed to write the file; never {@code null}\n\t * @since 5.14\n\t * @see org.junit.platform.engine.EngineExecutionListener#fileEntryPublished\n\t */\n\t@API(status = MAINTAINED, since = \"5.14\")\n\tvoid publishFile(String name, MediaType mediaType, ThrowingConsumer<Path> action);\n\n\t/**\n\t * Publish a directory with the supplied name written by the supplied action\n\t * and attach it to the current test or container.\n\t *\n\t * <p>The directory will be resolved and created in the report output directory\n\t * prior to invoking the supplied action, if it does not already exist.\n\t *\n\t * @param name the name of the directory to be published; never {@code null}\n\t * or blank and must not contain any path separators\n\t * @param action the action to be executed to write to the directory; never\n\t * {@code null}\n\t * @since 5.12\n\t * @see org.junit.platform.engine.EngineExecutionListener#fileEntryPublished\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tvoid publishDirectory(String name, ThrowingConsumer<Path> action);\n\n\t/**\n\t * Get the {@link Store} for the supplied {@link Namespace}.\n\t *\n\t * <p>Use {@code getStore(Namespace.GLOBAL)} to get the default, global {@link Namespace}.\n\t *\n\t * <p>A store is bound to its extension context lifecycle. When an extension\n\t * context lifecycle ends it closes its associated store. All stored values\n\t * that are instances of {@link ExtensionContext.Store.CloseableResource} are\n\t * notified by invoking their {@code close()} methods.\n\t *\n\t * @param namespace the {@code Namespace} to get the store for; never {@code null}\n\t * @return the store in which to put and get objects for other invocations\n\t * working in the same namespace; never {@code null}\n\t * @see Namespace#GLOBAL\n\t * @see #getStore(StoreScope, Namespace)\n\t */\n\tStore getStore(Namespace namespace);\n\n\t/**\n\t * Returns the store for supplied scope and namespace.\n\t *\n\t * <p>If {@code scope} is\n\t * {@link StoreScope#EXTENSION_CONTEXT EXTENSION_CONTEXT}, the store behaves\n\t * exactly like the one returned by {@link #getStore(Namespace)}. If the\n\t * {@code scope} is {@link StoreScope#LAUNCHER_SESSION LAUNCHER_SESSION} or\n\t * {@link StoreScope#EXECUTION_REQUEST EXECUTION_REQUEST}, all stored values\n\t * that are instances of {@link AutoCloseable} are notified by invoking\n\t * their {@code close()} methods when the scope is closed.\n\t *\n\t * @since 5.13\n\t * @see StoreScope\n\t * @see #getStore(Namespace)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tStore getStore(StoreScope scope, Namespace namespace);\n\n\t/**\n\t * Get the {@link ExecutionMode} associated with the current test or container.\n\t *\n\t * @return the {@code ExecutionMode} of the test; never {@code null}\n\t *\n\t * @since 5.8.1\n\t * @see org.junit.jupiter.api.parallel.ExecutionMode {@code @ExecutionMode}\n\t */\n\t@API(status = STABLE, since = \"5.8.1\")\n\tExecutionMode getExecutionMode();\n\n\t/**\n\t * Get an {@link ExecutableInvoker} to invoke methods and constructors\n\t * with support for dynamic resolution of parameters.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.11\")\n\tExecutableInvoker getExecutableInvoker();\n\n\t/**\n\t * {@code Store} provides methods for extensions to save and retrieve data.\n\t */\n\tinterface Store {\n\n\t\t/**\n\t\t * Classes implementing this interface indicate that they want to {@link #close}\n\t\t * some underlying resource or resources when the enclosing {@link Store Store}\n\t\t * is closed.\n\t\t *\n\t\t * <p>Note that the {@code CloseableResource} API is only honored for\n\t\t * objects stored within an extension context {@link Store Store}.\n\t\t *\n\t\t * <p>The resources stored in a {@link Store Store} are closed in the\n\t\t * inverse order they were added in.\n\t\t *\n\t\t * @since 5.1\n\t\t * @deprecated Please extend {@code AutoCloseable} directly.\n\t\t */\n\t\t@Deprecated(since = \"5.13\")\n\t\t@API(status = DEPRECATED, since = \"5.13\")\n\t\tinterface CloseableResource {\n\n\t\t\t/**\n\t\t\t * Close underlying resources.\n\t\t\t *\n\t\t\t * @throws Throwable any throwable will be caught and rethrown\n\t\t\t */\n\t\t\tvoid close() throws Throwable;\n\n\t\t}\n\n\t\t/**\n\t\t * Get the value that is stored under the supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store.\n\t\t *\n\t\t * <p>For greater type safety, consider using {@link #get(Object, Class)}\n\t\t * instead.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @return the value; potentially {@code null}\n\t\t * @see #get(Object, Class)\n\t\t * @see #getOrDefault(Object, Class, Object)\n\t\t */\n\t\t@Nullable\n\t\tObject get(Object key);\n\n\t\t/**\n\t\t * Get the value of the specified required type that is stored under\n\t\t * the supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param requiredType the required type of the value; never {@code null}\n\t\t * @param <V> the value type\n\t\t * @return the value; potentially {@code null}\n\t\t * @see #get(Object)\n\t\t * @see #getOrDefault(Object, Class, Object)\n\t\t */\n\t\t<V> @Nullable V get(Object key, Class<V> requiredType);\n\n\t\t/**\n\t\t * Get the value of the specified required type that is stored under\n\t\t * the supplied {@code key}, or the supplied {@code defaultValue} if no\n\t\t * value is found for the supplied {@code key} in this store or in an\n\t\t * ancestor.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param requiredType the required type of the value; never {@code null}\n\t\t * @param defaultValue the default value; never {@code null}\n\t\t * @param <V> the value type\n\t\t * @return the value; never {@code null}\n\t\t * @since 5.5\n\t\t * @see #get(Object, Class)\n\t\t */\n\t\t@API(status = STABLE, since = \"5.5\")\n\t\tdefault <V> V getOrDefault(Object key, Class<V> requiredType, V defaultValue) {\n\t\t\tV value = get(key, requiredType);\n\t\t\treturn (value != null ? value : defaultValue);\n\t\t}\n\n\t\t/**\n\t\t * Get the object of type {@code type} that is present in this\n\t\t * {@code Store} (<em>keyed</em> by {@code type}); and otherwise invoke\n\t\t * the default constructor for {@code type} to generate the object,\n\t\t * store it, and return it.\n\t\t *\n\t\t * <p>This method is a shortcut for the following, where {@code X} is\n\t\t * the type of object we wish to retrieve from the store.\n\t\t *\n\t\t * <pre style=\"code\">\n\t\t * X x = store.computeIfAbsent(X.class, key -&gt; new X(), X.class);\n\t\t * // Equivalent to:\n\t\t * // X x = store.computeIfAbsent(X.class);\n\t\t * </pre>\n\t\t *\n\t\t * <p>See {@link #computeIfAbsent(Object, Function, Class)} for further\n\t\t * details.\n\t\t *\n\t\t * <p>If {@code type} implements {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param type the type of object to retrieve; never {@code null}\n\t\t * @param <V> the key and value type\n\t\t * @return the object; never {@code null}\n\t\t * @since 5.1\n\t\t * @see #computeIfAbsent(Class)\n\t\t * @see #computeIfAbsent(Object, Function)\n\t\t * @see #computeIfAbsent(Object, Function, Class)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t * @deprecated Please use {@link #computeIfAbsent(Class)} instead.\n\t\t */\n\t\t@Deprecated(since = \"6.0\")\n\t\t@API(status = DEPRECATED, since = \"6.0\")\n\t\tdefault <V> V getOrComputeIfAbsent(Class<V> type) {\n\t\t\treturn computeIfAbsent(type);\n\t\t}\n\n\t\t/**\n\t\t * Return the object of type {@code type} if it is present and not\n\t\t * {@code null} in this {@code Store} (<em>keyed</em> by {@code type});\n\t\t * otherwise, invoke the default constructor for {@code type} to\n\t\t * generate the object, store it, and return it.\n\t\t *\n\t\t * <p>This method is a shortcut for the following, where {@code X} is\n\t\t * the type of object we wish to retrieve from the store.\n\t\t *\n\t\t * <pre style=\"code\">\n\t\t * X x = store.computeIfAbsent(X.class, key -&gt; new X(), X.class);\n\t\t * // Equivalent to:\n\t\t * // X x = store.computeIfAbsent(X.class);\n\t\t * </pre>\n\t\t *\n\t\t * <p>See {@link #computeIfAbsent(Object, Function, Class)} for further\n\t\t * details.\n\t\t *\n\t\t * <p>If {@code type} implements {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param type the type of object to retrieve; never {@code null}\n\t\t * @param <V> the key and value type\n\t\t * @return the object; never {@code null}\n\t\t * @since 6.0\n\t\t * @see #computeIfAbsent(Object, Function)\n\t\t * @see #computeIfAbsent(Object, Function, Class)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"6.0\")\n\t\tdefault <V> V computeIfAbsent(Class<V> type) {\n\t\t\treturn computeIfAbsent(type, ReflectionSupport::newInstance, type);\n\t\t}\n\n\t\t/**\n\t\t * Get the value that is stored under the supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store. If no value is found for the supplied {@code key},\n\t\t * a new value will be computed by the {@code defaultCreator} (given\n\t\t * the {@code key} as input), stored, and returned.\n\t\t *\n\t\t * <p>For greater type safety, consider using\n\t\t * {@link #computeIfAbsent(Object, Function, Class)} instead.\n\t\t *\n\t\t * <p>If the created value is an instance of {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param defaultCreator the function called with the supplied {@code key}\n\t\t * to create a new value; never {@code null} but may return {@code null}\n\t\t * @param <K> the key type\n\t\t * @param <V> the value type\n\t\t * @return the value; potentially {@code null}\n\t\t * @see #computeIfAbsent(Class)\n\t\t * @see #computeIfAbsent(Object, Function)\n\t\t * @see #computeIfAbsent(Object, Function, Class)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t * @deprecated Please use {@link #computeIfAbsent(Object, Function)} instead.\n\t\t */\n\t\t@Deprecated(since = \"6.0\")\n\t\t@API(status = DEPRECATED, since = \"6.0\")\n\t\t<K, V extends @Nullable Object> @Nullable Object getOrComputeIfAbsent(K key,\n\t\t\t\tFunction<? super K, ? extends V> defaultCreator);\n\n\t\t/**\n\t\t * Return the value of the specified required type that is stored under\n\t\t * the supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store. If no value is found for the supplied {@code key}\n\t\t * or the value is {@code null}, a new value will be computed by the\n\t\t * {@code defaultCreator} (given the {@code key} as input), stored, and\n\t\t * returned.\n\t\t *\n\t\t * <p>For greater type safety, consider using\n\t\t * {@link #computeIfAbsent(Object, Function, Class)} instead.\n\t\t *\n\t\t * <p>If the created value is an instance of {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param defaultCreator the function called with the supplied {@code key}\n\t\t * to create a new value; never {@code null} and must not return\n\t\t * {@code null}\n\t\t * @param <K> the key type\n\t\t * @param <V> the value type\n\t\t * @return the value; never {@code null}\n\t\t * @since 6.0\n\t\t * @see #computeIfAbsent(Class)\n\t\t * @see #computeIfAbsent(Object, Function, Class)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"6.0\")\n\t\t<K, V> Object computeIfAbsent(K key, Function<? super K, ? extends V> defaultCreator);\n\n\t\t/**\n\t\t * Get the value of the specified required type that is stored under the\n\t\t * supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store. If no value is found for the supplied {@code key},\n\t\t * a new value will be computed by the {@code defaultCreator} (given\n\t\t * the {@code key} as input), stored, and returned.\n\t\t *\n\t\t * <p>If the created value implements {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param defaultCreator the function called with the supplied {@code key}\n\t\t * to create a new value; never {@code null} but may return {@code null}\n\t\t * @param requiredType the required type of the value; never {@code null}\n\t\t * @param <K> the key type\n\t\t * @param <V> the value type\n\t\t * @return the value; potentially {@code null}\n\t\t * @see #computeIfAbsent(Class)\n\t\t * @see #computeIfAbsent(Object, Function)\n\t\t * @see #computeIfAbsent(Object, Function, Class)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t * @deprecated Please use {@link #computeIfAbsent(Object, Function, Class)} instead.\n\t\t */\n\t\t@Deprecated(since = \"6.0\")\n\t\t@API(status = DEPRECATED, since = \"6.0\")\n\t\t<K, V extends @Nullable Object> @Nullable V getOrComputeIfAbsent(K key,\n\t\t\t\tFunction<? super K, ? extends V> defaultCreator, Class<V> requiredType);\n\n\t\t/**\n\t\t * Get the value of the specified required type that is stored under the\n\t\t * supplied {@code key}.\n\t\t *\n\t\t * <p>If no value is stored in the current {@link ExtensionContext}\n\t\t * for the supplied {@code key}, ancestors of the context will be queried\n\t\t * for a value with the same {@code key} in the {@code Namespace} used\n\t\t * to create this store. If no value is found for the supplied {@code key}\n\t\t * or the value is {@code null}, a new value will be computed by the\n\t\t * {@code defaultCreator} (given the {@code key} as input), stored, and\n\t\t * returned.\n\t\t *\n\t\t * <p>If the created value is an instance of {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param defaultCreator the function called with the supplied {@code key}\n\t\t * to create a new value; never {@code null} and must not return\n\t\t * {@code null}\n\t\t * @param requiredType the required type of the value; never {@code null}\n\t\t * @param <K> the key type\n\t\t * @param <V> the value type\n\t\t * @return the value; never {@code null}\n\t\t * @since 6.0\n\t\t * @see #computeIfAbsent(Class)\n\t\t * @see #computeIfAbsent(Object, Function)\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"6.0\")\n\t\t<K, V> V computeIfAbsent(K key, Function<? super K, ? extends V> defaultCreator, Class<V> requiredType);\n\n\t\t/**\n\t\t * Store a {@code value} for later retrieval under the supplied {@code key}.\n\t\t *\n\t\t * <p>A stored {@code value} is visible in child {@link ExtensionContext\n\t\t * ExtensionContexts} for the store's {@code Namespace} unless they\n\t\t * overwrite it.\n\t\t *\n\t\t * <p>If the {@code value} is an instance of {@link CloseableResource} or\n\t\t * {@link AutoCloseable} (unless the\n\t\t * {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t\t * configuration parameter is set to {@code false}), then the {@code close()}\n\t\t * method will be invoked on the stored object when the store is closed.\n\t\t *\n\t\t * @param key the key under which the value should be stored; never\n\t\t * {@code null}\n\t\t * @param value the value to store; may be {@code null}\n\t\t * @see CloseableResource\n\t\t * @see AutoCloseable\n\t\t */\n\t\tvoid put(Object key, @Nullable Object value);\n\n\t\t/**\n\t\t * Remove the value that was previously stored under the supplied {@code key}.\n\t\t *\n\t\t * <p>The value will only be removed in the current {@link ExtensionContext},\n\t\t * not in ancestors. In addition, the {@link CloseableResource} and {@link AutoCloseable}\n\t\t * API will not be honored for values that are manually removed via this method.\n\t\t *\n\t\t * <p>For greater type safety, consider using {@link #remove(Object, Class)}\n\t\t * instead.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @return the previous value or {@code null} if no value was present\n\t\t * for the specified key\n\t\t * @see #remove(Object, Class)\n\t\t */\n\t\t@Nullable\n\t\tObject remove(Object key);\n\n\t\t/**\n\t\t * Remove the value of the specified required type that was previously stored\n\t\t * under the supplied {@code key}.\n\t\t *\n\t\t * <p>The value will only be removed in the current {@link ExtensionContext},\n\t\t * not in ancestors. In addition, the {@link CloseableResource} and {@link AutoCloseable}\n\t\t * API will not be honored for values that are manually removed via this method.\n\t\t *\n\t\t * @param key the key; never {@code null}\n\t\t * @param requiredType the required type of the value; never {@code null}\n\t\t * @param <V> the value type\n\t\t * @return the previous value or {@code null} if no value was present\n\t\t * for the specified key\n\t\t * @see #remove(Object)\n\t\t */\n\t\t<V> @Nullable V remove(Object key, Class<V> requiredType);\n\n\t}\n\n\t/**\n\t * A {@code Namespace} is used to provide a <em>scope</em> for data saved by\n\t * extensions within a {@link Store}.\n\t *\n\t * <p>Storing data in custom namespaces allows extensions to avoid accidentally\n\t * mixing data between extensions or across different invocations within the\n\t * lifecycle of a single extension.\n\t */\n\tfinal class Namespace {\n\n\t\t/**\n\t\t * The default, global namespace which allows access to stored data from\n\t\t * all extensions.\n\t\t */\n\t\tpublic static final Namespace GLOBAL = Namespace.create(new Object());\n\n\t\t/**\n\t\t * Create a namespace which restricts access to data to all extensions\n\t\t * which use the same sequence of {@code parts} for creating a namespace.\n\t\t *\n\t\t * <p>The order of the {@code parts} is significant.\n\t\t *\n\t\t * <p>Internally the {@code parts} are compared using {@link Object#equals(Object)}.\n\t\t */\n\t\tpublic static Namespace create(Object... parts) {\n\t\t\tPreconditions.notEmpty(parts, \"parts array must not be null or empty\");\n\t\t\tPreconditions.containsNoNullElements(parts, \"individual parts must not be null\");\n\t\t\treturn new Namespace(List.of(parts));\n\t\t}\n\n\t\tprivate final List<Object> parts;\n\n\t\tprivate Namespace(List<Object> parts) {\n\t\t\tthis.parts = List.copyOf(parts);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tNamespace that = (Namespace) o;\n\t\t\treturn this.parts.equals(that.parts);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn this.parts.hashCode();\n\t\t}\n\n\t\t/**\n\t\t * Create a new namespace by appending the supplied {@code parts} to the\n\t\t * existing sequence of parts in this namespace.\n\t\t *\n\t\t * @return new namespace; never {@code null}\n\t\t * @since 5.8\n\t\t */\n\t\t@API(status = STABLE, since = \"5.10\")\n\t\tpublic Namespace append(Object... parts) {\n\t\t\tPreconditions.notEmpty(parts, \"parts array must not be null or empty\");\n\t\t\tPreconditions.containsNoNullElements(parts, \"individual parts must not be null\");\n\t\t\tArrayList<Object> newParts = new ArrayList<>(this.parts.size() + parts.length);\n\t\t\tnewParts.addAll(this.parts);\n\t\t\tCollections.addAll(newParts, parts);\n\t\t\treturn new Namespace(newParts);\n\t\t}\n\n\t\t@API(status = INTERNAL, since = \"5.13\")\n\t\tpublic List<Object> getParts() {\n\t\t\treturn parts;\n\t\t}\n\t}\n\n\t/**\n\t * {@code StoreScope} is an enumeration of the different scopes for\n\t * {@link Store} instances.\n\t *\n\t * @since 5.13\n\t * @see #getStore(StoreScope, Namespace)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tenum StoreScope {\n\n\t\t/**\n\t\t * The store is scoped to the current {@code LauncherSession}.\n\t\t *\n\t\t * <p>Any data that is stored in a {@code Store} with this scope will be\n\t\t * available throughout the entire launcher session. Therefore, it may\n\t\t * be used to inject values from registered\n\t\t * {@code LauncherSessionListener} implementations, to share data across\n\t\t * multiple executions of the Jupiter engine within the same session, or\n\t\t * even to share data across multiple engines.\n\t\t *\n\t\t * @see org.junit.platform.launcher.LauncherSession#getStore()\n\t\t * @see org.junit.platform.launcher.LauncherSessionListener\n\t\t */\n\t\tLAUNCHER_SESSION,\n\n\t\t/**\n\t\t * The store is scoped to the current {@code ExecutionRequest} of the\n\t\t * JUnit Platform {@code Launcher}.\n\t\t *\n\t\t * <p>Any data that is stored in a {@code Store} with this scope will be\n\t\t * available for the duration of the current execution request.\n\t\t * Therefore, it may be used to share data across multiple engines.\n\t\t *\n\t\t * @see org.junit.platform.engine.ExecutionRequest#getStore()\n\t\t */\n\t\tEXECUTION_REQUEST,\n\n\t\t/**\n\t\t * The store is scoped to the current {@code ExtensionContext}.\n\t\t *\n\t\t * <p>Any data that is stored in a {@code Store} with this scope will be\n\t\t * bound to the current extension context lifecycle.\n\t\t */\n\t\tEXTENSION_CONTEXT\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ExtensionContextException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Thrown if an error is encountered regarding the use of an\n * {@link ExtensionContext} or {@link Store}.\n *\n * @since 5.0\n */\n@API(status = STABLE, since = \"5.0\")\npublic class ExtensionContextException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t@SuppressWarnings(\"unused\")\n\tpublic ExtensionContextException(@Nullable String message) {\n\t\tsuper(message);\n\t}\n\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic ExtensionContextException(@Nullable String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/Extensions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Extensions} is a container for one or more {@code @ExtendWith}\n * declarations.\n *\n * <p>Note, however, that use of the {@code @Extensions} container is completely\n * optional since {@code @ExtendWith} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * @since 5.0\n * @see ExtendWith\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.0\")\npublic @interface Extensions {\n\n\t/**\n\t * An array of one or more {@link ExtendWith @ExtendWith} declarations.\n\t */\n\tExtendWith[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/InvocationInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestTemplate;\n\n/**\n * {@code InvocationInterceptor} defines the API for {@link Extension\n * Extensions} that wish to intercept calls to test code.\n *\n * <h2>Invocation Contract</h2>\n *\n * <p>Each method in this class must call {@link Invocation#proceed()} or {@link\n * Invocation#skip()} exactly once on the supplied invocation. Otherwise, the\n * enclosing test or container will be reported as failed.\n *\n * <p>The default implementation calls {@link Invocation#proceed()\n * proceed()} on the supplied {@linkplain Invocation invocation}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>{@code ExtensionContext} Scope</h2>\n *\n * <p>As of JUnit Jupiter 5.12, this API participates in the\n * {@link TestInstantiationAwareExtension} contract. Implementations of this API\n * may therefore choose to override\n * {@link TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n * getTestInstantiationExtensionContextScope(ExtensionContext)}. See\n * {@link #interceptTestClassConstructor(Invocation, ReflectiveInvocationContext, ExtensionContext)}\n * for details.\n *\n * @since 5.5\n * @see Invocation\n * @see ReflectiveInvocationContext\n * @see ExtensionContext\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface InvocationInterceptor extends TestInstantiationAwareExtension {\n\n\t/**\n\t * Intercept the invocation of a test class constructor.\n\t *\n\t * <p>Note that the test class may <em>not</em> have been initialized\n\t * (static initialization) when this method is invoked.\n\t *\n\t * <p>By default, the supplied {@link ExtensionContext} represents the test\n\t * class that's about to be constructed. Extensions may override\n\t * {@link #getTestInstantiationExtensionContextScope} to return\n\t * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} in order to change\n\t * the scope of the {@code ExtensionContext} to the test method, unless the\n\t * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.\n\t * Changing the scope makes test-specific data available to the\n\t * implementation of this method and allows keeping state on the test level\n\t * by using the provided {@link ExtensionContext.Store Store} instance.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @param <T> the result type\n\t * @return the result of the invocation; never {@code null}\n\t * @throws Throwable in case of failure\n\t */\n\tdefault <T> T interceptTestClassConstructor(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Constructor<T>> invocationContext, ExtensionContext extensionContext)\n\t\t\tthrows Throwable {\n\t\treturn invocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link BeforeAll @BeforeAll} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link BeforeEach @BeforeEach} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link Test @Test} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link TestFactory @TestFactory} method,\n\t * such as a {@link org.junit.jupiter.api.RepeatedTest @RepeatedTest} or\n\t * {@code @ParameterizedTest} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @param <T> the result type\n\t * @return the result of the invocation; potentially {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault <T extends @Nullable Object> T interceptTestFactoryMethod(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\treturn invocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link TestTemplate @TestTemplate} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of a {@link DynamicTest}.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\t@API(status = STABLE, since = \"5.11\")\n\tdefault void interceptDynamicTest(Invocation<@Nullable Void> invocation,\n\t\t\tDynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of an {@link AfterEach @AfterEach} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * Intercept the invocation of an {@link AfterAll @AfterAll} method.\n\t *\n\t * @param invocation the invocation that is being intercepted; never\n\t * {@code null}\n\t * @param invocationContext the context of the invocation that is being\n\t * intercepted; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @throws Throwable in case of failures\n\t */\n\tdefault void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tinvocation.proceed();\n\t}\n\n\t/**\n\t * An invocation that returns a result and may throw a {@link Throwable}.\n\t *\n\t * <p>This interface is not intended to be implemented by clients.\n\t *\n\t * @param <T> the result type\n\t * @since 5.5\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tinterface Invocation<T extends @Nullable Object> {\n\n\t\t/**\n\t\t * Proceed with this invocation.\n\t\t *\n\t\t * @return the result of this invocation; potentially {@code null}.\n\t\t * @throws Throwable in case the invocation failed\n\t\t */\n\t\tT proceed() throws Throwable;\n\n\t\t/**\n\t\t * Explicitly skip this invocation.\n\t\t *\n\t\t * <p>This allows to bypass the check that {@link #proceed()} must be\n\t\t * called at least once. The default implementation does nothing.\n\t\t */\n\t\t@API(status = STABLE, since = \"5.10\")\n\t\tdefault void skip() {\n\t\t\t// do nothing\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/LifecycleMethodExecutionExceptionHandler.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code LifecycleMethodExecutionExceptionHandler} defines the API for\n * {@link Extension Extensions} that wish to handle exceptions thrown during\n * the execution of {@code @BeforeAll}, {@code @BeforeEach}, {@code @AfterEach},\n * and {@code @AfterAll} lifecycle methods.\n *\n * <p>Common use cases include swallowing an exception if it's anticipated,\n * logging errors, or rolling back a transaction in certain error scenarios.\n *\n * <p>Implementations of this extension API must be registered at the class level\n * if exceptions thrown from {@code @BeforeAll} or {@code @AfterAll} methods are\n * to be handled. When registered at the test level, only exceptions thrown from\n * {@code @BeforeEach} or {@code @AfterEach} methods will be handled.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on constructor\n * requirements.\n *\n * <h2 id=\"implementation-guidelines\">Implementation Guidelines</h2>\n *\n * <p>An implementation of an exception handler method defined in this API must\n * perform one of the following.\n *\n * <ol>\n * <li>Rethrow the supplied {@code Throwable} <em>as is</em>, which is the default implementation.</li>\n * <li>Swallow the supplied {@code Throwable}, thereby preventing propagation.</li>\n * <li>Throw a new exception, potentially wrapping the supplied {@code Throwable}.</li>\n * </ol>\n *\n * <p>If the supplied {@code Throwable} is swallowed by a handler method, subsequent\n * handler methods for the same lifecycle will not be invoked; otherwise, the\n * corresponding handler method of the next registered\n * {@code LifecycleMethodExecutionExceptionHandler} (if there is one) will be\n * invoked with any {@link Throwable} thrown by the previous handler.\n *\n * @since 5.5\n * @see TestExecutionExceptionHandler\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface LifecycleMethodExecutionExceptionHandler extends Extension {\n\n\t/**\n\t * Handle the supplied {@link Throwable} that was thrown during execution of\n\t * a {@code @BeforeAll} lifecycle method.\n\t *\n\t * <p>Please refer to the class-level Javadoc for\n\t * <a href=\"#implementation-guidelines\">Implementation Guidelines</a>.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param throwable the {@code Throwable} to handle; never {@code null}\n\t */\n\tdefault void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\tthrows Throwable {\n\n\t\tthrow throwable;\n\t}\n\n\t/**\n\t * Handle the supplied {@link Throwable} that was thrown during execution of\n\t * a {@code @BeforeEach} lifecycle method.\n\t *\n\t * <p>Please refer to the class-level Javadoc for\n\t * <a href=\"#implementation-guidelines\">Implementation Guidelines</a>.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param throwable the {@code Throwable} to handle; never {@code null}\n\t */\n\tdefault void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\tthrows Throwable {\n\n\t\tthrow throwable;\n\t}\n\n\t/**\n\t * Handle the supplied {@link Throwable} that was thrown during execution of\n\t * a {@code @AfterEach} lifecycle method.\n\t *\n\t * <p>Please refer to the class-level Javadoc for\n\t * <a href=\"#implementation-guidelines\">Implementation Guidelines</a>.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param throwable the {@code Throwable} to handle; never {@code null}\n\t */\n\tdefault void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\tthrows Throwable {\n\n\t\tthrow throwable;\n\t}\n\n\t/**\n\t * Handle the supplied {@link Throwable} that was thrown during execution of\n\t * a {@code @AfterAll} lifecycle method.\n\t *\n\t * <p>Please refer to the class-level Javadoc for\n\t * <a href=\"#implementation-guidelines\">Implementation Guidelines</a>.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param throwable the {@code Throwable} to handle; never {@code null}\n\t */\n\tdefault void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\tthrows Throwable {\n\n\t\tthrow throwable;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/MediaType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport java.nio.charset.Charset;\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Represents a media type as defined by\n * <a href=\"https://tools.ietf.org/html/rfc2045\">RFC 2045</a>.\n *\n * @since 5.12\n * @see org.junit.jupiter.api.TestReporter#publishFile(Path, MediaType)\n * @see org.junit.jupiter.api.TestReporter#publishFile(String, MediaType, org.junit.jupiter.api.function.ThrowingConsumer)\n * @see ExtensionContext#publishFile(String, MediaType, org.junit.jupiter.api.function.ThrowingConsumer)\n * @deprecated Use {@link org.junit.jupiter.api.MediaType} instead.\n */\n@Deprecated(since = \"5.14\", forRemoval = true)\n@API(status = DEPRECATED, since = \"5.14\")\npublic final class MediaType extends org.junit.jupiter.api.MediaType {\n\n\t/**\n\t * The {@code text/plain} media type.\n\t */\n\tpublic static final MediaType TEXT_PLAIN = create(\"text\", \"plain\");\n\n\t/**\n\t * The {@code text/plain; charset=UTF-8} media type.\n\t */\n\tpublic static final MediaType TEXT_PLAIN_UTF_8 = create(\"text\", \"plain\", UTF_8);\n\n\t/**\n\t * The {@code application/json} media type.\n\t */\n\tpublic static final MediaType APPLICATION_JSON = create(\"application\", \"json\");\n\n\t/**\n\t * The {@code application/json; charset=UTF-8} media type.\n\t * @deprecated Use {@link #APPLICATION_JSON} instead.\n\t */\n\t@Deprecated(since = \"5.14\")\n\t@API(status = DEPRECATED, since = \"5.14\")\n\tpublic static final MediaType APPLICATION_JSON_UTF_8 = create(\"application\", \"json\", UTF_8);\n\n\t/**\n\t * The {@code application/octet-stream} media type.\n\t */\n\tpublic static final MediaType APPLICATION_OCTET_STREAM = create(\"application\", \"octet-stream\");\n\n\t/**\n\t * The {@code image/jpeg} media type.\n\t */\n\tpublic static final MediaType IMAGE_JPEG = create(\"image\", \"jpeg\");\n\n\t/**\n\t * The {@code image/png} media type.\n\t */\n\tpublic static final MediaType IMAGE_PNG = create(\"image\", \"png\");\n\n\t/**\n\t * Parse the given media type value.\n\t *\n\t * <p>Must be valid according to\n\t * <a href=\"https://tools.ietf.org/html/rfc2045\">RFC 2045</a>.\n\t *\n\t * @param value the media type value to parse; never {@code null} or blank\n\t * @return the parsed media type\n\t * @throws PreconditionViolationException if the value is not a valid media type\n\t */\n\tpublic static MediaType parse(String value) {\n\t\treturn new MediaType(value);\n\t}\n\n\t/**\n\t * Create a media type with the given type and subtype.\n\t *\n\t * @param type the type; never {@code null} or blank\n\t * @param subtype the subtype; never {@code null} or blank\n\t * @return the media type\n\t */\n\tpublic static MediaType create(String type, String subtype) {\n\t\treturn new MediaType(type, subtype, null);\n\t}\n\n\t/**\n\t * Create a media type with the given type, subtype, and charset.\n\t *\n\t * @param type the type; never {@code null} or blank\n\t * @param subtype the subtype; never {@code null} or blank\n\t * @param charset the charset; never {@code null}\n\t * @return the media type\n\t */\n\tpublic static MediaType create(String type, String subtype, Charset charset) {\n\t\tPreconditions.notNull(charset, \"charset must not be null\");\n\t\treturn new MediaType(type, subtype, charset);\n\t}\n\n\tprivate MediaType(String type, String subtype, @Nullable Charset charset) {\n\t\tsuper(type, subtype, charset);\n\t}\n\n\tprivate MediaType(String value) {\n\t\tsuper(value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Parameter;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.AnnotationUtils;\n\n/**\n * {@code ParameterContext} encapsulates the <em>context</em> in which an\n * {@link #getDeclaringExecutable Executable} will be invoked for a given\n * {@link #getParameter Parameter}.\n *\n * <p>A {@code ParameterContext} is used to support parameter resolution via\n * a {@link ParameterResolver}.\n *\n * @since 5.0\n * @see ParameterResolver\n * @see java.lang.reflect.Parameter\n * @see java.lang.reflect.Executable\n * @see java.lang.reflect.Method\n * @see java.lang.reflect.Constructor\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface ParameterContext extends AnnotatedElementContext {\n\n\t/**\n\t * Get the {@link Parameter} for this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>When searching for annotations on the parameter in this context,\n\t * favor {@link #isAnnotated(Class)}, {@link #findAnnotation(Class)}, and\n\t * {@link #findRepeatableAnnotations(Class)} over methods in the\n\t * {@link Parameter} API due to a bug in {@code javac} on JDK versions prior\n\t * to JDK 9.\n\t *\n\t * @return the parameter; never {@code null}\n\t * @see #getIndex()\n\t */\n\tParameter getParameter();\n\n\t/**\n\t * Get the index of the {@link Parameter} for this context within the\n\t * parameter list of the {@link #getDeclaringExecutable Executable} that\n\t * declares the parameter.\n\t *\n\t * @return the index of the parameter\n\t * @see #getParameter()\n\t * @see Executable#getParameters()\n\t */\n\tint getIndex();\n\n\t/**\n\t * Get the {@link Executable} (i.e., the {@link java.lang.reflect.Method} or\n\t * {@link java.lang.reflect.Constructor}) that declares the {@code Parameter}\n\t * for this context.\n\t *\n\t * @return the declaring {@code Executable}; never {@code null}\n\t * @see Parameter#getDeclaringExecutable()\n\t */\n\tdefault Executable getDeclaringExecutable() {\n\t\treturn getParameter().getDeclaringExecutable();\n\t}\n\n\t/**\n\t * Get the target on which the {@link #getDeclaringExecutable Executable}\n\t * that declares the {@link #getParameter Parameter} for this context will\n\t * be invoked, if available.\n\t *\n\t * @return an {@link Optional} containing the target on which the\n\t * {@code Executable} will be invoked; never {@code null} but will be\n\t * <em>empty</em> if the {@code Executable} is a constructor or a\n\t * {@code static} method.\n\t */\n\tOptional<Object> getTarget();\n\n\t/**\n\t * {@inheritDoc}\n\t * @since 5.10\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\t@Override\n\tdefault AnnotatedElement getAnnotatedElement() {\n\t\treturn getParameter();\n\t}\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Parameter} for\n\t * this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking\n\t * {@link Parameter#isAnnotationPresent(Class)} due to a bug in {@code javac}\n\t * on JDK versions prior to JDK 9.\n\t *\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @since 5.1.1\n\t * @see #findAnnotation(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t@Override\n\tdefault boolean isAnnotated(Class<? extends Annotation> annotationType) {\n\t\treturn AnnotationUtils.isAnnotated(getParameter(), getIndex(), annotationType);\n\t}\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>present</em> or <em>meta-present</em> on the {@link Parameter} for\n\t * this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking annotation lookup\n\t * methods in the {@link Parameter} API due to a bug in {@code javac} on JDK\n\t * versions prior to JDK 9.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @since 5.1.1\n\t * @see #isAnnotated(Class)\n\t * @see #findRepeatableAnnotations(Class)\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t@Override\n\tdefault <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {\n\t\treturn AnnotationUtils.findAnnotation(getParameter(), getIndex(), annotationType);\n\t}\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of\n\t * {@code annotationType} that are either <em>present</em> or\n\t * <em>meta-present</em> on the {@link Parameter} for this context.\n\t *\n\t * <h4>WARNING</h4>\n\t * <p>Favor the use of this method over directly invoking annotation lookup\n\t * methods in the {@link Parameter} API due to a bug in {@code javac} on JDK\n\t * versions prior to JDK 9.\n\t *\n\t * @param <A> the annotation type\n\t * @param annotationType the repeatable annotation type to search for; never\n\t * {@code null}\n\t * @return the list of all such annotations found; neither {@code null} nor\n\t * mutable, but potentially empty\n\t * @since 5.1.1\n\t * @see #isAnnotated(Class)\n\t * @see #findAnnotation(Class)\n\t * @see java.lang.annotation.Repeatable\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\t@Override\n\tdefault <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {\n\t\treturn AnnotationUtils.findRepeatableAnnotations(getParameter(), getIndex(), annotationType);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolutionException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Thrown if an error is encountered in the configuration or execution of a\n * {@link ParameterResolver}.\n *\n * @since 5.0\n * @see ParameterResolver\n */\n@API(status = STABLE, since = \"5.0\")\npublic class ParameterResolutionException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Construct a {@code ParameterResolutionException} with the supplied message.\n\t *\n\t * @param message the message; never {@code null}\n\t */\n\tpublic ParameterResolutionException(String message) {\n\t\tsuper(Preconditions.notNull(message, \"message must not be null\"));\n\t}\n\n\t/**\n\t * Construct a {@code ParameterResolutionException} with the supplied message\n\t * and cause.\n\t *\n\t * @param message the message; never {@code null}\n\t * @param cause the cause; never {@code null}\n\t */\n\tpublic ParameterResolutionException(String message, Throwable cause) {\n\t\tsuper(Preconditions.notNull(message, \"message must not be null\"),\n\t\t\tPreconditions.notNull(cause, \"cause must not be null\"));\n\t}\n\n\t/**\n\t * Get the message, never {@code null}.\n\t */\n\t@Override\n\tpublic String getMessage() {\n\t\tString message = super.getMessage();\n\t\tif (message == null) {\n\t\t\tthrow new IllegalStateException(\"message must not be null\");\n\t\t}\n\t\treturn message;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Parameter;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInstance;\n\n/**\n * {@code ParameterResolver} defines the API for {@link Extension Extensions}\n * that wish to dynamically resolve arguments for {@linkplain Parameter parameters}\n * at runtime.\n *\n * <p>If a constructor for a test class or a\n * {@link org.junit.jupiter.api.Test @Test},\n * {@link org.junit.jupiter.api.BeforeEach @BeforeEach},\n * {@link org.junit.jupiter.api.AfterEach @AfterEach},\n * {@link org.junit.jupiter.api.BeforeAll @BeforeAll}, or\n * {@link org.junit.jupiter.api.AfterAll @AfterAll} method declares a parameter,\n * an argument for the parameter must be resolved at runtime by a\n * {@code ParameterResolver}.\n *\n * <p>By default, when the methods in this interface are called for a test class\n * constructor, the supplied {@link ExtensionContext} represents the test\n * class that's about to be instantiated. Extensions may override\n * {@link #getTestInstantiationExtensionContextScope} to return\n * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} in order to change\n * the scope of the {@code ExtensionContext} to the test method, unless the\n * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.\n * Changing the scope makes test-specific data available to the\n * implementation of this method and allows keeping state on the test level\n * by using the provided {@link ExtensionContext.Store Store} instance.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>{@code ExtensionContext} Scope</h2>\n *\n * <p>As of JUnit Jupiter 5.12, this API participates in the\n * {@link TestInstantiationAwareExtension} contract. Implementations of this API\n * may therefore choose to override\n * {@link TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n * getTestInstantiationExtensionContextScope(ExtensionContext)} to require a\n * test-method scoped {@code ExtensionContext}.\n *\n * @since 5.0\n * @see #supportsParameter(ParameterContext, ExtensionContext)\n * @see #resolveParameter(ParameterContext, ExtensionContext)\n * @see ParameterContext\n * @see TestInstanceFactory\n * @see TestInstancePostProcessor\n * @see TestInstancePreDestroyCallback\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface ParameterResolver extends TestInstantiationAwareExtension {\n\n\t/**\n\t * Determine if this resolver supports resolution of an argument for the\n\t * {@link Parameter} in the supplied {@link ParameterContext} for the supplied\n\t * {@link ExtensionContext}.\n\t *\n\t * <p>The {@link java.lang.reflect.Method} or {@link java.lang.reflect.Constructor}\n\t * in which the parameter is declared can be retrieved via\n\t * {@link ParameterContext#getDeclaringExecutable()}.\n\t *\n\t * @param parameterContext the context for the parameter for which an argument should\n\t * be resolved; never {@code null}\n\t * @param extensionContext the extension context for the {@code Executable}\n\t * about to be invoked; never {@code null}\n\t * @return {@code true} if this resolver can resolve an argument for the parameter\n\t * @see #resolveParameter\n\t * @see ParameterContext\n\t */\n\tboolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException;\n\n\t/**\n\t * Resolve an argument for the {@link Parameter} in the supplied {@link ParameterContext}\n\t * for the supplied {@link ExtensionContext}.\n\t *\n\t * <p>This method is only called by the framework if {@link #supportsParameter}\n\t * previously returned {@code true} for the same {@link ParameterContext}\n\t * and {@link ExtensionContext}.\n\t *\n\t * <p>The {@link java.lang.reflect.Method} or {@link java.lang.reflect.Constructor}\n\t * in which the parameter is declared can be retrieved via\n\t * {@link ParameterContext#getDeclaringExecutable()}.\n\t *\n\t * @param parameterContext the context for the parameter for which an argument should\n\t * be resolved; never {@code null}\n\t * @param extensionContext the extension context for the {@code Executable}\n\t * about to be invoked; never {@code null}\n\t * @return the resolved argument for the parameter; may only be {@code null} if the\n\t * parameter type is not a primitive\n\t * @see #supportsParameter\n\t * @see ParameterContext\n\t */\n\t@Nullable\n\tObject resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/PreInterruptCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code PreInterruptCallback} defines the API for {@link Extension\n * Extensions} that wish to be called prior to invocations of\n * {@link Thread#interrupt()} by the {@link org.junit.jupiter.api.Timeout}\n * extension.\n *\n * <p>JUnit registers a default implementation that dumps the stacks of all\n * {@linkplain Thread threads} to {@code System.out} if the\n * {@value #THREAD_DUMP_ENABLED_PROPERTY_NAME} configuration parameter is set to\n * {@code true}.\n *\n * @since 5.12\n * @see org.junit.jupiter.api.Timeout\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface PreInterruptCallback extends Extension {\n\n\t/**\n\t * Property name used to enable dumping the stack of all\n\t * {@linkplain Thread threads} to {@code System.out} when a timeout has occurred.\n\t *\n\t * <p>This behavior is disabled by default.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString THREAD_DUMP_ENABLED_PROPERTY_NAME = \"junit.jupiter.execution.timeout.threaddump.enabled\";\n\n\t/**\n\t * Callback that is invoked <em>before</em> a {@link Thread} is interrupted with\n\t * {@link Thread#interrupt()}.\n\t *\n\t * <p>Note: There is no guarantee on which {@link Thread} this callback will be\n\t * executed.\n\t *\n\t * @param preInterruptContext the context with the target {@link Thread}, which will get interrupted.\n\t * @param extensionContext the extension context for the callback; never {@code null}\n\t * @since 5.12\n\t * @see PreInterruptContext\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tvoid beforeThreadInterrupt(PreInterruptContext preInterruptContext, ExtensionContext extensionContext)\n\t\t\tthrows Exception;\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/PreInterruptContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code PreInterruptContext} encapsulates the <em>context</em> in which an\n * {@link PreInterruptCallback#beforeThreadInterrupt(PreInterruptContext, ExtensionContext) beforeThreadInterrupt} method is called.\n *\n * @since 5.12\n * @see PreInterruptCallback\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface PreInterruptContext {\n\n\t/**\n\t * Get the {@link Thread} which will be interrupted.\n\t *\n\t * @return the Thread; never {@code null}\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tThread getThreadToInterrupt();\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/ReflectiveInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Executable;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ReflectiveInvocationContext} encapsulates the <em>context</em> of\n * a reflective invocation of an executable (method or constructor).\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 5.5\n */\n@API(status = STABLE, since = \"5.10\")\npublic interface ReflectiveInvocationContext<T extends Executable> {\n\n\t/**\n\t * Get the target class of this invocation context.\n\t *\n\t * <p>If this invocation context represents an instance method, this\n\t * method returns the class of the object the method will be invoked on,\n\t * not the class it is declared in. Otherwise, if this invocation\n\t * represents a static method or constructor, this method returns the\n\t * class the method or constructor is declared in.\n\t *\n\t * @return the target class of this invocation context; never\n\t * {@code null}\n\t */\n\tClass<?> getTargetClass();\n\n\t/**\n\t * Get the method or constructor of this invocation context.\n\t *\n\t * @return the executable of this invocation context; never {@code null}\n\t */\n\tT getExecutable();\n\n\t/**\n\t * Get the arguments of the executable in this invocation context.\n\t *\n\t * @return the arguments of the executable in this invocation context;\n\t * immutable and never {@code null}\n\t */\n\tList<Object> getArguments();\n\n\t/**\n\t * Get the target object of this invocation context, if available.\n\t *\n\t * <p>If this invocation context represents an instance method, this\n\t * method returns the object the method will be invoked on. Otherwise,\n\t * if this invocation context represents a static method or\n\t * constructor, this method returns {@link Optional#empty() empty()}.\n\t *\n\t * @return the target of the executable of this invocation context; never\n\t * {@code null} but potentially empty\n\t */\n\tOptional<Object> getTarget();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/RegisterExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @RegisterExtension} is used to register an {@link Extension} via a\n * field in a test class.\n *\n * <p>In contrast to {@link ExtendWith @ExtendWith} which is used to register\n * extensions <em>declaratively</em>, {@code @RegisterExtension} can be used to\n * register an extension <em>programmatically</em> &mdash; for example, in order\n * to pass arguments to the extension's constructor, {@code static} factory\n * method, or builder API.\n *\n * <p>{@code @RegisterExtension} fields must not be {@code null} (when evaluated)\n * but may be either {@code static} or non-static.\n *\n * <h2>Static Fields</h2>\n *\n * <p>If a {@code @RegisterExtension} field is {@code static}, the extension\n * will be registered after extensions that are registered at the class level\n * via {@code @ExtendWith}. Such <em>static</em> extensions are not limited in\n * which extension APIs they can implement. Extensions registered via static\n * fields may therefore implement class-level and instance-level extension APIs\n * such as {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link TestInstanceFactory}, {@link TestInstancePostProcessor} and\n * {@link TestInstancePreDestroyCallback} as well as method-level extension APIs\n * such as {@link BeforeEachCallback}, etc.\n *\n * <h2>Instance Fields</h2>\n *\n * <p>If a {@code @RegisterExtension} field is non-static (i.e., an instance\n * field), the extension will be registered after the test class has been\n * instantiated and after all {@link TestInstancePostProcessor\n * TestInstancePostProcessors} have been given a chance to post-process the\n * test instance (potentially injecting the instance of the extension to be\n * used into the annotated field). Thus, if such an <em>instance</em> extension\n * implements class-level or instance-level extension APIs such as\n * {@link BeforeAllCallback}, {@link AfterAllCallback},\n * {@link TestInstanceFactory}, or {@link TestInstancePostProcessor} those APIs\n * will not be honored. By default, an instance extension will be registered\n * <em>after</em> extensions that are registered at the method level via\n * {@code @ExtendWith}; however, if the test class is configured with\n * {@link org.junit.jupiter.api.TestInstance.Lifecycle @TestInstance(Lifecycle.PER_CLASS)}\n * semantics, an instance extension will be registered <em>before</em> extensions\n * that are registered at the method level via {@code @ExtendWith}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @RegisterExtension} fields are inherited from superclasses.\n * Furthermore, {@code @RegisterExtension} fields from superclasses will be\n * registered before {@code @RegisterExtension} fields in subclasses unless\n * {@code @Order} is used to alter that behavior (see below).\n *\n * <h2>Registration Order</h2>\n *\n * <p>By default, if multiple extensions are registered via\n * {@code @RegisterExtension}, they will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute extensions in the same order, thereby allowing for\n * repeatable builds. However, there are times when extensions need to be\n * registered in an explicit order. To achieve that, you can annotate\n * {@code @RegisterExtension} fields with {@link org.junit.jupiter.api.Order @Order}.\n * Any {@code @RegisterExtension} field not annotated with {@code @Order} will be\n * ordered using the {@link org.junit.jupiter.api.Order#DEFAULT default} order\n * value. Note that {@code @ExtendWith} fields can also be ordered with\n * {@code @Order}, relative to {@code @RegisterExtension} fields and other\n * {@code @ExtendWith} fields.\n *\n * <h2>Example Usage</h2>\n *\n * <p>In the following example, the {@code docs} field in the test class is\n * initialized programmatically by supplying a custom {@code lookUpDocsDir()}\n * method to a {@code static} factory method in the {@code DocumentationExtension}.\n * The configured {@code DocumentationExtension} will be automatically registered\n * as an extension. In addition, test methods can access the instance of the\n * extension via the {@code docs} field if necessary.\n *\n * <pre style=\"code\">\n * class DocumentationTests {\n *\n *     static Path lookUpDocsDir() {\n *         // return path to docs dir\n *     }\n *\n *     {@literal @}RegisterExtension\n *     DocumentationExtension docs =\n *         DocumentationExtension.forPath(lookUpDocsDir());\n *\n *     {@literal @}Test\n *     void generateDocumentation() {\n *         // use docs ...\n *     }\n * }</pre>\n *\n * <h2>Supported Extension APIs</h2>\n *\n * <ul>\n * <li>{@link ExecutionCondition}</li>\n * <li>{@link InvocationInterceptor}</li>\n * <li>{@link BeforeAllCallback}</li>\n * <li>{@link AfterAllCallback}</li>\n * <li>{@link BeforeEachCallback}</li>\n * <li>{@link AfterEachCallback}</li>\n * <li>{@link BeforeTestExecutionCallback}</li>\n * <li>{@link AfterTestExecutionCallback}</li>\n * <li>{@link TestInstanceFactory}</li>\n * <li>{@link TestInstancePostProcessor}</li>\n * <li>{@link TestInstancePreConstructCallback}</li>\n * <li>{@link TestInstancePreDestroyCallback}</li>\n * <li>{@link ParameterResolver}</li>\n * <li>{@link LifecycleMethodExecutionExceptionHandler}</li>\n * <li>{@link TestExecutionExceptionHandler}</li>\n * <li>{@link TestTemplateInvocationContextProvider}</li>\n * <li>{@link TestWatcher}</li>\n * </ul>\n *\n * @since 5.1\n * @see ExtendWith @ExtendWith\n * @see Extension\n * @see org.junit.jupiter.api.Order @Order\n */\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.1\")\npublic @interface RegisterExtension {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TemplateInvocationValidationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code TemplateInvocationValidationException} is an exception thrown by a\n * {@link TestTemplateInvocationContextProvider} or\n * {@link ClassTemplateInvocationContextProvider} if a validation fails when\n * while providing or closing {@link java.util.stream.Stream} of invocation\n * contexts.\n *\n * @since 5.13\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic class TemplateInvocationValidationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic TemplateInvocationValidationException(String message) {\n\t\tsuper(message);\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestExecutionExceptionHandler.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestExecutionExceptionHandler} defines the API for {@link Extension\n * Extensions} that wish to handle exceptions thrown during test execution.\n *\n * <p>In this context, <em>test execution</em> refers to the physical\n * invocation of a {@code @Test} method and not to any test-level extensions\n * or callbacks.\n *\n * <p>Common use cases include swallowing an exception if it's anticipated\n * or rolling back a transaction in certain error scenarios.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * @since 5.0\n * @see LifecycleMethodExecutionExceptionHandler\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface TestExecutionExceptionHandler extends Extension {\n\n\t/**\n\t * Handle the supplied {@link Throwable throwable}.\n\t *\n\t * <p>Implementors must perform one of the following.\n\t * <ol>\n\t * <li>Swallow the supplied {@code throwable}, thereby preventing propagation.</li>\n\t * <li>Rethrow the supplied {@code throwable} <em>as is</em>.</li>\n\t * <li>Throw a new exception, potentially wrapping the supplied {@code throwable}.</li>\n\t * </ol>\n\t *\n\t * <p>If the supplied {@code throwable} is swallowed, subsequent\n\t * {@code TestExecutionExceptionHandlers} will not be invoked; otherwise,\n\t * the next registered {@code TestExecutionExceptionHandler} (if there is\n\t * one) will be invoked with any {@link Throwable} thrown by this handler.\n\t *\n\t * <p>Note that the {@link ExtensionContext#getExecutionException() execution\n\t * exception} in the supplied {@code ExtensionContext} will <em>not</em>\n\t * contain the {@code Throwable} thrown during invocation of the corresponding\n\t * {@code @Test} method.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param throwable the {@code Throwable} to handle; never {@code null}\n\t */\n\tvoid handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance;\n\n/**\n * {@code TestInstanceFactory} defines the API for {@link Extension\n * Extensions} that wish to {@linkplain #createTestInstance create} test instances.\n *\n * <p>Common use cases include creating test instances with special construction\n * requirements or acquiring the test instance from a dependency injection\n * framework.\n *\n * <p>Extensions that implement {@code TestInstanceFactory} must be registered\n * at the class level.\n *\n * <h2>Warning</h2>\n *\n * <p>Only one {@code TestInstanceFactory} is allowed to be registered for any\n * given test class. Registering multiple factories for any single test class\n * will result in an exception being thrown for all tests in that class, in any\n * subclass, and in any nested class. Note that any {@code TestInstanceFactory}\n * registered in a {@linkplain Class#getSuperclass() superclass} or\n * {@linkplain Class#getEnclosingClass() enclosing} class (i.e., in the case of\n * a {@code @Nested} test class) is <em>inherited</em>. It is therefore the\n * user's responsibility to ensure that only a single {@code TestInstanceFactory}\n * is registered for any specific test class.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>{@code ExtensionContext} Scope</h2>\n *\n * <p>As of JUnit Jupiter 5.12, this API participates in the\n * {@link TestInstantiationAwareExtension} contract. Implementations of this API\n * may therefore choose to override\n * {@link TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n * getTestInstantiationExtensionContextScope(ExtensionContext)} to require a\n * test-method scoped {@code ExtensionContext}. See\n * {@link #createTestInstance(TestInstanceFactoryContext, ExtensionContext)} for\n * further details.\n *\n * @since 5.3\n * @see #createTestInstance(TestInstanceFactoryContext, ExtensionContext)\n * @see TestInstanceFactoryContext\n * @see TestInstancePostProcessor\n * @see TestInstancePreDestroyCallback\n * @see ParameterResolver\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.7\")\npublic interface TestInstanceFactory extends TestInstantiationAwareExtension {\n\n\t/**\n\t * Callback for creating a test instance for the supplied context.\n\t *\n\t * <p>By default, the supplied {@link ExtensionContext} represents the test\n\t * class that's about to be instantiated. Extensions may override\n\t * {@link #getTestInstantiationExtensionContextScope} to return\n\t * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} in order to change\n\t * the scope of the {@code ExtensionContext} to the test method, unless the\n\t * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.\n\t * Changing the scope makes test-specific data available to the\n\t * implementation of this method and allows keeping state on the test level\n\t * by using the provided {@link ExtensionContext.Store Store} instance.\n\t *\n\t * <p><strong>Note</strong>: the {@code ExtensionContext} supplied to a\n\t * {@code TestInstanceFactory} will always return an empty\n\t * {@link java.util.Optional} value from\n\t * {@link ExtensionContext#getTestInstance() getTestInstance()} since the\n\t * test instance cannot exist before it has been created by a\n\t * {@code TestInstanceFactory} or the framework itself.\n\t *\n\t * @param factoryContext the context for the test class to be instantiated;\n\t * never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @return the test instance; never {@code null}\n\t * @throws TestInstantiationException if an error occurs while creating the\n\t * test instance\n\t */\n\tObject createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext)\n\t\t\tthrows TestInstantiationException;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstanceFactoryContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestInstanceFactoryContext} encapsulates the <em>context</em> in which\n * a {@linkplain #getTestClass test class} is to be instantiated by a\n * {@link TestInstanceFactory}.\n *\n * @since 5.3\n * @see TestInstanceFactory\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface TestInstanceFactoryContext {\n\n\t/**\n\t * Get the test class for this context.\n\t *\n\t * @return the test class to be instantiated; never {@code null}\n\t */\n\tClass<?> getTestClass();\n\n\t/**\n\t * Get the instance of the outer class, if available.\n\t *\n\t * <p>The returned {@link Optional} will be <em>empty</em> unless the\n\t * current {@linkplain #getTestClass() test class} is a\n\t * {@link org.junit.jupiter.api.Nested @Nested} test class.\n\t *\n\t * @return an {@code Optional} containing the outer test instance; never\n\t * {@code null} but potentially empty\n\t * @see org.junit.jupiter.api.Nested\n\t */\n\tOptional<Object> getOuterInstance();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePostProcessor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance;\n\n/**\n * {@code TestInstancePostProcessor} defines the API for {@link Extension\n * Extensions} that wish to <em>post-process</em> test instances.\n *\n * <p>Common use cases include injecting dependencies into the test\n * instance, invoking custom initialization methods on the test instance,\n * etc.\n *\n * <p>Extensions that implement {@code TestInstancePostProcessor} must be\n * registered at the class level, {@linkplain ExtendWith declaratively} via a\n * field of the test class, or {@linkplain RegisterExtension programmatically}\n * via a <em>static</em> field of the test class.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on\n * constructor requirements.\n *\n * <h2>{@code ExtensionContext} Scope</h2>\n *\n * <p>As of JUnit Jupiter 5.12, this API participates in the\n * {@link TestInstantiationAwareExtension} contract. Implementations of this API\n * may therefore choose to override\n * {@link TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n * getTestInstantiationExtensionContextScope(ExtensionContext)} to require a\n * test-method scoped {@code ExtensionContext}. See\n * {@link #postProcessTestInstance(Object, ExtensionContext)} for further details.\n *\n * @since 5.0\n * @see #postProcessTestInstance(Object, ExtensionContext)\n * @see TestInstancePreDestroyCallback\n * @see TestInstanceFactory\n * @see ParameterResolver\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface TestInstancePostProcessor extends TestInstantiationAwareExtension {\n\n\t/**\n\t * Callback for post-processing the supplied test instance.\n\t *\n\t * <p>By default, the supplied {@link ExtensionContext} represents the test\n\t * class that's being post-processed. Extensions may override\n\t * {@link #getTestInstantiationExtensionContextScope} to return\n\t * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} in order to change\n\t * the scope of the {@code ExtensionContext} to the test method, unless the\n\t * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.\n\t * Changing the scope makes test-specific data available to the\n\t * implementation of this method and allows keeping state on the test level\n\t * by using the provided {@link ExtensionContext.Store Store} instance.\n\t *\n\t * <p><strong>Note</strong>: the {@code ExtensionContext} supplied to a\n\t * {@code TestInstancePostProcessor} will always return an empty\n\t * {@link java.util.Optional} value from {@link ExtensionContext#getTestInstance()\n\t * getTestInstance()}. A {@code TestInstancePostProcessor} should therefore\n\t * only attempt to process the supplied {@code testInstance}.\n\t *\n\t * @param testInstance the instance to post-process; never {@code null}\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreConstructCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n/**\n * {@code TestInstancePreConstructCallback} defines the API for {@link Extension\n * Extensions} that wish to be invoked <em>prior</em> to creation of test instances.\n *\n * <p>This extension is a symmetric counterpart to {@link TestInstancePreDestroyCallback}.\n * The use cases for this extension may include preparing context-sensitive arguments\n * that are injected into the instance's constructor.\n *\n * <p>Extensions that implement {@code TestInstancePreConstructCallback} must be\n * registered at the class level if the test class is configured with\n * {@link Lifecycle @TestInstance(Lifecycle.PER_CLASS)} semantics. If the test\n * class is configured with\n * {@link Lifecycle @TestInstance(Lifecycle.PER_METHOD)} semantics,\n * {@code TestInstancePreConstructCallback} extensions may be registered at the\n * class level or at the method level. In the latter case, the extension will\n * only be applied to the test method for which it is registered.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on constructor\n * requirements.\n *\n * <h2>{@code ExtensionContext} Scope</h2>\n *\n * <p>As of JUnit Jupiter 5.12, this API participates in the\n * {@link TestInstantiationAwareExtension} contract. Implementations of this API\n * may therefore choose to override\n * {@link TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n * getTestInstantiationExtensionContextScope(ExtensionContext)} to require a\n * test-method scoped {@code ExtensionContext}. See\n * {@link #preConstructTestInstance(TestInstanceFactoryContext, ExtensionContext)}\n * for further details.\n *\n * @since 5.9\n * @see TestInstancePreDestroyCallback\n * @see TestInstanceFactory\n * @see ParameterResolver\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.11\")\npublic interface TestInstancePreConstructCallback extends TestInstantiationAwareExtension {\n\n\t/**\n\t * Callback invoked prior to test instances being constructed.\n\t *\n\t * <p>By default, the supplied {@link ExtensionContext} represents the test\n\t * class that's about to be constructed. Extensions may override\n\t * {@link #getTestInstantiationExtensionContextScope} to return\n\t * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} in order to change\n\t * the scope of the {@code ExtensionContext} to the test method, unless the\n\t * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle is used.\n\t * Changing the scope makes test-specific data available to the\n\t * implementation of this method and allows keeping state on the test level\n\t * by using the provided {@link ExtensionContext.Store Store} instance.\n\t *\n\t * @param factoryContext the context for the test instance about to be instantiated;\n\t * never {@code null}\n\t * @param context the current extension context; never {@code null}\n\t */\n\tvoid preConstructTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext context) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstancePreDestroyCallback.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n/**\n * {@code TestInstancePreDestroyCallback} defines the API for {@link Extension\n * Extensions} that wish to process test instances <em>after</em> they have been\n * used in tests but <em>before</em> they are destroyed.\n *\n * <p>Common use cases include releasing resources that have been created for\n * the test instance, invoking custom clean-up methods on the test instance, etc.\n *\n * <p>Extensions that implement {@code TestInstancePreDestroyCallback} must be\n * registered at the class level if the test class is configured with\n * {@link Lifecycle @TestInstance(Lifecycle.PER_CLASS)}\n * semantics. If the test class is configured with\n * {@link Lifecycle @TestInstance(Lifecycle.PER_METHOD)}\n * semantics, {@code TestInstancePreDestroyCallback} extensions may be registered\n * at the class level or at the method level. In the latter case, the\n * {@code TestInstancePreDestroyCallback} extension will only be applied to the\n * test method for which it is registered.\n *\n * <p>A symmetric {@link TestInstancePreConstructCallback} extension defines a callback\n * hook that is invoked prior to any test class instances being constructed.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on constructor\n * requirements.\n *\n * @since 5.6\n * @see #preDestroyTestInstance(ExtensionContext)\n * @see TestInstancePostProcessor\n * @see TestInstancePreConstructCallback\n * @see TestInstanceFactory\n * @see ParameterResolver\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.7\")\npublic interface TestInstancePreDestroyCallback extends Extension {\n\n\t/**\n\t * Callback for processing test instances before they are destroyed.\n\t *\n\t * <p>Contrary to {@link TestInstancePostProcessor#postProcessTestInstance}\n\t * this method is only called once for each {@link ExtensionContext} even if\n\t * there are multiple test instances about to be destroyed in case of\n\t * {@link Nested @Nested} tests. Please use the provided\n\t * {@link #preDestroyTestInstances(ExtensionContext, Consumer)} utility\n\t * method to ensure that all test instances are handled.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @see ExtensionContext#getTestInstance()\n\t * @see ExtensionContext#getRequiredTestInstance()\n\t * @see ExtensionContext#getTestInstances()\n\t * @see ExtensionContext#getRequiredTestInstances()\n\t * @see #preDestroyTestInstances(ExtensionContext, Consumer)\n\t */\n\tvoid preDestroyTestInstance(ExtensionContext context) throws Exception;\n\n\t/**\n\t * Utility method for processing <em>all</em> test instances of an\n\t * {@link ExtensionContext} that are not present in any of its parent\n\t * contexts.\n\t *\n\t * <p>This method should be called in order to implement this interface\n\t * correctly since it ensures that the right test instances are processed\n\t * regardless of the used {@linkplain Lifecycle lifecycle}. The supplied\n\t * callback is called once per test instance that is about to be destroyed\n\t * starting with the innermost one.\n\t *\n\t * <p>This method is intended to be called from an implementation of\n\t * {@link #preDestroyTestInstance(ExtensionContext)} like this:\n\t *\n\t * <pre>{@code class MyExtension implements TestInstancePreDestroyCallback {\n\t *    @Override\n\t *    public void preDestroyTestInstance(ExtensionContext context) {\n\t *        TestInstancePreDestroyCallback.preDestroyTestInstances(context, testInstance -> {\n\t *            // custom logic that processes testInstance\n\t *        });\n\t *    }\n\t *}}</pre>\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param callback the callback to be invoked for every test instance of the\n\t * current extension context that is about to be destroyed; never\n\t * {@code null}\n\t * @since 5.7.1\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tstatic void preDestroyTestInstances(ExtensionContext context, Consumer<Object> callback) {\n\t\tList<Object> destroyedInstances = new ArrayList<>(context.getRequiredTestInstances().getAllInstances());\n\t\tfor (Optional<ExtensionContext> current = context.getParent(); current.isPresent(); current = current.get().getParent()) {\n\t\t\tcurrent.get().getTestInstances().map(TestInstances::getAllInstances).ifPresent(\n\t\t\t\tdestroyedInstances::removeAll);\n\t\t}\n\t\tCollections.reverse(destroyedInstances);\n\t\tdestroyedInstances.forEach(callback);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstances.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestInstances} encapsulates the <em>test instances</em> of a test.\n *\n * <p>While top-level tests only have a single test instance, nested tests\n * have one additional instance for each enclosing test class.\n *\n * @since 5.4\n * @see ExtensionContext#getTestInstances()\n * @see ExtensionContext#getRequiredTestInstances()\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface TestInstances {\n\n\t/**\n\t * Get the innermost test instance.\n\t *\n\t * <p>The innermost instance is the one closest to the test method.\n\t *\n\t * @return the innermost test instance; never {@code null}\n\t */\n\tObject getInnermostInstance();\n\n\t/**\n\t * Get the enclosing test instances, excluding the innermost test instance,\n\t * ordered from outermost to innermost.\n\t *\n\t * @return the enclosing test instances; never {@code null} or containing\n\t * {@code null}, but potentially empty\n\t */\n\tList<Object> getEnclosingInstances();\n\n\t/**\n\t * Get all test instances, ordered from outermost to innermost.\n\t *\n\t * @return all test instances; never {@code null}, containing {@code null},\n\t * or empty\n\t */\n\tList<Object> getAllInstances();\n\n\t/**\n\t * Find the first test instance that is an instance of the supplied required\n\t * type, checking from innermost to outermost.\n\t *\n\t * @param requiredType the type to search for\n\t * @return the first test instance of the required type; never {@code null}\n\t * but potentially empty\n\t */\n\t<T> Optional<T> findInstance(Class<T> requiredType);\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationAwareExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestInstantiationAwareExtension} defines the API for {@link Extension\n * Extensions} that are aware of or influence the instantiation of test classes.\n *\n * <p>This interface is not intended to be implemented directly. Instead, extensions\n * should implement one of the sub-interfaces listed below.\n *\n * <ul>\n * <li>{@link InvocationInterceptor}</li>\n * <li>{@link ParameterResolver}</li>\n * <li>{@link TestInstancePreConstructCallback}</li>\n * <li>{@link TestInstancePostProcessor}</li>\n * <li>{@link TestInstanceFactory}</li>\n * </ul>\n *\n * <p>See {@link #getTestInstantiationExtensionContextScope(ExtensionContext)} for\n * further details.\n *\n * @since 5.12\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface TestInstantiationAwareExtension extends Extension {\n\n\t/**\n\t * Determine whether this extension should receive a test-method scoped\n\t * {@link ExtensionContext} during the instantiation of test classes or\n\t * processing of test instances.\n\t *\n\t * <p>If an extension returns {@link ExtensionContextScope#TEST_METHOD TEST_METHOD}\n\t * from this method, methods defined in the following extension APIs will be\n\t * called with a test-method scoped {@code ExtensionContext} instead of a\n\t * test-class scoped context. Note, however, that a test-class scoped context\n\t * will always be supplied if the\n\t * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n\t * test instance lifecycle is used.\n\t *\n\t * <ul>\n\t * <li>{@link InvocationInterceptor}: only the\n\t * {@link InvocationInterceptor#interceptTestClassConstructor\n\t * interceptTestClassConstructor(...)} method</li>\n\t * <li>{@link ParameterResolver}: only when resolving constructor parameters</li>\n\t * <li>{@link TestInstancePreConstructCallback}</li>\n\t * <li>{@link TestInstancePostProcessor}</li>\n\t * <li>{@link TestInstanceFactory}</li>\n\t * </ul>\n\t *\n\t * <p>When a test-method scoped {@code ExtensionContext} is supplied, implementations\n\t * of the above extension APIs will observe the following differences.\n\t *\n\t * <ul>\n\t *   <li>\n\t *     {@link ExtensionContext#getElement() getElement()} may refer to the\n\t *     test method.\n\t *   </li>\n\t *   <li>\n\t *     {@link ExtensionContext#getTestClass() getTestClass()} may refer to a\n\t *     nested test class.\n\t *     <ul>\n\t *       <li>\n\t *         For {@link TestInstancePostProcessor}, use {@code testInstance.getClass()}\n\t *         to get the test class associated with the supplied instance.\n\t *       </li>\n\t *       <li>\n\t *         For {@link TestInstanceFactory} and {@link TestInstancePreConstructCallback},\n\t *         use {@link TestInstanceFactoryContext#getTestClass()} to get the\n\t *         class under construction.\n\t *       </li>\n\t *       <li>\n\t *         For {@link ParameterResolver}, when resolving a parameter for a\n\t *         constructor, ensure that the\n\t *         {@link ParameterContext#getDeclaringExecutable() Executable} is a\n\t *         {@link java.lang.reflect.Constructor Constructor}, and then use\n\t *         {@code constructor.getDeclaringClass()} to get the test class\n\t *         associated with the constructor.\n\t *       </li>\n\t *     </ul>\n\t *   </li>\n\t *   <li>\n\t *     {@link ExtensionContext#getTestMethod() getTestMethod()} is no longer\n\t *     empty, unless the\n\t *     {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n\t *     test instance lifecycle is used.\n\t *   </li>\n\t *   <li>\n\t *     If the extension adds a {@link ExtensionContext.Store.CloseableResource\n\t *     CloseableResource} or {@link AutoCloseable} to the\n\t *     {@link ExtensionContext.Store Store} (unless the\n\t *     {@code junit.jupiter.extensions.store.close.autocloseable.enabled}\n\t *     configuration parameter is set to {@code false}), then the resource will\n\t *     be closed just after the instance is destroyed.\n\t *   </li>\n\t *   <li>\n\t *     Extensions can now access data previously stored by a\n\t *     {@link TestTemplateInvocationContext}, unless the\n\t *     {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n\t *     test instance lifecycle is used.\n\t *   </li>\n\t * </ul>\n\t *\n\t * <p><strong>Note</strong>: The behavior which is enabled by returning\n\t * {@link ExtensionContextScope#TEST_METHOD TEST_METHOD} from this method\n\t * will become the default in future versions of JUnit. To ensure forward\n\t * compatibility, extension authors are therefore advised to opt into this\n\t * feature, even if they do not require the new functionality.\n\t *\n\t * @implNote There are no guarantees about how often this method will be called.\n\t * Therefore, implementations should be idempotent and avoid side effects.\n\t * If computation of the return value is costly, implementations may wish to\n\t * cache the result in the {@link ExtensionContext.Store Store} of the supplied\n\t * {@code ExtensionContext}.\n\t * @param rootContext the root extension context to allow inspection of\n\t * configuration parameters; never {@code null}\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.DEFAULT;\n\t}\n\n\t/**\n\t * {@code ExtensionContextScope} is used to define the scope of the\n\t * {@link ExtensionContext} supplied to an extension during the instantiation\n\t * of test classes or processing of test instances.\n\t *\n\t * @since 5.12\n\t * @see TestInstantiationAwareExtension#getTestInstantiationExtensionContextScope(ExtensionContext)\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tenum ExtensionContextScope {\n\n\t\t/**\n\t\t * The extension should receive an {@link ExtensionContext} for the\n\t\t * the <em>default</em> scope.\n\t\t *\n\t\t * <p>The default scope is determined by the configuration parameter\n\t\t * {@link #DEFAULT_SCOPE_PROPERTY_NAME}. If not specified, extensions\n\t\t * will receive an {@link ExtensionContext} scoped to the test class.\n\t\t *\n\t\t * @deprecated This behavior will be removed from future versions of\n\t\t * JUnit, and {@link #TEST_METHOD} will become the default.\n\t\t *\n\t\t * @see #DEFAULT_SCOPE_PROPERTY_NAME\n\t\t */\n\t\t@Deprecated(since = \"5.12\") //\n\t\t@API(status = DEPRECATED, since = \"5.12\")\n\t\tDEFAULT,\n\n\t\t/**\n\t\t * The extension should receive an {@link ExtensionContext} scoped to\n\t\t * the test method.\n\t\t *\n\t\t * <p>Note, however, that a test-class scoped context will always be\n\t\t * supplied if the\n\t\t * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n\t\t * test instance lifecycle is used.\n\t\t */\n\t\tTEST_METHOD;\n\n\t\t/**\n\t\t * Property name used to set the default extension context scope: {@value}\n\t\t *\n\t\t * <h4>Supported Values</h4>\n\t\t *\n\t\t * <p>Supported values include names of enum constants defined in this\n\t\t * class, ignoring case.\n\t\t *\n\t\t * @see #DEFAULT\n\t\t */\n\t\tpublic static final String DEFAULT_SCOPE_PROPERTY_NAME = \"junit.jupiter.extensions.testinstantiation.extensioncontextscope.default\";\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestInstantiationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Thrown if an error is encountered during the execution of\n * a {@link TestInstanceFactory}.\n *\n * @since 5.3\n */\n@API(status = STABLE, since = \"5.10\")\npublic class TestInstantiationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic TestInstantiationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic TestInstantiationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestTemplateInvocationContext} represents the <em>context</em> of a\n * single invocation of a {@linkplain org.junit.jupiter.api.TestTemplate test\n * template}.\n *\n * <p>Each context is provided by a {@link TestTemplateInvocationContextProvider}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.TestTemplate\n * @see TestTemplateInvocationContextProvider\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface TestTemplateInvocationContext {\n\n\t/**\n\t * Get the display name for this invocation.\n\t *\n\t * <p>The supplied {@code invocationIndex} is incremented by the framework\n\t * with each test template invocation. Thus, in the case of multiple active\n\t * {@linkplain TestTemplateInvocationContextProvider providers}, only the\n\t * first active provider receives indices starting with {@code 1}.\n\t *\n\t * <p>The default implementation returns the supplied {@code invocationIndex}\n\t * wrapped in brackets &mdash; for example, {@code [1]}, {@code [42]}, etc.\n\t *\n\t * @param invocationIndex the index of this invocation (1-based).\n\t * @return the display name for this invocation; never {@code null} or blank\n\t */\n\tdefault String getDisplayName(int invocationIndex) {\n\t\treturn \"[\" + invocationIndex + \"]\";\n\t}\n\n\t/**\n\t * Get additional {@linkplain Extension extensions} for this invocation.\n\t *\n\t * <p>The extensions provided by this method will only be used for this\n\t * invocation of the test template. Thus, it does not make sense to return\n\t * an extension that needs to perform some action at the container level,\n\t * such as an implementation of {@link BeforeAllCallback}.\n\t *\n\t * <p>The default implementation returns an empty list.\n\t *\n\t * @return additional extensions for this invocation; never {@code null}\n\t * or containing {@code null} elements, but potentially empty\n\t */\n\tdefault List<Extension> getAdditionalExtensions() {\n\t\treturn emptyList();\n\t}\n\n\t/**\n\t * Prepare the imminent invocation of the test template.\n\t *\n\t * <p>This may be used, for example, to store entries in the\n\t * {@link ExtensionContext.Store Store} to benefit from its cleanup support\n\t * or for retrieval by other extensions.\n\t *\n\t * @param context the invocation-level extension context\n\t * @since 5.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tdefault void prepareInvocation(ExtensionContext context) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestTemplateInvocationContextProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code TestTemplateInvocationContextProvider} defines the API for\n * {@link Extension Extensions} that wish to provide one or multiple contexts\n * for the invocation of a\n * {@link org.junit.jupiter.api.TestTemplate @TestTemplate} method.\n *\n * <p>This extension API makes it possible to execute a test template in\n * different contexts &mdash; for example, with different parameters, by\n * preparing the test class instance differently, or multiple times without\n * modifying the context.\n *\n * <p>This interface defines two main methods: {@link #supportsTestTemplate} and\n * {@link #provideTestTemplateInvocationContexts}. The former is called by the\n * framework to determine whether this extension wants to act on a test template\n * that is about to be executed. If so, the latter is called and must return a\n * {@link Stream} of {@link TestTemplateInvocationContext} instances. Otherwise,\n * this provider is ignored for the execution of the current test template.\n *\n * <p>A provider that has returned {@code true} from its {@link #supportsTestTemplate}\n * method is called <em>active</em>. When multiple providers are active for a\n * test template method, the {@code Streams} returned by their\n * {@link #provideTestTemplateInvocationContexts} methods will be chained, and\n * the test template method will be invoked using the contexts of all active\n * providers.\n *\n * <p>An active provider may return zero invocation contexts from its\n * {@link #provideTestTemplateInvocationContexts} method if it overrides\n * {@link #mayReturnZeroTestTemplateInvocationContexts} to return {@code true}.\n *\n * <h2>Constructor Requirements</h2>\n *\n * <p>Consult the documentation in {@link Extension} for details on constructor\n * requirements.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.TestTemplate\n * @see TestTemplateInvocationContext\n */\n@API(status = STABLE, since = \"5.0\")\npublic interface TestTemplateInvocationContextProvider extends Extension {\n\n\t/**\n\t * Determine if this provider supports providing invocation contexts for the\n\t * test template method represented by the supplied {@code context}.\n\t *\n\t * @param context the extension context for the test template method about\n\t * to be invoked; never {@code null}\n\t * @return {@code true} if this provider can provide invocation contexts\n\t * @see #provideTestTemplateInvocationContexts\n\t * @see ExtensionContext\n\t */\n\tboolean supportsTestTemplate(ExtensionContext context);\n\n\t/**\n\t * Provide {@linkplain TestTemplateInvocationContext invocation contexts}\n\t * for the test template method represented by the supplied {@code context}.\n\t *\n\t * <p>This method is only called by the framework if {@link #supportsTestTemplate}\n\t * previously returned {@code true} for the same {@link ExtensionContext};\n\t * this method is allowed to return an empty {@code Stream} but not {@code null}.\n\t *\n\t * <p>The returned {@code Stream} will be properly closed by calling\n\t * {@link Stream#close()}, making it safe to use a resource such as\n\t * {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.\n\t *\n\t * @param context the extension context for the test template method about\n\t * to be invoked; never {@code null}\n\t * @return a {@code Stream} of {@code TestTemplateInvocationContext}\n\t * instances for the invocation of the test template method; never {@code null}\n\t * @throws TemplateInvocationValidationException if validation fails while\n\t * providing or closing the {@link Stream}\n\t * @see #supportsTestTemplate\n\t * @see ExtensionContext\n\t */\n\tStream<? extends TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context);\n\n\t/**\n\t * Signal that this provider may provide zero\n\t * {@linkplain TestTemplateInvocationContext invocation contexts} for the test\n\t * template method represented by the supplied {@code context}.\n\t *\n\t * <p>If this method returns {@code false} (which is the default) and the\n\t * provider returns an empty stream from\n\t * {@link #provideTestTemplateInvocationContexts}, this will be considered\n\t * an execution error. Override this method to return {@code true} to ignore\n\t * the absence of invocation contexts for this provider.\n\t *\n\t * @param context the extension context for the test template method about\n\t * to be invoked; never {@code null}\n\t * @return {@code true} to allow zero contexts, {@code false} to fail\n\t * execution in case of zero contexts\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tdefault boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/TestWatcher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code TestWatcher} defines the API for {@link Extension Extensions} that\n * wish to process test results.\n *\n * <p>The methods in this API are called after a test has been skipped or\n * executed. Any {@link ExtensionContext.Store.CloseableResource CloseableResource}\n * objects stored in the {@link ExtensionContext.Store Store} of the supplied\n * {@link ExtensionContext} will have already been <strong>closed</strong> before\n * methods in this API are invoked.\n *\n * <p>Please note that this API is currently only used to report the results of\n * {@link org.junit.jupiter.api.Test @Test} methods and\n * {@link org.junit.jupiter.api.TestTemplate @TestTemplate} methods (e.g.,\n * {@code @RepeatedTest} and {@code @ParameterizedTest}). Moreover, if there is a\n * failure at the class level &mdash; for example, an exception thrown by a\n * {@code @BeforeAll} method &mdash; no test results will be reported. Similarly,\n * if the test class is disabled via an {@link ExecutionCondition} &mdash; for\n * example, {@code @Disabled} &mdash; no test results will be reported.\n *\n * <p>Extensions implementing this interface can be registered at the class level,\n * instance level, or method level. When registered at the class level, a\n * {@code TestWatcher} will be invoked for any contained test method including\n * those in {@link org.junit.jupiter.api.Nested @Nested} classes. When registered\n * at the method level, a {@code TestWatcher} will only be invoked for the test\n * method for which it was registered.\n *\n * <p><strong>WARNING</strong>: If a {@code TestWatcher} is registered via a\n * non-static (instance) field &mdash; for example, using\n * {@link RegisterExtension @RegisterExtension} &mdash; and the test class is\n * configured with\n * {@link org.junit.jupiter.api.TestInstance @TestInstance(Lifecycle.PER_METHOD)}\n * semantics (which is the default lifecycle mode), the {@code TestWatcher} will\n * <strong>not</strong> be invoked with events for {@code @TestTemplate} methods\n * (such as {@code @RepeatedTest} and {@code @ParameterizedTest}). To ensure that\n * a {@code TestWatcher} is invoked for all test methods in a given class, it is\n * therefore recommended that the {@code TestWatcher} be registered at the class\n * level with {@link ExtendWith @ExtendWith} or via a {@code static} field with\n * {@code @RegisterExtension} or {@code @ExtendWith}.\n *\n * <h2>Exception Handling</h2>\n *\n * <p>In contrast to other {@link Extension} APIs, a {@code TestWatcher} is not\n * permitted to adversely influence the execution of tests. Consequently, any\n * exception thrown by a method in the {@code TestWatcher} API will be logged at\n * {@code WARNING} level and will not be allowed to propagate or fail test\n * execution.\n *\n * @since 5.4\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface TestWatcher extends Extension {\n\n\t/**\n\t * Invoked after a disabled test has been skipped.\n\t *\n\t * <p>The default implementation does nothing. Concrete implementations can\n\t * override this method as appropriate.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param reason the reason the test is disabled; never {@code null} but\n\t * potentially <em>empty</em>\n\t */\n\tdefault void testDisabled(ExtensionContext context, Optional<String> reason) {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Invoked after a test has completed successfully.\n\t *\n\t * <p>The default implementation does nothing. Concrete implementations can\n\t * override this method as appropriate.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t */\n\tdefault void testSuccessful(ExtensionContext context) {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Invoked after a test has been aborted.\n\t *\n\t * <p>The default implementation does nothing. Concrete implementations can\n\t * override this method as appropriate.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param cause the throwable responsible for the test being aborted; may be {@code null}\n\t */\n\tdefault void testAborted(ExtensionContext context, @Nullable Throwable cause) {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Invoked after a test has failed.\n\t *\n\t * <p>The default implementation does nothing. Concrete implementations can\n\t * override this method as appropriate.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param cause the throwable that caused test failure; may be {@code null}\n\t */\n\tdefault void testFailed(ExtensionContext context, @Nullable Throwable cause) {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter API for writing extensions.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.extension;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension.support;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@link ParameterResolver} adapter which resolves a parameter based on its exact type.\n *\n * @param <T> the type of the parameter supported by this {@code ParameterResolver}\n * @since 5.6\n */\n@API(status = STABLE, since = \"5.10\")\npublic abstract class TypeBasedParameterResolver<T extends @Nullable Object> implements ParameterResolver {\n\n\tprivate final Type supportedParameterType;\n\n\tpublic TypeBasedParameterResolver() {\n\t\tthis.supportedParameterType = enclosedTypeOfParameterResolver();\n\t}\n\n\t@Override\n\tpublic final boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn this.supportedParameterType.equals(getParameterType(parameterContext));\n\t}\n\n\t@Override\n\tpublic abstract T resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException;\n\n\tprivate Type getParameterType(ParameterContext parameterContext) {\n\t\treturn parameterContext.getParameter().getParameterizedType();\n\t}\n\n\tprivate Type enclosedTypeOfParameterResolver() {\n\t\tParameterizedType typeBasedParameterResolverSuperclass = Preconditions.notNull(\n\t\t\tfindTypeBasedParameterResolverSuperclass(getClass()), () -> \"\"\"\n\t\t\t\t\tFailed to discover parameter type supported by %s; \\\n\t\t\t\t\tpotentially caused by lacking parameterized type in class declaration.\"\"\".formatted(\n\t\t\t\tgetClass().getName()));\n\t\treturn typeBasedParameterResolverSuperclass.getActualTypeArguments()[0];\n\t}\n\n\tprivate @Nullable ParameterizedType findTypeBasedParameterResolverSuperclass(Class<?> clazz) {\n\t\tClass<?> superclass = clazz.getSuperclass();\n\n\t\t// Abort?\n\t\tif (superclass == null || superclass == Object.class) {\n\t\t\treturn null;\n\t\t}\n\n\t\tType genericSuperclass = clazz.getGenericSuperclass();\n\t\tif (genericSuperclass instanceof ParameterizedType type) {\n\t\t\tType rawType = type.getRawType();\n\t\t\tif (rawType == TypeBasedParameterResolver.class) {\n\t\t\t\treturn type;\n\t\t\t}\n\t\t}\n\t\treturn findTypeBasedParameterResolverSuperclass(superclass);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/extension/support/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter API support for writing extensions.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.extension.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/Executable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.function;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code Executable} is a functional interface that can be used to\n * implement any generic block of code that potentially throws a\n * {@link Throwable}.\n *\n * <p>The {@code Executable} interface is similar to {@link java.lang.Runnable},\n * except that an {@code Executable} can throw any kind of exception.\n *\n * <h2>Rationale for throwing {@code Throwable} instead of {@code Exception}</h2>\n *\n * <p>Although Java applications typically throw exceptions that are instances\n * of {@link java.lang.Exception}, {@link java.lang.RuntimeException},\n * {@link java.lang.Error}, or {@link java.lang.AssertionError} (in testing\n * scenarios), there may be use cases where an {@code Executable} needs to\n * explicitly throw a {@code Throwable}. In order to support such specialized\n * use cases, {@link #execute()} is declared to throw {@code Throwable}.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.Assertions#assertAll(Executable...)\n * @see org.junit.jupiter.api.Assertions#assertAll(String, Executable...)\n * @see org.junit.jupiter.api.Assertions#assertThrows(Class, Executable)\n * @see org.junit.jupiter.api.Assumptions#assumingThat(java.util.function.BooleanSupplier, Executable)\n * @see org.junit.jupiter.api.DynamicTest#dynamicTest(String, Executable)\n * @see ThrowingConsumer\n * @see ThrowingSupplier\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface Executable {\n\n\tvoid execute() throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingConsumer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.function;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code ThrowingConsumer} is a functional interface that can be used to\n * implement any generic block of code that consumes an argument and\n * potentially throws a {@link Throwable}.\n *\n * <p>The {@code ThrowingConsumer} interface is similar to\n * {@link java.util.function.Consumer}, except that a {@code ThrowingConsumer}\n * can throw any kind of exception, including checked exceptions.\n *\n * <h2>Rationale for throwing {@code Throwable} instead of {@code Exception}</h2>\n *\n * <p>Although Java applications typically throw exceptions that are instances\n * of {@link java.lang.Exception}, {@link java.lang.RuntimeException},\n * {@link java.lang.Error}, or {@link java.lang.AssertionError} (in testing\n * scenarios), there may be use cases where a {@code ThrowingConsumer} needs to\n * explicitly throw a {@code Throwable}. In order to support such specialized\n * use cases, {@link #accept} is declared to throw {@code Throwable}.\n *\n * @param <T> the type of argument consumed\n * @since 5.0\n * @see java.util.function.Consumer\n * @see org.junit.jupiter.api.DynamicTest#stream\n * @see Executable\n * @see ThrowingSupplier\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface ThrowingConsumer<T extends @Nullable Object> {\n\n\t/**\n\t * Consume the supplied argument, potentially throwing an exception.\n\t *\n\t * @param t the argument to consume\n\t */\n\tvoid accept(T t) throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/ThrowingSupplier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.function;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code ThrowingSupplier} is a functional interface that can be used to\n * implement any generic block of code that returns an object and\n * potentially throws a {@link Throwable}.\n *\n * <p>The {@code ThrowingSupplier} interface is similar to\n * {@link java.util.function.Supplier}, except that a {@code ThrowingSupplier}\n * can throw any kind of exception, including checked exceptions.\n *\n * <h2>Rationale for throwing {@code Throwable} instead of {@code Exception}</h2>\n *\n * <p>Although Java applications typically throw exceptions that are instances\n * of {@link Exception}, {@link RuntimeException},\n * {@link Error}, or {@link AssertionError} (in testing\n * scenarios), there may be use cases where a {@code ThrowingSupplier} needs to\n * explicitly throw a {@code Throwable}. In order to support such specialized\n * use cases, {@link #get} is declared to throw {@code Throwable}.\n *\n * @param <T> the type of argument supplied\n * @since 5.0\n * @see java.util.function.Supplier\n * @see org.junit.jupiter.api.Assertions#assertTimeout(java.time.Duration, ThrowingSupplier)\n * @see org.junit.jupiter.api.Assertions#assertTimeoutPreemptively(java.time.Duration, ThrowingSupplier)\n * @see Executable\n * @see ThrowingConsumer\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.0\")\npublic interface ThrowingSupplier<T extends @Nullable Object> {\n\n\t/**\n\t * Get a result, potentially throwing an exception.\n\t *\n\t * @return a result\n\t */\n\tT get() throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/function/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Functional interfaces used within JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.function;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/CleanupMode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * Enumeration of cleanup modes for {@link TempDir @TempDir}.\n *\n * <p>When a test with a temporary directory completes, it might be useful in\n * some cases to be able to view the contents of the temporary directory used by\n * the test. {@code CleanupMode} allows you to control how a {@code TempDir}\n * is cleaned up.\n *\n * @since 5.9\n * @see TempDir\n */\n@API(status = STABLE, since = \"5.11\")\npublic enum CleanupMode {\n\n\t/**\n\t * Use the default cleanup mode.\n\t *\n\t * @see TempDir#DEFAULT_CLEANUP_MODE_PROPERTY_NAME\n\t */\n\tDEFAULT,\n\n\t/**\n\t * Always clean up a temporary directory after the test has completed.\n\t */\n\tALWAYS,\n\n\t/**\n\t * Only clean up a temporary directory if the test completed successfully.\n\t */\n\tON_SUCCESS,\n\n\t/**\n\t * Never clean up a temporary directory after the test has completed.\n\t */\n\tNEVER\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/DefaultDeletionResult.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.stream.Collectors.joining;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.DeletionException;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.DeletionFailure;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.DeletionResult;\nimport org.junit.platform.commons.util.Preconditions;\n\nrecord DefaultDeletionResult(Path rootDir, List<DeletionFailure> failures) implements DeletionResult {\n\n\tDefaultDeletionResult(Path rootDir, List<DeletionFailure> failures) {\n\t\tthis.rootDir = rootDir;\n\t\tthis.failures = List.copyOf(failures);\n\t}\n\n\t@Override\n\tpublic Optional<DeletionException> toException() {\n\t\tif (isSuccessful()) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tvar joinedPaths = failures().stream() //\n\t\t\t\t.map(DeletionFailure::path) //\n\t\t\t\t.sorted() //\n\t\t\t\t.distinct() //\n\t\t\t\t.map(path -> relativizeSafely(rootDir(), path).toString()) //\n\t\t\t\t.map(path -> path.isEmpty() ? \"<root>\" : path) //\n\t\t\t\t.collect(joining(\", \"));\n\t\tvar exception = new DeletionException(\"Failed to delete temp directory \" + rootDir().toAbsolutePath()\n\t\t\t\t+ \". The following paths could not be deleted (see suppressed exceptions for details): \" + joinedPaths);\n\t\tfailures().stream() //\n\t\t\t\t.sorted(comparing(DeletionFailure::path)) //\n\t\t\t\t.map(DeletionFailure::cause) //\n\t\t\t\t.forEach(exception::addSuppressed);\n\t\treturn Optional.of(exception);\n\t}\n\n\tprivate static Path relativizeSafely(Path rootDir, Path path) {\n\t\ttry {\n\t\t\treturn rootDir.relativize(path);\n\t\t}\n\t\tcatch (IllegalArgumentException e) {\n\t\t\treturn path;\n\t\t}\n\t}\n\n\tstatic final class Builder implements DeletionResult.Builder {\n\n\t\tprivate final Path rootDir;\n\t\tprivate final List<DeletionFailure> failures = new ArrayList<>();\n\n\t\tBuilder(Path rootDir) {\n\t\t\tthis.rootDir = rootDir;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder addFailure(Path path, Exception cause) {\n\t\t\tPreconditions.notNull(path, \"path must not be null\");\n\t\t\tPreconditions.notNull(cause, \"cause must not be null\");\n\t\t\tfailures.add(new DefaultDeletionFailure(path, cause));\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic DefaultDeletionResult build() {\n\t\t\treturn new DefaultDeletionResult(rootDir, failures);\n\t\t}\n\n\t}\n\n\trecord DefaultDeletionFailure(Path path, Exception cause) implements DeletionFailure {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDir.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\n\n/**\n * {@code @TempDir} can be used to annotate a field in a test class or a parameter\n * in a test class constructor, lifecycle method, or test method of type\n * {@link Path} or {@link File} that should be resolved into a temporary directory.\n *\n * <h2>Creation</h2>\n *\n * <p>The temporary directory is only created if a field in a test class or a\n * parameter in a test class constructor, lifecycle method, or test method is\n * annotated with {@code @TempDir}. An {@link ExtensionConfigurationException} or\n * a {@link ParameterResolutionException} will be thrown in one of the following\n * cases:\n *\n * <ul>\n * <li>If the field type or parameter type is neither {@code Path} nor {@code File}.</li>\n * <li>If a field is declared as {@code final}.</li>\n * <li>If the temporary directory cannot be created.</li>\n * <li>If the field type or parameter type is {@code File} and a custom\n *     {@link TempDir#factory() factory} is used, which creates a temporary\n *     directory that does not belong to the\n *     {@linkplain java.nio.file.FileSystems#getDefault() default file system}.\n * </li>\n * </ul>\n *\n * <h2>Scope</h2>\n *\n * <p>By default, a separate temporary directory is created for every declaration\n * of the {@code @TempDir} annotation. For better isolation when using\n * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_METHOD @TestInstance(Lifecycle.PER_METHOD)}\n * semantics, you can annotate an instance field or a parameter in the test class\n * constructor with {@code @TempDir} so that each test method uses a separate\n * temporary directory. Alternatively, if you want to share a temporary directory\n * across all tests in a test class, you should declare the annotation on a\n * {@code static} field or on a parameter of a\n * {@link org.junit.jupiter.api.BeforeAll @BeforeAll} method.\n *\n * <h2>Cleanup/Deletion</h2>\n *\n * <p>By default, when the end of the scope of a temporary directory is reached,\n * &mdash; when the test method or class has finished execution &mdash; JUnit will\n * attempt to clean up the temporary directory by recursively deleting all files\n * and directories in the temporary directory and, finally, the temporary directory\n * itself.\n *\n * <p>Two attributes allow customizing <em>when</em> (see {@link #cleanup()})\n * and <em>how</em> (see {@link #deletionStrategy()}) to clean up.\n *\n * <p>The {@link #cleanup} attribute allows you to configure the {@link CleanupMode}.\n * If the cleanup mode is set to {@link CleanupMode#NEVER NEVER}, the temporary\n * directory will not be cleaned up after the test completes. If the cleanup mode is\n * set to {@link CleanupMode#ON_SUCCESS ON_SUCCESS}, the temporary directory will\n * only be cleaned up if the test completes successfully. By default, the\n * {@link CleanupMode#ALWAYS ALWAYS} clean up mode will be used, but this can be\n * configured globally by setting the {@value #DEFAULT_CLEANUP_MODE_PROPERTY_NAME}\n * configuration parameter.\n *\n * <p>The {@link #deletionStrategy()} attribute defines the strategy for\n * performing the cleanup and dealing with errors such as undeletable files.\n * By default, the {@link TempDirDeletionStrategy.Standard Standard} strategy is\n * used which will cause a test or test class to fail in case deletion of a file\n * or directory fails. This can be configured globally by setting the\n * {@value #DEFAULT_DELETION_STRATEGY_PROPERTY_NAME} configuration parameter.\n *\n * @since 5.4\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.10\")\npublic @interface TempDir {\n\n\t/**\n\t * Property name used to set the default temporary directory factory class name:\n\t * {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include fully qualified class names for types that\n\t * implement {@link TempDirFactory}.\n\t *\n\t * <p>If not specified, the default is {@link TempDirFactory.Standard}.\n\t *\n\t * @since 5.10\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString DEFAULT_FACTORY_PROPERTY_NAME = \"junit.jupiter.tempdir.factory.default\";\n\n\t/**\n\t * Factory for the temporary directory.\n\t *\n\t * <p>Defaults to {@link TempDirFactory.Standard}.\n\t *\n\t * <p>As an alternative to setting this attribute, a global\n\t * {@link TempDirFactory} can be configured for the entire test suite via\n\t * the {@value #DEFAULT_FACTORY_PROPERTY_NAME} configuration parameter.\n\t * See the User Guide for details. Note, however, that a {@code @TempDir}\n\t * declaration with a custom {@code factory} always overrides a global\n\t * {@code TempDirFactory}.\n\t *\n\t * @return the type of {@code TempDirFactory} to use\n\t * @since 5.10\n\t * @see TempDirFactory\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tClass<? extends TempDirFactory> factory() default TempDirFactory.class;\n\n\t/**\n\t * Property name used to configure the default {@link CleanupMode}: {@value}\n\t *\n\t * <p>Supported values include names of enum constants defined in\n\t * {@link CleanupMode}, ignoring case.\n\t *\n\t * <p>If this configuration parameter is not set, {@link CleanupMode#ALWAYS}\n\t * will be used as the default.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString DEFAULT_CLEANUP_MODE_PROPERTY_NAME = \"junit.jupiter.tempdir.cleanup.mode.default\";\n\n\t/**\n\t * In which cases the temporary directory gets cleaned up after the test completes.\n\t *\n\t * @since 5.9\n\t */\n\t@API(status = STABLE, since = \"5.11\")\n\tCleanupMode cleanup() default CleanupMode.DEFAULT;\n\n\t/**\n\t * Property name used to set the default deletion strategy class name:\n\t * {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include fully qualified class names for types that\n\t * implement {@link TempDirDeletionStrategy}.\n\t *\n\t * <p>If not specified, the default is {@link TempDirDeletionStrategy.Standard}.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tString DEFAULT_DELETION_STRATEGY_PROPERTY_NAME = \"junit.jupiter.tempdir.deletion.strategy.default\";\n\n\t/**\n\t * Deletion strategy for the temporary directory.\n\t *\n\t * <p>Defaults to {@link TempDirDeletionStrategy.Standard}.\n\t *\n\t * <p>As an alternative to setting this attribute, a global\n\t * {@link TempDirDeletionStrategy} can be configured for the entire test\n\t * suite via the {@value #DEFAULT_DELETION_STRATEGY_PROPERTY_NAME}\n\t * configuration parameter. See the User Guide for details. Note, however,\n\t * that a {@code @TempDir} declaration with a custom\n\t * {@code deletionStrategy} always overrides a global\n\t * {@code TempDirDeletionStrategy}.\n\t *\n\t * @return the type of {@code TempDirDeletionStrategy} to use\n\t * @since 6.1\n\t * @see TempDirDeletionStrategy\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tClass<? extends TempDirDeletionStrategy> deletionStrategy() default TempDirDeletionStrategy.class;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirDeletionStrategy.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static java.nio.file.FileVisitResult.CONTINUE;\nimport static java.nio.file.FileVisitResult.SKIP_SUBTREE;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Parameter;\nimport java.nio.file.DirectoryNotEmptyException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Files;\nimport java.nio.file.LinkOption;\nimport java.nio.file.NoSuchFileException;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.nio.file.attribute.DosFileAttributeView;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code TempDirDeletionStrategy} defines the SPI for deleting temporary\n * directories programmatically.\n *\n * <p>A deletion strategy controls how a temporary directory is cleaned up\n * when the end of its scope is reached.\n *\n * <p>Implementations must provide a no-args constructor.\n *\n * <p>A {@link TempDirDeletionStrategy} can be configured <em>globally</em>\n * for the entire test suite via the\n * {@value TempDir#DEFAULT_DELETION_STRATEGY_PROPERTY_NAME} configuration\n * parameter (see the User Guide for details) or <em>locally</em> for a test\n * class field or method parameter via the {@link TempDir @TempDir} annotation.\n *\n * @since 6.1\n * @see TempDir @TempDir\n */\n@API(status = EXPERIMENTAL, since = \"6.1\")\npublic interface TempDirDeletionStrategy {\n\n\t/**\n\t * Delete the supplied temporary directory and all of its contents.\n\t *\n\t * <p>Depending on the used {@link TempDirFactory}, the supplied\n\t * {@link Path} may or may not be associated with the\n\t * {@linkplain java.nio.file.FileSystems#getDefault() default FileSystem}.\n\t *\n\t * @param tempDir the temporary directory to delete; never {@code null}\n\t * @param elementContext the context of the field or parameter where\n\t * {@code @TempDir} is declared; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @return a {@link DeletionResult}, potentially containing failures for\n\t * {@link Path Paths} that could not be deleted or no failures if deletion\n\t * was successful; never {@code null}\n\t * @throws IOException in case of general failures\n\t */\n\tDeletionResult delete(Path tempDir, AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\tthrows IOException;\n\n\t/**\n\t * A {@link TempDirDeletionStrategy} that delegates to {@link Standard} but\n\t * suppresses deletion failures by logging a warning instead of propagating\n\t * them.\n\t */\n\tfinal class IgnoreFailures implements TempDirDeletionStrategy {\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(IgnoreFailures.class);\n\t\tprivate final TempDirDeletionStrategy delegate;\n\n\t\t/**\n\t\t * Create a new {@code IgnoreFailures} strategy that delegates to\n\t\t * {@link Standard}.\n\t\t */\n\t\tpublic IgnoreFailures() {\n\t\t\tthis(Standard.INSTANCE);\n\t\t}\n\n\t\tIgnoreFailures(TempDirDeletionStrategy delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\t@Override\n\t\tpublic DeletionResult delete(Path tempDir, AnnotatedElementContext elementContext,\n\t\t\t\tExtensionContext extensionContext) throws IOException {\n\n\t\t\tvar result = delegate.delete(tempDir, elementContext, extensionContext);\n\n\t\t\tresult.toException().ifPresent(ex -> logWarning(elementContext, ex));\n\n\t\t\treturn DeletionResult.builder(tempDir).build();\n\t\t}\n\n\t\tprivate void logWarning(AnnotatedElementContext elementContext, DeletionException exception) {\n\t\t\tlogger.warn(exception, () -> \"Failed to delete all temporary files for %s\".formatted(\n\t\t\t\tdescriptionFor(elementContext.getAnnotatedElement())));\n\t\t}\n\n\t\t@API(status = INTERNAL, since = \"6.1\")\n\t\tpublic static String descriptionFor(AnnotatedElement annotatedElement) {\n\t\t\tif (annotatedElement instanceof Field field) {\n\t\t\t\treturn \"field \" + field.getDeclaringClass().getSimpleName() + \".\" + field.getName();\n\t\t\t}\n\t\t\tif (annotatedElement instanceof Parameter parameter) {\n\t\t\t\tExecutable executable = parameter.getDeclaringExecutable();\n\t\t\t\treturn \"parameter '\" + parameter.getName() + \"' in \" + descriptionFor(executable);\n\t\t\t}\n\t\t\tthrow new IllegalStateException(\"Unsupported AnnotatedElement type for @TempDir: \" + annotatedElement);\n\t\t}\n\n\t\tprivate static String descriptionFor(Executable executable) {\n\t\t\tboolean isConstructor = executable instanceof Constructor<?>;\n\t\t\tString type = isConstructor ? \"constructor\" : \"method\";\n\t\t\tString name = isConstructor ? executable.getDeclaringClass().getSimpleName() : executable.getName();\n\t\t\treturn \"%s %s(%s)\".formatted(type, name,\n\t\t\t\tClassUtils.nullSafeToString(Class::getSimpleName, executable.getParameterTypes()));\n\t\t}\n\t}\n\n\t/**\n\t * Standard {@link TempDirDeletionStrategy} implementation that recursively\n\t * deletes all files and directories within the temporary directory.\n\t *\n\t * <p>Symbolic and other types of links, such as junctions on Windows, are\n\t * not followed. A warning is logged when deleting a link that targets a\n\t * location outside the temporary directory.\n\t *\n\t * <p>If a file or directory cannot be deleted, its permissions are reset\n\t * and deletion is attempted again. If deletion still fails, the path is\n\t * scheduled for deletion on JVM exit via\n\t * {@link java.io.File#deleteOnExit()}, if it belongs to the default file\n\t * system.\n\t */\n\tfinal class Standard implements TempDirDeletionStrategy {\n\n\t\t/**\n\t\t * The singleton instance of {@code Standard}.\n\t\t */\n\t\tpublic static final Standard INSTANCE = new Standard();\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(Standard.class);\n\n\t\tprivate Standard() {\n\t\t}\n\n\t\t@Override\n\t\tpublic DeletionResult delete(Path tempDir, AnnotatedElementContext elementContext,\n\t\t\t\tExtensionContext extensionContext) throws IOException {\n\n\t\t\treturn delete(tempDir, Files::delete);\n\t\t}\n\n\t\t// package-private for testing\n\t\tDeletionResult delete(Path tempDir, FileOperations fileOperations) throws IOException {\n\t\t\tvar result = DeletionResult.builder(tempDir);\n\t\t\tdelete(tempDir, fileOperations, (path, cause) -> {\n\t\t\t\tresult.addFailure(path, cause);\n\t\t\t\ttryToDeleteOnExit(path);\n\t\t\t});\n\t\t\treturn result.build();\n\t\t}\n\n\t\tprivate void delete(Path tempDir, FileOperations fileOperations, BiConsumer<Path, Exception> failureHandler)\n\t\t\t\tthrows IOException {\n\t\t\tSet<Path> retriedPaths = new HashSet<>();\n\t\t\tPath rootRealPath = tempDir.toRealPath();\n\n\t\t\ttryToResetPermissions(tempDir);\n\t\t\tFiles.walkFileTree(tempDir, new SimpleFileVisitor<>() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {\n\t\t\t\t\tlogger.trace(() -> \"preVisitDirectory: \" + dir);\n\t\t\t\t\tif (isLinkWithTargetOutsideTempDir(dir)) {\n\t\t\t\t\t\twarnAboutLinkWithTargetOutsideTempDir(\"link\", dir);\n\t\t\t\t\t\tdelete(dir, fileOperations);\n\t\t\t\t\t\treturn SKIP_SUBTREE;\n\t\t\t\t\t}\n\t\t\t\t\tif (!dir.equals(tempDir)) {\n\t\t\t\t\t\ttryToResetPermissions(dir);\n\t\t\t\t\t}\n\t\t\t\t\treturn CONTINUE;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic FileVisitResult visitFileFailed(Path file, IOException exc) {\n\t\t\t\t\tlogger.trace(exc, () -> \"visitFileFailed: \" + file);\n\t\t\t\t\tif (exc instanceof NoSuchFileException && !Files.exists(file, LinkOption.NOFOLLOW_LINKS)) {\n\t\t\t\t\t\treturn CONTINUE;\n\t\t\t\t\t}\n\t\t\t\t\t// IOException includes `AccessDeniedException` thrown by non-readable or non-executable flags\n\t\t\t\t\tresetPermissionsAndTryToDeleteAgain(file, exc);\n\t\t\t\t\treturn CONTINUE;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {\n\t\t\t\t\tlogger.trace(() -> \"visitFile: \" + file);\n\t\t\t\t\tif (Files.isSymbolicLink(file) && isLinkWithTargetOutsideTempDir(file)) {\n\t\t\t\t\t\twarnAboutLinkWithTargetOutsideTempDir(\"symbolic link\", file);\n\t\t\t\t\t}\n\t\t\t\t\tdelete(file, fileOperations);\n\t\t\t\t\treturn CONTINUE;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic FileVisitResult postVisitDirectory(Path dir, @Nullable IOException exc) {\n\t\t\t\t\tlogger.trace(exc, () -> \"postVisitDirectory: \" + dir);\n\t\t\t\t\tdelete(dir, fileOperations);\n\t\t\t\t\treturn CONTINUE;\n\t\t\t\t}\n\n\t\t\t\tprivate boolean isLinkWithTargetOutsideTempDir(Path path) {\n\t\t\t\t\t// While `Files.walkFileTree` does not follow symbolic links, it may follow other links\n\t\t\t\t\t// such as \"junctions\" on Windows\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn !path.toRealPath().startsWith(rootRealPath);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (IOException e) {\n\t\t\t\t\t\tlogger.trace(e,\n\t\t\t\t\t\t\t() -> \"Failed to determine real path for \" + path + \"; assuming it is not a link\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprivate void warnAboutLinkWithTargetOutsideTempDir(String linkType, Path file) throws IOException {\n\t\t\t\t\tPath realPath = file.toRealPath();\n\t\t\t\t\tlogger.warn(() -> \"\"\"\n\t\t\t\t\t\t\tDeleting %s from location inside of temp dir (%s) \\\n\t\t\t\t\t\t\tto location outside of temp dir (%s) but not the target file/directory\"\"\".formatted(\n\t\t\t\t\t\tlinkType, file, realPath));\n\t\t\t\t}\n\n\t\t\t\tprivate void delete(Path path, FileOperations fileOperations) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tdeleteWithLogging(path, fileOperations);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (NoSuchFileException ignore) {\n\t\t\t\t\t\t// ignore\n\t\t\t\t\t}\n\t\t\t\t\tcatch (DirectoryNotEmptyException exception) {\n\t\t\t\t\t\tfailureHandler.accept(path, exception);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (IOException exception) {\n\t\t\t\t\t\t// IOException includes `AccessDeniedException` thrown by non-readable or non-executable flags\n\t\t\t\t\t\tresetPermissionsAndTryToDeleteAgain(path, exception);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tprivate void resetPermissionsAndTryToDeleteAgain(Path path, IOException exception) {\n\t\t\t\t\tboolean notYetRetried = retriedPaths.add(path);\n\t\t\t\t\tif (notYetRetried) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ttryToResetPermissions(path);\n\t\t\t\t\t\t\tif (Files.isDirectory(path)) {\n\t\t\t\t\t\t\t\tFiles.walkFileTree(path, this);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tdeleteWithLogging(path, fileOperations);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception suppressed) {\n\t\t\t\t\t\t\texception.addSuppressed(suppressed);\n\t\t\t\t\t\t\tfailureHandler.accept(path, exception);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tfailureHandler.accept(path, exception);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tprivate void deleteWithLogging(Path file, FileOperations fileOperations) throws IOException {\n\t\t\tlogger.trace(() -> \"Attempting to delete \" + file);\n\t\t\ttry {\n\t\t\t\tfileOperations.delete(file);\n\t\t\t\tlogger.trace(() -> \"Successfully deleted \" + file);\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tlogger.trace(e, () -> \"Failed to delete \" + file);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\t\tprivate static void tryToResetPermissions(Path path) {\n\t\t\tFile file;\n\t\t\ttry {\n\t\t\t\tfile = path.toFile();\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ignore) {\n\t\t\t\t// Might happen when the `TempDirFactory` uses a custom `FileSystem`\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfile.setReadable(true);\n\t\t\tfile.setWritable(true);\n\t\t\tif (Files.isDirectory(path)) {\n\t\t\t\tfile.setExecutable(true);\n\t\t\t}\n\t\t\tDosFileAttributeView dos = Files.getFileAttributeView(path, DosFileAttributeView.class);\n\t\t\tif (dos != null) {\n\t\t\t\ttry {\n\t\t\t\t\tdos.setReadOnly(false);\n\t\t\t\t}\n\t\t\t\tcatch (IOException ignore) {\n\t\t\t\t\t// nothing we can do\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"EmptyCatch\")\n\t\tprivate static void tryToDeleteOnExit(Path path) {\n\t\t\ttry {\n\t\t\t\tif (FileSystems.getDefault().equals(path.getFileSystem())) {\n\t\t\t\t\tpath.toFile().deleteOnExit();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (UnsupportedOperationException ignore) {\n\t\t\t}\n\t\t}\n\n\t\t// For testing only\n\t\tinterface FileOperations {\n\n\t\t\tvoid delete(Path path) throws IOException;\n\n\t\t}\n\t}\n\n\t/**\n\t * Represents the result of a {@link TempDirDeletionStrategy#delete} operation,\n\t * including any paths that could not be deleted.\n\t */\n\tsealed interface DeletionResult permits DefaultDeletionResult {\n\n\t\t/**\n\t\t * Create a new {@link Builder} for the supplied root directory.\n\t\t *\n\t\t * @param rootDir the root temporary directory; never {@code null}\n\t\t * @return a new {@code Builder}; never {@code null}\n\t\t */\n\t\tstatic Builder builder(Path rootDir) {\n\t\t\treturn new DefaultDeletionResult.Builder(Preconditions.notNull(rootDir, \"rootDir must not be null\"));\n\t\t}\n\n\t\t/**\n\t\t * Return the root temporary directory of this deletion operation.\n\t\t *\n\t\t * @return the root directory; never {@code null}\n\t\t */\n\t\tPath rootDir();\n\n\t\t/**\n\t\t * Return the list of failures that occurred during deletion.\n\t\t *\n\t\t * @return the list of failures; never {@code null}\n\t\t */\n\t\tList<DeletionFailure> failures();\n\n\t\t/**\n\t\t * Return {@code true} if the deletion was successful, i.e., no\n\t\t * {@linkplain #failures() failures} were recorded.\n\t\t */\n\t\tdefault boolean isSuccessful() {\n\t\t\treturn failures().isEmpty();\n\t\t}\n\n\t\t/**\n\t\t * Convert this result to a {@link DeletionException} summarizing all\n\t\t * failures.\n\t\t *\n\t\t * <p>Must only be called if {@link #isSuccessful()} returns\n\t\t * {@code false}.\n\t\t *\n\t\t * @return an {@link DeletionException}, if the deletion\n\t\t * {@linkplain #isSuccessful() was successful; otherwise, empty}; never\n\t\t * {@code null}\n\t\t */\n\t\tOptional<DeletionException> toException();\n\n\t\t/**\n\t\t * Builder for {@link DeletionResult}.\n\t\t */\n\t\tsealed interface Builder permits DefaultDeletionResult.Builder {\n\n\t\t\t/**\n\t\t\t * Record a failure for the supplied path.\n\t\t\t *\n\t\t\t * @param path the path that could not be deleted; never {@code null}\n\t\t\t * @param cause the exception that caused the failure; never {@code null}\n\t\t\t * @return this builder; never {@code null}\n\t\t\t */\n\t\t\tBuilder addFailure(Path path, Exception cause);\n\n\t\t\t/**\n\t\t\t * Build the {@link DeletionResult}.\n\t\t\t *\n\t\t\t * @return a new {@link DeletionResult}; never {@code null}\n\t\t\t */\n\t\t\tDeletionResult build();\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents a single failure that occurred while attempting to delete a\n\t * path during a {@link TempDirDeletionStrategy#delete} operation.\n\t */\n\tsealed interface DeletionFailure permits DefaultDeletionResult.DefaultDeletionFailure {\n\n\t\t/**\n\t\t * Return the path that could not be deleted.\n\t\t *\n\t\t * @return the path; never {@code null}\n\t\t */\n\t\tPath path();\n\n\t\t/**\n\t\t * Return the exception that caused the failure.\n\t\t *\n\t\t * @return the cause; never {@code null}\n\t\t */\n\t\tException cause();\n\n\t}\n\n\t/**\n\t * Exception thrown when one or more paths in a temporary directory could\n\t * not be deleted by a {@link TempDirDeletionStrategy}.\n\t */\n\tfinal class DeletionException extends JUnitException {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tDeletionException(String message) {\n\t\t\tsuper(message, null, true, false);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/TempDirFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * {@code TempDirFactory} defines the SPI for creating temporary directories\n * programmatically.\n *\n * <p>A temporary directory factory is typically used to gain control over the\n * temporary directory creation, like defining the parent directory or the file\n * system that should be used.\n *\n * <p>Implementations must provide a no-args constructor and should not make any\n * assumptions regarding when and how many times they are instantiated, but they\n * can assume that {@link #createTempDirectory(AnnotatedElementContext, ExtensionContext)}\n * and {@link #close()} will both be called once per instance, in this order,\n * and from the same thread.\n *\n * <p>A {@link TempDirFactory} can be configured <em>globally</em> for the\n * entire test suite via the {@value TempDir#DEFAULT_FACTORY_PROPERTY_NAME}\n * configuration parameter (see the User Guide for details) or <em>locally</em>\n * for a test class field or method parameter via the {@link TempDir @TempDir}\n * annotation.\n *\n * @since 5.10\n * @see TempDir @TempDir\n */\n@FunctionalInterface\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface TempDirFactory extends Closeable {\n\n\t/**\n\t * Create a new temporary directory.\n\t *\n\t * <p>Depending on the implementation, the resulting {@link Path} may or may\n\t * not be associated with the {@link java.nio.file.FileSystems#getDefault()\n\t * default FileSystem}.\n\t *\n\t * @param elementContext the context of the field or parameter where\n\t * {@code @TempDir} is declared; never {@code null}\n\t * @param extensionContext the current extension context; never {@code null}\n\t * @return the path to the newly created temporary directory; never {@code null}\n\t * @throws Exception in case of failures\n\t */\n\tPath createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\tthrows Exception;\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tdefault void close() throws IOException {\n\t}\n\n\t/**\n\t * Standard {@link TempDirFactory} implementation which delegates to\n\t * {@link Files#createTempDirectory} using {@code \"junit-\"} as prefix.\n\t *\n\t * <p>The created temporary directory is always created in the default\n\t * file system with the system's default temporary directory as its parent.\n\t *\n\t * @see Files#createTempDirectory(java.lang.String, java.nio.file.attribute.FileAttribute[])\n\t */\n\tclass Standard implements TempDirFactory {\n\n\t\tpublic static final TempDirFactory INSTANCE = new Standard();\n\n\t\tprivate static final String TEMP_DIR_PREFIX = \"junit-\";\n\n\t\tpublic Standard() {\n\t\t}\n\n\t\t@Override\n\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\tthrows IOException {\n\t\t\treturn Files.createTempDirectory(TEMP_DIR_PREFIX);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/io/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * IO-related support in JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.io;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter API for writing tests.\n */\n\n@NullMarked\npackage org.junit.jupiter.api;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Execution.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.TestInstance;\n\n/**\n * {@code @Execution} is used to configure the parallel execution\n * {@linkplain #value mode} of a test class or test method.\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * <h2>Default Execution Mode</h2>\n *\n * <p>If this annotation is not present, {@link ExecutionMode#SAME_THREAD} is\n * used unless a default execution mode is defined via one of the following\n * configuration parameters:\n *\n * <dl>\n *     <dt>{@value #DEFAULT_EXECUTION_MODE_PROPERTY_NAME}</dt>\n *     <dd>Default execution mode for all classes and tests</dd>\n *     <dt>{@value #DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME}</dt>\n *     <dd>Default execution mode for top-level classes</dd>\n * </dl>\n *\n * <p>{@value #DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME} overrides\n * {@value #DEFAULT_EXECUTION_MODE_PROPERTY_NAME} for top-level classes.\n *\n * <p>The default execution mode is not applied to classes that use the\n * {@link TestInstance.Lifecycle#PER_CLASS PER_CLASS} lifecycle or a\n * {@link MethodOrderer}. In both cases, test methods in such test classes are\n * only executed concurrently if the {@code @Execution(CONCURRENT)} annotation\n * is present on the test class or method.\n *\n * @see Isolated\n * @see ResourceLock\n * @since 5.3\n */\n@API(status = STABLE, since = \"5.10\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\npublic @interface Execution {\n\n\t/**\n\t * Property name used to set the default test execution mode: {@value}\n\t *\n\t * <p>This setting is only effective if parallel execution is enabled.\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include names of enum constants defined in\n\t * {@link ExecutionMode}, ignoring case.\n\t *\n\t * <p>If not specified, the default is \"same_thread\" which corresponds to\n\t * {@code @Execution(ExecutionMode.SAME_THREAD)}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString DEFAULT_EXECUTION_MODE_PROPERTY_NAME = \"junit.jupiter.execution.parallel.mode.default\";\n\n\t/**\n\t * Property name used to set the default test execution mode for top-level\n\t * classes: {@value}\n\t *\n\t * <p>This setting is only effective if parallel execution is enabled.\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include names of enum constants defined in\n\t * {@link ExecutionMode}, ignoring case.\n\t *\n\t * <p>If not specified, it will be resolved into the same value as\n\t * {@link #DEFAULT_EXECUTION_MODE_PROPERTY_NAME}.\n\t *\n\t * @since 5.4\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME = \"junit.jupiter.execution.parallel.mode.classes.default\";\n\n\t/**\n\t * The required/preferred execution mode.\n\t *\n\t * @see ExecutionMode\n\t */\n\tExecutionMode value();\n\n\t/**\n\t * The reason for using the selected execution mode.\n\t *\n\t * <p>This is for informational purposes only.\n\t *\n\t * @since 5.10\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tString reason() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ExecutionMode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * Supported execution modes for parallel test execution.\n *\n * @since 5.3\n * @see #SAME_THREAD\n * @see #CONCURRENT\n */\n@API(status = STABLE, since = \"5.10\")\npublic enum ExecutionMode {\n\n\t/**\n\t * Force execution in same thread as the parent node.\n\t *\n\t * @see #CONCURRENT\n\t */\n\tSAME_THREAD,\n\n\t/**\n\t * Allow concurrent execution with any other node.\n\t *\n\t * @see #SAME_THREAD\n\t */\n\tCONCURRENT\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Isolated.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Isolated} is used to declare that the annotated test class should be\n * executed in isolation from other test classes.\n *\n * <p>When a test class is run in isolation, no other test class is executed\n * concurrently. This can be used to enable parallel test execution for the\n * entire test suite while running some tests in isolation (e.g. if they modify\n * some global resource).\n *\n * @since 5.7\n * @see ExecutionMode\n * @see ResourceLock\n */\n@API(status = STABLE, since = \"5.10\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@ResourceLock(Resources.GLOBAL)\npublic @interface Isolated {\n\n\t/**\n\t * The reason this test class needs to run in isolation.\n\t *\n\t * <p>The supplied string is currently not reported in any way but can be\n\t * used for documentation purposes.\n\t */\n\tString value() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceAccessMode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * The access mode required by a test class or method for a given resource.\n *\n * @since 5.3\n * @see ResourceLock\n * @see ResourceLocksProvider.Lock\n */\n@API(status = STABLE, since = \"5.10\")\npublic enum ResourceAccessMode {\n\n\t/**\n\t * Require read and write access to the resource.\n\t */\n\tREAD_WRITE,\n\n\t/**\n\t * Require only read access to the resource.\n\t */\n\tREAD\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\n\n/**\n * {@code @ResourceLock} is used to declare that the annotated test class or test\n * method requires access to a shared resource identified by a key.\n *\n * <p>The resource key is specified via {@link #value}. In addition, {@link #mode}\n * allows one to specify whether the annotated test class or test method requires\n * {@link ResourceAccessMode#READ_WRITE READ_WRITE} or\n * {@link ResourceAccessMode#READ READ} access to the resource. In the former case,\n * execution of the annotated element will occur while no other test class or\n * test method that uses the shared resource is being executed. In the latter case,\n * the annotated element may be executed concurrently with other test classes or\n * methods that also require {@code READ} access but not at the same time as any\n * other test that requires {@code READ_WRITE} access.\n *\n * <p>This guarantee extends to lifecycle methods of a test class or method. For\n * example, if a test method is annotated with {@code @ResourceLock} the lock\n * will be acquired before any {@link org.junit.jupiter.api.BeforeEach @BeforeEach}\n * methods are executed and released after all\n * {@link org.junit.jupiter.api.AfterEach @AfterEach} methods have been executed.\n *\n * <p>This annotation can be repeated to declare the use of multiple shared resources.\n *\n * <p>Uniqueness of a shared resource is determined by both the {@link #value()}\n * and the {@link #mode()}. Duplicated shared resources do not cause errors.\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * <p>Since JUnit Jupiter 5.12, this annotation supports adding shared resources\n * dynamically at runtime via {@link #providers}. Resources declared \"statically\"\n * using {@link #value()} and {@link #mode()} are combined with \"dynamic\" resources\n * added via {@link #providers()}. For example, declaring resource \"A\" via\n * {@code @ResourceLock(\"A\")} and resource \"B\" via a provider returning\n * {@code new Lock(\"B\")} will result in two shared resources \"A\" and \"B\".\n *\n * <p>Since JUnit Jupiter 5.12, this annotation supports declaring \"static\"\n * shared resources for <em>direct</em> child nodes via the {@link #target()}\n * attribute. Using {@link ResourceLockTarget#CHILDREN} in a class-level annotation\n * has the same semantics as adding an annotation with the same {@link #value()}\n * and {@link #mode()} to each test method and nested test class declared in the\n * annotated class. This may improve parallelization when a test class declares a\n * {@link ResourceAccessMode#READ READ} lock, but only a few methods hold\n * {@link ResourceAccessMode#READ_WRITE READ_WRITE} lock. Note that\n * {@code target = CHILDREN} means that {@link #value()} and {@link #mode()} no\n * longer apply to a node declaring the annotation. However, the {@link #providers()}\n * attribute remains applicable, and the target of \"dynamic\" shared resources added\n * via implementations of {@link ResourceLocksProvider} is not changed.\n *\n * <p>Shared resources declared on or provided for methods or nested test\n * classes in a {@link ClassTemplate @ClassTemplate} are propagated as if they\n * were declared on the outermost enclosing {@code @ClassTemplate} class itself.\n *\n * @see Isolated\n * @see Resources\n * @see ResourceAccessMode\n * @see ResourceLockTarget\n * @see ResourceLocks\n * @see ResourceLocksProvider\n * @since 5.3\n */\n@API(status = STABLE, since = \"5.10\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@Repeatable(ResourceLocks.class)\npublic @interface ResourceLock {\n\n\t/**\n\t * The resource key.\n\t *\n\t * <p>Defaults to an empty string.\n\t *\n\t * @see Resources\n\t * @see ResourceLocksProvider.Lock#getKey()\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * The resource access mode.\n\t *\n\t * <p>Defaults to {@link ResourceAccessMode#READ_WRITE READ_WRITE}.\n\t *\n\t * @see ResourceAccessMode\n\t * @see ResourceLocksProvider.Lock#getAccessMode()\n\t */\n\tResourceAccessMode mode() default ResourceAccessMode.READ_WRITE;\n\n\t/**\n\t * An array of one or more classes implementing {@link ResourceLocksProvider}.\n\t *\n\t * <p>Defaults to an empty array.\n\t *\n\t * @see ResourceLocksProvider.Lock\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tClass<? extends ResourceLocksProvider>[] providers() default {};\n\n\t/**\n\t * The target of a resource created from {@link #value()} and {@link #mode()}.\n\t *\n\t * <p>Defaults to {@link ResourceLockTarget#SELF SELF}.\n\t *\n\t * <p>Note that using {@link ResourceLockTarget#CHILDREN} in a method-level\n\t * annotation results in an exception.\n\t *\n\t * @see ResourceLockTarget\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tResourceLockTarget target() default ResourceLockTarget.SELF;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLockTarget.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ResourceLockTarget} is used to define the target of a shared resource.\n *\n * @since 5.12\n * @see ResourceLock#target()\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic enum ResourceLockTarget {\n\n\t/**\n\t * Add a shared resource to the current node.\n\t */\n\tSELF,\n\n\t/**\n\t * Add a shared resource to the <em>direct</em> children of the current node.\n\t *\n\t * <p>Examples of \"parent - child\" relationships in the context of\n\t * {@code ResourceLockTarget}:\n\t * <ul>\n\t *   <li><strong>test class</strong>: test methods and nested test classes\n\t *   declared in the test class are children of the test class.</li>\n\t *   <li><strong>nested test class</strong>: test methods and nested test\n\t *   classes declared in the nested class are children of the nested test class.\n\t *   </li>\n\t *   <li><strong>test method</strong>: test methods are not considered to have\n\t *   children. Using {@code CHILDREN} for a test method results in an\n\t *   exception.</li>\n\t * </ul>\n\t */\n\tCHILDREN\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocks.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ResourceLocks} is a container for one or more\n * {@link ResourceLock @ResourceLock} declarations.\n *\n * <p>Note, however, that use of the {@code @ResourceLocks} container is\n * completely optional since {@code @ResourceLock} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @see ResourceLock\n * @since 5.3\n */\n@API(status = STABLE, since = \"5.10\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\npublic @interface ResourceLocks {\n\n\t/**\n\t * An array of one or more {@linkplain ResourceLock @ResourceLock} declarations.\n\t */\n\tResourceLock[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/ResourceLocksProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static java.util.Collections.emptySet;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * A {@code ResourceLocksProvider} is used to programmatically add shared resources\n * to a test class or its test methods dynamically at runtime.\n *\n * <p>Each shared resource is represented by an instance of {@link Lock}.\n *\n * <p>Adding shared resources via this API has the same semantics as declaring\n * them declaratively via {@link ResourceLock @ResourceLock(value, mode)}, but for\n * some use cases the programmatic approach may be more flexible and less verbose.\n *\n * <p>Implementations must provide a no-args constructor.\n *\n * @since 5.12\n * @see ResourceLock#providers()\n * @see Resources\n * @see ResourceAccessMode\n * @see Lock\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic interface ResourceLocksProvider {\n\n\t/**\n\t * Add shared resources for a test class.\n\t *\n\t * <p>Invoked in case a test class or its parent class is annotated with\n\t * {@code @ResourceLock(providers)}.\n\t *\n\t * @apiNote Adding {@linkplain Lock a shared resource} via this method has\n\t * the same semantics as annotating a test class with an analogous\n\t * {@code @ResourceLock(value, mode)} declaration.\n\t *\n\t * @param testClass a test class for which to add shared resources\n\t * @return a set of {@link Lock}; may be empty\n\t */\n\tdefault Set<Lock> provideForClass(Class<?> testClass) {\n\t\treturn emptySet();\n\t}\n\n\t/**\n\t * Add shared resources for a\n\t * {@link org.junit.jupiter.api.Nested @Nested} test class.\n\t *\n\t * <p>Invoked in case:\n\t * <ul>\n\t *   <li>an enclosing test class of any level or its parent class is\n\t *   annotated with {@code @ResourceLock(providers = ...)}.</li>\n\t *   <li>a nested test class or its parent class is annotated with\n\t *   {@code @ResourceLock(providers = ...)}.</li>\n\t * </ul>\n\t *\n\t * @apiNote Adding {@linkplain Lock a shared resource} via this method has\n\t * the same semantics as annotating a nested test class with an analogous\n\t * {@code @ResourceLock(value, mode)} declaration.\n\t *\n\t * @implNote The classes supplied as {@code enclosingInstanceTypes} may\n\t * differ from the classes returned from invocations of\n\t * {@link Class#getEnclosingClass()} &mdash; for example, when a nested test\n\t * class is inherited from a superclass.\n\t *\n\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t * instances for the test class, ordered from outermost to innermost,\n\t * excluding {@code testClass}; never {@code null}\n\t * @param testClass a nested test class for which to add shared resources\n\t * @return a set of {@link Lock}; may be empty\n\t * @see org.junit.jupiter.api.Nested @Nested\n\t */\n\tdefault Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\treturn emptySet();\n\t}\n\n\t/**\n\t * Add shared resources for a test method.\n\t *\n\t * <p>Invoked in case:\n\t * <ul>\n\t *   <li>an enclosing test class of any level or its parent class is\n\t *   annotated with {@code @ResourceLock(providers)}.</li>\n\t *   <li>a test method is annotated with {@code @ResourceLock(providers)}.</li>\n\t * </ul>\n\t *\n\t * @apiNote Adding {@linkplain Lock a shared resource} with this method\n\t * has the same semantics as annotating a test method\n\t * with analogous {@code @ResourceLock(value, mode)}.\n\t *\n\t * @implNote The classes supplied as {@code enclosingInstanceTypes} may\n\t * differ from the classes returned from invocations of\n\t * {@link Class#getEnclosingClass()} &mdash; for example, when a nested test\n\t * class is inherited from a superclass. Similarly, the class instance\n\t * supplied as {@code testClass} may differ from the class returned by\n\t * {@code testMethod.getDeclaringClass()} &mdash; for example, when a test\n\t * method is inherited from a superclass.\n\t *\n\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t * instances for the test class, ordered from outermost to innermost,\n\t * excluding {@code testClass}; never {@code null}\n\t * @param testClass the test class or {@link org.junit.jupiter.api.Nested @Nested}\n\t * test class that contains the {@code testMethod}\n\t * @param testMethod a test method for which to add shared resources\n\t * @return a set of {@link Lock}; may be empty\n\t * @see org.junit.jupiter.api.Nested @Nested\n\t */\n\tdefault Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass, Method testMethod) {\n\t\treturn emptySet();\n\t}\n\n\t/**\n\t * {@code Lock} represents a shared resource.\n\t *\n\t * <p>Each resource is identified by a {@linkplain #getKey() key}. In addition,\n\t * the {@linkplain #getAccessMode() access mode} allows one to specify whether\n\t * a test class or test method requires {@link ResourceAccessMode#READ_WRITE\n\t * READ_WRITE} or {@link ResourceAccessMode#READ READ} access to the resource.\n\t *\n\t * @apiNote {@link #getKey()} and {@link #getAccessMode()} have the same\n\t * semantics as {@link ResourceLock#value()} and {@link ResourceLock#mode()}\n\t * respectively.\n\t *\n\t * @since 5.12\n\t * @see Isolated\n\t * @see Resources\n\t * @see ResourceAccessMode\n\t * @see ResourceLock\n\t * @see ResourceLocksProvider\n\t */\n\tfinal class Lock {\n\n\t\tprivate final String key;\n\n\t\tprivate final ResourceAccessMode accessMode;\n\n\t\t/**\n\t\t * Create a new {@code Lock} with {@link ResourceAccessMode#READ_WRITE}.\n\t\t *\n\t\t * @param key the identifier of the resource; never {@code null} or blank\n\t\t * @see ResourceLock#value()\n\t\t */\n\t\tpublic Lock(String key) {\n\t\t\tthis(key, ResourceAccessMode.READ_WRITE);\n\t\t}\n\n\t\t/**\n\t\t * Create a new {@code Lock}.\n\t\t *\n\t\t * @param key the identifier of the resource; never {@code null} or blank\n\t\t * @param accessMode the lock mode to use to synchronize access to the\n\t\t * resource; never {@code null}\n\t\t * @see ResourceLock#value()\n\t\t * @see ResourceLock#mode()\n\t\t */\n\t\tpublic Lock(String key, ResourceAccessMode accessMode) {\n\t\t\tthis.key = Preconditions.notBlank(key, \"key must not be null or blank\");\n\t\t\tthis.accessMode = Preconditions.notNull(accessMode, \"accessMode must not be null\");\n\t\t}\n\n\t\t/**\n\t\t * Get the key for this lock.\n\t\t *\n\t\t * @see ResourceLock#value()\n\t\t */\n\t\tpublic String getKey() {\n\t\t\treturn this.key;\n\t\t}\n\n\t\t/**\n\t\t * Get the access mode for this lock.\n\t\t *\n\t\t * @see ResourceLock#mode()\n\t\t */\n\t\tpublic ResourceAccessMode getAccessMode() {\n\t\t\treturn this.accessMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tLock that = (Lock) o;\n\t\t\treturn this.key.equals(that.key) && this.accessMode == that.accessMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(this.key, this.accessMode);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn new ToStringBuilder(this) //\n\t\t\t\t\t.append(\"key\", this.key) //\n\t\t\t\t\t.append(\"accessMode\", this.accessMode) //\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/Resources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * Common resource names for synchronizing test execution.\n *\n * @since 5.3\n * @see ResourceLock\n * @see ResourceLocksProvider.Lock\n */\n@API(status = STABLE, since = \"5.10\")\npublic class Resources {\n\n\t/**\n\t * Represents Java's system properties: {@value}\n\t *\n\t * @see System#getProperties()\n\t * @see System#setProperties(java.util.Properties)\n\t */\n\tpublic static final String SYSTEM_PROPERTIES = \"java.lang.System.properties\";\n\n\t/**\n\t * Represents the standard output stream of the current process: {@value}\n\t *\n\t * @see System#out\n\t * @see System#setOut(java.io.PrintStream)\n\t */\n\tpublic static final String SYSTEM_OUT = \"java.lang.System.out\";\n\n\t/**\n\t * Represents the standard error stream of the current process: {@value}\n\t *\n\t * @see System#err\n\t * @see System#setErr(java.io.PrintStream)\n\t */\n\tpublic static final String SYSTEM_ERR = \"java.lang.System.err\";\n\n\t/**\n\t * Represents the default locale for the current instance of the JVM:\n\t * {@value}\n\t *\n\t * @since 5.4\n\t * @see java.util.Locale#setDefault(java.util.Locale)\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static final String LOCALE = \"java.util.Locale.default\";\n\n\t/**\n\t * Represents the default time zone for the current instance of the JVM:\n\t * {@value}\n\t *\n\t * @since 5.4\n\t * @see java.util.TimeZone#setDefault(java.util.TimeZone)\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static final String TIME_ZONE = \"java.util.TimeZone.default\";\n\n\t/**\n\t * Represents the global resource lock: {@value}\n\t *\n\t * @since 5.8\n\t * @see Isolated\n\t * @see org.junit.platform.engine.support.hierarchical.ExclusiveResource\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tpublic static final String GLOBAL = \"org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY\";\n\n\tprivate Resources() {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/parallel/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter API for influencing parallel test execution.\n */\n\n@NullMarked\npackage org.junit.jupiter.api.parallel;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/timeout/PreemptiveTimeoutUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.timeout;\n\nimport static java.util.Objects.requireNonNullElse;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\n\nimport java.io.Serial;\nimport java.time.Duration;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Internal utilities for executing code with a preemptive timeout.\n *\n * @since 6.0\n */\n@API(status = INTERNAL, since = \"6.0\")\npublic class PreemptiveTimeoutUtils {\n\n\t/**\n\t * <em>Assert</em> that execution of the supplied {@code supplier}\n\t * completes before the given {@code timeout} is exceeded.\n\t *\n\t * <p>See the {@linkplain Assertions Preemptive Timeouts} section of the\n\t * class-level Javadoc for further details.\n\t *\n\t * <p>If the assertion passes then the {@code supplier}'s result is returned.\n\t *\n\t * <p>In the case the assertion does not pass, the supplied\n\t * {@link TimeoutFailureFactory} is invoked to create an exception which is\n\t * then thrown.\n\t *\n\t * <p>If necessary, the failure message will be retrieved lazily from the\n\t * supplied {@code messageSupplier}.\n\t */\n\tpublic static <T extends @Nullable Object, E extends Throwable> T executeWithPreemptiveTimeout(Duration timeout,\n\t\t\tThrowingSupplier<T> supplier, @Nullable Supplier<@Nullable String> messageSupplier,\n\t\t\tTimeoutFailureFactory<E> failureFactory) throws E {\n\n\t\tAtomicReference<Thread> threadReference = new AtomicReference<>();\n\t\tExecutorService executorService = Executors.newSingleThreadExecutor(new TimeoutThreadFactory());\n\n\t\ttry {\n\t\t\tFuture<T> future = submitTask(supplier, threadReference, executorService);\n\t\t\treturn resolveFutureAndHandleException(future, timeout, messageSupplier, threadReference::get,\n\t\t\t\tfailureFactory);\n\t\t}\n\t\tfinally {\n\t\t\texecutorService.shutdownNow();\n\t\t}\n\t}\n\n\tprivate static <T extends @Nullable Object> Future<T> submitTask(ThrowingSupplier<T> supplier,\n\t\t\tAtomicReference<Thread> threadReference, ExecutorService executorService) {\n\t\treturn executorService.submit(() -> {\n\t\t\ttry {\n\t\t\t\tthreadReference.set(Thread.currentThread());\n\t\t\t\treturn supplier.get();\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tthrow throwAsUncheckedException(throwable);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static <T extends @Nullable Object, E extends Throwable> T resolveFutureAndHandleException(Future<T> future,\n\t\t\tDuration timeout, @Nullable Supplier<@Nullable String> messageSupplier,\n\t\t\tSupplier<@Nullable Thread> threadSupplier, TimeoutFailureFactory<E> failureFactory)\n\t\t\tthrows E, RuntimeException {\n\t\ttry {\n\t\t\treturn future.get(timeout.toMillis(), TimeUnit.MILLISECONDS);\n\t\t}\n\t\tcatch (TimeoutException ex) {\n\t\t\tThread thread = threadSupplier.get();\n\t\t\tExecutionTimeoutException cause = null;\n\t\t\tif (thread != null) {\n\t\t\t\tcause = new ExecutionTimeoutException(\"Execution timed out in thread \" + thread.getName());\n\t\t\t\tcause.setStackTrace(thread.getStackTrace());\n\t\t\t}\n\t\t\tthrow failureFactory.createTimeoutFailure(timeout, messageSupplier, cause, thread);\n\t\t}\n\t\tcatch (ExecutionException ex) {\n\t\t\tthrow throwAsUncheckedException(requireNonNullElse(ex.getCause(), ex));\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tthrow throwAsUncheckedException(ex);\n\t\t}\n\t}\n\n\tprivate PreemptiveTimeoutUtils() {\n\t}\n\n\t/**\n\t * Factory for timeout failures.\n\t *\n\t * @param <T> The type of error or exception created\n\t * @since 5.9.1\n\t * @see PreemptiveTimeoutUtils#executeWithPreemptiveTimeout(Duration, ThrowingSupplier, Supplier, TimeoutFailureFactory)\n\t */\n\tpublic interface TimeoutFailureFactory<T extends Throwable> {\n\n\t\t/**\n\t\t * Create a failure for the given timeout, message, and cause.\n\t\t *\n\t\t * @return timeout failure; never {@code null}\n\t\t */\n\t\tT createTimeoutFailure(Duration timeout, @Nullable Supplier<@Nullable String> messageSupplier,\n\t\t\t\t@Nullable Throwable cause, @Nullable Thread testThread);\n\t}\n\n\tprivate static class ExecutionTimeoutException extends JUnitException {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tExecutionTimeoutException(String message) {\n\t\t\tsuper(message);\n\t\t}\n\n\t}\n\n\t/**\n\t * The thread factory used for preemptive timeout.\n\t *\n\t * <p>The factory creates threads with meaningful names, helpful for debugging\n\t * purposes.\n\t */\n\tprivate static class TimeoutThreadFactory implements ThreadFactory {\n\n\t\tprivate static final AtomicInteger threadNumber = new AtomicInteger(1);\n\n\t\t@Override\n\t\tpublic Thread newThread(Runnable r) {\n\t\t\treturn new Thread(r, \"junit-timeout-thread-\" + threadNumber.getAndIncrement());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/timeout/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal JUnit Jupiter timeout utilities.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n */\n\n@NullMarked\npackage org.junit.jupiter.api.timeout;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/ClearSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @ClearSystemProperty} is an annotation that is used to clear the value\n * of a JVM system property for test execution.\n *\n * <p>The key of the system property must be specified via the {@link #key() key}\n * attribute.\n *\n * <p>{@code @ClearSystemProperty} can be used on the method and on the class level.\n * It is {@linkplain Repeatable repeatable} and {@link Inherited @Inherited} from\n * higher-level containers. If a class is annotated, the configured property will\n * be cleared before every test inside that class.  After a test has completed,\n * the original value of the system property will be restored.\n *\n * <p>During <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">\n * parallel test execution</a>, all tests annotated with\n * {@link SetSystemProperty @SetSystemProperty},\n * {@link ClearSystemProperty @ClearSystemProperty},\n * {@link ReadsSystemProperty @ReadsSystemProperty}, and\n * {@link WritesSystemProperty @WritesSystemProperty} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For further details and examples, see the documentation on all JVM system\n * property annotations in the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#system-properties\">\n * User Guide</a>.\n *\n * @since 6.1\n * @see SetSystemProperty @SetSystemProperty\n * @see RestoreSystemProperties @RestoreSystemProperties\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\n@Repeatable(ClearSystemProperty.ClearSystemProperties.class)\n@WritesSystemProperty\n@ExtendWith(SystemPropertiesExtension.class)\n@API(status = EXPERIMENTAL, since = \"6.1\")\n@SuppressWarnings(\"exports\")\npublic @interface ClearSystemProperty {\n\n\t/**\n\t * The key of the system property to clear.\n\t */\n\tString key();\n\n\t/**\n\t * {@code @ClearSystemProperties} is a container for one or more\n\t * {@code ClearSystemProperty} declarations.\n\t */\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Inherited\n\t@WritesSystemProperty\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\t@interface ClearSystemProperties {\n\n\t\t/**\n\t\t * An array of one or more {@link ClearSystemProperty @ClearSystemProperty}\n\t\t * declarations.\n\t\t */\n\t\tClearSystemProperty[] value();\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultLocale.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DefaultLocale} is a JUnit Jupiter extension for changing the value\n * returned by {@link Locale#getDefault()} for a test execution.\n *\n * <p>The {@link Locale} to set as the default locale can be\n * configured in several ways:\n *\n * <ul>\n *     <li>using a {@link Locale#forLanguageTag(String) language tag}</li>\n *     <li>using a {@link Locale.Builder Locale.Builder} together with\n *         <ul>\n *            <li>a language</li>\n *            <li>a language and a country</li>\n *            <li>a language, a country, and a variant</li>\n *         </ul>\n *     </li>\n * </ul>\n *\n * <p>Please keep in mind that the {@code Locale.Builder} does a syntax check,\n * if you use a variant. The given string must match the BCP 47 (or more\n * detailed <a href=\"https://www.rfc-editor.org/rfc/rfc5646.html\">RFC 5646</a>) syntax.\n *\n * <p>If a language tag is set, none of the other fields must be set. Otherwise, an\n * {@link org.junit.jupiter.api.extension.ExtensionConfigurationException} will\n * be thrown. Specifying a {@link #country()} but no {@link #language()}, or a\n * {@link #variant()} but no {@link #country()} and {@link #language()} will\n * also cause an {@code ExtensionConfigurationException}. After the annotated\n * element has been executed, the default {@code Locale} will be restored to\n * its original value.\n *\n * <p>{@code @DefaultLocale} can be used on the method and on the class level. It\n * is inherited from higher-level containers, but can only be used once per method\n * or class. If a class is annotated, the configured {@code Locale} will be the\n * default {@code Locale} for all tests inside that class. Any method level\n * configurations will override the class level default {@code Locale}.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultLocale @DefaultLocale},\n * {@link ReadsDefaultLocale @ReadsDefaultLocale}, and\n * {@link WritesDefaultLocale @WritesDefaultLocale} are scheduled in a way that guarantees\n * correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultLocale\">User Guide</a>.\n *\n * @since 6.1\n * @see Locale#getDefault()\n * @see ReadsDefaultLocale\n * @see WritesDefaultLocale\n * @see DefaultTimeZone\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\n@WritesDefaultLocale\n@API(status = STABLE, since = \"6.1\")\n@ExtendWith(DefaultLocaleExtension.class)\n@SuppressWarnings(\"exports\")\npublic @interface DefaultLocale {\n\n\t/**\n\t * A language tag string as specified by IETF BCP 47. See\n\t * {@link Locale#forLanguageTag(String)} for more information about valid\n\t * language tag values.\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * An ISO 639 alpha-2 or alpha-3 language code, or a language subtag up to\n\t * 8 characters in length. See the {@link Locale} class\n\t * description about valid language values.\n\t */\n\tString language() default \"\";\n\n\t/**\n\t * An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. See\n\t * the {@link Locale} class description about valid country\n\t * values.\n\t */\n\tString country() default \"\";\n\n\t/**\n\t * An IETF BCP 47 language string that matches the\n\t * <a href=\"https://www.rfc-editor.org/rfc/rfc5646.html\">RFC 5646</a>\n\t * syntax. It's validated by the {@code Locale.Builder}, using\n\t * {@code sun.util.locale.LanguageTag#isVariant}.\n\t */\n\tString variant() default \"\";\n\n\t/**\n\t * A class implementing {@link LocaleProvider} to be used for custom\n\t * {@code Locale} resolution. This is mutually exclusive with other\n\t * properties, if any other property is given a value it will result in an\n\t * {@link org.junit.jupiter.api.extension.ExtensionConfigurationException}.\n\t */\n\tClass<? extends LocaleProvider> localeProvider() default NullLocaleProvider.class;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultLocaleExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport java.util.Locale;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * @since 6.1\n */\nfinal class DefaultLocaleExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {\n\n\tprivate static final Namespace NAMESPACE = Namespace.create(DefaultLocaleExtension.class);\n\n\tprivate static final String CUSTOM_KEY = \"CustomLocale\";\n\tprivate static final String DEFAULT_KEY = \"DefaultLocale\";\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\tcreateLocaleFromAnnotation(context) //\n\t\t\t\t.ifPresent(locale -> store(context, CUSTOM_KEY, locale));\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tcreateLocaleFromAnnotation(context) //\n\t\t\t\t.or(() -> load(context, CUSTOM_KEY)) //\n\t\t\t\t.ifPresent(locale -> setDefaultLocale(context, locale));\n\t}\n\n\tprivate void setDefaultLocale(ExtensionContext context, Locale customLocale) {\n\t\tstore(context, DEFAULT_KEY, Locale.getDefault());\n\t\tLocale.setDefault(customLocale);\n\t}\n\n\tprivate static Optional<Locale> createLocaleFromAnnotation(ExtensionContext context) {\n\t\treturn AnnotationSupport.findAnnotation(context.getElement(), DefaultLocale.class) //\n\t\t\t\t.map(DefaultLocaleExtension::createLocale);\n\t}\n\n\tprivate static Locale createLocale(DefaultLocale annotation) {\n\t\tif (!annotation.value().isEmpty()) {\n\t\t\treturn createFromLanguageTag(annotation);\n\t\t}\n\t\telse if (!annotation.language().isEmpty()) {\n\t\t\treturn createFromParts(annotation);\n\t\t}\n\t\telse {\n\t\t\treturn getFromProvider(annotation);\n\t\t}\n\t}\n\n\tprivate static Locale createFromLanguageTag(DefaultLocale annotation) {\n\t\tif (!annotation.language().isEmpty() || !annotation.country().isEmpty() || !annotation.variant().isEmpty()\n\t\t\t\t|| annotation.localeProvider() != NullLocaleProvider.class) {\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"@DefaultLocale can only be used with language tag if language, country, variant and provider are not set\");\n\t\t}\n\t\treturn Locale.forLanguageTag(annotation.value());\n\t}\n\n\tprivate static Locale createFromParts(DefaultLocale annotation) {\n\t\tif (annotation.localeProvider() != NullLocaleProvider.class)\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"@DefaultLocale can only be used with language tag if provider is not set\");\n\t\tString language = annotation.language();\n\t\tString country = annotation.country();\n\t\tString variant = annotation.variant();\n\t\tif (!language.isEmpty() && !country.isEmpty() && !variant.isEmpty()) {\n\t\t\treturn JupiterLocaleUtils.createLocale(language, country, variant);\n\t\t}\n\t\telse if (!language.isEmpty() && !country.isEmpty()) {\n\t\t\treturn JupiterLocaleUtils.createLocale(language, country);\n\t\t}\n\t\telse if (!language.isEmpty() && variant.isEmpty()) {\n\t\t\treturn JupiterLocaleUtils.createLocale(language);\n\t\t}\n\t\telse {\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"@DefaultLocale not configured correctly. When not using a language tag, specify either\"\n\t\t\t\t\t\t+ \" language, or language and country, or language and country and variant.\");\n\t\t}\n\t}\n\n\tprivate static Locale getFromProvider(DefaultLocale annotation) {\n\t\tif (!annotation.country().isEmpty() || !annotation.variant().isEmpty())\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"@DefaultLocale can only be used with a provider if value, language, country and variant are not set.\");\n\t\tvar providerClass = annotation.localeProvider();\n\t\tLocaleProvider provider;\n\t\ttry {\n\t\t\tprovider = ReflectionSupport.newInstance(providerClass);\n\t\t}\n\t\tcatch (Exception exception) {\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"LocaleProvider instance could not be constructed because of an exception\", exception);\n\t\t}\n\t\treturn invoke(provider);\n\t}\n\n\t@SuppressWarnings(\"ConstantValue\")\n\tprivate static Locale invoke(LocaleProvider provider) {\n\t\tvar locale = provider.get();\n\t\tif (locale == null) {\n\t\t\tthrow new ExtensionConfigurationException(\"LocaleProvider instance returned with null\");\n\t\t}\n\t\treturn locale;\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\tload(context, DEFAULT_KEY).ifPresent(Locale::setDefault);\n\t}\n\n\tprivate static void store(ExtensionContext context, String key, Locale value) {\n\t\tgetStore(context).put(key, value);\n\t}\n\n\tprivate static Optional<Locale> load(ExtensionContext context, String key) {\n\t\treturn Optional.ofNullable(getStore(context).get(key, Locale.class));\n\t}\n\n\tprivate static ExtensionContext.Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(NAMESPACE);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultTimeZone.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.TimeZone;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @DefaultTimeZone} is a JUnit Jupiter extension for changing the value\n * returned by {@link TimeZone#getDefault()} for a test execution.\n *\n * <p>The {@link TimeZone} to set as the default {@code TimeZone} is configured\n * by specifying the {@code TimeZone} ID as defined by\n * {@link TimeZone#getTimeZone(String)}. After the annotated element has been\n * executed, the default {@code TimeZone} will be restored to its original\n * value.\n *\n * <p>{@code @DefaultTimeZone} can be used on the method and on the class\n * level. It is inherited from higher-level containers, but can only be used\n * once per method or class. If a class is annotated, the configured\n * {@code TimeZone} will be the default {@code TimeZone} for all tests inside\n * that class. Any method level configurations will override the class level\n * default {@code TimeZone}.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultTimeZone @DefaultTimeZone},\n * {@link ReadsDefaultTimeZone @ReadsDefaultTimeZone}, and\n * {@link WritesDefaultTimeZone @WritesDefaultTimeZone} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultTimeZone\">User Guide</a>.\n *\n * @since 6.1\n * @see TimeZone#getDefault()\n * @see ReadsDefaultTimeZone\n * @see WritesDefaultTimeZone\n * @see DefaultLocale\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\n@WritesDefaultTimeZone\n@API(status = STABLE, since = \"6.1\")\n@ExtendWith(DefaultTimeZoneExtension.class)\n@SuppressWarnings(\"exports\")\npublic @interface DefaultTimeZone {\n\n\t/**\n\t * The ID for a {@code TimeZone}, either an abbreviation such as \"PST\", a\n\t * full name such as \"America/Los_Angeles\", or a custom ID such as\n\t * \"GMT-8:00\". Note that the support of abbreviations is for JDK 1.1.x\n\t * compatibility only and full names should be used.\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * A class implementing {@link TimeZoneProvider} to be used for custom {@code TimeZone} resolution.\n\t * This is mutually exclusive with other properties, if any other property is given a value it\n\t * will result in an {@link org.junit.jupiter.api.extension.ExtensionConfigurationException}.\n\t */\n\tClass<? extends TimeZoneProvider> timeZoneProvider() default NullTimeZoneProvider.class;\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/DefaultTimeZoneExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport java.util.Optional;\nimport java.util.TimeZone;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * @since 6.1\n */\nfinal class DefaultTimeZoneExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback {\n\n\tprivate static final Namespace NAMESPACE = Namespace.create(DefaultTimeZoneExtension.class);\n\n\tprivate static final String CUSTOM_KEY = \"CustomTimeZone\";\n\tprivate static final String DEFAULT_KEY = \"DefaultTimeZone\";\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\tcreateTimeZoneFromAnnotation(context) //\n\t\t\t\t.ifPresent(timeZone -> store(context, CUSTOM_KEY, timeZone));\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tcreateTimeZoneFromAnnotation(context) //\n\t\t\t\t.or(() -> load(context, CUSTOM_KEY)) //\n\t\t\t\t.ifPresent(timeZone -> setDefaultTimeZone(context, timeZone));\n\t}\n\n\tprivate void setDefaultTimeZone(ExtensionContext context, TimeZone customTimeZone) {\n\t\tstore(context, DEFAULT_KEY, TimeZone.getDefault());\n\t\tTimeZone.setDefault(customTimeZone);\n\t}\n\n\tprivate static Optional<TimeZone> createTimeZoneFromAnnotation(ExtensionContext context) {\n\t\treturn AnnotationSupport.findAnnotation(context.getElement(), DefaultTimeZone.class) //\n\t\t\t\t.map(DefaultTimeZoneExtension::createTimeZone);\n\t}\n\n\tprivate static TimeZone createTimeZone(DefaultTimeZone annotation) {\n\t\tvalidateCorrectConfiguration(annotation);\n\n\t\tif (!annotation.value().isEmpty()) {\n\t\t\treturn createTimeZoneFromZoneId(annotation.value());\n\t\t}\n\t\telse {\n\t\t\treturn createTimeZoneFromProvider(annotation.timeZoneProvider());\n\t\t}\n\t}\n\n\tprivate static void validateCorrectConfiguration(DefaultTimeZone annotation) {\n\t\tboolean noValue = annotation.value().isEmpty();\n\t\tboolean noProvider = annotation.timeZoneProvider() == NullTimeZoneProvider.class;\n\t\tif (noValue == noProvider) {\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"Either a valid time zone id or a TimeZoneProvider must be provided to \"\n\t\t\t\t\t\t+ DefaultTimeZone.class.getSimpleName());\n\t\t}\n\t}\n\n\tprivate static TimeZone createTimeZoneFromZoneId(String timeZoneId) {\n\t\tTimeZone configuredTimeZone = TimeZone.getTimeZone(timeZoneId);\n\t\t// TimeZone::getTimeZone returns with GMT as fallback if the given ID cannot be understood\n\t\tif (configuredTimeZone.equals(TimeZone.getTimeZone(\"GMT\")) && !\"GMT\".equals(timeZoneId)) {\n\t\t\tthrow new ExtensionConfigurationException(\"\"\"\n\t\t\t\t\t@DefaultTimeZone not configured correctly.\n\t\t\t\t\tCould not find the specified time zone + '%s'.\n\t\t\t\t\tPlease use correct identifiers, e.g. \"GMT\" for Greenwich Mean Time.\n\t\t\t\t\t\"\"\".formatted(timeZoneId));\n\t\t}\n\t\treturn configuredTimeZone;\n\t}\n\n\tprivate static TimeZone createTimeZoneFromProvider(Class<? extends TimeZoneProvider> providerClass) {\n\t\ttry {\n\t\t\tTimeZoneProvider provider = ReflectionSupport.newInstance(providerClass);\n\t\t\treturn Optional.ofNullable(provider.get()).orElse(TimeZone.getTimeZone(\"GMT\"));\n\t\t}\n\t\tcatch (Exception exception) {\n\t\t\tthrow new ExtensionConfigurationException(\"Could not instantiate TimeZoneProvider because of exception\",\n\t\t\t\texception);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\tload(context, DEFAULT_KEY).ifPresent(TimeZone::setDefault);\n\t}\n\n\tprivate static void store(ExtensionContext context, String key, TimeZone value) {\n\t\tgetStore(context).put(key, value);\n\t}\n\n\tprivate static Optional<TimeZone> load(ExtensionContext context, String key) {\n\t\treturn Optional.ofNullable(getStore(context).get(key, TimeZone.class));\n\t}\n\n\tprivate static ExtensionContext.Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(NAMESPACE);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/JupiterLocaleUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport java.util.Locale;\n\n/**\n * Utility class to create {@code Locale}.\n *\n * @since 6.1\n */\nfinal class JupiterLocaleUtils {\n\n\tprivate JupiterLocaleUtils() {\n\t\t// private constructor to prevent instantiation of utility class\n\t}\n\n\tpublic static Locale createLocale(String language, String country, String variant) {\n\t\treturn new Locale.Builder().setLanguage(language).setRegion(country).setVariant(variant).build();\n\t}\n\n\tpublic static Locale createLocale(String language, String country) {\n\t\treturn new Locale.Builder().setLanguage(language).setRegion(country).build();\n\t}\n\n\tpublic static Locale createLocale(String language) {\n\t\treturn new Locale.Builder().setLanguage(language).build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/JupiterPropertyUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\nfinal class JupiterPropertyUtils {\n\n\tprivate JupiterPropertyUtils() {\n\t\t/* no-op */\n\t}\n\n\tstatic Properties cloneWithoutDefaults(ExtensionContext context, Properties properties) {\n\t\t// Custom implementations have to implement clone correctly.\n\t\tif (properties.getClass() == Properties.class) {\n\t\t\tthrowIfHasObservableDefaults(context, properties);\n\t\t}\n\t\treturn (Properties) properties.clone();\n\t}\n\n\tprivate static void throwIfHasObservableDefaults(ExtensionContext context, Properties properties) {\n\t\tSet<Object> keySet = properties.keySet();\n\t\t// A best effort check.\n\t\tList<String> defaultPropertyNames = properties.stringPropertyNames().stream() //\n\t\t\t\t.filter(propertyName -> !keySet.contains(propertyName)) //\n\t\t\t\t.toList();\n\t\tif (!defaultPropertyNames.isEmpty()) {\n\t\t\tthrow new ExtensionConfigurationException(\"\"\"\n\t\t\t\t\tSystemPropertiesExtension was configured to restore the system properties by [%s]. \\\n\t\t\t\t\tHowever, it was not possible to create an accurate snapshot of the system properties \\\n\t\t\t\t\tusing Properties::clone, because default properties were present: %s\"\"\" //\n\t\t\t\t\t.formatted(context.getElement().orElseThrow(), defaultPropertyNames));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/LocaleProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Locale;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\n\n/**\n * Custom {@link Locale} provider for use with\n * {@link DefaultLocale#localeProvider()}.\n *\n * @since 6.1\n */\n@API(status = STABLE, since = \"6.1\")\npublic interface LocaleProvider extends Supplier<Locale> {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/NullLocaleProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\n/**\n * @since 6.1\n */\ninterface NullLocaleProvider extends LocaleProvider {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/NullTimeZoneProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\n/**\n * @since 6.1\n */\ninterface NullTimeZoneProvider extends TimeZoneProvider {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/ReadsDefaultLocale.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * Marks tests that <em>read</em> the default locale but don't use the\n * {@link DefaultLocale @DefaultLocale} extension themselves.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultLocale @DefaultLocale},\n * {@link ReadsDefaultLocale @ReadsDefaultLocale}, and\n * {@link WritesDefaultLocale} are scheduled in a way that guarantees\n * correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultLocale\">User Guide</a>.\n *\n * @since 6.1\n * @see DefaultLocale\n * @see WritesDefaultLocale\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ)\n@API(status = STABLE, since = \"6.1\")\npublic @interface ReadsDefaultLocale {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/ReadsDefaultTimeZone.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * Marks tests that <em>read</em> the default time zone but don't use the\n * {@link DefaultTimeZone @DefaultTimeZone} extension themselves.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultTimeZone @DefaultTimeZone},\n * {@link ReadsDefaultTimeZone @ReadsDefaultTimeZone}, and\n * {@link WritesDefaultTimeZone @WritesDefaultTimeZone} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultTimeZone\">User Guide</a>.\n *\n * @since 6.1\n * @see DefaultTimeZone\n * @see WritesDefaultTimeZone\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.TIME_ZONE, mode = ResourceAccessMode.READ)\n@API(status = STABLE, since = \"6.1\")\npublic @interface ReadsDefaultTimeZone {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/ReadsSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * {@code @ReadsSystemProperty} marks tests that read system properties but do\n * not use the JVM system properties extensions themselves.\n *\n * <p>During <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">\n * parallel test execution</a>, all tests annotated with\n * {@link SetSystemProperty @SetSystemProperty},\n * {@link ClearSystemProperty @ClearSystemProperty},\n * {@link ReadsSystemProperty @ReadsSystemProperty}, and\n * {@link WritesSystemProperty @WritesSystemProperty} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For further details and examples, see the documentation on all JVM system\n * property annotations in the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#system-properties\">\n * User Guide</a>.\n *\n * @since 6.1\n * @see WritesSystemProperty @WritesSystemProperty\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ)\n@API(status = EXPERIMENTAL, since = \"6.1\")\npublic @interface ReadsSystemProperty {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/RestoreSystemProperties.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Properties;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @RestoreSystemProperties} is an annotation that is used to restore the\n * entire set of JVM system properties to its original state, or the state of the\n * higher-level container, after execution of the annotated element has completed.\n *\n * <p>Use this annotation when there is a need to programmatically modify system\n * properties in a test method or in {@code @BeforeAll} / {@code @BeforeEach}\n * lifecycle methods. To set or clear a system property, consider\n * {@link SetSystemProperty @SetSystemProperty} or\n * {@link ClearSystemProperty @ClearSystemProperty} instead.\n *\n * <p>{@code @RestoreSystemProperties} can be used on the method and on the class\n * level.\n *\n * <p>When declared on a test method, a snapshot of all JVM system properties is\n * stored prior to that test. The snapshot is created before any {@code @BeforeEach}\n * lifecycle methods in scope and before any {@link SetSystemProperty @SetSystemProperty}\n * or {@link ClearSystemProperty @ClearSystemProperty} annotations on that method.\n * After the test, system properties are restored from the snapshot after all\n * {@code @AfterEach} lifecycle methods have completed.\n *\n * <p>When placed on a test class, a snapshot of all JVM system properties is stored\n * prior to any {@code @BeforeAll} lifecycle methods in scope and before any\n * {@link SetSystemProperty @SetSystemProperty} or\n * {@link ClearSystemProperty @ClearSystemProperty} annotations on that class.\n * After the test class completes, system properties are restored from the snapshot\n * after any {@code @AfterAll} lifecycle methods have completed. In addition, a\n * class-level annotation is inherited by each test method just as if each one were\n * annotated with {@code RestoreSystemProperties}.\n *\n * <p>During <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">\n * parallel test execution</a>, all tests annotated with\n * {@link SetSystemProperty @SetSystemProperty},\n * {@link ClearSystemProperty @ClearSystemProperty},\n * {@link ReadsSystemProperty @ReadsSystemProperty}, and\n * {@link WritesSystemProperty @WritesSystemProperty} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For further details and examples, see the documentation on all JVM system\n * property annotations in the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#system-properties\">\n * User Guide</a>.\n *\n * <p><em>Note:</em> The snapshot of the properties object is created using\n * {@link Properties#clone()}. However, this cloned properties object will not\n * include any default values from the original properties object. Consequently,\n * this extension will make a best effort attempt to detect default values and\n * fail if any are detected. For classes that extend {@code Properties}, it is\n * assumed that {@code clone()} is implemented with sufficient fidelity.\n *\n * @since 6.1\n * @see ClearSystemProperty @ClearSystemProperty\n * @see SetSystemProperty @SetSystemProperty\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\n@WritesSystemProperty\n@ExtendWith(SystemPropertiesExtension.class)\n@API(status = EXPERIMENTAL, since = \"6.1\")\n@SuppressWarnings(\"exports\")\npublic @interface RestoreSystemProperties {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/SetSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * {@code @SetSystemProperty} is an annotation that is used to set the value of\n * a JVM system property for test execution.\n *\n * <p>The key and value of the system property must be specified via the\n * {@link #key() key} and {@link #value() value} attributes.\n *\n * <p>This annotation can be used both at the type level and the method level.\n * It is {@linkplain Repeatable repeatable} and {@link Inherited @Inherited} from\n * higher-level containers &mdash; for example, a test method inherits a\n * {@code @SetSystemProperty} declaration from the test class in which the test\n * is declared. If this annotation is declared on a test class (or implemented\n * interface), the configured property will be set before every test inside that\n * class. After a test has completed, the original value of the system property\n * (or the value configured via {@code @SetSystemProperty} at a higher level) will\n * be restored. Note that method-level configuration always overrides class-level\n * configuration.\n *\n * <p>During <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">\n * parallel test execution</a>, all tests annotated with\n * {@link SetSystemProperty @SetSystemProperty},\n * {@link ClearSystemProperty @ClearSystemProperty},\n * {@link ReadsSystemProperty @ReadsSystemProperty}, and\n * {@link WritesSystemProperty @WritesSystemProperty} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For further details and examples, see the documentation on all JVM system\n * property annotations in the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#system-properties\">\n * User Guide</a>.\n *\n * @since 6.1\n * @see ClearSystemProperty @ClearSystemProperty\n * @see RestoreSystemProperties @RestoreSystemProperties\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Inherited\n@Repeatable(SetSystemProperty.SetSystemProperties.class)\n@WritesSystemProperty\n@ExtendWith(SystemPropertiesExtension.class)\n@API(status = EXPERIMENTAL, since = \"6.1\")\n@SuppressWarnings(\"exports\")\npublic @interface SetSystemProperty {\n\n\t/**\n\t * The key of the system property to set.\n\t */\n\tString key();\n\n\t/**\n\t * The value of the system property to set.\n\t */\n\tString value();\n\n\t/**\n\t * {@code @SetSystemProperties} is a container for one or more\n\t * {@code SetSystemProperty} declarations.\n\t */\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Inherited\n\t@WritesSystemProperty\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\t@interface SetSystemProperties {\n\n\t\t/**\n\t\t * An array of one or more {@link SetSystemProperty @SetSystemProperty}\n\t\t * declarations.\n\t\t */\n\t\tSetSystemProperty[] value();\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/SystemPropertiesExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * {@code Extension} which provides support for the following annotations.\n *\n * <ul>\n * <li>{@link SetSystemProperty @SetSystemProperty}</li>\n * <li>{@link ClearSystemProperty @ClearSystemProperty}</li>\n * <li>{@link RestoreSystemProperties @RestoreSystemProperties}</li>\n * </ul>\n *\n * @since 6.1\n */\nfinal class SystemPropertiesExtension\n\t\timplements BeforeEachCallback, AfterEachCallback, BeforeAllCallback, AfterAllCallback {\n\n\t/**\n\t * Prepare for entering a context that must be restorable.\n\t *\n\t * <p>The context is prepared by pre-emptively swapping out the current\n\t * System properties with a snapshot. The original system properties are\n\t * restored after the test.\n\t *\n\t * @return the original {@link System#getProperties} object\n\t */\n\tProperties prepareToEnterRestorableContext(ExtensionContext context) {\n\t\tvar current = System.getProperties();\n\t\tvar clone = JupiterPropertyUtils.cloneWithoutDefaults(context, current);\n\t\tSystem.setProperties(clone);\n\t\treturn current;\n\t}\n\n\t/**\n\t * Prepare to exit a restorable context.\n\t *\n\t * <p>The entry environment will be restored to the state passed in as {@code Properties}.\n\t *\n\t * @param properties a non-null {@code Properties} that contains all entries\n\t * of the entry environment\n\t */\n\tvoid prepareToExitRestorableContext(Properties properties) {\n\t\tSystem.setProperties(properties);\n\t}\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\tapplyForAllContexts(context);\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tapplyForAllContexts(context);\n\t}\n\n\tprivate void applyForAllContexts(ExtensionContext originalContext) {\n\t\tvar allContexts = findAllExtensionContexts(originalContext);\n\n\t\tvar restoreAnnotationContext = findFirstRestoreAnnotationContext(allContexts);\n\t\trestoreAnnotationContext.ifPresent(annotatedElementContext -> {\n\t\t\tvar properties = this.prepareToEnterRestorableContext(annotatedElementContext);\n\t\t\tstoreCompleteBackup(originalContext, properties);\n\t\t});\n\n\t\t// we have to apply the annotations from the outermost to the innermost context.\n\t\tforEachInReverseOrder(allContexts,\n\t\t\tcurrentContext -> clearAndSetEntries(currentContext, originalContext, restoreAnnotationContext.isEmpty()));\n\t}\n\n\tprivate Optional<ExtensionContext> findFirstRestoreAnnotationContext(List<ExtensionContext> contexts) {\n\t\treturn contexts.stream() //\n\t\t\t\t.filter(context -> isAnnotated(context.getElement(), RestoreSystemProperties.class)) //\n\t\t\t\t.findFirst();\n\t}\n\n\tprivate void clearAndSetEntries(ExtensionContext currentContext, ExtensionContext originalContext,\n\t\t\tboolean doIncrementalBackup) {\n\n\t\tcurrentContext.getElement().ifPresent(element -> {\n\t\t\tvar entriesToClear = findEntriesToClear(element);\n\t\t\tvar entriesToSet = findEntriesToSet(element);\n\t\t\tpreventClearAndSetSameEntries(element, entriesToClear, entriesToSet.keySet());\n\n\t\t\tif (entriesToClear.isEmpty() && entriesToSet.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Only backup original values if we didn't already do bulk storage of the original state\n\t\t\tif (doIncrementalBackup) {\n\t\t\t\tstoreIncrementalBackup(originalContext, entriesToClear, entriesToSet.keySet());\n\t\t\t}\n\n\t\t\t// For consistency don't use Properties::setProperty or System.setProperty here\n\t\t\tvar properties = System.getProperties();\n\t\t\tentriesToClear.forEach(properties::remove);\n\t\t\tproperties.putAll(entriesToSet);\n\t\t});\n\t}\n\n\tprivate Set<String> findEntriesToClear(AnnotatedElement element) {\n\t\treturn findRepeatableAnnotations(element, ClearSystemProperty.class).stream() //\n\t\t\t\t.map(ClearSystemProperty::key) //\n\t\t\t\t// already distinct due to findRepeatableAnnotations\n\t\t\t\t.collect(toSet());\n\t}\n\n\tprivate Map<String, String> findEntriesToSet(AnnotatedElement element) {\n\t\tvar entries = new HashMap<String, String>();\n\t\tvar duplicatePropertyNames = new HashSet<String>();\n\n\t\tfindRepeatableAnnotations(element, SetSystemProperty.class) //\n\t\t\t\t.forEach(annotation -> {\n\t\t\t\t\tvar key = annotation.key();\n\t\t\t\t\tif (entries.put(key, annotation.value()) != null) {\n\t\t\t\t\t\tduplicatePropertyNames.add(key);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\trequireUniqueEntries(element, duplicatePropertyNames);\n\t\treturn entries;\n\t}\n\n\tprivate void preventClearAndSetSameEntries(AnnotatedElement element, Set<String> entriesToClear,\n\t\t\tSet<String> entriesToSet) {\n\t\trequireUniqueEntries(element, //\n\t\t\tentriesToClear.stream() //\n\t\t\t\t\t.filter(entriesToSet::contains) //\n\t\t\t\t\t.collect(toSet()));\n\t}\n\n\tprivate static void requireUniqueEntries(AnnotatedElement annotatedElement, Set<String> duplicatePropertNames) {\n\t\tif (duplicatePropertNames.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tthrow new ExtensionConfigurationException(\n\t\t\t\"SystemPropertyExtension was configured to set/clear %s [%s] more than once by [%s].\" //\n\t\t\t\t\t.formatted( //\n\t\t\t\t\t\tduplicatePropertNames.size() == 1 ? \"property\" : \"properties\", //\n\t\t\t\t\t\tString.join(\", \", duplicatePropertNames), //\n\t\t\t\t\t\tannotatedElement //\n\t\t\t\t\t));\n\t}\n\n\tprivate void storeIncrementalBackup(ExtensionContext context, Collection<String> entriesToClear,\n\t\t\tCollection<String> entriesToSet) {\n\t\tvar backup = new EntriesBackup(entriesToClear, entriesToSet);\n\t\tgetStore(context).put(getStoreKey(context, BackupType.INCREMENTAL), backup);\n\t}\n\n\tprivate void storeCompleteBackup(ExtensionContext context, Properties backup) {\n\t\tgetStore(context).put(getStoreKey(context, BackupType.COMPLETE), backup);\n\t}\n\n\t/**\n\t * Restore the complete original state of the entries as they were prior to\n\t * this {@code ExtensionContext}, if the complete state was initially stored\n\t * in a before all/each event.\n\t *\n\t * @param context the {@code ExtensionContext} which may have a bulk backup stored\n\t * @return true if a complete backup exists and was used to restore, false if not\n\t */\n\tprivate boolean restoreOriginalCompleteBackup(ExtensionContext context) {\n\t\tvar backup = getCompleteBackup(context);\n\t\tif (backup != null) {\n\t\t\tprepareToExitRestorableContext(backup);\n\t\t\treturn true;\n\t\t}\n\t\t// No complete backup - false will let the caller know to continue w/ an incremental restore\n\t\treturn false;\n\t}\n\n\tprivate @Nullable Properties getCompleteBackup(ExtensionContext context) {\n\t\tvar key = getStoreKey(context, BackupType.COMPLETE);\n\t\treturn getStore(context).get(key, Properties.class);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\trestoreForAllContexts(context);\n\t}\n\n\t@Override\n\tpublic void afterAll(ExtensionContext context) {\n\t\trestoreForAllContexts(context);\n\t}\n\n\tprivate void restoreForAllContexts(ExtensionContext originalContext) {\n\t\t// Try a complete restore first\n\t\tif (!restoreOriginalCompleteBackup(originalContext)) {\n\t\t\t// A complete backup is not available, so restore incrementally from innermost to outermost\n\t\t\tfindAllExtensionContexts(originalContext).forEach(__ -> restoreOriginalIncrementalBackup(originalContext));\n\t\t}\n\t}\n\n\tprivate void restoreOriginalIncrementalBackup(ExtensionContext originalContext) {\n\t\tvar backup = getIncrementalBackup(originalContext);\n\t\tif (backup != null) {\n\t\t\tbackup.restoreBackup();\n\t\t}\n\t}\n\n\tprivate static List<ExtensionContext> findAllExtensionContexts(ExtensionContext context) {\n\t\tvar contexts = new ArrayList<ExtensionContext>();\n\t\tdo {\n\t\t\tcontexts.add(context);\n\t\t\tcontext = context.getParent().orElse(null);\n\t\t} while (context != null);\n\t\treturn contexts;\n\t}\n\n\tprivate @Nullable EntriesBackup getIncrementalBackup(ExtensionContext originalContext) {\n\t\tvar key = getStoreKey(originalContext, BackupType.INCREMENTAL);\n\t\treturn getStore(originalContext).get(key, EntriesBackup.class);\n\t}\n\n\tprivate ExtensionContext.Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(ExtensionContext.Namespace.create(getClass()));\n\t}\n\n\tprivate StoreKey getStoreKey(ExtensionContext context, BackupType type) {\n\t\treturn new StoreKey(context.getUniqueId(), type);\n\t}\n\n\tprivate record StoreKey(String uniqueId, BackupType type) {\n\t}\n\n\tprivate enum BackupType {\n\t\t/**\n\t\t * Store entry is for an incremental backup object.\n\t\t */\n\t\tINCREMENTAL,\n\t\t/**\n\t\t * Store entry is for a complete backup object.\n\t\t */\n\t\tCOMPLETE\n\t}\n\n\tprivate static final class EntriesBackup {\n\n\t\tprivate final Set<String> entriesToClear = new HashSet<>();\n\t\tprivate final Map<String, Object> entriesToSet = new HashMap<>();\n\n\t\tEntriesBackup(Collection<String> entriesToClear, Collection<String> entriesToSet) {\n\t\t\tvar properties = System.getProperties();\n\t\t\tStream.concat(entriesToClear.stream(), entriesToSet.stream()).forEach(entry -> {\n\t\t\t\t// Do not use Properties::getProperty or System.getProperty here, since\n\t\t\t\t// this would prevent backing up non-string values.\n\t\t\t\tObject backup = properties.get(entry);\n\t\t\t\tif (backup == null) {\n\t\t\t\t\tthis.entriesToClear.add(entry);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.entriesToSet.put(entry, backup);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\tvoid restoreBackup() {\n\t\t\t// We can't use Properties::setProperty or System.setProperty here, since\n\t\t\t// this would prevent restoring non-string values.\n\t\t\tvar properties = System.getProperties();\n\t\t\tentriesToClear.forEach(properties::remove);\n\t\t\tproperties.putAll(entriesToSet);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/TimeZoneProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.TimeZone;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\n\n/**\n * Custom {@link TimeZone} provider for use with\n * {@link DefaultTimeZone#timeZoneProvider()}.\n *\n * @since 6.1\n */\n@API(status = STABLE, since = \"6.1\")\npublic interface TimeZoneProvider extends Supplier<TimeZone> {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/WritesDefaultLocale.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * Marks tests that <em>write</em> the default locale but don't use the\n * {@link DefaultLocale @DefaultLocale} extension themselves.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultLocale @DefaultLocale},\n * {@link ReadsDefaultLocale @ReadsDefaultLocale}, and\n * {@link WritesDefaultLocale} are scheduled in a way that guarantees\n * correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultLocale\">User Guide</a>.\n *\n * @since 6.1\n * @see DefaultLocale\n * @see ReadsDefaultLocale\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.LOCALE, mode = ResourceAccessMode.READ_WRITE)\n@API(status = STABLE, since = \"6.1\")\npublic @interface WritesDefaultLocale {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/WritesDefaultTimeZone.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * Marks tests that <em>write</em> the default time zone but don't use the\n * {@link DefaultTimeZone @DefaultTimeZone} extension themselves.\n *\n * <p>During\n * <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">parallel test execution</a>,\n * all tests annotated with {@link DefaultTimeZone @DefaultTimeZone},\n * {@link ReadsDefaultTimeZone @ReadsDefaultTimeZone}, and\n * {@link WritesDefaultTimeZone @WritesDefaultTimeZone} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For more details and examples, see the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#DefaultTimeZone\">User Guide</a>.\n *\n * @since 6.1\n * @see DefaultTimeZone\n * @see WritesDefaultTimeZone\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.TIME_ZONE, mode = ResourceAccessMode.READ_WRITE)\n@API(status = STABLE, since = \"6.1\")\npublic @interface WritesDefaultTimeZone {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/WritesSystemProperty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\n\n/**\n * {@code @WritesSystemProperty} marks tests that write system properties but do\n * not use the JVM system properties extensions themselves.\n *\n * <p>During <a href=\"https://docs.junit.org/current/writing-tests/parallel-execution.html\">\n * parallel test execution</a>, all tests annotated with\n * {@link SetSystemProperty @SetSystemProperty},\n * {@link ClearSystemProperty @ClearSystemProperty},\n * {@link ReadsSystemProperty @ReadsSystemProperty}, and\n * {@link WritesSystemProperty @WritesSystemProperty} are scheduled in a way that\n * guarantees correctness under mutation of shared global state.\n *\n * <p>For further details and examples, see the documentation on all JVM system\n * property annotations in the\n * <a href=\"https://docs.junit.org/current/writing-tests/built-in-extensions.html#system-properties\">\n * User Guide</a>.\n *\n * @since 6.1\n * @see ReadsSystemProperty @ReadsSystemProperty\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@Inherited\n@ResourceLock(value = Resources.SYSTEM_PROPERTIES, mode = ResourceAccessMode.READ_WRITE)\n@API(status = EXPERIMENTAL, since = \"6.1\")\npublic @interface WritesSystemProperty {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/main/java/org/junit/jupiter/api/util/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * {@code java.util}-related support in JUnit Jupiter.\n *\n * @see org.junit.jupiter.api.util.DefaultLocale\n * @see org.junit.jupiter.api.util.DefaultTimeZone\n * @see org.junit.jupiter.api.util.SetSystemProperty\n */\n\n@NullMarked\npackage org.junit.jupiter.api.util;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n@file:API(status = STABLE, since = \"5.7\")\n\npackage org.junit.jupiter.api\n\nimport org.apiguardian.api.API\nimport org.apiguardian.api.API.Status.MAINTAINED\nimport org.apiguardian.api.API.Status.STABLE\nimport org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure\nimport org.junit.jupiter.api.function.Executable\nimport org.junit.platform.commons.util.UnrecoverableExceptions.rethrowIfUnrecoverable\nimport java.time.Duration\nimport java.util.stream.Stream\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind.AT_MOST_ONCE\nimport kotlin.contracts.InvocationKind.EXACTLY_ONCE\nimport kotlin.contracts.contract\n\n/**\n * @see Assertions.fail\n */\nfun fail(\n    message: String?,\n    throwable: Throwable? = null\n): Nothing = Assertions.fail<Nothing>(message, throwable)\n\n/**\n * @see Assertions.fail\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\n@JvmName(\"fail_nonNullableLambda\")\nfun fail(message: () -> String): Nothing {\n    contract {\n        callsInPlace(message, EXACTLY_ONCE)\n    }\n\n    return Assertions.fail(message())\n}\n\n/**\n * @see Assertions.fail\n */\nfun fail(message: (() -> String)?): Nothing = Assertions.fail<Nothing>(message ?: { null })\n\n/**\n * @see Assertions.fail\n */\nfun fail(throwable: Throwable?): Nothing = Assertions.fail<Nothing>(throwable)\n\n/**\n * [Stream] of functions to be executed.\n */\nprivate typealias ExecutableStream = Stream<() -> Unit>\n\nprivate fun ExecutableStream.convert() = map { Executable(it) }\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(executables: ExecutableStream) = Assertions.assertAll(executables.convert())\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(\n    heading: String?,\n    executables: ExecutableStream\n) = Assertions.assertAll(heading, executables.convert())\n\n/**\n * [Collection] of functions to be executed.\n */\nprivate typealias ExecutableCollection = Collection<() -> Unit>\n\nprivate fun ExecutableCollection.convert() = map { Executable(it) }\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(executables: ExecutableCollection) = Assertions.assertAll(executables.convert())\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(\n    heading: String?,\n    executables: ExecutableCollection\n) = Assertions.assertAll(heading, executables.convert())\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(vararg executables: () -> Unit) = assertAll(executables.toList().stream())\n\n/**\n * @see Assertions.assertAll\n */\nfun assertAll(\n    heading: String?,\n    vararg executables: () -> Unit\n) = assertAll(heading, executables.toList().stream())\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNull(nullableString)\n *\n * // The compiler won't allow even safe calls, since nullableString is always null.\n * // nullableString?.isNotEmpty()\n * ```\n * @see Assertions.assertNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNull(actual: Any?) {\n    contract {\n        returns() implies (actual == null)\n    }\n\n    Assertions.assertNull(actual)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNull(nullableString, \"Should be nullable\")\n *\n * // The compiler won't allow even safe calls, since nullableString is always null.\n * // nullableString?.isNotEmpty()\n * ```\n * @see Assertions.assertNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNull(\n    actual: Any?,\n    message: String\n) {\n    contract {\n        returns() implies (actual == null)\n    }\n\n    Assertions.assertNull(actual, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNull(nullableString) { \"Should be nullable\" }\n *\n * // The compiler won't allow even safe calls, since nullableString is always null.\n * // nullableString?.isNotEmpty()\n * ```\n * @see Assertions.assertNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNull(\n    actual: Any?,\n    messageSupplier: () -> String\n) {\n    contract {\n        returns() implies (actual == null)\n\n        callsInPlace(messageSupplier, AT_MOST_ONCE)\n    }\n\n    Assertions.assertNull(actual, messageSupplier)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNotNull(nullableString)\n *\n * // The compiler smart casts nullableString to a non-nullable object.\n * assertTrue(nullableString.isNotEmpty())\n * ```\n * @see Assertions.assertNotNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNotNull(actual: Any?) {\n    contract {\n        returns() implies (actual != null)\n    }\n\n    Assertions.assertNotNull(actual)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNotNull(nullableString, \"Should be non-nullable\")\n *\n * // The compiler smart casts nullableString to a non-nullable object.\n * assertTrue(nullableString.isNotEmpty())\n * ```\n * @see Assertions.assertNotNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNotNull(\n    actual: Any?,\n    message: String\n) {\n    contract {\n        returns() implies (actual != null)\n    }\n\n    Assertions.assertNotNull(actual, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val nullableString: String? = ...\n *\n * assertNotNull(nullableString) { \"Should be non-nullable\" }\n *\n * // The compiler smart casts nullableString to a non-nullable object.\n * assertTrue(nullableString.isNotEmpty())\n * ```\n * @see Assertions.assertNotNull\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\nfun assertNotNull(\n    actual: Any?,\n    messageSupplier: () -> String\n) {\n    contract {\n        returns() implies (actual != null)\n\n        callsInPlace(messageSupplier, AT_MOST_ONCE)\n    }\n\n    Assertions.assertNotNull(actual, messageSupplier)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrows<IllegalArgumentException> {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrows\n */\ninline fun <reified T : Throwable> assertThrows(executable: () -> Unit): T {\n    // no contract for `executable` because it is expected to throw an exception instead\n    // of being executed completely (see https://youtrack.jetbrains.com/issue/KT-27748)\n    val throwable: Throwable? =\n        try {\n            executable()\n        } catch (caught: Throwable) {\n            caught\n        } as? Throwable\n\n    return Assertions.assertThrows(T::class.java) {\n        if (throwable != null) {\n            throw throwable\n        }\n    }\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrows<IllegalArgumentException>(\"Should throw an Exception\") {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrows\n */\ninline fun <reified T : Throwable> assertThrows(\n    message: String,\n    executable: () -> Unit\n): T = assertThrows({ message }, executable)\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrows<IllegalArgumentException>({ \"Should throw an Exception\" }) {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrows\n */\n@OptIn(ExperimentalContracts::class)\ninline fun <reified T : Throwable> assertThrows(\n    noinline message: () -> String,\n    executable: () -> Unit\n): T {\n    contract {\n        callsInPlace(message, AT_MOST_ONCE)\n        // no contract for `executable` because it is expected to throw an exception instead\n        // of being executed completely (see https://youtrack.jetbrains.com/issue/KT-27748)\n    }\n\n    val throwable: Throwable? =\n        try {\n            executable()\n        } catch (caught: Throwable) {\n            caught\n        } as? Throwable\n\n    return Assertions.assertThrows(\n        T::class.java,\n        {\n            if (throwable != null) {\n                throw throwable\n            }\n        },\n        message\n    )\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrows<IllegalArgumentException> {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrowsExactly\n */\ninline fun <reified T : Throwable> assertThrowsExactly(executable: () -> Unit): T {\n    // no contract for `executable` because it is expected to throw an exception instead\n    // of being executed completely (see https://youtrack.jetbrains.com/issue/KT-27748)\n    val throwable: Throwable? =\n        try {\n            executable()\n        } catch (caught: Throwable) {\n            caught\n        } as? Throwable\n\n    return Assertions.assertThrowsExactly(T::class.java) {\n        if (throwable != null) {\n            throw throwable\n        }\n    }\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrowsExactly<IllegalArgumentException>(\"Should throw an Exception\") {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrowsExactly\n */\ninline fun <reified T : Throwable> assertThrowsExactly(\n    message: String,\n    executable: () -> Unit\n): T = assertThrowsExactly({ message }, executable)\n\n/**\n * Example usage:\n * ```kotlin\n * val exception = assertThrowsExactly<IllegalArgumentException>({ \"Should throw an Exception\" }) {\n *     throw IllegalArgumentException(\"Talk to a duck\")\n * }\n * assertEquals(\"Talk to a duck\", exception.message)\n * ```\n * @see Assertions.assertThrowsExactly\n */\n@OptIn(ExperimentalContracts::class)\ninline fun <reified T : Throwable> assertThrowsExactly(\n    noinline message: () -> String,\n    executable: () -> Unit\n): T {\n    contract {\n        callsInPlace(message, AT_MOST_ONCE)\n        // no contract for `executable` because it is expected to throw an exception instead\n        // of being executed completely (see https://youtrack.jetbrains.com/issue/KT-27748)\n    }\n\n    val throwable: Throwable? =\n        try {\n            executable()\n        } catch (caught: Throwable) {\n            caught\n        } as? Throwable\n\n    return Assertions.assertThrowsExactly(\n        T::class.java,\n        {\n            if (throwable != null) {\n                throw throwable\n            }\n        },\n        message\n    )\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertDoesNotThrow {\n *     // Code block that is expected to not throw an exception\n * }\n * ```\n * @see Assertions.assertDoesNotThrow\n * @param R the result type of the [executable]\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\ninline fun <R> assertDoesNotThrow(executable: () -> R): R {\n    contract {\n        callsInPlace(executable, EXACTLY_ONCE)\n    }\n\n    try {\n        return executable()\n    } catch (t: Throwable) {\n        rethrowIfUnrecoverable(t)\n        val suffix = t.message?.let { if (it.isNotBlank()) \": ${t.message}\" else null } ?: \"\"\n        throw assertionFailure()\n            .reason(\"Unexpected exception thrown: ${t.javaClass.getName()}$suffix\")\n            .cause(t)\n            .trimStacktrace(AssertionFailureBuilder::class.java)\n            // we don't want to retain any stacktrace elements from the AssertionFailureBuilder\n            .retainStackTraceElements(0)\n            .build()\n    }\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertDoesNotThrow(\"Should not throw an exception\") {\n *     // Code block that is expected to not throw an exception\n * }\n * ```\n * @see Assertions.assertDoesNotThrow\n * @param R the result type of the [executable]\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\ninline fun <R> assertDoesNotThrow(\n    message: String,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(executable, EXACTLY_ONCE)\n    }\n\n    return assertDoesNotThrow({ message }, executable)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertDoesNotThrow({ \"Should not throw an exception\" }) {\n *     // Code block that is expected to not throw an exception\n * }\n * ```\n * @see Assertions.assertDoesNotThrow\n * @param R the result type of the [executable]\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\ninline fun <R> assertDoesNotThrow(\n    noinline message: () -> String,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(executable, EXACTLY_ONCE)\n        callsInPlace(message, AT_MOST_ONCE)\n    }\n\n    try {\n        return executable()\n    } catch (t: Throwable) {\n        rethrowIfUnrecoverable(t)\n        val suffix = t.message?.let { if (it.isNotBlank()) \": ${t.message}\" else null } ?: \"\"\n        throw assertionFailure()\n            .message(message())\n            .reason(\"Unexpected exception thrown: ${t.javaClass.getName()}$suffix\")\n            .cause(t)\n            .trimStacktrace(AssertionFailureBuilder::class.java)\n            // we don't want to retain any stacktrace elements from the AssertionFailureBuilder\n            .retainStackTraceElements(0)\n            .build()\n    }\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeout(Duration.seconds(1)) {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeout\n * @param R the result of the [executable].\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeout(\n    timeout: Duration,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(executable, AT_MOST_ONCE)\n    }\n\n    return Assertions.assertTimeout(timeout, executable)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeout(Duration.seconds(1), \"Should only take one second\") {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeout\n * @param R the result of the [executable].\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeout(\n    timeout: Duration,\n    message: String,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(executable, AT_MOST_ONCE)\n    }\n\n    return Assertions.assertTimeout(timeout, executable, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeout(Duration.seconds(1), { \"Should only take one second\" }) {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeout\n * @param R the result of the [executable].\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeout(\n    timeout: Duration,\n    message: () -> String,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(executable, AT_MOST_ONCE)\n        callsInPlace(message, AT_MOST_ONCE)\n    }\n\n    return Assertions.assertTimeout(timeout, executable, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeoutPreemptively(Duration.seconds(1)) {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeoutPreemptively\n * @param R the result of the [executable].\n */\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeoutPreemptively(\n    timeout: Duration,\n    executable: () -> R\n): R =\n    // no contract for `executable` because it might be interrupted and throw an exception\n    // (see https://youtrack.jetbrains.com/issue/KT-27748)\n    Assertions.assertTimeoutPreemptively(timeout, executable)\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeoutPreemptively(Duration.seconds(1), \"Should only take one second\") {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeoutPreemptively\n * @param R the result of the [executable].\n */\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeoutPreemptively(\n    timeout: Duration,\n    message: String,\n    executable: () -> R\n): R =\n    // no contract for `executable` because it might be interrupted and throw an exception\n    // (see https://youtrack.jetbrains.com/issue/KT-27748)\n    Assertions.assertTimeoutPreemptively(timeout, executable, message)\n\n/**\n * Example usage:\n * ```kotlin\n * val result = assertTimeoutPreemptively(Duration.seconds(1), { \"Should only take one second\" }) {\n *     // Code block that is being timed.\n * }\n * ```\n * @see Assertions.assertTimeoutPreemptively\n * @param R the result of the [executable].\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = STABLE, since = \"5.11\")\nfun <R> assertTimeoutPreemptively(\n    timeout: Duration,\n    message: () -> String,\n    executable: () -> R\n): R {\n    contract {\n        callsInPlace(message, AT_MOST_ONCE)\n        // no contract for `executable` because it might be interrupted and throw an exception\n        // (see https://youtrack.jetbrains.com/issue/KT-27748)\n    }\n\n    return Assertions.assertTimeoutPreemptively(timeout, executable, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val maybeString: Any = ...\n *\n * assertInstanceOf<String>(maybeString)\n *\n * // The compiler smart casts maybeString to a String object.\n * assertTrue(maybeString.isNotEmpty())\n * ```\n * @see Assertions.assertInstanceOf\n * @since 5.11\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\ninline fun <reified T : Any> assertInstanceOf(\n    actualValue: Any?,\n    message: String? = null\n): T {\n    contract {\n        returns() implies (actualValue is T)\n    }\n    return Assertions.assertInstanceOf(T::class.java, actualValue, message)\n}\n\n/**\n * Example usage:\n * ```kotlin\n * val maybeString: Any = ...\n *\n * assertInstanceOf<String>(maybeString) { \"Should be a string\" }\n *\n * // The compiler smart casts maybeString to a String object.\n * assertTrue(maybeString.isNotEmpty())\n * ```\n * @see Assertions.assertInstanceOf\n * @since 5.11\n */\n@OptIn(ExperimentalContracts::class)\n@API(status = MAINTAINED, since = \"5.13.3\")\ninline fun <reified T : Any> assertInstanceOf(\n    actualValue: Any?,\n    noinline message: () -> String\n): T {\n    contract {\n        returns() implies (actualValue is T)\n\n        callsInPlace(message, AT_MOST_ONCE)\n    }\n    return Assertions.assertInstanceOf(T::class.java, actualValue, message)\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/templates/resources/main/org/junit/jupiter/api/condition/JRE.java.jte",
    "content": "@import java.util.List\n@import gg.jte.support.ForSupport\n@import junitbuild.generator.model.JRE\n\n@param String classNamePrefix = \"\"\n@param int minRuntimeVersion\n@param List<JRE> allJres\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * Enumeration of Java Runtime Environment (JRE) versions.\n *\n * <p>If the current JRE version can be detected but is not one of the predefined\n * constants in this enum, {@link #OTHER} will be considered to be the\n * {@linkplain #isCurrentVersion current JRE version}.\n *\n * @since 5.1\n@for(JRE jre : allJres)<%--\n--%> * @see #JAVA_${jre.getVersion()}\n@endfor<%--\n--%> * @see #OTHER\n * @see EnabledOnJre\n * @see DisabledOnJre\n * @see EnabledForJreRange\n * @see DisabledForJreRange\n */\n@API(status = STABLE, since = \"5.1\")\npublic enum ${classNamePrefix}JRE {\n\n\t/**\n\t * An undefined JRE version.\n\t *\n\t * <p>This constant is used by JUnit as a default configuration value but is\n\t * not intended to be used by users.\n\t *\n\t * <p>This constant returns {@code -1} for its {@linkplain #version() version}.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tUNDEFINED(-1),\n@for(var jre : allJres)\n\t/**\n\t * Java ${jre.getVersion()}.\n\t@if(jre.getSince() != null || jre.getVersion() < minRuntimeVersion)<%--\n--%> *\n\t@endif<%--\n--%>@if(jre.getSince() != null)<%--\n--%> * @since ${jre.getSince()}\n\t@endif<%--\n--%>@if(jre.getVersion() < minRuntimeVersion)<%--\n--%> * @deprecated No longer supported at runtime; please use {@link #JAVA_17} or later\n\t@endif<%--\n--%> */\n\t@if(jre.getVersion() < minRuntimeVersion)<%--\n--%>@API(status = DEPRECATED, since = \"6.0\") //\n\t@Deprecated(since = \"6.0\", forRemoval = true)\n\t@elseif(jre.getSince() != null)<%--\n--%>@API(status = STABLE, since = \"${jre.getSince()}\")\n\t@endif<%--\n--%>JAVA_${jre.getVersion()}(${jre.getVersion()}),\n@endfor\n\t/**\n\t * A JRE version other than <%--\n--%>@for(var jre : ForSupport.of(allJres))<%--\n\t--%>@if(jre.isLast())or @endif<%--\n\t--%>{@link #JAVA_${jre.get().getVersion()}}<%--\n\t--%>@if(jre.isLast()).@else,@endif<%--\n\t--%>@if(jre.getIndex() % 3 == 1 && !jre.isLast())\n\t * @elseif(!jre.isLast()) @endif<%--\n--%>@endfor\n\t *\n\t * <p>This constant returns {@link Integer#MAX_VALUE} for its\n\t * {@linkplain #version() version}. To retrieve the actual version number,\n\t * use {@link #currentVersionNumber()}.\n\t *\n\t * @deprecated You should not reference this constant directly. If you need\n\t * to reference a later version than supported by this enum, you should\n\t * instead use an {@code int} with one of the following annotation\n\t * attributes:\n\t *\n\t * <ul>\n\t *     <li>{@link EnabledOnJre#versions()}</li>\n\t *     <li>{@link DisabledOnJre#versions()}</li>\n\t *     <li>{@link EnabledForJreRange#minVersion()}</li>\n\t *     <li>{@link EnabledForJreRange#maxVersion()}</li>\n\t *     <li>{@link DisabledForJreRange#minVersion()}</li>\n\t *     <li>{@link DisabledForJreRange#maxVersion()}</li>\n\t * </ul>\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\") //\n\t@Deprecated(since = \"6.1\")\n\tOTHER(Integer.MAX_VALUE);\n\n\tstatic final int UNDEFINED_VERSION = -1;\n\n\tstatic final int MINIMUM_VERSION = ${allJres.getFirst().getVersion()};\n\n\tprivate static final int CURRENT_VERSION = Runtime.version().feature();\n\n\tprivate final int version;\n\n\t${classNamePrefix}JRE(int version) {\n\t\tthis.version = version;\n\t}\n\n\t/**\n\t * Get the version of <em>this</em> {@code JRE}.\n\t *\n\t * <p>If this {@code JRE} is {@link #UNDEFINED}, this method returns\n\t * {@code -1}. If this {@code JRE} is {@link #OTHER}, this method returns\n\t * {@link Integer#MAX_VALUE}.\n\t *\n\t * @return the version of this {@code JRE}\n\t * @since 5.12\n\t * @see Runtime.Version#feature()\n\t * @see #currentVersionNumber()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic int version() {\n\t\treturn this.version;\n\t}\n\n\t/**\n\t * {@return {@code true} if <em>this</em> {@code JRE} is known to be the\n\t * Java Runtime Environment version for the currently executing JVM or if\n\t * the version is {@link #OTHER}}\n\t *\n\t * @see #currentJre()\n\t * @see #currentVersionNumber()\n\t */\n\tpublic boolean isCurrentVersion() {\n\t\treturn this == currentJre();\n\t}\n\n\t/**\n\t * {@return the {@link ${classNamePrefix}JRE} for the currently executing JVM, potentially\n\t * {@link #OTHER}}\n\t *\n\t * @since 5.7\n\t * @see #currentVersionNumber()\n\t * @deprecated in favor of {@link #currentJre()}\n\t */\n\t@API(status = DEPRECATED, since = \"5.12\")\n\t@Deprecated(since = \"5.12\")\n\tpublic static ${classNamePrefix}JRE currentVersion() {\n\t\treturn currentJre();\n\t}\n\n\t/**\n\t * {@return the {@link ${classNamePrefix}JRE} for the currently executing JVM, potentially\n\t * {@link #OTHER}}\n\t *\n\t * @since 5.12\n\t * @see #currentVersionNumber()\n\t */\n\t@API(status = STABLE, since = \"5.12\")\n\tpublic static ${classNamePrefix}JRE currentJre() {\n\t\treturn switch (CURRENT_VERSION) {<%--\n\t\t\t\t--%>@for(var jre : allJres)\n\t\t\tcase ${jre.getVersion()} -> JAVA_${jre.getVersion()};<%--\n\t\t\t\t--%>@endfor\n\t\t\tdefault -> OTHER;\n\t\t};\n\t}\n\n\t/**\n\t * {@return the version number for the currently executing JVM, or {@code -1}\n\t * if the current JVM version could not be determined}\n\t *\n\t * @since 5.12\n\t * @see Runtime.Version#feature()\n\t * @see #currentJre()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static int currentVersionNumber() {\n\t\treturn CURRENT_VERSION;\n\t}\n\n\t/**\n\t * Determine if the supplied version number is considered to be the current\n\t * JRE version.\n\t *\n\t * <p>Returns {@code true} if either of the following is {@code true}.\n\t *\n\t * <ul>\n\t * <li>The supplied version number is known to be the Java Runtime Environment\n\t * version for the currently executing JVM.</li>\n\t * <li>The supplied version number is {@link Integer#MAX_VALUE} and the current\n\t * {@code JRE} is {@link JRE#OTHER OTHER}.</li>\n\t * </ul>\n\t *\n\t * @return {@code true} if the supplied version number is considered to be\n\t * the current JRE version\n\t * @since 5.12\n\t * @see Runtime.Version#feature()\n\t * @see #isCurrentVersion()\n\t * @see #currentJre()\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static boolean isCurrentVersion(int version) {\n\t\treturn (version == CURRENT_VERSION) || (version == ${classNamePrefix}JRE.currentJre().version);\n\t}\n\n\tstatic boolean isCurrentVersionWithinRange(int min, int max) {\n\t\treturn CURRENT_VERSION >= min && CURRENT_VERSION <= max;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/templates/resources/testFixtures/org/junit/jupiter/api/condition/JavaVersionPredicates.java.jte",
    "content": "@import java.util.List\n@import gg.jte.support.ForSupport\n@import junitbuild.generator.model.JRE\n\n@param List<JRE> supportedJres\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\npublic class JavaVersionPredicates {\n\n\tprivate static final int JAVA_VERSION = Runtime.version().feature();\n@for(JRE jre : supportedJres)\n\tstatic boolean onJava${jre.getVersion()}() {\n\t\treturn JAVA_VERSION == ${jre.getVersion()};\n\t}\n@endfor\n\tstatic boolean onKnownVersion() {\n\t\treturn @for(var jre : ForSupport.of(supportedJres))onJava${jre.get().getVersion()}()@if(!jre.isLast()) //\n\t\t\t\t|| @endif@endfor;\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tstatic boolean onOtherVersion() {\n\t\treturn JRE.OTHER.isCurrentVersion();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project.\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/AssertionTestUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport java.io.Serializable;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.MultipleFailuresError;\nimport org.opentest4j.ValueWrapper;\n\npublic class AssertionTestUtils {\n\n\tprivate AssertionTestUtils() {\n\t\t/* no-op */\n\t}\n\n\tpublic static void expectAssertionFailedError() {\n\t\tthrow new AssertionError(\"Should have thrown an \" + AssertionFailedError.class.getName());\n\t}\n\n\tpublic static void assertEmptyMessage(Throwable ex) throws AssertionError {\n\t\tif (!(ex.getMessage() == null || ex.getMessage().isEmpty())) {\n\t\t\tthrow new AssertionError(\"Exception message should be empty, but was [\" + ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertMessageEquals(Throwable ex, String msg) throws AssertionError {\n\t\tif (!msg.equals(ex.getMessage())) {\n\t\t\tthrow new AssertionError(\"Exception message should be [\" + msg + \"], but was [\" + ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertMessageMatches(Throwable ex, String regex) throws AssertionError {\n\t\tif (ex.getMessage() == null || !ex.getMessage().matches(regex)) {\n\t\t\tthrow new AssertionError(\"Exception message should match regular expression [\" + regex + \"], but was [\"\n\t\t\t\t\t+ ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertMessageStartsWith(@Nullable Throwable ex, String msg) throws AssertionError {\n\t\tif (ex == null) {\n\t\t\tthrow new AssertionError(\"Cause should not have been null\");\n\t\t}\n\t\tif (ex.getMessage() == null || !ex.getMessage().startsWith(msg)) {\n\t\t\tthrow new AssertionError(\n\t\t\t\t\"Exception message should start with [\" + msg + \"], but was [\" + ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertMessageEndsWith(Throwable ex, String msg) throws AssertionError {\n\t\tif (ex.getMessage() == null || !ex.getMessage().endsWith(msg)) {\n\t\t\tthrow new AssertionError(\n\t\t\t\t\"Exception message should end with [\" + msg + \"], but was [\" + ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertMessageContains(@Nullable Throwable ex, String msg) throws AssertionError {\n\t\tif (ex == null) {\n\t\t\tthrow new AssertionError(\"Cause should not have been null\");\n\t\t}\n\t\tif (ex.getMessage() == null || !ex.getMessage().contains(msg)) {\n\t\t\tthrow new AssertionError(\n\t\t\t\t\"Exception message should contain [\" + msg + \"], but was [\" + ex.getMessage() + \"].\");\n\t\t}\n\t}\n\n\tpublic static void assertExpectedAndActualValues(AssertionFailedError ex, @Nullable Object expected,\n\t\t\t@Nullable Object actual) throws AssertionError {\n\t\tif (!wrapsEqualValue(ex.getExpected(), expected)) {\n\t\t\tthrow new AssertionError(\"Expected value in AssertionFailedError should equal [\"\n\t\t\t\t\t+ ValueWrapper.create(expected) + \"], but was [\" + ex.getExpected() + \"].\");\n\t\t}\n\t\tif (!wrapsEqualValue(ex.getActual(), actual)) {\n\t\t\tthrow new AssertionError(\"Actual value in AssertionFailedError should equal [\" + ValueWrapper.create(actual)\n\t\t\t\t\t+ \"], but was [\" + ex.getActual() + \"].\");\n\t\t}\n\t}\n\n\tpublic static boolean wrapsEqualValue(ValueWrapper wrapper, @Nullable Object value) {\n\t\tif (value == null || value instanceof Serializable) {\n\t\t\treturn Objects.equals(value, wrapper.getValue());\n\t\t}\n\t\treturn wrapper.getIdentityHashCode() == System.identityHashCode(value)\n\t\t\t\t&& Objects.equals(wrapper.getStringRepresentation(), String.valueOf(value))\n\t\t\t\t&& Objects.equals(wrapper.getType(), value.getClass());\n\t}\n\n\tpublic static void recurseIndefinitely() {\n\t\t// simulate infinite recursion\n\t\tthrow new StackOverflowError();\n\t}\n\n\tpublic static void runOutOfMemory() {\n\t\t// simulate running out of memory\n\t\tthrow new OutOfMemoryError(\"boom\");\n\t}\n\n\t@SafeVarargs\n\tpublic static void assertExpectedExceptionTypes(MultipleFailuresError multipleFailuresError,\n\t\t\tClass<? extends Throwable>... exceptionTypes) {\n\n\t\tassertNotNull(multipleFailuresError, \"MultipleFailuresError\");\n\t\tList<Throwable> failures = multipleFailuresError.getFailures();\n\t\tassertEquals(exceptionTypes.length, failures.size(), \"number of failures\");\n\n\t\t// Verify that exceptions are also present as suppressed exceptions.\n\t\t// https://github.com/junit-team/junit-framework/issues/1602\n\t\tThrowable[] suppressed = multipleFailuresError.getSuppressed();\n\t\tassertEquals(exceptionTypes.length, suppressed.length, \"number of suppressed exceptions\");\n\n\t\tfor (int i = 0; i < exceptionTypes.length; i++) {\n\t\t\tassertEquals(exceptionTypes[i], failures.get(i).getClass(), \"exception type [\" + i + \"]\");\n\t\t\tassertEquals(exceptionTypes[i], suppressed[i].getClass(), \"suppressed exception type [\" + i + \"]\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/EqualsAndHashCodeAssertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n/**\n * Assertions for unit tests that wish to test\n * {@link Object#equals(Object)} and {@link Object#hashCode()}.\n *\n * @since 5.3\n */\npublic class EqualsAndHashCodeAssertions {\n\n\tprivate EqualsAndHashCodeAssertions() {\n\t}\n\n\t@SuppressWarnings(\"EqualsWithItself\")\n\tpublic static <T> void assertEqualsAndHashCode(T equal1, T equal2, T different) {\n\t\t// Prerequisites\n\t\tassertThat(equal1).isNotNull();\n\t\tassertThat(equal2).isNotNull();\n\t\tassertThat(different).isNotNull();\n\t\tassertThat(equal1).isNotSameAs(equal2);\n\n\t\t// Are equal\n\t\tassertThat(equal1).isEqualTo(equal1);\n\t\tassertThat(equal1).isEqualTo(equal2);\n\t\tassertThat(equal2).isEqualTo(equal1);\n\n\t\t// Are not equal\n\t\tassertThat(equal1).isNotEqualTo(null);\n\t\tassertThat(equal1).isNotEqualTo(new Object());\n\t\tassertThat(equal1).isNotEqualTo(different);\n\t\tassertThat(different).isNotEqualTo(equal1);\n\t\tassertThat(different).isNotEqualTo(equal2);\n\n\t\t// Hash codes\n\t\tassertThat(equal1).hasSameHashCodeAs(equal2);\n\t\tassertThat(equal1).doesNotHaveSameHashCodeAs(different);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/TemporaryClasspathExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\n/**\n * Utility class for executing code with a temporary classpath.\n *\n * @since 5.13\n */\npublic class TemporaryClasspathExecutor {\n\n\tprivate TemporaryClasspathExecutor() {\n\t}\n\n\t/**\n\t * Execute the {@link Runnable} within a custom classpath, temporarily modifying the\n\t * thread's {@link Thread#getContextClassLoader() context class loader} to include\n\t * the provided {@code classpathRoot}.\n\t *\n\t * <p>After the given {@code Runnable} completes, the original context class loader is\n\t * restored.\n\t *\n\t * @param classpathRoot the root path to be added to the classpath, resolved relative\n\t * to the current thread's context class loader.\n\t * @param runnable the {@code Runnable} to execute with the temporary classpath.\n\t */\n\tpublic static void withAdditionalClasspathRoot(String classpathRoot, Runnable runnable) {\n\t\tvar current = Thread.currentThread().getContextClassLoader();\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { current.getResource(classpathRoot) }, current)) {\n\t\t\tThread.currentThread().setContextClassLoader(classLoader);\n\t\t\trunnable.run();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(current);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/DisabledInEclipse.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.condition.DisabledIf;\n\n/**\n * @see org.junit.platform.commons.test.IdeUtils#runningInEclipse()\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE, ElementType.METHOD })\n@DisabledIf(\"org.junit.platform.commons.test.IdeUtils#runningInEclipse()\")\npublic @interface DisabledInEclipse {\n\tString value() default \"\";\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/DisabledOnOpenJ9.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.condition.DisabledIfSystemProperty;\n\n@Target({ ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@DisabledIfSystemProperty(named = \"java.vm.vendor\", matches = \".*OpenJ9.*\")\npublic @interface DisabledOnOpenJ9 {\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/extension/ExtensionContextParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\npublic class ExtensionContextParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\t\treturn ExtensionContext.class.equals(parameterContext.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\t\treturn extensionContext;\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/fixtures/TrackLogRecords.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.fixtures;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.logging.LoggerFactory;\n\n/**\n * {@code @TrackLogRecords} registers an extension that tracks log records\n * logged via JUnit's logging facade for JUL.\n *\n * <p>Log records are tracked on a per-method basis (e.g., for a single\n * test method).\n *\n * <p>Test methods can gain access to the {@link LogRecordListener} managed by\n * the extension by having an instance of {@code LogRecordListener} injected as\n * a method parameter.\n *\n * @since 5.1\n * @see LoggerFactory\n * @see LogRecordListener\n */\n@Target({ ElementType.TYPE, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(TrackLogRecords.Extension.class)\npublic @interface TrackLogRecords {\n\n\tclass Extension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tLoggerFactory.addListener(getListener(context));\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tLoggerFactory.removeListener(getListener(context));\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\tboolean isTestMethodLevel = extensionContext.getTestMethod().isPresent();\n\t\t\tboolean isListener = parameterContext.getParameter().getType() == LogRecordListener.class;\n\t\t\treturn isTestMethodLevel && isListener;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn getListener(extensionContext);\n\t\t}\n\n\t\tprivate LogRecordListener getListener(ExtensionContext context) {\n\t\t\treturn getStore(context).computeIfAbsent(LogRecordListener.class);\n\t\t}\n\n\t\tprivate Store getStore(ExtensionContext context) {\n\t\t\treturn context.getStore(Namespace.create(getClass(), context.getRequiredTestMethod()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/io/FailingTempDirDeletionStrategy.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * A {@link TempDirDeletionStrategy} for testing that simulates a deletion\n * failure for any path ending in {@link #UNDELETABLE_PATH}.\n */\n@NullMarked\npublic class FailingTempDirDeletionStrategy implements TempDirDeletionStrategy {\n\n\t/**\n\t * A path segment that, when present at the end of a path, causes deletion\n\t * to fail with a simulated {@link java.io.IOException}.\n\t */\n\tpublic static final Path UNDELETABLE_PATH = Path.of(\"undeletable\");\n\n\t@Override\n\tpublic DeletionResult delete(Path tempDir, AnnotatedElementContext elementContext,\n\t\t\tExtensionContext extensionContext) throws IOException {\n\n\t\treturn Standard.INSTANCE.delete(tempDir, path -> {\n\t\t\tif (path.endsWith(UNDELETABLE_PATH)) {\n\t\t\t\tthrow new IOException(\"Simulated failure\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\tFiles.delete(path);\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/junit-jupiter-engine.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Jupiter Engine\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformEngine)\n\tapi(projects.junitJupiterApi)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\ntasks {\n\tjar {\n\t\tbundle {\n\t\t\tbnd(\"\"\"\n\t\t\t\tProvide-Capability:\\\n\t\t\t\t\torg.junit.platform.engine;\\\n\t\t\t\t\t\torg.junit.platform.engine='junit-jupiter';\\\n\t\t\t\t\t\tversion:Version=\"${'$'}{version_cleanup;${project.version}}\"\n\t\t\t\tRequire-Capability:\\\n\t\t\t\t\torg.junit.platform.launcher;\\\n\t\t\t\t\t\tfilter:='(&(org.junit.platform.launcher=junit-platform-launcher)(version>=${'$'}{version_cleanup;${project.version}})(!(version>=${'$'}{versionmask;+;${'$'}{version_cleanup;${project.version}}})))';\\\n\t\t\t\t\t\teffective:=active\n\t\t\t\"\"\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Provides the JUnit Jupiter {@link org.junit.platform.engine.TestEngine}\n * implementation.\n *\n * @since 5.0\n * @uses org.junit.jupiter.api.extension.Extension\n * @provides org.junit.platform.engine.TestEngine The {@code JupiterTestEngine}\n * runs Jupiter based tests on the platform.\n */\nmodule org.junit.jupiter.engine {\n\n\trequires static org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires org.junit.jupiter.api;\n\trequires org.junit.platform.commons;\n\trequires org.junit.platform.engine;\n\trequires org.opentest4j;\n\n\tuses org.junit.jupiter.api.extension.Extension;\n\n\tprovides org.junit.platform.engine.TestEngine\n\t\t\twith org.junit.jupiter.engine.JupiterTestEngine;\n\n\topens org.junit.jupiter.engine.extension to org.junit.platform.commons;\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/Constants.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.parallel.Execution;\n\n/**\n * Collection of constants related to the Jupiter test engine.\n *\n * @since 5.0\n * @see org.junit.platform.engine.ConfigurationParameters\n * @deprecated Please use {@link org.junit.jupiter.api.Constants} instead.\n */\n@API(status = DEPRECATED, since = \"6.1\")\n@Deprecated(forRemoval = true, since = \"6.1\")\npublic final class Constants {\n\n\t/**\n\t * Property name used to include patterns for auto-detecting extensions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * extensions will be included. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each extension.\n\t * Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: includes all extensions.\n\t * <li>{@code org.junit.*}: includes every extension under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyExtension}: includes every extension whose simple class name is\n\t * exactly {@code MyExtension}.\n\t * <li>{@code *System*}: includes every extension whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: includes every extension whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyExtension, org.example.TheirExtension}: includes\n\t * extensions whose FQCN is exactly {@code org.example.MyExtension} or\n\t * {@code org.example.TheirExtension}.\n\t * </ul>\n\t *\n\t * <p>Note: A class that matches both an inclusion and exclusion pattern will be excluded.\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME = org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to exclude patterns for auto-detecting extensions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * extensions will be excluded. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each extension.\n\t * Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: excludes all extensions.\n\t * <li>{@code org.junit.*}: excludes every extension under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyExtension}: excludes every extension whose simple class name is\n\t * exactly {@code MyExtension}.\n\t * <li>{@code *System*}: excludes every extension whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: excludes every extension whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyExtension, org.example.TheirExtension}: excludes\n\t * extensions whose FQCN is exactly {@code org.example.MyExtension} or\n\t * {@code org.example.TheirExtension}.\n\t * </ul>\n\t *\n\t * <p>Note: A class that matches both an inclusion and exclusion pattern will be excluded.\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME = org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to provide patterns for deactivating conditions: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * conditions will be deactivated. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each registered\n\t * condition. Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: deactivates all conditions.\n\t * <li>{@code org.junit.*}: deactivates every condition under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyCondition}: deactivates every condition whose simple class name is\n\t * exactly {@code MyCondition}.\n\t * <li>{@code *System*}: deactivates every condition whose FQCN contains\n\t * {@code System}.\n\t * <li>{@code *System*, *Dev*}: deactivates every condition whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyCondition, org.example.TheirCondition}: deactivates\n\t * conditions whose FQCN is exactly {@code org.example.MyCondition} or\n\t * {@code org.example.TheirCondition}.\n\t * </ul>\n\t *\n\t * @see #DEACTIVATE_ALL_CONDITIONS_PATTERN\n\t * @see org.junit.jupiter.api.extension.ExecutionCondition\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME;\n\n\t/**\n\t * Wildcard pattern which signals that all conditions should be deactivated: {@value}\n\t *\n\t * @see #DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME\n\t * @see org.junit.jupiter.api.extension.ExecutionCondition\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEACTIVATE_ALL_CONDITIONS_PATTERN}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEACTIVATE_ALL_CONDITIONS_PATTERN = org.junit.jupiter.api.Constants.DEACTIVATE_ALL_CONDITIONS_PATTERN;\n\n\t/**\n\t * Property name used to set the default display name generator class name: {@value}\n\t *\n\t * @see DisplayNameGenerator#DEFAULT_GENERATOR_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable auto-detection and registration of extensions via\n\t * Java's {@link java.util.ServiceLoader} mechanism: {@value}\n\t *\n\t * <p>The default behavior is not to perform auto-detection.\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME = org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable dumping the stack of all\n\t * {@linkplain Thread threads} to {@code System.out} when a timeout has occurred: {@value}\n\t *\n\t * <p>This behavior is disabled by default.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME = org.junit.jupiter.api.Constants.EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default test instance lifecycle mode: {@value}\n\t *\n\t * @see TestInstance.Lifecycle#DEFAULT_LIFECYCLE_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable parallel test execution: {@value}\n\t *\n\t * <p>By default, tests are executed sequentially in a single thread.\n\t *\n\t * @since 5.3\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to enable auto-closing of {@link AutoCloseable} instances: {@value}\n\t *\n\t * <p>By default, auto-closing is enabled.\n\t *\n\t * @since 5.13\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME = org.junit.jupiter.api.Constants.CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default test execution mode: {@value}\n\t *\n\t * @see Execution#DEFAULT_EXECUTION_MODE_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_EXECUTION_MODE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_PARALLEL_EXECUTION_MODE = org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default test execution mode for top-level\n\t * classes: {@value}\n\t *\n\t * @see Execution#DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to determine the desired parallel executor service\n\t * type: {@value}\n\t *\n\t * <p>Value must be {@code FORK_JOIN_POOL} or {@code WORKER_THREAD_POOL},\n\t * ignoring case.\n\t *\n\t * @since 6.1\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to select the parallel execution configuration\n\t * strategy: {@value}\n\t *\n\t * <p>Potential values: {@code dynamic} (default), {@code fixed}, or\n\t * {@code custom}.\n\t *\n\t * @since 5.3\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the desired parallelism for the {@code fixed}\n\t * configuration strategy: {@value}\n\t *\n\t * <p>No default value; must be a positive integer.\n\t *\n\t * @since 5.3\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to configure the maximum pool size of the underlying\n\t * fork-join pool for the {@code fixed} configuration strategy: {@value}\n\t *\n\t * <p>Value must be an integer and greater than or equal to\n\t * {@value #PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME}; defaults to\n\t * {@code 256 + fixed.parallelism}.\n\t *\n\t * @since 5.10\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to disable saturation of the underlying fork-join pool\n\t * for the {@code fixed} configuration strategy: {@value}\n\t *\n\t * <p>When set to {@code false} the underlying fork-join pool will reject\n\t * additional tasks if all available workers are busy and the maximum\n\t * pool-size would be exceeded.\n\t *\n\t * <p>Value must either {@code true} or {@code false}; defaults to {@code true}.\n\t *\n\t * @since 5.10\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the factor to be multiplied with the number of\n\t * available processors/cores to determine the desired parallelism for the\n\t * {@code dynamic} configuration strategy: {@value}\n\t *\n\t * <p>Value must be a positive decimal number; defaults to {@code 1}.\n\t *\n\t * @since 5.3\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to specify the fully qualified class name of the\n\t * {@code custom} parallel execution configuration strategy to be used:\n\t * {@value}\n\t *\n\t * @since 5.3\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#PARALLEL_CONFIG_CUSTOM_CLASS_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String PARALLEL_CONFIG_CUSTOM_CLASS_PROPERTY_NAME = org.junit.jupiter.api.Constants.PARALLEL_CONFIG_CUSTOM_CLASS_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all testable and\n\t * lifecycle methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all testable methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link Test @Test} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestTemplate @TestTemplate} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link TestFactory @TestFactory} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all lifecycle methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeAll @BeforeAll} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link BeforeEach @BeforeEach} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterEach @AfterEach} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout for all\n\t * {@link AfterAll @AfterAll} methods: {@value}\n\t *\n\t * @see Timeout#DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to configure whether timeouts are applied to tests: {@value}\n\t *\n\t * @see Timeout#TIMEOUT_MODE_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#TIMEOUT_MODE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String TIMEOUT_MODE_PROPERTY_NAME = org.junit.jupiter.api.Constants.TIMEOUT_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default method orderer class name: {@value}\n\t *\n\t * @see MethodOrderer#DEFAULT_ORDER_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default class orderer class name: {@value}\n\t *\n\t * @see ClassOrderer#DEFAULT_ORDER_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default timeout thread mode: {@value}\n\t *\n\t * @since 5.9\n\t * @see Timeout\n\t * @see Timeout.ThreadMode\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default factory for temporary directories created via\n\t * the {@link TempDir @TempDir} annotation: {@value}\n\t *\n\t * @since 5.10\n\t * @see TempDir#DEFAULT_FACTORY_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME;\n\n\t/**\n\t * Property name used to set the default extension context scope for\n\t * extensions that participate in test instantiation: {@value}\n\t *\n\t * @since 5.12\n\t * @see org.junit.jupiter.api.extension.TestInstantiationAwareExtension\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.api.Constants#DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(forRemoval = true, since = \"6.1\")\n\tpublic static final String DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME = org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME;\n\n\tprivate Constants() {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/JupiterTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.engine.config.CachingJupiterConfiguration;\nimport org.junit.jupiter.engine.config.DefaultJupiterConfiguration;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.discovery.DiscoverySelectorResolver;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.config.PrefixedConfigurationParameters;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine;\nimport org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * The JUnit Jupiter {@link org.junit.platform.engine.TestEngine TestEngine}.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic final class JupiterTestEngine extends HierarchicalTestEngine<JupiterEngineExecutionContext> {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn JupiterEngineDescriptor.ENGINE_ID;\n\t}\n\n\t/**\n\t * Returns {@code org.junit.jupiter} as the group ID.\n\t */\n\t@Override\n\tpublic Optional<String> getGroupId() {\n\t\treturn Optional.of(\"org.junit.jupiter\");\n\t}\n\n\t/**\n\t * Returns {@code junit-jupiter-engine} as the artifact ID.\n\t */\n\t@Override\n\tpublic Optional<String> getArtifactId() {\n\t\treturn Optional.of(\"junit-jupiter-engine\");\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tDiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.deduplicating(\n\t\t\tDiscoveryIssueReporter.forwarding(discoveryRequest.getDiscoveryListener(), uniqueId));\n\t\tJupiterConfiguration configuration = new CachingJupiterConfiguration(\n\t\t\tnew DefaultJupiterConfiguration(discoveryRequest.getConfigurationParameters(),\n\t\t\t\tdiscoveryRequest.getOutputDirectoryCreator(), issueReporter));\n\t\tJupiterEngineDescriptor engineDescriptor = new JupiterEngineDescriptor(uniqueId, configuration);\n\t\tDiscoverySelectorResolver.resolveSelectors(discoveryRequest, engineDescriptor, issueReporter);\n\t\treturn engineDescriptor;\n\t}\n\n\t@Override\n\tprotected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request) {\n\t\tJupiterConfiguration configuration = getJupiterConfiguration(request);\n\t\tif (configuration.isParallelExecutionEnabled()) {\n\t\t\treturn ParallelHierarchicalTestExecutorServiceFactory.create(new PrefixedConfigurationParameters(\n\t\t\t\trequest.getConfigurationParameters(), Constants.PARALLEL_CONFIG_PREFIX));\n\t\t}\n\t\treturn super.createExecutorService(request);\n\t}\n\n\t@Override\n\tprotected JupiterEngineExecutionContext createExecutionContext(ExecutionRequest request) {\n\t\treturn new JupiterEngineExecutionContext(request.getEngineExecutionListener(), getJupiterConfiguration(request),\n\t\t\tnew LauncherStoreFacade(request.getStore()));\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\t@Override\n\tprotected ThrowableCollector.Factory createThrowableCollectorFactory(ExecutionRequest request) {\n\t\treturn JupiterThrowableCollectorFactory::createThrowableCollector;\n\t}\n\n\tprivate JupiterConfiguration getJupiterConfiguration(ExecutionRequest request) {\n\t\tJupiterEngineDescriptor engineDescriptor = (JupiterEngineDescriptor) request.getRootTestDescriptor();\n\t\treturn engineDescriptor.getConfiguration();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/CachingJupiterConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.Constants.CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\n\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.engine.OutputDirectoryCreator;\n\n/**\n * Caching implementation of the {@link JupiterConfiguration} API.\n *\n * @since 5.4\n */\n@API(status = INTERNAL, since = \"5.4\")\npublic class CachingJupiterConfiguration implements JupiterConfiguration {\n\n\tprivate final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();\n\tprivate final JupiterConfiguration delegate;\n\n\tpublic CachingJupiterConfiguration(JupiterConfiguration delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic Predicate<Class<? extends Extension>> getFilterForAutoDetectedExtensions() {\n\t\treturn delegate.getFilterForAutoDetectedExtensions();\n\t}\n\n\t@Override\n\tpublic Optional<String> getRawConfigurationParameter(String key) {\n\t\treturn delegate.getRawConfigurationParameter(key);\n\t}\n\n\t@Override\n\tpublic <T> Optional<T> getRawConfigurationParameter(String key,\n\t\t\tFunction<? super String, ? extends @Nullable T> transformer) {\n\t\treturn delegate.getRawConfigurationParameter(key, transformer);\n\t}\n\n\t@Override\n\tpublic boolean isParallelExecutionEnabled() {\n\t\treturn (boolean) cache.computeIfAbsent(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME,\n\t\t\t__ -> delegate.isParallelExecutionEnabled());\n\t}\n\n\t@Override\n\tpublic boolean isClosingStoredAutoCloseablesEnabled() {\n\t\treturn (boolean) cache.computeIfAbsent(CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME,\n\t\t\t__ -> delegate.isClosingStoredAutoCloseablesEnabled());\n\t}\n\n\t@Override\n\tpublic boolean isExtensionAutoDetectionEnabled() {\n\t\treturn (boolean) cache.computeIfAbsent(EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME,\n\t\t\t__ -> delegate.isExtensionAutoDetectionEnabled());\n\t}\n\n\t@Override\n\tpublic boolean isThreadDumpOnTimeoutEnabled() {\n\t\treturn (boolean) cache.computeIfAbsent(EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME,\n\t\t\t__ -> delegate.isThreadDumpOnTimeoutEnabled());\n\t}\n\n\t@Override\n\tpublic ExecutionMode getDefaultExecutionMode() {\n\t\treturn (ExecutionMode) cache.computeIfAbsent(DEFAULT_EXECUTION_MODE_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultExecutionMode());\n\t}\n\n\t@Override\n\tpublic ExecutionMode getDefaultClassesExecutionMode() {\n\t\treturn (ExecutionMode) cache.computeIfAbsent(DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultClassesExecutionMode());\n\t}\n\n\t@Override\n\tpublic TestInstance.Lifecycle getDefaultTestInstanceLifecycle() {\n\t\treturn (TestInstance.Lifecycle) cache.computeIfAbsent(DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTestInstanceLifecycle());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Predicate<ExecutionCondition> getExecutionConditionFilter() {\n\t\treturn (Predicate<ExecutionCondition>) cache.computeIfAbsent(\n\t\t\tConstants.DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME, __ -> delegate.getExecutionConditionFilter());\n\t}\n\n\t@Override\n\tpublic DisplayNameGenerator getDefaultDisplayNameGenerator() {\n\t\treturn (DisplayNameGenerator) cache.computeIfAbsent(DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultDisplayNameGenerator());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Optional<MethodOrderer> getDefaultTestMethodOrderer() {\n\t\treturn (Optional<MethodOrderer>) cache.computeIfAbsent(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTestMethodOrderer());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Optional<ClassOrderer> getDefaultTestClassOrderer() {\n\t\treturn (Optional<ClassOrderer>) cache.computeIfAbsent(DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTestClassOrderer());\n\t}\n\n\t@Override\n\tpublic CleanupMode getDefaultTempDirCleanupMode() {\n\t\treturn (CleanupMode) cache.computeIfAbsent(DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTempDirCleanupMode());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Supplier<TempDirFactory> getDefaultTempDirFactorySupplier() {\n\t\treturn (Supplier<TempDirFactory>) cache.computeIfAbsent(DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTempDirFactorySupplier());\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tpublic Supplier<TempDirDeletionStrategy> getDefaultTempDirDeletionStrategySupplier() {\n\t\treturn (Supplier<TempDirDeletionStrategy>) cache.computeIfAbsent(\n\t\t\tDEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTempDirDeletionStrategySupplier());\n\t}\n\n\t@Override\n\tpublic ExtensionContextScope getDefaultTestInstantiationExtensionContextScope() {\n\t\treturn (ExtensionContextScope) cache.computeIfAbsent(\n\t\t\tDEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME,\n\t\t\t__ -> delegate.getDefaultTestInstantiationExtensionContextScope());\n\t}\n\n\t@Override\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn delegate.getOutputDirectoryCreator();\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/ConfigurationParameterConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport java.util.Optional;\n\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 6.0\n */\ninterface ConfigurationParameterConverter<T> {\n\n\tdefault T getOrDefault(ConfigurationParameters configParams, String key, T defaultValue) {\n\t\treturn get(configParams, key).orElse(defaultValue);\n\t}\n\n\tOptional<T> get(ConfigurationParameters configurationParameters, String key);\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/DefaultJupiterConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.Constants.CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.io.CleanupMode.ALWAYS;\nimport static org.junit.jupiter.engine.config.FilteringConfigurationParameterConverter.exclude;\nimport static org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType.FORK_JOIN_POOL;\nimport static org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType.WORKER_THREAD_POOL;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.commons.util.ClassNamePatternFilterUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Default implementation of the {@link JupiterConfiguration} API.\n *\n * @since 5.4\n */\n@API(status = INTERNAL, since = \"5.4\")\npublic class DefaultJupiterConfiguration implements JupiterConfiguration {\n\n\tprivate static final List<String> UNSUPPORTED_CONFIGURATION_PARAMETERS = List.of( //\n\t\t\"junit.jupiter.tempdir.scope\", //\n\t\t\"junit.jupiter.params.arguments.conversion.locale.format\" //\n\t);\n\n\tprivate static final ConfigurationParameterConverter<ExecutionMode> executionModeConverter = //\n\t\tnew EnumConfigurationParameterConverter<>(ExecutionMode.class, \"parallel execution mode\");\n\n\tprivate static final ConfigurationParameterConverter<Lifecycle> lifecycleConverter = //\n\t\tnew EnumConfigurationParameterConverter<>(Lifecycle.class, \"test instance lifecycle mode\");\n\n\tprivate static final ConfigurationParameterConverter<DisplayNameGenerator> displayNameGeneratorConverter = //\n\t\tnew InstantiatingConfigurationParameterConverter<>(DisplayNameGenerator.class, \"display name generator\");\n\n\tprivate static final ConfigurationParameterConverter<MethodOrderer> methodOrdererConverter = //\n\t\texclude(isEqual(MethodOrderer.Default.class.getName()),\n\t\t\tnew InstantiatingConfigurationParameterConverter<>(MethodOrderer.class, \"method orderer\"));\n\n\tprivate static final ConfigurationParameterConverter<ClassOrderer> classOrdererConverter = //\n\t\texclude(isEqual(ClassOrderer.Default.class.getName()),\n\t\t\tnew InstantiatingConfigurationParameterConverter<>(ClassOrderer.class, \"class orderer\"));\n\n\tprivate static final ConfigurationParameterConverter<CleanupMode> cleanupModeConverter = //\n\t\tnew EnumConfigurationParameterConverter<>(CleanupMode.class, \"cleanup mode\");\n\n\tprivate static final InstantiatingConfigurationParameterConverter<TempDirFactory> tempDirFactoryConverter = //\n\t\tnew InstantiatingConfigurationParameterConverter<>(TempDirFactory.class, \"temp dir factory\");\n\n\tprivate static final InstantiatingConfigurationParameterConverter<TempDirDeletionStrategy> tempDirDeletionStrategyConverter = //\n\t\tnew InstantiatingConfigurationParameterConverter<>(TempDirDeletionStrategy.class, \"temp dir deletion strategy\");\n\n\tprivate static final ConfigurationParameterConverter<ExtensionContextScope> extensionContextScopeConverter = //\n\t\tnew EnumConfigurationParameterConverter<>(ExtensionContextScope.class, \"extension context scope\");\n\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\n\tpublic DefaultJupiterConfiguration(ConfigurationParameters configurationParameters,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator, DiscoveryIssueReporter issueReporter) {\n\t\tthis.configurationParameters = Preconditions.notNull(configurationParameters,\n\t\t\t\"ConfigurationParameters must not be null\");\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t\tvalidateConfigurationParameters(issueReporter);\n\t}\n\n\tprivate void validateConfigurationParameters(DiscoveryIssueReporter issueReporter) {\n\t\tUNSUPPORTED_CONFIGURATION_PARAMETERS.forEach(key -> configurationParameters.get(key) //\n\t\t\t\t.ifPresent(value -> {\n\t\t\t\t\tvar warning = DiscoveryIssue.create(Severity.WARNING, \"\"\"\n\t\t\t\t\t\t\tThe '%s' configuration parameter is no longer supported. \\\n\t\t\t\t\t\t\tPlease remove it from your configuration.\"\"\".formatted(key));\n\t\t\t\t\tissueReporter.reportIssue(warning);\n\t\t\t\t}));\n\t\tif (isParallelExecutionEnabled()\n\t\t\t\t&& configurationParameters.get(PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME).isEmpty()) {\n\t\t\tvar info = DiscoveryIssue.create(Severity.INFO,\n\t\t\t\t\"Parallel test execution is enabled but the default ForkJoinPool-based executor service will be used. \"\n\t\t\t\t\t\t+ \"Please give the new implementation based on a regular thread pool a try by setting the '\"\n\t\t\t\t\t\t+ PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME + \"' configuration parameter to '\"\n\t\t\t\t\t\t+ WORKER_THREAD_POOL + \"' and report any issues to the JUnit team. \"\n\t\t\t\t\t\t+ \"Alternatively, set the configuration parameter to '\" + FORK_JOIN_POOL\n\t\t\t\t\t\t+ \"' to hide this message and keep using the original implementation.\");\n\t\t\tissueReporter.reportIssue(info);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Predicate<Class<? extends Extension>> getFilterForAutoDetectedExtensions() {\n\t\tString includePattern = getExtensionAutoDetectionIncludePattern();\n\t\tString excludePattern = getExtensionAutoDetectionExcludePattern();\n\t\tPredicate<String> predicate = ClassNamePatternFilterUtils.includeMatchingClassNames(includePattern) //\n\t\t\t\t.and(ClassNamePatternFilterUtils.excludeMatchingClassNames(excludePattern));\n\t\treturn clazz -> predicate.test(clazz.getName());\n\t}\n\n\tprivate String getExtensionAutoDetectionIncludePattern() {\n\t\treturn configurationParameters.get(EXTENSIONS_AUTODETECTION_INCLUDE_PROPERTY_NAME) //\n\t\t\t\t.orElse(ClassNamePatternFilterUtils.ALL_PATTERN);\n\t}\n\n\tprivate String getExtensionAutoDetectionExcludePattern() {\n\t\treturn configurationParameters.get(EXTENSIONS_AUTODETECTION_EXCLUDE_PROPERTY_NAME) //\n\t\t\t\t.orElse(ClassNamePatternFilterUtils.BLANK);\n\t}\n\n\t@Override\n\tpublic Optional<String> getRawConfigurationParameter(String key) {\n\t\treturn configurationParameters.get(key);\n\t}\n\n\t@Override\n\tpublic <T> Optional<T> getRawConfigurationParameter(String key,\n\t\t\tFunction<? super String, ? extends @Nullable T> transformer) {\n\t\treturn configurationParameters.get(key, transformer);\n\t}\n\n\t@Override\n\tpublic boolean isParallelExecutionEnabled() {\n\t\treturn configurationParameters.getBoolean(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME).orElse(false);\n\t}\n\n\t@Override\n\tpublic boolean isClosingStoredAutoCloseablesEnabled() {\n\t\treturn configurationParameters.getBoolean(CLOSING_STORED_AUTO_CLOSEABLE_ENABLED_PROPERTY_NAME).orElse(true);\n\t}\n\n\t@Override\n\tpublic boolean isExtensionAutoDetectionEnabled() {\n\t\treturn configurationParameters.getBoolean(EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME).orElse(false);\n\t}\n\n\t@Override\n\tpublic boolean isThreadDumpOnTimeoutEnabled() {\n\t\treturn configurationParameters.getBoolean(EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME).orElse(false);\n\t}\n\n\t@Override\n\tpublic ExecutionMode getDefaultExecutionMode() {\n\t\treturn executionModeConverter.getOrDefault(configurationParameters, DEFAULT_EXECUTION_MODE_PROPERTY_NAME,\n\t\t\tExecutionMode.SAME_THREAD);\n\t}\n\n\t@Override\n\tpublic ExecutionMode getDefaultClassesExecutionMode() {\n\t\treturn executionModeConverter.getOrDefault(configurationParameters,\n\t\t\tDEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME, getDefaultExecutionMode());\n\t}\n\n\t@Override\n\tpublic Lifecycle getDefaultTestInstanceLifecycle() {\n\t\treturn lifecycleConverter.getOrDefault(configurationParameters, DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME,\n\t\t\tLifecycle.PER_METHOD);\n\t}\n\n\t@Override\n\tpublic Predicate<ExecutionCondition> getExecutionConditionFilter() {\n\t\treturn ClassNamePatternFilterUtils.excludeMatchingClasses(\n\t\t\tconfigurationParameters.get(DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME).orElse(null));\n\t}\n\n\t@Override\n\tpublic DisplayNameGenerator getDefaultDisplayNameGenerator() {\n\t\treturn displayNameGeneratorConverter.get(configurationParameters, DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME) //\n\t\t\t\t.orElseGet(() -> DisplayNameGenerator.getDisplayNameGenerator(DisplayNameGenerator.Standard.class));\n\t}\n\n\t@Override\n\tpublic Optional<MethodOrderer> getDefaultTestMethodOrderer() {\n\t\treturn methodOrdererConverter.get(configurationParameters, DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME);\n\t}\n\n\t@Override\n\tpublic Optional<ClassOrderer> getDefaultTestClassOrderer() {\n\t\treturn classOrdererConverter.get(configurationParameters, DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME);\n\t}\n\n\t@Override\n\tpublic CleanupMode getDefaultTempDirCleanupMode() {\n\t\treturn cleanupModeConverter.getOrDefault(configurationParameters, DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME,\n\t\t\tALWAYS);\n\t}\n\n\t@Override\n\tpublic Supplier<TempDirFactory> getDefaultTempDirFactorySupplier() {\n\t\tSupplier<Optional<TempDirFactory>> supplier = tempDirFactoryConverter.supply(configurationParameters,\n\t\t\tDEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME);\n\t\treturn () -> supplier.get().orElse(TempDirFactory.Standard.INSTANCE);\n\t}\n\n\t@Override\n\tpublic Supplier<TempDirDeletionStrategy> getDefaultTempDirDeletionStrategySupplier() {\n\t\tSupplier<Optional<TempDirDeletionStrategy>> supplier = tempDirDeletionStrategyConverter.supply(\n\t\t\tconfigurationParameters, DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME);\n\t\treturn () -> supplier.get().orElse(TempDirDeletionStrategy.Standard.INSTANCE);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Override\n\tpublic ExtensionContextScope getDefaultTestInstantiationExtensionContextScope() {\n\t\treturn extensionContextScopeConverter.getOrDefault(configurationParameters,\n\t\t\tDEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME,\n\t\t\tExtensionContextScope.DEFAULT);\n\t}\n\n\t@Override\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn outputDirectoryCreator;\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/EnumConfigurationParameterConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Locale;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 5.4\n */\n@API(status = INTERNAL, since = \"5.8\")\npublic class EnumConfigurationParameterConverter<E extends Enum<E>> implements ConfigurationParameterConverter<E> {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(EnumConfigurationParameterConverter.class);\n\n\tprivate final Class<E> enumType;\n\tprivate final String enumDisplayName;\n\n\tpublic EnumConfigurationParameterConverter(Class<E> enumType, String enumDisplayName) {\n\t\tthis.enumType = enumType;\n\t\tthis.enumDisplayName = enumDisplayName;\n\t}\n\n\t@Override\n\tpublic Optional<E> get(ConfigurationParameters configParams, String key) {\n\t\treturn configParams.get(key) //\n\t\t\t\t.map(value -> convert(key, value));\n\t}\n\n\tpublic Optional<E> get(ExtensionContext extensionContext, String key) {\n\t\treturn extensionContext.getConfigurationParameter(key, value -> convert(key, value));\n\t}\n\n\tprivate E convert(String key, String value) {\n\t\tString constantName = null;\n\t\ttry {\n\t\t\tconstantName = value.strip().toUpperCase(Locale.ROOT);\n\t\t\tE result = Enum.valueOf(enumType, constantName);\n\t\t\tlogger.config(() -> \"Using %s '%s' set via the '%s' configuration parameter.\".formatted(enumDisplayName,\n\t\t\t\tresult, key));\n\t\t\treturn result;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new JUnitException(\"Invalid %s '%s' set via the '%s' configuration parameter.\".formatted(\n\t\t\t\tenumDisplayName, constantName, key));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/FilteringConfigurationParameterConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static java.util.function.Predicate.not;\n\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 6.0\n */\nclass FilteringConfigurationParameterConverter<T> implements ConfigurationParameterConverter<T> {\n\n\tprivate final Predicate<? super String> predicate;\n\tprivate final ConfigurationParameterConverter<T> delegate;\n\n\tstatic <T> FilteringConfigurationParameterConverter<T> exclude(Predicate<? super String> exclusion,\n\t\t\tConfigurationParameterConverter<T> delegate) {\n\t\treturn new FilteringConfigurationParameterConverter<>(not(exclusion), delegate);\n\t}\n\n\tprivate FilteringConfigurationParameterConverter(Predicate<? super String> predicate,\n\t\t\tConfigurationParameterConverter<T> delegate) {\n\t\tthis.predicate = predicate;\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic Optional<T> get(ConfigurationParameters configurationParameters, String key) {\n\t\treturn configurationParameters.get(key) //\n\t\t\t\t.map(String::strip) //\n\t\t\t\t.filter(predicate) //\n\t\t\t\t.flatMap(__ -> delegate.get(configurationParameters, key));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 5.5\n */\nclass InstantiatingConfigurationParameterConverter<T> implements ConfigurationParameterConverter<T> {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(InstantiatingConfigurationParameterConverter.class);\n\n\tprivate final Class<T> clazz;\n\tprivate final String name;\n\n\tInstantiatingConfigurationParameterConverter(Class<T> clazz, String name) {\n\t\tthis.clazz = clazz;\n\t\tthis.name = name;\n\t}\n\n\t@Override\n\tpublic Optional<T> get(ConfigurationParameters configurationParameters, String key) {\n\t\treturn supply(configurationParameters, key).get();\n\t}\n\n\tSupplier<Optional<T>> supply(ConfigurationParameters configurationParameters, String key) {\n\t\t// @formatter:off\n\t\treturn configurationParameters.get(key)\n\t\t\t\t.map(String::strip)\n\t\t\t\t.filter(className -> !className.isEmpty())\n\t\t\t\t.map(className -> newInstanceSupplier(className, key))\n\t\t\t\t.orElse(Optional::empty);\n\t\t// @formatter:on\n\t}\n\n\tprivate Supplier<Optional<T>> newInstanceSupplier(String className, String key) {\n\t\tTry<Class<?>> clazz = ReflectionSupport.tryToLoadClass(className);\n\t\t// @formatter:off\n\t\treturn () -> clazz.andThenTry(ReflectionSupport::newInstance)\n\t\t\t\t.andThenTry(this.clazz::cast)\n\t\t\t\t.ifSuccess(generator -> logSuccessMessage(className, key))\n\t\t\t\t.ifFailure(cause -> logFailureMessage(className, key, cause))\n\t\t\t\t.toOptional();\n\t\t// @formatter:on\n\t}\n\n\tprivate void logFailureMessage(String className, String key, Exception cause) {\n\t\tlogger.warn(cause, () -> \"\"\"\n\t\t\t\tFailed to load default %s class '%s' set via the '%s' configuration parameter. \\\n\t\t\t\tFalling back to default behavior.\"\"\".formatted(this.name, className, key));\n\t}\n\n\tprivate void logSuccessMessage(String className, String key) {\n\t\tlogger.config(() -> \"Using default %s '%s' set via the '%s' configuration parameter.\".formatted(this.name,\n\t\t\tclassName, key));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/JupiterConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.platform.engine.OutputDirectoryCreator;\n\n/**\n * @since 5.4\n */\n@API(status = INTERNAL, since = \"5.4\")\npublic interface JupiterConfiguration {\n\n\tPredicate<Class<? extends Extension>> getFilterForAutoDetectedExtensions();\n\n\tOptional<String> getRawConfigurationParameter(String key);\n\n\t<T> Optional<T> getRawConfigurationParameter(String key,\n\t\t\tFunction<? super String, ? extends @Nullable T> transformer);\n\n\tboolean isParallelExecutionEnabled();\n\n\tboolean isClosingStoredAutoCloseablesEnabled();\n\n\tboolean isExtensionAutoDetectionEnabled();\n\n\tboolean isThreadDumpOnTimeoutEnabled();\n\n\tExecutionMode getDefaultExecutionMode();\n\n\tExecutionMode getDefaultClassesExecutionMode();\n\n\tTestInstance.Lifecycle getDefaultTestInstanceLifecycle();\n\n\tPredicate<ExecutionCondition> getExecutionConditionFilter();\n\n\tDisplayNameGenerator getDefaultDisplayNameGenerator();\n\n\tOptional<MethodOrderer> getDefaultTestMethodOrderer();\n\n\tOptional<ClassOrderer> getDefaultTestClassOrderer();\n\n\tCleanupMode getDefaultTempDirCleanupMode();\n\n\tSupplier<TempDirFactory> getDefaultTempDirFactorySupplier();\n\n\tSupplier<TempDirDeletionStrategy> getDefaultTempDirDeletionStrategySupplier();\n\n\tExtensionContextScope getDefaultTestInstantiationExtensionContextScope();\n\n\tOutputDirectoryCreator getOutputDirectoryCreator();\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/config/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Configuration specific to the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.config;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/AbstractExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.stream.Collectors.collectingAndThen;\nimport static java.util.stream.Collectors.toCollection;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.extension.ExecutableInvoker;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.DefaultExecutableInvoker;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionContextInternal;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.hierarchical.Node;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * @since 5.0\n */\nabstract class AbstractExtensionContext<T extends TestDescriptor> implements ExtensionContextInternal, AutoCloseable {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(AbstractExtensionContext.class);\n\tprivate static final Namespace CLOSEABLE_RESOURCE_LOGGING_NAMESPACE = Namespace.create(\n\t\tAbstractExtensionContext.class, \"CloseableResourceLogging\");\n\n\tprivate final @Nullable ExtensionContext parent;\n\tprivate final EngineExecutionListener engineExecutionListener;\n\tprivate final T testDescriptor;\n\tprivate final Set<String> tags;\n\tprivate final JupiterConfiguration configuration;\n\tprivate final ExecutableInvoker executableInvoker;\n\tprivate final ExtensionRegistry extensionRegistry;\n\tprivate final LauncherStoreFacade launcherStoreFacade;\n\tprivate final NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> valuesStore;\n\n\tAbstractExtensionContext(@Nullable ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tT testDescriptor, JupiterConfiguration configuration, ExtensionRegistry extensionRegistry,\n\t\t\tLauncherStoreFacade launcherStoreFacade) {\n\n\t\tPreconditions.notNull(testDescriptor, \"TestDescriptor must not be null\");\n\t\tPreconditions.notNull(configuration, \"JupiterConfiguration must not be null\");\n\t\tPreconditions.notNull(extensionRegistry, \"ExtensionRegistry must not be null\");\n\t\tthis.executableInvoker = new DefaultExecutableInvoker(this, extensionRegistry);\n\t\tthis.parent = parent;\n\t\tthis.engineExecutionListener = engineExecutionListener;\n\t\tthis.testDescriptor = testDescriptor;\n\t\tthis.configuration = configuration;\n\t\tthis.extensionRegistry = extensionRegistry;\n\t\tthis.launcherStoreFacade = launcherStoreFacade;\n\n\t\t// @formatter:off\n\t\tthis.tags = testDescriptor.getTags().stream()\n\t\t\t\t.map(TestTag::getName)\n\t\t\t\t.collect(collectingAndThen(toCollection(LinkedHashSet::new), Collections::unmodifiableSet));\n\t\t// @formatter:on\n\n\t\tthis.valuesStore = new NamespacedHierarchicalStore<>(getParentStore(parent), createCloseAction());\n\t}\n\n\tprivate NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> getParentStore(\n\t\t\t@Nullable ExtensionContext parent) {\n\t\treturn parent == null //\n\t\t\t\t? this.launcherStoreFacade.getRequestLevelStore() //\n\t\t\t\t: ((AbstractExtensionContext<?>) parent).valuesStore;\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate <N> NamespacedHierarchicalStore.CloseAction<N> createCloseAction() {\n\t\tvar store = this.launcherStoreFacade.getSessionLevelStore(CLOSEABLE_RESOURCE_LOGGING_NAMESPACE);\n\t\treturn (__, ___, value) -> {\n\t\t\tboolean isAutoCloseEnabled = this.configuration.isClosingStoredAutoCloseablesEnabled();\n\n\t\t\tif (isAutoCloseEnabled && value instanceof @SuppressWarnings(\"resource\") AutoCloseable closeable) {\n\t\t\t\tcloseable.close();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (value instanceof Store.CloseableResource resource) {\n\t\t\t\tif (isAutoCloseEnabled) {\n\t\t\t\t\tstore.computeIfAbsent(value.getClass(), type -> {\n\t\t\t\t\t\tlogger.warn(() -> \"Type implements CloseableResource but not AutoCloseable: \" + type.getName());\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tresource.close();\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tthis.valuesStore.close();\n\t}\n\n\t@Override\n\tpublic String getUniqueId() {\n\t\treturn getTestDescriptor().getUniqueId().toString();\n\t}\n\n\t@Override\n\tpublic String getDisplayName() {\n\t\treturn getTestDescriptor().getDisplayName();\n\t}\n\n\t@Override\n\tpublic void publishReportEntry(Map<String, String> values) {\n\t\tthis.engineExecutionListener.reportingEntryPublished(this.testDescriptor, ReportEntry.from(values));\n\t}\n\n\t@Override\n\tpublic void publishFile(String name, MediaType mediaType, ThrowingConsumer<Path> action) {\n\t\tPreconditions.notBlank(name, \"name must not be null or blank\");\n\t\tPreconditions.notNull(mediaType, \"mediaType must not be null\");\n\t\tPreconditions.notNull(action, \"action must not be null\");\n\n\t\tpublishFileEntry(name, action, path -> {\n\t\t\tPreconditions.condition(Files.isRegularFile(path), () -> \"Published path must be a regular file: \" + path);\n\t\t\treturn FileEntry.from(path, mediaType.toString());\n\t\t});\n\t}\n\n\t@Override\n\tpublic void publishDirectory(String name, ThrowingConsumer<Path> action) {\n\t\tPreconditions.notBlank(name, \"name must not be null or blank\");\n\t\tPreconditions.notNull(action, \"action must not be null\");\n\n\t\tThrowingConsumer<Path> enhancedAction = path -> {\n\t\t\tif (!Files.isDirectory(path)) {\n\t\t\t\tFiles.createDirectory(path);\n\t\t\t}\n\t\t\taction.accept(path);\n\t\t};\n\t\tpublishFileEntry(name, enhancedAction, path -> {\n\t\t\tPreconditions.condition(Files.isDirectory(path), () -> \"Published path must be a directory: \" + path);\n\t\t\treturn FileEntry.from(path, null);\n\t\t});\n\t}\n\n\tprivate void publishFileEntry(String name, ThrowingConsumer<Path> action,\n\t\t\tFunction<Path, FileEntry> fileEntryCreator) {\n\t\tPath dir = createOutputDirectory();\n\t\tPath path = dir.resolve(name);\n\t\tPreconditions.condition(path.getParent() != null && path.getParent().equals(dir),\n\t\t\t() -> \"name must not contain path separators: \" + name);\n\t\ttry {\n\t\t\taction.accept(path);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tthrow new JUnitException(\"Failed to publish path\", t);\n\t\t}\n\t\tPreconditions.condition(Files.exists(path), () -> \"Published path must exist: \" + path);\n\t\tFileEntry fileEntry = fileEntryCreator.apply(path);\n\t\tthis.engineExecutionListener.fileEntryPublished(this.testDescriptor, fileEntry);\n\t}\n\n\tprivate Path createOutputDirectory() {\n\t\ttry {\n\t\t\treturn configuration.getOutputDirectoryCreator().createOutputDirectory(this.testDescriptor);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new JUnitException(\"Failed to create output directory\", e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Optional<ExtensionContext> getParent() {\n\t\treturn Optional.ofNullable(this.parent);\n\t}\n\n\t@Override\n\tpublic ExtensionContext getRoot() {\n\t\tif (this.parent != null) {\n\t\t\treturn this.parent.getRoot();\n\t\t}\n\t\treturn this;\n\t}\n\n\tprotected T getTestDescriptor() {\n\t\treturn this.testDescriptor;\n\t}\n\n\t@Override\n\tpublic Store getStore(Namespace namespace) {\n\t\treturn launcherStoreFacade.getStoreAdapter(this.valuesStore, namespace);\n\t}\n\n\t@Override\n\tpublic Store getStore(StoreScope scope, Namespace namespace) {\n\t\treturn switch (scope) {\n\t\t\tcase LAUNCHER_SESSION -> launcherStoreFacade.getSessionLevelStore(namespace);\n\t\t\tcase EXECUTION_REQUEST -> launcherStoreFacade.getRequestLevelStore(namespace);\n\t\t\tcase EXTENSION_CONTEXT -> getStore(namespace);\n\t\t};\n\t}\n\n\t@Override\n\tpublic Set<String> getTags() {\n\t\t// return modifiable copy\n\t\treturn new LinkedHashSet<>(this.tags);\n\t}\n\n\t@Override\n\tpublic Optional<String> getConfigurationParameter(String key) {\n\t\treturn this.configuration.getRawConfigurationParameter(key);\n\t}\n\n\t@Override\n\tpublic <V> Optional<V> getConfigurationParameter(String key,\n\t\t\tFunction<? super String, ? extends @Nullable V> transformer) {\n\t\treturn this.configuration.getRawConfigurationParameter(key, transformer);\n\t}\n\n\t@Override\n\tpublic ExecutionMode getExecutionMode() {\n\t\treturn toJupiterExecutionMode(getPlatformExecutionMode());\n\t}\n\n\t@Override\n\tpublic ExecutableInvoker getExecutableInvoker() {\n\t\treturn executableInvoker;\n\t}\n\n\t@Override\n\tpublic <E extends Extension> List<E> getExtensions(Class<E> extensionType) {\n\t\treturn extensionRegistry.getExtensions(extensionType);\n\t}\n\n\tprotected abstract Node.ExecutionMode getPlatformExecutionMode();\n\n\tprivate ExecutionMode toJupiterExecutionMode(Node.ExecutionMode mode) {\n\t\treturn switch (mode) {\n\t\t\tcase CONCURRENT -> ExecutionMode.CONCURRENT;\n\t\t\tcase SAME_THREAD -> ExecutionMode.SAME_THREAD;\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/CallbackSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * @since 5.13\n */\nclass CallbackSupport {\n\n\tstatic <T extends Extension> void invokeBeforeCallbacks(Class<T> type, JupiterEngineExecutionContext context,\n\t\t\tCallbackInvoker<T> callbackInvoker) {\n\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\n\t\tfor (T callback : registry.getExtensions(type)) {\n\t\t\tthrowableCollector.execute(() -> callbackInvoker.invoke(callback, extensionContext));\n\t\t\tif (throwableCollector.isNotEmpty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic <T extends Extension> void invokeAfterCallbacks(Class<T> type, JupiterEngineExecutionContext context,\n\t\t\tCallbackInvoker<T> callbackInvoker) {\n\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\n\t\tforEachInReverseOrder(registry.getExtensions(type), //\n\t\t\tcallback -> throwableCollector.execute(() -> callbackInvoker.invoke(callback, extensionContext)));\n\t}\n\n\t@FunctionalInterface\n\tprotected interface CallbackInvoker<T extends Extension> {\n\n\t\tvoid invoke(T t, ExtensionContext context) throws Throwable;\n\n\t}\n\n\tprivate CallbackSupport() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassBasedTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeBeforeCallbacks;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromConstructorParameters;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromExecutableParameters;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromInstanceFields;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromStaticFields;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterAllMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterEachMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeAllMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeEachMethods;\nimport static org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils.getTestInstanceLifecycle;\nimport static org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory.createThrowableCollector;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;\nimport org.junit.jupiter.api.extension.TestInstanceFactory;\nimport org.junit.jupiter.api.extension.TestInstanceFactoryContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestInstancePreConstructCallback;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.extension.TestInstantiationException;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.AfterEachMethodAdapter;\nimport org.junit.jupiter.engine.execution.BeforeEachMethodAdapter;\nimport org.junit.jupiter.engine.execution.DefaultTestInstances;\nimport org.junit.jupiter.engine.execution.ExtensionContextSupplier;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall.VoidMethodInterceptorCall;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.execution.TestInstancesProvider;\nimport org.junit.jupiter.engine.extension.ExtensionRegistrar;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * {@link TestDescriptor} for tests based on Java classes.\n *\n * @since 5.5\n */\n@API(status = INTERNAL, since = \"5.5\")\npublic abstract class ClassBasedTestDescriptor extends JupiterTestDescriptor\n\t\timplements ResourceLockAware, TestClassAware, Validatable {\n\n\tprivate static final InterceptingExecutableInvoker executableInvoker = new InterceptingExecutableInvoker();\n\n\tprotected final ClassInfo classInfo;\n\n\tprivate @Nullable LifecycleMethods lifecycleMethods;\n\n\tprivate @Nullable TestInstanceFactory testInstanceFactory;\n\n\tClassBasedTestDescriptor(UniqueId uniqueId, Class<?> testClass, Supplier<String> displayNameSupplier,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, displayNameSupplier, ClassSource.from(testClass), configuration);\n\n\t\tthis.classInfo = new ClassInfo(testClass, configuration);\n\t\tthis.lifecycleMethods = new LifecycleMethods(this.classInfo);\n\t}\n\n\tClassBasedTestDescriptor(UniqueId uniqueId, Class<?> testClass, String displayName,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, displayName, ClassSource.from(testClass), configuration);\n\n\t\tthis.classInfo = new ClassInfo(testClass, configuration);\n\t\tthis.lifecycleMethods = new LifecycleMethods(this.classInfo);\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic final Class<?> getTestClass() {\n\t\treturn this.classInfo.testClass;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic final Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tprotected final String getLegacyReportingBaseName() {\n\t\treturn getTestClass().getName();\n\t}\n\n\t// --- Validatable ---------------------------------------------------------\n\n\t@Override\n\tpublic final void validate(DiscoveryIssueReporter reporter) {\n\t\tvalidateCoreLifecycleMethods(reporter);\n\t\tvalidateClassTemplateInvocationLifecycleMethods(reporter);\n\t\tvalidateTags(reporter);\n\t\tvalidateDisplayNameAnnotation(reporter);\n\t}\n\n\tprivate void validateDisplayNameAnnotation(DiscoveryIssueReporter reporter) {\n\t\tDisplayNameUtils.validateAnnotation(getTestClass(), //\n\t\t\t() -> \"class '%s'\".formatted(getTestClass().getName()), //\n\t\t\t() -> getSource().orElse(null), //\n\t\t\treporter);\n\t}\n\n\tprotected void validateCoreLifecycleMethods(DiscoveryIssueReporter reporter) {\n\t\tValidatable.reportAndClear(requireLifecycleMethods().discoveryIssues, reporter);\n\t}\n\n\tprotected void validateClassTemplateInvocationLifecycleMethods(DiscoveryIssueReporter reporter) {\n\t\tLifecycleMethodUtils.validateNoClassTemplateInvocationLifecycleMethodsAreDeclared(getTestClass(), reporter);\n\t}\n\n\tprivate void validateTags(DiscoveryIssueReporter reporter) {\n\t\tValidatable.reportAndClear(this.classInfo.discoveryIssues, reporter);\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tprotected final Optional<ExecutionMode> getExplicitExecutionMode() {\n\t\treturn getExecutionModeFromAnnotation(getTestClass());\n\t}\n\n\t@Override\n\tprotected final Optional<ExecutionMode> getDefaultChildExecutionMode() {\n\t\treturn Optional.ofNullable(this.classInfo.defaultChildExecutionMode);\n\t}\n\n\tpublic final void setDefaultChildExecutionMode(ExecutionMode defaultChildExecutionMode) {\n\t\tthis.classInfo.defaultChildExecutionMode = defaultChildExecutionMode;\n\t}\n\n\t@Override\n\tpublic final ExclusiveResourceCollector getExclusiveResourceCollector() {\n\t\treturn this.classInfo.exclusiveResourceCollector;\n\t}\n\n\t@Override\n\tpublic final JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(\n\t\t\tcontext.getExtensionRegistry(), getTestClass());\n\n\t\t// Register extensions from static fields here, at the class level but\n\t\t// after extensions registered via @ExtendWith.\n\t\tregisterExtensionsFromStaticFields(registry, getTestClass());\n\n\t\t// Resolve the TestInstanceFactory at the class level in order to fail\n\t\t// the entire class in case of configuration errors (e.g., more than\n\t\t// one factory registered per class).\n\t\tthis.testInstanceFactory = resolveTestInstanceFactory(registry);\n\n\t\tif (this.testInstanceFactory == null) {\n\t\t\tregisterExtensionsFromConstructorParameters(registry, getTestClass());\n\t\t}\n\n\t\trequireLifecycleMethods().beforeAll.forEach(\n\t\t\tmethod -> registerExtensionsFromExecutableParameters(registry, method));\n\t\t// Since registerBeforeEachMethodAdapters() and registerAfterEachMethodAdapters() also\n\t\t// invoke registerExtensionsFromExecutableParameters(), we invoke those methods before\n\t\t// invoking registerExtensionsFromExecutableParameters() for @AfterAll methods,\n\t\t// thereby ensuring proper registration order for extensions registered via @ExtendWith\n\t\t// on parameters in lifecycle methods.\n\t\tregisterBeforeEachMethodAdapters(registry);\n\t\tregisterAfterEachMethodAdapters(registry);\n\t\trequireLifecycleMethods().afterAll.forEach(\n\t\t\tmethod -> registerExtensionsFromExecutableParameters(registry, method));\n\t\tregisterExtensionsFromInstanceFields(registry, getTestClass());\n\n\t\tThrowableCollector throwableCollector = createThrowableCollector();\n\t\tClassExtensionContext extensionContext = new ClassExtensionContext(context.getExtensionContext(),\n\t\t\tcontext.getExecutionListener(), this, this.classInfo.lifecycle, context.getConfiguration(), registry,\n\t\t\tcontext.getLauncherStoreFacade(), throwableCollector);\n\n\t\t// @formatter:off\n\t\treturn context.extend()\n\t\t\t\t.withTestInstancesProvider(testInstancesProvider(context, extensionContext))\n\t\t\t\t.withExtensionRegistry(registry)\n\t\t\t\t.withExtensionContext(extensionContext)\n\t\t\t\t.withThrowableCollector(throwableCollector)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic final JupiterEngineExecutionContext before(JupiterEngineExecutionContext context) {\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\n\t\tif (isPerClassLifecycle(context)) {\n\t\t\t// Eagerly load test instance for BeforeAllCallbacks, if necessary,\n\t\t\t// and store the instance in the ExtensionContext.\n\t\t\tClassExtensionContext extensionContext = (ClassExtensionContext) context.getExtensionContext();\n\t\t\tthrowableCollector.execute(() -> {\n\t\t\t\tTestInstances testInstances = context.getTestInstancesProvider().getTestInstances(context);\n\t\t\t\textensionContext.setTestInstances(testInstances);\n\t\t\t});\n\t\t}\n\n\t\tif (throwableCollector.isEmpty()) {\n\t\t\tcontext.beforeAllCallbacksExecuted(true);\n\t\t\tinvokeBeforeAllCallbacks(context);\n\n\t\t\tif (throwableCollector.isEmpty()) {\n\t\t\t\tcontext.beforeAllMethodsExecuted(true);\n\t\t\t\tinvokeBeforeAllMethods(context);\n\t\t\t}\n\t\t}\n\n\t\tthrowableCollector.assertEmpty();\n\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic final void after(JupiterEngineExecutionContext context) {\n\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\t\tThrowable previousThrowable = throwableCollector.getThrowable();\n\n\t\tif (context.beforeAllMethodsExecuted()) {\n\t\t\tinvokeAfterAllMethods(context);\n\t\t}\n\n\t\tif (context.beforeAllCallbacksExecuted()) {\n\t\t\tinvokeAfterAllCallbacks(context);\n\t\t}\n\n\t\tif (isPerClassLifecycle(context) && context.getExtensionContext().getTestInstance().isPresent()) {\n\t\t\tinvokeTestInstancePreDestroyCallbacks(context);\n\t\t}\n\n\t\t// If the previous Throwable was not null when this method was called,\n\t\t// that means an exception was already thrown either before or during\n\t\t// the execution of this Node. If an exception was already thrown, any\n\t\t// later exceptions were added as suppressed exceptions to that original\n\t\t// exception unless a more severe exception occurred in the meantime.\n\t\tif (previousThrowable != throwableCollector.getThrowable()) {\n\t\t\tthrowableCollector.assertEmpty();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\tsuper.cleanUp(context);\n\t\tthis.lifecycleMethods = null;\n\t\tthis.testInstanceFactory = null;\n\t}\n\n\tprivate @Nullable TestInstanceFactory resolveTestInstanceFactory(ExtensionRegistry registry) {\n\t\tList<TestInstanceFactory> factories = registry.getExtensions(TestInstanceFactory.class);\n\n\t\tif (factories.size() == 1) {\n\t\t\treturn factories.get(0);\n\t\t}\n\n\t\tif (factories.size() > 1) {\n\t\t\tString factoryNames = factories.stream()//\n\t\t\t\t\t.map(factory -> factory.getClass().getName())//\n\t\t\t\t\t.collect(joining(\", \"));\n\n\t\t\tString errorMessage = \"The following TestInstanceFactory extensions were registered for test class [%s], but only one is permitted: %s\".formatted(\n\t\t\t\tgetTestClass().getName(), factoryNames);\n\n\t\t\tthrow new ExtensionConfigurationException(errorMessage);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate TestInstancesProvider testInstancesProvider(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tClassExtensionContext ourExtensionContext) {\n\n\t\t// For Lifecycle.PER_CLASS, ourExtensionContext.getTestInstances() is used to store the instance.\n\t\t// Otherwise, extensionContext.getTestInstances() is always empty and we always create a new instance.\n\t\treturn (registry, context) -> ourExtensionContext.getTestInstances().orElseGet(\n\t\t\t() -> instantiateAndPostProcessTestInstance(parentExecutionContext, ourExtensionContext, registry,\n\t\t\t\tcontext));\n\t}\n\n\tprivate TestInstances instantiateAndPostProcessTestInstance(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tClassExtensionContext ourExtensionContext, ExtensionRegistry registry,\n\t\t\tJupiterEngineExecutionContext context) {\n\n\t\tExtensionContextSupplier extensionContext = ExtensionContextSupplier.create(context.getExtensionContext(),\n\t\t\tourExtensionContext, configuration);\n\t\tTestInstances instances = instantiateTestClass(parentExecutionContext, extensionContext, registry, context);\n\t\tcontext.getThrowableCollector().execute(() -> {\n\t\t\tinvokeTestInstancePostProcessors(instances.getInnermostInstance(), registry, extensionContext);\n\t\t\t// In addition, we initialize extension registered programmatically from instance fields here\n\t\t\t// since the best time to do that is immediately following test class instantiation\n\t\t\t// and post-processing.\n\t\t\tcontext.getExtensionRegistry().initializeExtensions(getTestClass(), instances.getInnermostInstance());\n\t\t});\n\t\treturn instances;\n\t}\n\n\tprotected abstract TestInstances instantiateTestClass(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry registry,\n\t\t\tJupiterEngineExecutionContext context);\n\n\tprotected final TestInstances instantiateTestClass(Optional<TestInstances> outerInstances,\n\t\t\tExtensionRegistry registry, ExtensionContextSupplier extensionContext) {\n\n\t\tOptional<Object> outerInstance = outerInstances.map(TestInstances::getInnermostInstance);\n\t\tinvokeTestInstancePreConstructCallbacks(new DefaultTestInstanceFactoryContext(getTestClass(), outerInstance),\n\t\t\tregistry, extensionContext);\n\t\tObject instance = this.testInstanceFactory != null //\n\t\t\t\t? invokeTestInstanceFactory(this.testInstanceFactory, outerInstance, extensionContext) //\n\t\t\t\t: invokeTestClassConstructor(outerInstance, registry, extensionContext);\n\t\treturn outerInstances.map(instances -> DefaultTestInstances.of(instances, instance)) //\n\t\t\t\t.orElse(DefaultTestInstances.of(instance));\n\t}\n\n\tprivate Object invokeTestInstanceFactory(TestInstanceFactory testInstanceFactory, Optional<Object> outerInstance,\n\t\t\tExtensionContextSupplier extensionContext) {\n\t\tObject instance;\n\n\t\ttry {\n\t\t\tExtensionContext actualExtensionContext = extensionContext.get(testInstanceFactory);\n\t\t\tinstance = testInstanceFactory.createTestInstance(\n\t\t\t\tnew DefaultTestInstanceFactoryContext(getTestClass(), outerInstance), actualExtensionContext);\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\n\t\t\tif (throwable instanceof TestInstantiationException exception) {\n\t\t\t\tthrow exception;\n\t\t\t}\n\n\t\t\tString message = \"TestInstanceFactory [%s] failed to instantiate test class [%s]\".formatted(\n\t\t\t\ttestInstanceFactory.getClass().getName(), getTestClass().getName());\n\t\t\tif (StringUtils.isNotBlank(throwable.getMessage())) {\n\t\t\t\tmessage += \": \" + throwable.getMessage();\n\t\t\t}\n\t\t\tthrow new TestInstantiationException(message, throwable);\n\t\t}\n\n\t\tif (!getTestClass().isInstance(instance)) {\n\t\t\tString testClassName = getTestClass().getName();\n\t\t\tClass<?> instanceClass = (instance == null ? null : instance.getClass());\n\t\t\tString instanceClassName = (instanceClass == null ? \"null\" : instanceClass.getName());\n\n\t\t\t// If the test instance was loaded via a different ClassLoader, append\n\t\t\t// the identity hash codes to the type names to help users disambiguate\n\t\t\t// between otherwise identical \"fully qualified class names\".\n\t\t\tif (testClassName.equals(instanceClassName)) {\n\t\t\t\ttestClassName += \"@\" + Integer.toHexString(System.identityHashCode(getTestClass()));\n\t\t\t\tinstanceClassName += \"@\" + Integer.toHexString(System.identityHashCode(instanceClass));\n\t\t\t}\n\t\t\tString message = \"TestInstanceFactory [%s] failed to return an instance of [%s] and instead returned an instance of [%s].\".formatted(\n\t\t\t\ttestInstanceFactory.getClass().getName(), testClassName, instanceClassName);\n\n\t\t\tthrow new TestInstantiationException(message);\n\t\t}\n\n\t\treturn instance;\n\t}\n\n\tprivate Object invokeTestClassConstructor(Optional<Object> outerInstance, ExtensionRegistry registry,\n\t\t\tExtensionContextSupplier extensionContext) {\n\n\t\tConstructor<?> constructor = ReflectionUtils.getDeclaredConstructor(getTestClass());\n\t\treturn executableInvoker.invoke(constructor, outerInstance, extensionContext, registry,\n\t\t\tInvocationInterceptor::interceptTestClassConstructor);\n\t}\n\n\tprivate void invokeTestInstancePreConstructCallbacks(TestInstanceFactoryContext factoryContext,\n\t\t\tExtensionRegistry registry, ExtensionContextSupplier context) {\n\t\tregistry.stream(TestInstancePreConstructCallback.class).forEach(extension -> executeAndMaskThrowable(\n\t\t\t() -> extension.preConstructTestInstance(factoryContext, context.get(extension))));\n\t}\n\n\tprivate void invokeTestInstancePostProcessors(Object instance, ExtensionRegistry registry,\n\t\t\tExtensionContextSupplier context) {\n\n\t\tregistry.stream(TestInstancePostProcessor.class).forEach(extension -> executeAndMaskThrowable(\n\t\t\t() -> extension.postProcessTestInstance(instance, context.get(extension))));\n\t}\n\n\tprivate void executeAndMaskThrowable(Executable executable) {\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(throwable);\n\t\t}\n\t}\n\n\tprivate void invokeBeforeAllCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeBeforeCallbacks(BeforeAllCallback.class, context, BeforeAllCallback::beforeAll);\n\t}\n\n\tprivate void invokeBeforeAllMethods(JupiterEngineExecutionContext context) {\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\t\tObject testInstance = extensionContext.getTestInstance().orElse(null);\n\n\t\tfor (Method method : requireLifecycleMethods().beforeAll) {\n\t\t\tthrowableCollector.execute(() -> {\n\t\t\t\ttry {\n\t\t\t\t\texecutableInvoker.invokeVoid(method, testInstance, extensionContext, registry,\n\t\t\t\t\t\tInvocationInterceptor::interceptBeforeAllMethod);\n\t\t\t\t}\n\t\t\t\tcatch (Throwable throwable) {\n\t\t\t\t\tinvokeBeforeAllMethodExecutionExceptionHandlers(registry, extensionContext, throwable);\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (throwableCollector.isNotEmpty()) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void invokeBeforeAllMethodExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,\n\t\t\tThrowable throwable) {\n\n\t\tinvokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,\n\t\t\t(handler, handledThrowable) -> handler.handleBeforeAllMethodExecutionException(context, handledThrowable));\n\t}\n\n\tprivate void invokeAfterAllMethods(JupiterEngineExecutionContext context) {\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\t\tObject testInstance = extensionContext.getTestInstance().orElse(null);\n\n\t\trequireLifecycleMethods().afterAll.forEach(method -> throwableCollector.execute(() -> {\n\t\t\ttry {\n\t\t\t\texecutableInvoker.invokeVoid(method, testInstance, extensionContext, registry,\n\t\t\t\t\tInvocationInterceptor::interceptAfterAllMethod);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tinvokeAfterAllMethodExecutionExceptionHandlers(registry, extensionContext, throwable);\n\t\t\t}\n\t\t}));\n\t}\n\n\tprivate void invokeAfterAllMethodExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,\n\t\t\tThrowable throwable) {\n\n\t\tinvokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,\n\t\t\t(handler, handledThrowable) -> handler.handleAfterAllMethodExecutionException(context, handledThrowable));\n\t}\n\n\tprivate void invokeAfterAllCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeAfterCallbacks(AfterAllCallback.class, context, AfterAllCallback::afterAll);\n\t}\n\n\tprivate void invokeTestInstancePreDestroyCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeAfterCallbacks(TestInstancePreDestroyCallback.class, context,\n\t\t\tTestInstancePreDestroyCallback::preDestroyTestInstance);\n\t}\n\n\tprivate boolean isPerClassLifecycle(JupiterEngineExecutionContext context) {\n\t\treturn context.getExtensionContext().getTestInstanceLifecycle().orElse(\n\t\t\tLifecycle.PER_METHOD) == Lifecycle.PER_CLASS;\n\t}\n\n\tprivate void registerBeforeEachMethodAdapters(ExtensionRegistrar registrar) {\n\t\tregisterMethodsAsExtensions(requireLifecycleMethods().beforeEach, registrar,\n\t\t\tthis::synthesizeBeforeEachMethodAdapter);\n\t}\n\n\tprivate void registerAfterEachMethodAdapters(ExtensionRegistrar registrar) {\n\t\t// Make a local copy since findAfterEachMethods() returns an immutable list.\n\t\tList<Method> afterEachMethods = new ArrayList<>(requireLifecycleMethods().afterEach);\n\n\t\t// Since the bottom-up ordering of afterEachMethods will later be reversed when the\n\t\t// synthesized AfterEachMethodAdapters are executed within TestMethodTestDescriptor,\n\t\t// we have to reverse the afterEachMethods list to put them in top-down order before\n\t\t// we register them as synthesized extensions.\n\t\tCollections.reverse(afterEachMethods);\n\n\t\tregisterMethodsAsExtensions(afterEachMethods, registrar, this::synthesizeAfterEachMethodAdapter);\n\t}\n\n\tprivate void registerMethodsAsExtensions(List<Method> methods, ExtensionRegistrar registrar,\n\t\t\tFunction<Method, Extension> extensionSynthesizer) {\n\n\t\tmethods.forEach(method -> {\n\t\t\tregisterExtensionsFromExecutableParameters(registrar, method);\n\t\t\tregistrar.registerSyntheticExtension(extensionSynthesizer.apply(method), method);\n\t\t});\n\t}\n\n\tprivate BeforeEachMethodAdapter synthesizeBeforeEachMethodAdapter(Method method) {\n\t\treturn (extensionContext, registry) -> invokeMethodInExtensionContext(method, extensionContext, registry,\n\t\t\tInvocationInterceptor::interceptBeforeEachMethod);\n\t}\n\n\tprivate AfterEachMethodAdapter synthesizeAfterEachMethodAdapter(Method method) {\n\t\treturn (extensionContext, registry) -> invokeMethodInExtensionContext(method, extensionContext, registry,\n\t\t\tInvocationInterceptor::interceptAfterEachMethod);\n\t}\n\n\tprivate void invokeMethodInExtensionContext(Method method, ExtensionContext context, ExtensionRegistry registry,\n\t\t\tVoidMethodInterceptorCall interceptorCall) {\n\t\tTestInstances testInstances = context.getRequiredTestInstances();\n\t\tObject target = testInstances.findInstance(getTestClass()).orElseThrow(\n\t\t\t() -> new JUnitException(\"Failed to find instance for method: \" + method.toGenericString()));\n\n\t\texecutableInvoker.invokeVoid(method, target, context, registry, interceptorCall);\n\t}\n\n\tprivate LifecycleMethods requireLifecycleMethods() {\n\t\treturn requireNonNull(this.lifecycleMethods);\n\t}\n\n\tprotected static class ClassInfo {\n\n\t\tprivate final List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\n\t\tfinal Class<?> testClass;\n\t\tfinal Set<TestTag> tags;\n\t\tfinal Lifecycle lifecycle;\n\n\t\t@Nullable\n\t\tExecutionMode defaultChildExecutionMode;\n\n\t\tfinal ExclusiveResourceCollector exclusiveResourceCollector;\n\n\t\tClassInfo(Class<?> testClass, JupiterConfiguration configuration) {\n\t\t\tthis.testClass = testClass;\n\t\t\tthis.tags = getTags(testClass, //\n\t\t\t\t() -> \"class '%s'\".formatted(testClass.getName()), //\n\t\t\t\t() -> ClassSource.from(testClass), //\n\t\t\t\tdiscoveryIssues::add);\n\t\t\tthis.lifecycle = getTestInstanceLifecycle(testClass, configuration);\n\t\t\tthis.defaultChildExecutionMode = (this.lifecycle == Lifecycle.PER_CLASS ? ExecutionMode.SAME_THREAD : null);\n\t\t\tthis.exclusiveResourceCollector = ExclusiveResourceCollector.from(testClass);\n\t\t}\n\t}\n\n\tprivate static class LifecycleMethods {\n\n\t\tprivate final List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\n\t\tprivate final List<Method> beforeAll;\n\t\tprivate final List<Method> afterAll;\n\t\tprivate final List<Method> beforeEach;\n\t\tprivate final List<Method> afterEach;\n\n\t\tLifecycleMethods(ClassInfo classInfo) {\n\t\t\tClass<?> testClass = classInfo.testClass;\n\t\t\tboolean requireStatic = classInfo.lifecycle == Lifecycle.PER_METHOD;\n\t\t\tDiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.collecting(discoveryIssues);\n\t\t\tthis.beforeAll = findBeforeAllMethods(testClass, requireStatic, issueReporter);\n\t\t\tthis.afterAll = findAfterAllMethods(testClass, requireStatic, issueReporter);\n\t\t\tthis.beforeEach = findBeforeEachMethods(testClass, issueReporter);\n\t\t\tthis.afterEach = findAfterEachMethods(testClass, issueReporter);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * @since 5.0\n */\nfinal class ClassExtensionContext extends AbstractExtensionContext<ClassBasedTestDescriptor> {\n\n\tprivate final Lifecycle lifecycle;\n\n\tprivate final ThrowableCollector throwableCollector;\n\n\tprivate @Nullable TestInstances testInstances;\n\n\tClassExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tClassBasedTestDescriptor testDescriptor, Lifecycle lifecycle, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade,\n\t\t\tThrowableCollector throwableCollector) {\n\n\t\tsuper(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\n\t\tthis.lifecycle = lifecycle;\n\t\tthis.throwableCollector = throwableCollector;\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getTestDescriptor().getEnclosingTestClasses();\n\t}\n\n\t@Override\n\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\treturn Optional.of(this.lifecycle);\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn getTestInstances().map(TestInstances::getInnermostInstance);\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn Optional.ofNullable(testInstances);\n\t}\n\n\tvoid setTestInstances(TestInstances testInstances) {\n\t\tthis.testInstances = testInstances;\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.ofNullable(this.throwableCollector.getThrowable());\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.13\n */\nfinal class ClassTemplateInvocationExtensionContext\n\t\textends AbstractExtensionContext<ClassTemplateInvocationTestDescriptor> {\n\n\tClassTemplateInvocationExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tClassTemplateInvocationTestDescriptor testDescriptor, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade) {\n\t\tsuper(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getTestDescriptor().getEnclosingTestClasses();\n\t}\n\n\t@Override\n\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\treturn getParent().flatMap(ExtensionContext::getTestInstanceLifecycle);\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn getParent().flatMap(ExtensionContext::getTestInstance);\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn getParent().flatMap(ExtensionContext::getTestInstances);\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateInvocationTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeBeforeCallbacks;\nimport static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryFrom;\nimport static org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory.createThrowableCollector;\n\nimport java.util.List;\nimport java.util.OptionalInt;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\npublic class ClassTemplateInvocationTestDescriptor extends JupiterTestDescriptor\n\t\timplements TestClassAware, ResourceLockAware {\n\n\tpublic static final String SEGMENT_TYPE = \"class-template-invocation\";\n\n\tprivate final ClassTemplateTestDescriptor parent;\n\n\tprivate @Nullable ClassTemplateInvocationContext invocationContext;\n\n\tprivate final int index;\n\n\tpublic ClassTemplateInvocationTestDescriptor(UniqueId uniqueId, ClassTemplateTestDescriptor parent,\n\t\t\tClassTemplateInvocationContext invocationContext, int index, @Nullable TestSource source,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, invocationContext.getDisplayName(index), source, configuration);\n\t\tthis.parent = parent;\n\t\tthis.invocationContext = invocationContext;\n\t\tthis.index = index;\n\t}\n\n\tpublic int getIndex() {\n\t\treturn index;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected ClassTemplateInvocationTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new ClassTemplateInvocationTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), parent,\n\t\t\trequiredInvocationContext(), this.index, getSource().orElse(null), this.configuration);\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tprotected String getLegacyReportingBaseName() {\n\t\treturn getTestClass().getName();\n\t}\n\n\t@Override\n\tprotected OptionalInt getLegacyReportingIndex() {\n\t\treturn OptionalInt.of(index);\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic Class<?> getTestClass() {\n\t\treturn parent.getTestClass();\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn parent.getEnclosingTestClasses();\n\t}\n\n\t// --- ResourceLockAware ---------------------------------------------------\n\n\t@Override\n\tpublic ExclusiveResourceCollector getExclusiveResourceCollector() {\n\t\treturn parent.getExclusiveResourceCollector();\n\t}\n\n\t@Override\n\tpublic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {\n\t\treturn parent.getResourceLocksProviderEvaluator();\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic Set<ExclusiveResource> getExclusiveResources() {\n\t\t// Resources are already collected and returned by the enclosing ClassTemplateTestDescriptor\n\t\treturn emptySet();\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = context.getExtensionRegistry();\n\t\tList<Extension> additionalExtensions = requiredInvocationContext().getAdditionalExtensions();\n\t\tif (!additionalExtensions.isEmpty()) {\n\t\t\tMutableExtensionRegistry childRegistry = createRegistryFrom(registry, Stream.empty());\n\t\t\tadditionalExtensions.forEach(\n\t\t\t\textension -> childRegistry.registerExtension(extension, requiredInvocationContext()));\n\t\t\tregistry = childRegistry;\n\t\t}\n\t\tExtensionContext extensionContext = new ClassTemplateInvocationExtensionContext(context.getExtensionContext(),\n\t\t\tcontext.getExecutionListener(), this, context.getConfiguration(), registry,\n\t\t\tcontext.getLauncherStoreFacade());\n\t\tThrowableCollector throwableCollector = createThrowableCollector();\n\t\tthrowableCollector.execute(() -> requiredInvocationContext().prepareInvocation(extensionContext));\n\t\treturn context.extend() //\n\t\t\t\t.withExtensionRegistry(registry) //\n\t\t\t\t.withExtensionContext(extensionContext) //\n\t\t\t\t.withThrowableCollector(throwableCollector) //\n\t\t\t\t.build();\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(JupiterEngineExecutionContext context) {\n\t\tcontext.getThrowableCollector().assertEmpty();\n\t\treturn SkipResult.doNotSkip();\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext before(JupiterEngineExecutionContext context) throws Exception {\n\t\tinvokeBeforeCallbacks(BeforeClassTemplateInvocationCallback.class, context,\n\t\t\tBeforeClassTemplateInvocationCallback::beforeClassTemplateInvocation);\n\t\tcontext.getThrowableCollector().assertEmpty();\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) throws Exception {\n\t\tVisitor visitor = context.getExecutionListener()::dynamicTestRegistered;\n\t\tgetChildren().forEach(child -> child.accept(visitor));\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic void after(JupiterEngineExecutionContext context) throws Exception {\n\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\t\tThrowable previousThrowable = throwableCollector.getThrowable();\n\n\t\tinvokeAfterCallbacks(AfterClassTemplateInvocationCallback.class, context,\n\t\t\tAfterClassTemplateInvocationCallback::afterClassTemplateInvocation);\n\n\t\t// If the previous Throwable was not null when this method was called,\n\t\t// that means an exception was already thrown either before or during\n\t\t// the execution of this Node. If an exception was already thrown, any\n\t\t// later exceptions were added as suppressed exceptions to that original\n\t\t// exception unless a more severe exception occurred in the meantime.\n\t\tif (previousThrowable != throwableCollector.getThrowable()) {\n\t\t\tthrowableCollector.assertEmpty();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\t// forget invocationContext so it can be garbage collected\n\t\tthis.invocationContext = null;\n\t\tsuper.cleanUp(context);\n\t}\n\n\tprivate ClassTemplateInvocationContext requiredInvocationContext() {\n\t\treturn requireNonNull(this.invocationContext);\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTemplateTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.stream.Collectors.toCollection;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.validateClassTemplateInvocationLifecycleMethodsAreDeclaredCorrectly;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.jupiter.engine.execution.ExtensionContextSupplier;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\npublic class ClassTemplateTestDescriptor extends ClassBasedTestDescriptor implements Filterable {\n\n\tpublic static final String STANDALONE_CLASS_SEGMENT_TYPE = \"class-template\";\n\tpublic static final String NESTED_CLASS_SEGMENT_TYPE = \"nested-class-template\";\n\n\tprivate final Map<Integer, Collection<? extends TestDescriptor>> childrenPrototypesByIndex = new HashMap<>();\n\tprivate final List<TestDescriptor> childrenPrototypes = new ArrayList<>();\n\tprivate final ClassBasedTestDescriptor delegate;\n\tprivate final DynamicDescendantFilter dynamicDescendantFilter;\n\n\tpublic ClassTemplateTestDescriptor(UniqueId uniqueId, ClassBasedTestDescriptor delegate) {\n\t\tthis(uniqueId, delegate, new DynamicDescendantFilter());\n\t}\n\n\tprivate ClassTemplateTestDescriptor(UniqueId uniqueId, ClassBasedTestDescriptor delegate,\n\t\t\tDynamicDescendantFilter dynamicDescendantFilter) {\n\t\tsuper(uniqueId, delegate.getTestClass(), delegate.getDisplayName(), delegate.configuration);\n\t\tthis.delegate = delegate;\n\t\tthis.dynamicDescendantFilter = dynamicDescendantFilter;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\treturn this.delegate.getTags();\n\t}\n\n\t// --- Validatable ---------------------------------------------------------\n\n\t@Override\n\tprotected void validateCoreLifecycleMethods(DiscoveryIssueReporter reporter) {\n\t\tthis.delegate.validateCoreLifecycleMethods(reporter);\n\t}\n\n\t@Override\n\tprotected void validateClassTemplateInvocationLifecycleMethods(DiscoveryIssueReporter reporter) {\n\t\tboolean requireStatic = this.classInfo.lifecycle == PER_METHOD;\n\t\tvalidateClassTemplateInvocationLifecycleMethodsAreDeclaredCorrectly(getTestClass(), requireStatic, reporter);\n\t}\n\n\t// --- Filterable ----------------------------------------------------------\n\n\t@Override\n\tpublic DynamicDescendantFilter getDynamicDescendantFilter() {\n\t\treturn this.dynamicDescendantFilter;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected JupiterTestDescriptor copyIncludingDescendants(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\tClassTemplateTestDescriptor copy = (ClassTemplateTestDescriptor) super.copyIncludingDescendants(\n\t\t\tuniqueIdTransformer);\n\t\tthis.childrenPrototypes.forEach(oldChild -> {\n\t\t\tTestDescriptor newChild = ((JupiterTestDescriptor) oldChild).copyIncludingDescendants(uniqueIdTransformer);\n\t\t\tcopy.childrenPrototypes.add(newChild);\n\t\t});\n\t\tthis.childrenPrototypesByIndex.forEach((index, oldChildren) -> {\n\t\t\tList<? extends TestDescriptor> newChildren = oldChildren.stream() //\n\t\t\t\t\t.map(oldChild -> ((JupiterTestDescriptor) oldChild).copyIncludingDescendants(uniqueIdTransformer)) //\n\t\t\t\t\t.toList();\n\t\t\tcopy.childrenPrototypesByIndex.put(index, newChildren);\n\t\t});\n\t\treturn copy;\n\t}\n\n\t@Override\n\tprotected ClassTemplateTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new ClassTemplateTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), this.delegate,\n\t\t\tthis.dynamicDescendantFilter.copy(uniqueIdTransformer));\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic void prune() {\n\t\tsuper.prune();\n\t\tif (this.children.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\t// Create copy to avoid ConcurrentModificationException\n\t\tnew LinkedHashSet<>(this.children).forEach(child -> child.accept(TestDescriptor::prune));\n\t\t// Second iteration to avoid processing children that were pruned in the first iteration\n\t\tthis.children.forEach(child -> {\n\t\t\tif (child instanceof ClassTemplateInvocationTestDescriptor descriptor) {\n\t\t\t\tint index = descriptor.getIndex();\n\t\t\t\tthis.dynamicDescendantFilter.allowIndex(index - 1);\n\t\t\t\tthis.childrenPrototypesByIndex.put(index, child.getChildren());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.childrenPrototypes.add(child);\n\t\t\t}\n\t\t});\n\t\tthis.children.clear();\n\t}\n\n\t@Override\n\tpublic boolean mayRegisterTests() {\n\t\treturn !childrenPrototypes.isEmpty() || !childrenPrototypesByIndex.isEmpty();\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn delegate.getEnclosingTestClasses();\n\t}\n\n\t// --- ClassBasedTestDescriptor --------------------------------------------\n\n\t@Override\n\tpublic TestInstances instantiateTestClass(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry registry,\n\t\t\tJupiterEngineExecutionContext context) {\n\t\treturn delegate.instantiateTestClass(parentExecutionContext, extensionContext, registry, context);\n\t}\n\n\t// --- ResourceLockAware ---------------------------------------------------\n\n\t@Override\n\tpublic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {\n\t\treturn delegate.getResourceLocksProviderEvaluator();\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic Set<ExclusiveResource> getExclusiveResources() {\n\t\tSet<ExclusiveResource> result = determineExclusiveResources().collect(toCollection(HashSet::new));\n\t\tVisitor visitor = testDescriptor -> {\n\t\t\tif (testDescriptor instanceof Node<?> node) {\n\t\t\t\tresult.addAll(node.getExclusiveResources());\n\t\t\t}\n\t\t};\n\t\tthis.childrenPrototypes.forEach(child -> child.accept(visitor));\n\t\tthis.childrenPrototypesByIndex.values() //\n\t\t\t\t.forEach(prototypes -> prototypes //\n\t\t\t\t\t\t.forEach(child -> child.accept(visitor)));\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\tthis.childrenPrototypes.clear();\n\t\tthis.childrenPrototypesByIndex.clear();\n\t\tthis.dynamicDescendantFilter.allowAll();\n\t\tsuper.cleanUp(context);\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) throws Exception {\n\n\t\tnew ClassTemplateExecutor().execute(context, dynamicTestExecutor);\n\t\treturn context;\n\t}\n\n\tclass ClassTemplateExecutor\n\t\t\textends TemplateExecutor<ClassTemplateInvocationContextProvider, ClassTemplateInvocationContext> {\n\n\t\tClassTemplateExecutor() {\n\t\t\tsuper(ClassTemplateTestDescriptor.this, ClassTemplateInvocationContextProvider.class);\n\t\t}\n\n\t\t@Override\n\t\tboolean supports(ClassTemplateInvocationContextProvider provider, ExtensionContext extensionContext) {\n\t\t\treturn provider.supportsClassTemplate(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tprotected String getNoRegisteredProviderErrorMessage() {\n\t\t\treturn \"You must register at least one %s that supports @%s class [%s]\".formatted(\n\t\t\t\tClassTemplateInvocationContextProvider.class.getSimpleName(), ClassTemplate.class.getSimpleName(),\n\t\t\t\tgetTestClass().getName());\n\t\t}\n\n\t\t@Override\n\t\tStream<? extends ClassTemplateInvocationContext> provideContexts(\n\t\t\t\tClassTemplateInvocationContextProvider provider, ExtensionContext extensionContext) {\n\t\t\treturn provider.provideClassTemplateInvocationContexts(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tboolean mayReturnZeroContexts(ClassTemplateInvocationContextProvider provider,\n\t\t\t\tExtensionContext extensionContext) {\n\t\t\treturn provider.mayReturnZeroClassTemplateInvocationContexts(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tprotected String getZeroContextsProvidedErrorMessage(ClassTemplateInvocationContextProvider provider) {\n\t\t\treturn \"\"\"\n\t\t\t\t\tProvider [%s] did not provide any invocation contexts, but was expected to do so. \\\n\t\t\t\t\tYou may override mayReturnZeroClassTemplateInvocationContexts() to allow this.\"\"\".formatted(\n\t\t\t\tprovider.getClass().getSimpleName());\n\t\t}\n\n\t\t@Override\n\t\tUniqueId createInvocationUniqueId(UniqueId parentUniqueId, int index) {\n\t\t\treturn parentUniqueId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#\" + index);\n\t\t}\n\n\t\t@Override\n\t\tTestDescriptor createInvocationTestDescriptor(UniqueId uniqueId,\n\t\t\t\tClassTemplateInvocationContext invocationContext, int index) {\n\t\t\tClassTemplateInvocationTestDescriptor containerInvocationDescriptor = new ClassTemplateInvocationTestDescriptor(\n\t\t\t\tuniqueId, ClassTemplateTestDescriptor.this, invocationContext, index, getSource().orElse(null),\n\t\t\t\tClassTemplateTestDescriptor.this.configuration);\n\n\t\t\tcollectChildren(index, uniqueId) //\n\t\t\t\t\t.forEach(containerInvocationDescriptor::addChild);\n\n\t\t\treturn containerInvocationDescriptor;\n\t\t}\n\n\t\tprivate Stream<? extends TestDescriptor> collectChildren(int index, UniqueId invocationUniqueId) {\n\t\t\tif (ClassTemplateTestDescriptor.this.childrenPrototypesByIndex.containsKey(index)) {\n\t\t\t\treturn ClassTemplateTestDescriptor.this.childrenPrototypesByIndex.remove(index).stream();\n\t\t\t}\n\t\t\tUnaryOperator<UniqueId> transformer = new UniqueIdPrefixTransformer(getUniqueId(), invocationUniqueId);\n\t\t\treturn ClassTemplateTestDescriptor.this.childrenPrototypes.stream() //\n\t\t\t\t\t.map(JupiterTestDescriptor.class::cast) //\n\t\t\t\t\t.map(it -> it.copyIncludingDescendants(transformer));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.DisplayNameUtils.createDisplayNameSupplierForClass;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.ExtensionContextSupplier;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@link TestDescriptor} for tests based on Java classes.\n *\n * <h2>Default Display Names</h2>\n *\n * <p>The default display name for a top-level or nested static test class is\n * the fully qualified name of the class with the package name and leading dot\n * (\".\") removed.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class ClassTestDescriptor extends ClassBasedTestDescriptor {\n\n\tpublic static final String SEGMENT_TYPE = \"class\";\n\n\tpublic ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, createDisplayNameSupplierForClass(testClass, configuration), configuration);\n\t}\n\n\tprivate ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, String displayName,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, displayName, configuration);\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected ClassTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new ClassTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getTestClass(), getDisplayName(),\n\t\t\tconfiguration);\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\t// return modifiable copy\n\t\treturn new LinkedHashSet<>(this.classInfo.tags);\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn emptyList();\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tExecutionMode getDefaultExecutionMode() {\n\t\treturn toExecutionMode(configuration.getDefaultClassesExecutionMode());\n\t}\n\n\t// --- ClassBasedTestDescriptor --------------------------------------------\n\n\t@Override\n\tprotected TestInstances instantiateTestClass(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry registry,\n\t\t\tJupiterEngineExecutionContext context) {\n\t\treturn instantiateTestClass(Optional.empty(), registry, extensionContext);\n\t}\n\n\t// --- ResourceLockAware ---------------------------------------------------\n\n\t@Override\n\tpublic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {\n\t\treturn provider -> provider.provideForClass(getTestClass());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultDynamicTestInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport org.junit.jupiter.api.extension.DynamicTestInvocationContext;\nimport org.junit.jupiter.api.function.Executable;\n\n/**\n * Default implementation of the {@link DynamicTestInvocationContext} API.\n *\n * @since 5.8\n */\nrecord DefaultDynamicTestInvocationContext(Executable executable) implements DynamicTestInvocationContext {\n\n\t@Override\n\tpublic Executable getExecutable() {\n\t\treturn this.executable;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DefaultTestInstanceFactoryContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.extension.TestInstanceFactoryContext;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of the {@link TestInstanceFactoryContext} API.\n *\n * @since 5.3\n */\nrecord DefaultTestInstanceFactoryContext(Class<?> testClass, Optional<Object> outerInstance)\n\t\timplements TestInstanceFactoryContext {\n\n\t@Override\n\tpublic Class<?> getTestClass() {\n\t\treturn this.testClass;\n\t}\n\n\t@Override\n\tpublic Optional<Object> getOuterInstance() {\n\t\treturn this.outerInstance;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"testClass\", this.testClass)\n\t\t\t\t.append(\"outerInstance\", this.outerInstance)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DisplayNameUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.BiFunction;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DisplayNameGeneration;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences;\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\nimport org.junit.jupiter.api.DisplayNameGenerator.Simple;\nimport org.junit.jupiter.api.DisplayNameGenerator.Standard;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Collection of utilities for working with display names.\n *\n * @since 5.4\n * @see DisplayName\n * @see DisplayNameGenerator\n * @see DisplayNameGeneration\n */\nfinal class DisplayNameUtils {\n\n\t/**\n\t * Pre-defined standard display name generator instance.\n\t */\n\tprivate static final DisplayNameGenerator standardGenerator = DisplayNameGenerator.getDisplayNameGenerator(\n\t\tStandard.class);\n\n\t/**\n\t * Pre-defined simple display name generator instance.\n\t */\n\tprivate static final DisplayNameGenerator simpleGenerator = DisplayNameGenerator.getDisplayNameGenerator(\n\t\tSimple.class);\n\n\t/**\n\t * Pre-defined display name generator instance replacing underscores.\n\t */\n\tprivate static final DisplayNameGenerator replaceUnderscoresGenerator = DisplayNameGenerator.getDisplayNameGenerator(\n\t\tReplaceUnderscores.class);\n\n\t/**\n\t * Pre-defined display name generator instance producing indicative sentences.\n\t */\n\tprivate static final DisplayNameGenerator indicativeSentencesGenerator = DisplayNameGenerator.getDisplayNameGenerator(\n\t\tIndicativeSentences.class);\n\n\tstatic String determineDisplayName(AnnotatedElement element, Supplier<String> displayNameSupplier) {\n\t\tPreconditions.notNull(element, \"Annotated element must not be null\");\n\t\treturn findAnnotation(element, DisplayName.class) //\n\t\t\t\t.map(DisplayName::value) //\n\t\t\t\t.filter(StringUtils::isNotBlank) //\n\t\t\t\t.map(String::strip) //\n\t\t\t\t.orElseGet(displayNameSupplier);\n\t}\n\n\tstatic void validateAnnotation(AnnotatedElement element, Supplier<String> elementDescription,\n\t\t\tSupplier<@Nullable TestSource> sourceProvider, DiscoveryIssueReporter reporter) {\n\t\tfindAnnotation(element, DisplayName.class) //\n\t\t\t\t.map(DisplayName::value) //\n\t\t\t\t.filter(StringUtils::isBlank) //\n\t\t\t\t.ifPresent(__ -> {\n\t\t\t\t\tString message = \"@DisplayName on %s must be declared with a non-blank value.\".formatted(\n\t\t\t\t\t\telementDescription.get());\n\t\t\t\t\treporter.reportIssue(\n\t\t\t\t\t\tDiscoveryIssue.builder(Severity.WARNING, message).source(sourceProvider.get()).build());\n\t\t\t\t});\n\t}\n\n\tstatic String determineDisplayNameForMethod(Supplier<List<Class<?>>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\tMethod testMethod, JupiterConfiguration configuration) {\n\t\treturn determineDisplayName(testMethod,\n\t\t\tcreateDisplayNameSupplierForMethod(enclosingInstanceTypes, testClass, testMethod, configuration));\n\t}\n\n\tstatic Supplier<String> createDisplayNameSupplierForClass(Class<?> testClass, JupiterConfiguration configuration) {\n\t\treturn createDisplayNameSupplier(Collections::emptyList, testClass, configuration,\n\t\t\t(generator, __) -> generator.generateDisplayNameForClass(testClass));\n\t}\n\n\tstatic Supplier<String> createDisplayNameSupplierForNestedClass(\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypesSupplier, Class<?> testClass,\n\t\t\tJupiterConfiguration configuration) {\n\t\treturn createDisplayNameSupplier(enclosingInstanceTypesSupplier, testClass, configuration,\n\t\t\t(generator, enclosingInstanceTypes) -> generator.generateDisplayNameForNestedClass(enclosingInstanceTypes,\n\t\t\t\ttestClass));\n\t}\n\n\tprivate static Supplier<String> createDisplayNameSupplierForMethod(\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypesSupplier, Class<?> testClass, Method testMethod,\n\t\t\tJupiterConfiguration configuration) {\n\t\treturn createDisplayNameSupplier(enclosingInstanceTypesSupplier, testClass, configuration,\n\t\t\t(generator, enclosingInstanceTypes) -> generator.generateDisplayNameForMethod(enclosingInstanceTypes,\n\t\t\t\ttestClass, testMethod));\n\t}\n\n\tprivate static Supplier<String> createDisplayNameSupplier(Supplier<List<Class<?>>> enclosingInstanceTypesSupplier,\n\t\t\tClass<?> testClass, JupiterConfiguration configuration,\n\t\t\tBiFunction<DisplayNameGenerator, List<Class<?>>, String> generatorFunction) {\n\t\treturn () -> {\n\t\t\tList<Class<?>> enclosingInstanceTypes = List.copyOf(enclosingInstanceTypesSupplier.get());\n\t\t\treturn findDisplayNameGenerator(enclosingInstanceTypes, testClass) //\n\t\t\t\t\t.map(it -> generatorFunction.apply(it, enclosingInstanceTypes)) //\n\t\t\t\t\t.orElseGet(() -> generatorFunction.apply(configuration.getDefaultDisplayNameGenerator(),\n\t\t\t\t\t\tenclosingInstanceTypes));\n\t\t};\n\t}\n\n\tprivate static Optional<DisplayNameGenerator> findDisplayNameGenerator(List<Class<?>> enclosingInstanceTypes,\n\t\t\tClass<?> testClass) {\n\t\tPreconditions.notNull(testClass, \"Test class must not be null\");\n\n\t\treturn findAnnotation(testClass, DisplayNameGeneration.class, enclosingInstanceTypes) //\n\t\t\t\t.map(DisplayNameGeneration::value) //\n\t\t\t\t.map(displayNameGeneratorClass -> {\n\t\t\t\t\tif (displayNameGeneratorClass == Standard.class) {\n\t\t\t\t\t\treturn standardGenerator;\n\t\t\t\t\t}\n\t\t\t\t\tif (displayNameGeneratorClass == Simple.class) {\n\t\t\t\t\t\treturn simpleGenerator;\n\t\t\t\t\t}\n\t\t\t\t\tif (displayNameGeneratorClass == ReplaceUnderscores.class) {\n\t\t\t\t\t\treturn replaceUnderscoresGenerator;\n\t\t\t\t\t}\n\t\t\t\t\tif (displayNameGeneratorClass == IndicativeSentences.class) {\n\t\t\t\t\t\treturn indicativeSentencesGenerator;\n\t\t\t\t\t}\n\t\t\t\t\treturn ReflectionSupport.newInstance(displayNameGeneratorClass);\n\t\t\t\t});\n\t}\n\n\tprivate DisplayNameUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicContainerTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.createDynamicDescriptor;\n\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicContainer;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@link TestDescriptor} for a {@link DynamicContainer}.\n *\n * @since 5.0\n */\nclass DynamicContainerTestDescriptor extends DynamicNodeTestDescriptor {\n\n\tprivate final DynamicContainer dynamicContainer;\n\tprivate final TestSource testSource;\n\tprivate final DynamicDescendantFilter dynamicDescendantFilter;\n\n\tDynamicContainerTestDescriptor(UniqueId uniqueId, int index, DynamicContainer dynamicContainer,\n\t\t\tTestSource testSource, DynamicDescendantFilter dynamicDescendantFilter,\n\t\t\tJupiterConfiguration configuration) {\n\n\t\tsuper(uniqueId, index, dynamicContainer, testSource, configuration);\n\t\tthis.dynamicContainer = dynamicContainer;\n\t\tthis.testSource = testSource;\n\t\tthis.dynamicDescendantFilter = dynamicDescendantFilter;\n\t}\n\n\t@Override\n\tprotected DynamicContainerTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new DynamicContainerTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), this.index,\n\t\t\tthis.dynamicContainer, this.testSource, this.dynamicDescendantFilter, this.configuration);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tOptional<ExecutionMode> getExplicitChildExecutionMode() {\n\t\treturn this.dynamicContainer.getChildExecutionMode().map(JupiterTestDescriptor::toExecutionMode);\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) throws Exception {\n\n\t\tAtomicInteger index = new AtomicInteger(1);\n\t\ttry (Stream<? extends DynamicNode> children = dynamicContainer.getChildren()) {\n\t\t\t// @formatter:off\n\t\t\tchildren.map(child -> {\n\t\t\t\t\t\tPreconditions.notNull(child, \"individual dynamic node must not be null\");\n\t\t\t\t\t\treturn toDynamicDescriptor(index.getAndIncrement(), child);\n\t\t\t\t\t})\n\t\t\t\t\t.flatMap(Optional::stream)\n\t\t\t\t\t.forEachOrdered(dynamicTestExecutor::execute);\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn context;\n\t}\n\n\tprivate Optional<JupiterTestDescriptor> toDynamicDescriptor(int index, DynamicNode childNode) {\n\t\treturn createDynamicDescriptor(this, childNode, index, testSource, dynamicDescendantFilter, configuration);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicDescendantFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.BiPredicate;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Filter for dynamic descendants of {@link TestDescriptor TestDescriptors} that\n * implement {@link Filterable}.\n *\n * @since 5.1\n * @see Filterable\n */\n@API(status = INTERNAL, since = \"5.1\")\npublic class DynamicDescendantFilter implements BiPredicate<UniqueId, Integer> {\n\n\tprivate final Set<UniqueId> allowedUniqueIds = new HashSet<>();\n\tprivate final Set<Integer> allowedIndices = new HashSet<>();\n\tprivate Mode mode = Mode.EXPLICIT;\n\n\tpublic void allowUniqueIdPrefix(UniqueId uniqueId) {\n\t\tif (this.mode == Mode.EXPLICIT) {\n\t\t\tthis.allowedUniqueIds.add(uniqueId);\n\t\t}\n\t}\n\n\tpublic void allowIndex(int index) {\n\t\tif (this.mode == Mode.EXPLICIT) {\n\t\t\tthis.allowedIndices.add(index);\n\t\t}\n\t}\n\n\tpublic void allowIndex(Set<Integer> indices) {\n\t\tif (this.mode == Mode.EXPLICIT) {\n\t\t\tthis.allowedIndices.addAll(indices);\n\t\t}\n\t}\n\n\tpublic void allowAll() {\n\t\tthis.mode = Mode.ALLOW_ALL;\n\t\tthis.allowedUniqueIds.clear();\n\t\tthis.allowedIndices.clear();\n\t}\n\n\t@Override\n\tpublic boolean test(UniqueId uniqueId, Integer index) {\n\t\treturn isEverythingAllowed() //\n\t\t\t\t|| isUniqueIdAllowed(uniqueId) //\n\t\t\t\t|| allowedIndices.contains(index);\n\t}\n\n\tprivate boolean isEverythingAllowed() {\n\t\treturn allowedUniqueIds.isEmpty() && allowedIndices.isEmpty();\n\t}\n\n\tprivate boolean isUniqueIdAllowed(UniqueId uniqueId) {\n\t\treturn allowedUniqueIds.stream().anyMatch(allowedUniqueId -> isPrefixOrViceVersa(uniqueId, allowedUniqueId));\n\t}\n\n\tprivate boolean isPrefixOrViceVersa(UniqueId currentUniqueId, UniqueId allowedUniqueId) {\n\t\treturn allowedUniqueId.hasPrefix(currentUniqueId) || currentUniqueId.hasPrefix(allowedUniqueId);\n\t}\n\n\tpublic DynamicDescendantFilter withoutIndexFiltering() {\n\t\treturn new WithoutIndexFiltering();\n\t}\n\n\tprivate enum Mode {\n\t\tEXPLICIT, ALLOW_ALL\n\t}\n\n\tpublic DynamicDescendantFilter copy(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn configure(uniqueIdTransformer, new DynamicDescendantFilter());\n\t}\n\n\tprotected DynamicDescendantFilter configure(UnaryOperator<UniqueId> uniqueIdTransformer,\n\t\t\tDynamicDescendantFilter copy) {\n\t\tthis.allowedUniqueIds.stream().map(uniqueIdTransformer).forEach(copy.allowedUniqueIds::add);\n\t\tcopy.allowedIndices.addAll(this.allowedIndices);\n\t\tcopy.mode = this.mode;\n\t\treturn copy;\n\t}\n\n\tprivate class WithoutIndexFiltering extends DynamicDescendantFilter {\n\n\t\t@Override\n\t\tpublic boolean test(UniqueId uniqueId, Integer index) {\n\t\t\treturn isEverythingAllowed() || isUniqueIdAllowed(uniqueId);\n\t\t}\n\n\t\t@Override\n\t\tpublic DynamicDescendantFilter withoutIndexFiltering() {\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic DynamicDescendantFilter copy(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\t\treturn configure(uniqueIdTransformer, new WithoutIndexFiltering());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptyList;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\nclass DynamicExtensionContext extends AbstractExtensionContext<DynamicNodeTestDescriptor> {\n\n\tDynamicExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tDynamicNodeTestDescriptor testDescriptor, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade) {\n\t\tsuper(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn emptyList();\n\t}\n\n\t@Override\n\tpublic Optional<TestInstance.Lifecycle> getTestInstanceLifecycle() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.util.Optional;\nimport java.util.OptionalInt;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Base {@link TestDescriptor} for a {@link DynamicNode}.\n *\n * @since 5.0.3\n */\nabstract class DynamicNodeTestDescriptor extends JupiterTestDescriptor {\n\n\tprotected final int index;\n\tprivate final Optional<ExecutionMode> executionMode;\n\n\tDynamicNodeTestDescriptor(UniqueId uniqueId, int index, DynamicNode dynamicNode, @Nullable TestSource testSource,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, dynamicNode.getDisplayName(), testSource, configuration);\n\t\tthis.index = index;\n\t\tthis.executionMode = dynamicNode.getExecutionMode().map(JupiterTestDescriptor::toExecutionMode);\n\t}\n\n\t@Override\n\tOptional<ExecutionMode> getExplicitExecutionMode() {\n\t\treturn executionMode;\n\t}\n\n\t@Override\n\tprotected final String getLegacyReportingBaseName() {\n\t\t// @formatter:off\n\t\treturn getParent()\n\t\t\t\t.map(it -> {\n\t\t\t\t\tif (it instanceof JupiterTestDescriptor parent) {\n\t\t\t\t\t\treturn parent.getLegacyReportingBaseName();\n\t\t\t\t\t}\n\t\t\t\t\treturn it.getLegacyReportingName();\n\t\t\t\t})\n\t\t\t\t.orElseGet(this::getDisplayName);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tprotected OptionalInt getLegacyReportingIndex() {\n\t\treturn OptionalInt.of(index);\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tExtensionContext extensionContext = new DynamicExtensionContext(context.getExtensionContext(),\n\t\t\tcontext.getExecutionListener(), this, context.getConfiguration(), context.getExtensionRegistry(),\n\t\t\tcontext.getLauncherStoreFacade());\n\t\t// @formatter:off\n\t\treturn context.extend()\n\t\t\t\t.withExtensionContext(extensionContext)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(JupiterEngineExecutionContext context) {\n\t\treturn SkipResult.doNotSkip();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicTestTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.util.function.UnaryOperator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.extension.DynamicTestInvocationContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.InvocationInterceptorChain;\nimport org.junit.jupiter.engine.execution.InvocationInterceptorChain.InterceptorCall;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@link TestDescriptor} for a {@link DynamicTest}.\n *\n * @since 5.0\n */\nclass DynamicTestTestDescriptor extends DynamicNodeTestDescriptor {\n\n\tprivate static final InvocationInterceptorChain interceptorChain = new InvocationInterceptorChain();\n\n\tprivate @Nullable DynamicTest dynamicTest;\n\n\tDynamicTestTestDescriptor(UniqueId uniqueId, int index, DynamicTest dynamicTest, @Nullable TestSource source,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, index, dynamicTest, source, configuration);\n\t\tthis.dynamicTest = dynamicTest;\n\t}\n\n\t@Override\n\tprotected DynamicTestTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new DynamicTestTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), this.index,\n\t\t\trequireNonNull(this.dynamicTest), this.getSource().orElse(null), this.configuration);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.TEST;\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) {\n\t\tDynamicTestInvocationContext dynamicTestInvocationContext = new DefaultDynamicTestInvocationContext(\n\t\t\trequiredDynamicTest().getExecutable());\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tExtensionRegistry extensionRegistry = context.getExtensionRegistry();\n\t\tinterceptorChain.<@Nullable Void> invoke(toInvocation(), extensionRegistry, InterceptorCall.ofVoid((\n\t\t\t\tInvocationInterceptor interceptor,\n\t\t\t\tInvocationInterceptor.Invocation<@Nullable Void> wrappedInvocation) -> interceptor.interceptDynamicTest(\n\t\t\t\t\twrappedInvocation, dynamicTestInvocationContext, extensionContext)));\n\t\treturn context;\n\t}\n\n\tprivate InvocationInterceptor.Invocation<@Nullable Void> toInvocation() {\n\t\treturn () -> {\n\t\t\trequiredDynamicTest().getExecutable().execute();\n\t\t\treturn null;\n\t\t};\n\t}\n\n\t/**\n\t * Avoid an {@link OutOfMemoryError} by releasing the reference to this\n\t * descriptor's {@link DynamicTest} which holds a reference to the user-supplied\n\t * {@link Executable} which may potentially consume large amounts of memory\n\t * on the heap.\n\t *\n\t * @since 5.5\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1865\">Issue 1865</a>\n\t */\n\t@Override\n\tpublic void after(JupiterEngineExecutionContext context) throws Exception {\n\t\tsuper.after(context);\n\t\tthis.dynamicTest = null;\n\t}\n\n\tprivate DynamicTest requiredDynamicTest() {\n\t\treturn requireNonNull(dynamicTest);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExclusiveResourceCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.api.parallel.ResourceLockTarget.SELF;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.ResourceLockTarget;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\n\n/**\n * @since 5.12\n */\nabstract class ExclusiveResourceCollector {\n\n\tprivate static final ExclusiveResourceCollector NO_EXCLUSIVE_RESOURCES = new ExclusiveResourceCollector() {\n\n\t\t@Override\n\t\tStream<ExclusiveResource> getAllExclusiveResources(\n\t\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@Override\n\t\tStream<ExclusiveResource> getStaticResourcesFor(ResourceLockTarget target) {\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@Override\n\t\tStream<ExclusiveResource> getDynamicResources(\n\t\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {\n\t\t\treturn Stream.empty();\n\t\t}\n\t};\n\n\tStream<ExclusiveResource> getAllExclusiveResources(\n\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {\n\t\treturn Stream.concat(getStaticResourcesFor(SELF), getDynamicResources(providerToLocks));\n\t}\n\n\tabstract Stream<ExclusiveResource> getStaticResourcesFor(ResourceLockTarget target);\n\n\tabstract Stream<ExclusiveResource> getDynamicResources(\n\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks);\n\n\tstatic ExclusiveResourceCollector from(AnnotatedElement element) {\n\t\tList<ResourceLock> annotations = findRepeatableAnnotations(element, ResourceLock.class);\n\t\treturn annotations.isEmpty() ? NO_EXCLUSIVE_RESOURCES : new DefaultExclusiveResourceCollector(annotations);\n\t}\n\n\tprivate static class DefaultExclusiveResourceCollector extends ExclusiveResourceCollector {\n\n\t\tprivate final List<ResourceLock> annotations;\n\n\t\t@Nullable\n\t\tprivate List<ResourceLocksProvider> providers;\n\n\t\tDefaultExclusiveResourceCollector(List<ResourceLock> annotations) {\n\t\t\tthis.annotations = annotations;\n\t\t}\n\n\t\t@Override\n\t\tStream<ExclusiveResource> getStaticResourcesFor(ResourceLockTarget target) {\n\t\t\treturn annotations.stream() //\n\t\t\t\t\t.filter(annotation -> StringUtils.isNotBlank(annotation.value())) //\n\t\t\t\t\t.filter(annotation -> annotation.target() == target) //\n\t\t\t\t\t.map(annotation -> new ExclusiveResource(annotation.value(), toLockMode(annotation.mode())));\n\t\t}\n\n\t\t@Override\n\t\tStream<ExclusiveResource> getDynamicResources(\n\t\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {\n\t\t\tList<ResourceLocksProvider> providers = getProviders();\n\t\t\tif (providers.isEmpty()) {\n\t\t\t\treturn Stream.empty();\n\t\t\t}\n\t\t\treturn providers.stream() //\n\t\t\t\t\t.map(providerToLocks) //\n\t\t\t\t\t.flatMap(Collection::stream) //\n\t\t\t\t\t.map(lock -> new ExclusiveResource(lock.getKey(), toLockMode(lock.getAccessMode())));\n\t\t}\n\n\t\tprivate List<ResourceLocksProvider> getProviders() {\n\t\t\tif (this.providers == null) {\n\t\t\t\tthis.providers = annotations.stream() //\n\t\t\t\t\t\t.flatMap(annotation -> instantiate(annotation.providers())) //\n\t\t\t\t\t\t.toList();\n\t\t\t}\n\t\t\treturn providers;\n\t\t}\n\n\t\tprivate static Stream<ResourceLocksProvider> instantiate(Class<? extends ResourceLocksProvider>[] providers) {\n\t\t\treturn Stream.of(providers).map(ReflectionUtils::newInstance);\n\t\t}\n\n\t\tprivate static ExclusiveResource.LockMode toLockMode(ResourceAccessMode mode) {\n\t\t\treturn switch (mode) {\n\t\t\t\tcase READ -> ExclusiveResource.LockMode.READ;\n\t\t\t\tcase READ_WRITE -> ExclusiveResource.LockMode.READ_WRITE;\n\t\t\t};\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ExtensionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.stream.Collectors.toList;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.util.ReflectionUtils.getDeclaredConstructor;\nimport static org.junit.platform.commons.util.ReflectionUtils.streamFields;\nimport static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.engine.extension.ExtensionRegistrar;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Collection of utilities for working with extensions and the extension registry.\n *\n * @since 5.1\n * @see ExtensionRegistrar\n * @see MutableExtensionRegistry\n * @see ExtendWith\n * @see RegisterExtension\n */\nfinal class ExtensionUtils {\n\n\tprivate ExtensionUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Populate a new {@link MutableExtensionRegistry} from extension types declared via\n\t * {@link ExtendWith @ExtendWith} on the supplied {@link AnnotatedElement}.\n\t *\n\t * @param parentRegistry the parent extension registry to set in the newly\n\t * created registry; never {@code null}\n\t * @param annotatedElement the annotated element on which to search for\n\t * declarations of {@code @ExtendWith}; never {@code null}\n\t *\n\t * @return the new extension registry; never {@code null}\n\t * @since 5.0\n\t */\n\tstatic MutableExtensionRegistry populateNewExtensionRegistryFromExtendWithAnnotation(\n\t\t\tMutableExtensionRegistry parentRegistry, AnnotatedElement annotatedElement) {\n\n\t\tPreconditions.notNull(parentRegistry, \"Parent ExtensionRegistry must not be null\");\n\t\tPreconditions.notNull(annotatedElement, \"AnnotatedElement must not be null\");\n\n\t\treturn MutableExtensionRegistry.createRegistryFrom(parentRegistry,\n\t\t\tstreamDeclarativeExtensionTypes(annotatedElement));\n\t}\n\n\t/**\n\t * Register extensions using the supplied registrar from static fields in\n\t * the supplied class that are annotated with {@link ExtendWith @ExtendWith}\n\t * or {@link RegisterExtension @RegisterExtension}.\n\t *\n\t * <p>The extensions will be sorted according to {@link Order @Order} semantics\n\t * prior to registration.\n\t *\n\t * @param registrar the registrar with which to register the extensions; never {@code null}\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @since 5.11\n\t */\n\tstatic void registerExtensionsFromStaticFields(ExtensionRegistrar registrar, Class<?> clazz) {\n\t\tstreamExtensionRegisteringFields(clazz, ModifierSupport::isStatic) //\n\t\t\t\t.forEach(field -> {\n\t\t\t\t\tList<Class<? extends Extension>> extensionTypes = streamDeclarativeExtensionTypes(field).collect(\n\t\t\t\t\t\ttoList());\n\t\t\t\t\tboolean isExtendWithPresent = !extensionTypes.isEmpty();\n\n\t\t\t\t\tif (isExtendWithPresent) {\n\t\t\t\t\t\textensionTypes.forEach(registrar::registerExtension);\n\t\t\t\t\t}\n\t\t\t\t\tif (isAnnotated(field, RegisterExtension.class)) {\n\t\t\t\t\t\tExtension extension = readAndValidateExtensionFromField(field, null, extensionTypes);\n\t\t\t\t\t\tregistrar.registerExtension(extension, field);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Register extensions using the supplied registrar from instance fields in\n\t * the supplied class that are annotated with {@link ExtendWith @ExtendWith}\n\t * or {@link RegisterExtension @RegisterExtension}.\n\t *\n\t * <p>The extensions will be sorted according to {@link Order @Order} semantics\n\t * prior to registration.\n\t *\n\t * @param registrar the registrar with which to register the extensions; never {@code null}\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @since 5.11\n\t */\n\tstatic void registerExtensionsFromInstanceFields(ExtensionRegistrar registrar, Class<?> clazz) {\n\t\tstreamExtensionRegisteringFields(clazz, ReflectionUtils::isNotStatic) //\n\t\t\t\t.forEach(field -> {\n\t\t\t\t\tList<Class<? extends Extension>> extensionTypes = streamDeclarativeExtensionTypes(field).collect(\n\t\t\t\t\t\ttoList());\n\t\t\t\t\tboolean isExtendWithPresent = !extensionTypes.isEmpty();\n\n\t\t\t\t\tif (isExtendWithPresent) {\n\t\t\t\t\t\textensionTypes.forEach(registrar::registerExtension);\n\t\t\t\t\t}\n\t\t\t\t\tif (isAnnotated(field, RegisterExtension.class)) {\n\t\t\t\t\t\tregistrar.registerUninitializedExtension(clazz, field,\n\t\t\t\t\t\t\tinstance -> readAndValidateExtensionFromField(field, instance, extensionTypes));\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\t/**\n\t * @since 5.11\n\t */\n\tprivate static Extension readAndValidateExtensionFromField(Field field, @Nullable Object instance,\n\t\t\tList<Class<? extends Extension>> declarativeExtensionTypes) {\n\t\tObject value = tryToReadFieldValue(field, instance) //\n\t\t\t\t.getOrThrow(e -> new PreconditionViolationException(\n\t\t\t\t\t\"Failed to read @RegisterExtension field [%s]\".formatted(field), e));\n\n\t\tPreconditions.condition(value instanceof Extension,\n\t\t\t() -> \"Failed to register extension via @RegisterExtension field [%s]: field value's type [%s] must implement an [%s] API.\".formatted(\n\t\t\t\tfield, (value != null ? value.getClass().getName() : null), Extension.class.getName()));\n\n\t\tdeclarativeExtensionTypes.forEach(extensionType -> {\n\t\t\tClass<?> valueType = value.getClass();\n\t\t\tPreconditions.condition(!extensionType.equals(valueType), () -> \"\"\"\n\t\t\t\t\tFailed to register extension via field [%s]. \\\n\t\t\t\t\tThe field registers an extension of type [%s] via @RegisterExtension and @ExtendWith, \\\n\t\t\t\t\tbut only one registration of a given extension type is permitted.\"\"\".formatted(field,\n\t\t\t\tvalueType.getName()));\n\t\t});\n\n\t\treturn (Extension) value;\n\t}\n\n\t/**\n\t * Register extensions using the supplied registrar from parameters in the\n\t * declared constructor of the supplied class that are annotated with\n\t * {@link ExtendWith @ExtendWith}.\n\t *\n\t * @param registrar the registrar with which to register the extensions; never {@code null}\n\t * @param clazz the class in which to find the declared constructor; never {@code null}\n\t * @since 5.8\n\t */\n\tstatic void registerExtensionsFromConstructorParameters(ExtensionRegistrar registrar, Class<?> clazz) {\n\t\tregisterExtensionsFromExecutableParameters(registrar, getDeclaredConstructor(clazz));\n\t}\n\n\t/**\n\t * Register extensions using the supplied registrar from parameters in the\n\t * supplied {@link Executable} (i.e., a {@link java.lang.reflect.Constructor}\n\t * or {@link java.lang.reflect.Method}) that are annotated with\n\t * {@link ExtendWith @ExtendWith}.\n\t *\n\t * @param registrar the registrar with which to register the extensions; never {@code null}\n\t * @param executable the constructor or method whose parameters should be searched; never {@code null}\n\t * @since 5.8\n\t */\n\tstatic void registerExtensionsFromExecutableParameters(ExtensionRegistrar registrar, Executable executable) {\n\t\tPreconditions.notNull(registrar, \"ExtensionRegistrar must not be null\");\n\t\tPreconditions.notNull(executable, \"Executable must not be null\");\n\n\t\tAtomicInteger index = new AtomicInteger();\n\n\t\t// @formatter:off\n\t\tArrays.stream(executable.getParameters())\n\t\t\t\t.map(parameter -> findRepeatableAnnotations(parameter, index.getAndIncrement(), ExtendWith.class))\n\t\t\t\t.flatMap(ExtensionUtils::streamDeclarativeExtensionTypes)\n\t\t\t\t.forEach(registrar::registerExtension);\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * @since 5.11\n\t */\n\tprivate static Stream<Field> streamExtensionRegisteringFields(Class<?> clazz, Predicate<Field> predicate) {\n\t\treturn streamFields(clazz, predicate.and(registersExtension), TOP_DOWN)//\n\t\t\t\t.sorted(orderComparator);\n\t}\n\n\t/**\n\t * @since 5.11\n\t */\n\tprivate static Stream<Class<? extends Extension>> streamDeclarativeExtensionTypes(\n\t\t\tAnnotatedElement annotatedElement) {\n\t\treturn streamDeclarativeExtensionTypes(findRepeatableAnnotations(annotatedElement, ExtendWith.class));\n\t}\n\n\t/**\n\t * @since 5.11\n\t */\n\tprivate static Stream<Class<? extends Extension>> streamDeclarativeExtensionTypes(\n\t\t\tList<ExtendWith> extendWithAnnotations) {\n\t\treturn extendWithAnnotations.stream().map(ExtendWith::value).flatMap(Arrays::stream);\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tprivate static final Comparator<Field> orderComparator = //\n\t\tComparator.comparingInt(ExtensionUtils::getOrder);\n\n\t/**\n\t * @since 5.4\n\t */\n\tprivate static int getOrder(Field field) {\n\t\treturn findAnnotation(field, Order.class).map(Order::value).orElse(Order.DEFAULT);\n\t}\n\n\t/**\n\t * {@link Predicate} which determines if a {@link Field} registers an extension via\n\t * {@link RegisterExtension @RegisterExtension} or {@link ExtendWith @ExtendWith}.\n\t *\n\t * @since 5.11.3\n\t */\n\tprivate static final Predicate<Field> registersExtension = //\n\t\tfield -> isAnnotated(field, RegisterExtension.class)\n\t\t\t\t|| !findRepeatableAnnotations(field, ExtendWith.class).isEmpty();\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Filterable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code Filterable} is implemented by\n * {@link org.junit.platform.engine.TestDescriptor TestDescriptors} that may\n * register dynamic tests during execution and support selective test execution.\n *\n * @since 5.1\n * @see DynamicDescendantFilter\n */\n@API(status = INTERNAL, since = \"5.1\")\npublic interface Filterable {\n\n\tDynamicDescendantFilter getDynamicDescendantFilter();\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.toExecutionMode;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class JupiterEngineDescriptor extends EngineDescriptor implements Node<JupiterEngineExecutionContext> {\n\n\tpublic static final String ENGINE_ID = \"junit-jupiter\";\n\tprivate final JupiterConfiguration configuration;\n\n\tpublic JupiterEngineDescriptor(UniqueId uniqueId, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, \"JUnit Jupiter\");\n\t\tthis.configuration = configuration;\n\t}\n\n\tpublic JupiterConfiguration getConfiguration() {\n\t\treturn configuration;\n\t}\n\n\t@Override\n\tpublic ExecutionMode getExecutionMode() {\n\t\treturn toExecutionMode(configuration.getDefaultExecutionMode());\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions(\n\t\t\tcontext.getConfiguration());\n\t\tEngineExecutionListener executionListener = context.getExecutionListener();\n\t\tExtensionContext extensionContext = new JupiterEngineExtensionContext(executionListener, this,\n\t\t\tcontext.getConfiguration(), extensionRegistry, context.getLauncherStoreFacade());\n\n\t\t// @formatter:off\n\t\treturn context.extend()\n\t\t\t\t.withExtensionRegistry(extensionRegistry)\n\t\t\t\t.withExtensionContext(extensionContext)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\tcontext.close();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterEngineExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptyList;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.0\n */\nfinal class JupiterEngineExtensionContext extends AbstractExtensionContext<JupiterEngineDescriptor> {\n\n\tJupiterEngineExtensionContext(EngineExecutionListener engineExecutionListener,\n\t\t\tJupiterEngineDescriptor testDescriptor, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade) {\n\n\t\tsuper(null, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn emptyList();\n\t}\n\n\t@Override\n\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.stream.Collectors.collectingAndThen;\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toSet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.DisplayNameUtils.determineDisplayName;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.OptionalInt;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.ConditionEvaluator;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic abstract class JupiterTestDescriptor extends AbstractTestDescriptor\n\t\timplements Node<JupiterEngineExecutionContext> {\n\n\tprivate static final ConditionEvaluator conditionEvaluator = new ConditionEvaluator();\n\n\tfinal JupiterConfiguration configuration;\n\n\tJupiterTestDescriptor(UniqueId uniqueId, AnnotatedElement element, Supplier<String> displayNameSupplier,\n\t\t\t@Nullable TestSource source, JupiterConfiguration configuration) {\n\t\tthis(uniqueId, determineDisplayName(element, displayNameSupplier), source, configuration);\n\t}\n\n\tJupiterTestDescriptor(UniqueId uniqueId, String displayName, @Nullable TestSource source,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, displayName, source);\n\t\tthis.configuration = configuration;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic final String getLegacyReportingName() {\n\t\treturn getLegacyReportingBaseName()\n\t\t\t\t+ getLegacyReportingIndexes().mapToObj(i -> \"[\" + i + \"]\").collect(joining());\n\t}\n\n\tprotected String getLegacyReportingBaseName() {\n\t\treturn getDisplayName();\n\t}\n\n\tprivate IntStream getLegacyReportingIndexes() {\n\t\tvar ownIndex = getLegacyReportingIndex();\n\t\treturn getParent() //\n\t\t\t\t.map(it -> it instanceof JupiterTestDescriptor parent //\n\t\t\t\t\t\t? parent.getLegacyReportingIndexes() //\n\t\t\t\t\t\t: null) //\n\t\t\t\t.map(parentIndexes -> IntStream.concat(parentIndexes, ownIndex.stream())) //\n\t\t\t\t.orElseGet(ownIndex::stream);\n\t}\n\n\tprotected OptionalInt getLegacyReportingIndex() {\n\t\treturn OptionalInt.empty();\n\t}\n\n\tstatic Set<TestTag> getTags(AnnotatedElement element, Supplier<String> elementDescription,\n\t\t\tSupplier<TestSource> sourceProvider, Consumer<DiscoveryIssue> issueCollector) {\n\t\tAtomicReference<@Nullable TestSource> source = new AtomicReference<>();\n\t\treturn findRepeatableAnnotations(element, Tag.class).stream() //\n\t\t\t\t.map(Tag::value) //\n\t\t\t\t.filter(tag -> {\n\t\t\t\t\tboolean isValid = TestTag.isValid(tag);\n\t\t\t\t\tif (!isValid) {\n\t\t\t\t\t\tString message = \"Invalid tag syntax in @Tag(\\\"%s\\\") declaration on %s. Tag will be ignored.\".formatted(\n\t\t\t\t\t\t\ttag, elementDescription.get());\n\t\t\t\t\t\tif (source.get() == null) {\n\t\t\t\t\t\t\tsource.set(sourceProvider.get());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tissueCollector.accept(\n\t\t\t\t\t\t\tDiscoveryIssue.builder(Severity.WARNING, message).source(source.get()).build());\n\t\t\t\t\t}\n\t\t\t\t\treturn isValid;\n\t\t\t\t}) //\n\t\t\t\t.map(TestTag::create) //\n\t\t\t\t.collect(collectingAndThen(toCollection(LinkedHashSet::new), Collections::unmodifiableSet));\n\t}\n\n\t/**\n\t * Invoke exception handlers for the supplied {@code Throwable} one-by-one\n\t * until none are left or the throwable to handle has been swallowed.\n\t */\n\t<E extends Extension> void invokeExecutionExceptionHandlers(Class<E> handlerType, ExtensionRegistry registry,\n\t\t\tThrowable throwable, ExceptionHandlerInvoker<E> handlerInvoker) {\n\n\t\tList<E> extensions = registry.getExtensions(handlerType);\n\t\tCollections.reverse(extensions);\n\t\tinvokeExecutionExceptionHandlers(extensions, throwable, handlerInvoker);\n\t}\n\n\tprivate <E extends Extension> void invokeExecutionExceptionHandlers(List<E> exceptionHandlers, Throwable throwable,\n\t\t\tExceptionHandlerInvoker<E> handlerInvoker) {\n\n\t\t// No handlers left?\n\t\tif (exceptionHandlers.isEmpty()) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(throwable);\n\t\t}\n\n\t\ttry {\n\t\t\t// Invoke next available handler\n\t\t\thandlerInvoker.invoke(exceptionHandlers.remove(0), throwable);\n\t\t}\n\t\tcatch (Throwable handledThrowable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(handledThrowable);\n\t\t\tinvokeExecutionExceptionHandlers(exceptionHandlers, handledThrowable, handlerInvoker);\n\t\t}\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic final ExecutionMode getExecutionMode() {\n\t\treturn getExplicitExecutionMode() //\n\t\t\t\t.or(this::determineExecutionModeFromAncestors) //\n\t\t\t\t.orElseGet(this::getDefaultExecutionMode);\n\t}\n\n\tprivate Optional<ExecutionMode> determineExecutionModeFromAncestors() {\n\t\treturn ancestors() //\n\t\t\t\t.takeWhile(JupiterTestDescriptor.class::isInstance) //\n\t\t\t\t.flatMap(ancestor -> determineExecutionModeFromAncestor((JupiterTestDescriptor) ancestor).stream()) //\n\t\t\t\t.findFirst();\n\t}\n\n\tprivate static Optional<ExecutionMode> determineExecutionModeFromAncestor(JupiterTestDescriptor ancestor) {\n\t\treturn ancestor.getExplicitChildExecutionMode() //\n\t\t\t\t.or(ancestor::getExplicitExecutionMode) //\n\t\t\t\t.or(ancestor::getDefaultChildExecutionMode);\n\t}\n\n\tStream<TestDescriptor> ancestors() {\n\t\treturn Stream.iterate(getParent(), Optional::isPresent, it -> it.flatMap(TestDescriptor::getParent)) //\n\t\t\t\t.map(Optional::orElseThrow);\n\t}\n\n\tOptional<ExecutionMode> getExplicitChildExecutionMode() {\n\t\treturn Optional.empty();\n\t}\n\n\tOptional<ExecutionMode> getExplicitExecutionMode() {\n\t\treturn Optional.empty();\n\t}\n\n\tOptional<ExecutionMode> getDefaultChildExecutionMode() {\n\t\treturn getExplicitChildExecutionMode();\n\t}\n\n\tExecutionMode getDefaultExecutionMode() {\n\t\treturn toExecutionMode(configuration.getDefaultExecutionMode());\n\t}\n\n\tOptional<ExecutionMode> getExecutionModeFromAnnotation(AnnotatedElement element) {\n\t\t// @formatter:off\n\t\treturn findAnnotation(element, Execution.class)\n\t\t\t\t.map(Execution::value)\n\t\t\t\t.map(JupiterTestDescriptor::toExecutionMode);\n\t\t// @formatter:on\n\t}\n\n\tpublic static ExecutionMode toExecutionMode(org.junit.jupiter.api.parallel.ExecutionMode mode) {\n\t\treturn switch (mode) {\n\t\t\tcase CONCURRENT -> ExecutionMode.CONCURRENT;\n\t\t\tcase SAME_THREAD -> ExecutionMode.SAME_THREAD;\n\t\t};\n\t}\n\n\t@Override\n\tpublic Set<ExclusiveResource> getExclusiveResources() {\n\t\tif (this instanceof ResourceLockAware resourceLockAware) {\n\t\t\treturn resourceLockAware.determineExclusiveResources().collect(toSet());\n\t\t}\n\t\treturn emptySet();\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(JupiterEngineExecutionContext context) {\n\t\tcontext.getThrowableCollector().assertEmpty();\n\t\tConditionEvaluationResult evaluationResult = conditionEvaluator.evaluate(context.getExtensionRegistry(),\n\t\t\tcontext.getConfiguration(), context.getExtensionContext());\n\t\treturn toSkipResult(evaluationResult);\n\t}\n\n\tprivate SkipResult toSkipResult(ConditionEvaluationResult evaluationResult) {\n\t\tif (evaluationResult.isDisabled()) {\n\t\t\treturn SkipResult.skip(evaluationResult.getReason().orElse(\"<unknown>\"));\n\t\t}\n\t\treturn SkipResult.doNotSkip();\n\t}\n\n\t/**\n\t * Must be overridden and return a new context with a new {@link ExtensionContext}\n\t * so cleanUp() does not accidentally close the parent context.\n\t */\n\t@Override\n\tpublic abstract JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) throws Exception;\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\tcontext.close();\n\t}\n\n\t/**\n\t * {@return a deep copy (with copies of children) of this descriptor with the supplied unique ID}\n\t */\n\tprotected JupiterTestDescriptor copyIncludingDescendants(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\tJupiterTestDescriptor result = withUniqueId(uniqueIdTransformer);\n\t\tgetChildren().forEach(oldChild -> {\n\t\t\tTestDescriptor newChild = ((JupiterTestDescriptor) oldChild).copyIncludingDescendants(uniqueIdTransformer);\n\t\t\tresult.addChild(newChild);\n\t\t});\n\t\treturn result;\n\t}\n\n\t/**\n\t * {@return shallow copy (without children) of this descriptor with the supplied unique ID}\n\t */\n\tprotected abstract JupiterTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer);\n\n\t/**\n\t * @since 5.5\n\t */\n\t@FunctionalInterface\n\tinterface ExceptionHandlerInvoker<E extends Extension> {\n\n\t\t/**\n\t\t * Invoke the supplied {@code exceptionHandler} with the supplied {@code throwable}.\n\t\t */\n\t\tvoid invoke(E exceptionHandler, Throwable throwable) throws Throwable;\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.engine.support.MethodReflectionUtils.getReturnType;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedMethods;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition.alwaysSatisfied;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationLifecycleMethod;\nimport org.junit.platform.commons.support.HierarchyTraversalMode;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\n\n/**\n * Collection of utilities for working with test lifecycle methods.\n *\n * @since 5.0\n */\nfinal class LifecycleMethodUtils {\n\n\tprivate LifecycleMethodUtils() {\n\t\t/* no-op */\n\t}\n\n\tstatic List<Method> findBeforeAllMethods(Class<?> testClass, boolean requireStatic,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckStatic(testClass, requireStatic, BeforeAll.class, HierarchyTraversalMode.TOP_DOWN,\n\t\t\tissueReporter);\n\t}\n\n\tstatic List<Method> findAfterAllMethods(Class<?> testClass, boolean requireStatic,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckStatic(testClass, requireStatic, AfterAll.class, HierarchyTraversalMode.BOTTOM_UP,\n\t\t\tissueReporter);\n\t}\n\n\tstatic List<Method> findBeforeEachMethods(Class<?> testClass, DiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckNonStatic(testClass, BeforeEach.class, HierarchyTraversalMode.TOP_DOWN,\n\t\t\tissueReporter);\n\t}\n\n\tstatic List<Method> findAfterEachMethods(Class<?> testClass, DiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckNonStatic(testClass, AfterEach.class, HierarchyTraversalMode.BOTTOM_UP,\n\t\t\tissueReporter);\n\t}\n\n\tstatic void validateNoClassTemplateInvocationLifecycleMethodsAreDeclared(Class<?> testClass,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\n\t\tfindAllClassTemplateInvocationLifecycleMethods(testClass) //\n\t\t\t\t.forEach(method -> findClassTemplateInvocationLifecycleMethodAnnotation(method) //\n\t\t\t\t\t\t.ifPresent(annotation -> {\n\t\t\t\t\t\t\tString message = \"@%s method '%s' must not be declared in test class '%s' because it is not annotated with @%s.\".formatted(\n\t\t\t\t\t\t\t\tannotation.lifecycleMethodAnnotation().getSimpleName(), method.toGenericString(),\n\t\t\t\t\t\t\t\ttestClass.getName(), annotation.classTemplateAnnotation().getSimpleName());\n\t\t\t\t\t\t\tissueReporter.reportIssue(createIssue(Severity.ERROR, message, method));\n\t\t\t\t\t\t}));\n\t}\n\n\tstatic void validateClassTemplateInvocationLifecycleMethodsAreDeclaredCorrectly(Class<?> testClass,\n\t\t\tboolean requireStatic, DiscoveryIssueReporter issueReporter) {\n\n\t\tfindAllClassTemplateInvocationLifecycleMethods(testClass) //\n\t\t\t\t.forEach(isNotPrivateError(issueReporter) //\n\t\t\t\t\t\t.and(returnsPrimitiveVoid(issueReporter,\n\t\t\t\t\t\t\tLifecycleMethodUtils::classTemplateInvocationLifecycleMethodAnnotationName)) //\n\t\t\t\t\t\t.and(requireStatic\n\t\t\t\t\t\t\t\t? isStatic(issueReporter,\n\t\t\t\t\t\t\t\t\tLifecycleMethodUtils::classTemplateInvocationLifecycleMethodAnnotationName)\n\t\t\t\t\t\t\t\t: alwaysSatisfied()) //\n\t\t\t\t\t\t.toConsumer());\n\t}\n\n\tprivate static Stream<Method> findAllClassTemplateInvocationLifecycleMethods(Class<?> testClass) {\n\t\tStream<Method> allMethods = Stream.concat( //\n\t\t\tfindAnnotatedMethods(testClass, ClassTemplateInvocationLifecycleMethod.class,\n\t\t\t\tHierarchyTraversalMode.TOP_DOWN).stream(), //\n\t\t\tfindAnnotatedMethods(testClass, ClassTemplateInvocationLifecycleMethod.class,\n\t\t\t\tHierarchyTraversalMode.BOTTOM_UP).stream() //\n\t\t);\n\t\treturn allMethods.distinct();\n\t}\n\n\tprivate static List<Method> findMethodsAndCheckStatic(Class<?> testClass, boolean requireStatic,\n\t\t\tClass<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\n\t\tCondition<Method> additionalCondition = requireStatic\n\t\t\t\t? isStatic(issueReporter, __ -> annotationType.getSimpleName())\n\t\t\t\t: alwaysSatisfied();\n\t\treturn findMethodsAndCheckVoidReturnType(testClass, annotationType, traversalMode, issueReporter,\n\t\t\tadditionalCondition);\n\t}\n\n\tprivate static List<Method> findMethodsAndCheckNonStatic(Class<?> testClass,\n\t\t\tClass<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\n\t\treturn findMethodsAndCheckVoidReturnType(testClass, annotationType, traversalMode, issueReporter,\n\t\t\tisNotStatic(issueReporter, __ -> annotationType.getSimpleName()));\n\t}\n\n\tprivate static List<Method> findMethodsAndCheckVoidReturnType(Class<?> testClass,\n\t\t\tClass<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,\n\t\t\tDiscoveryIssueReporter issueReporter, Condition<? super Method> additionalCondition) {\n\n\t\treturn findAnnotatedMethods(testClass, annotationType, traversalMode).stream() //\n\t\t\t\t.peek(isNotPrivateWarning(issueReporter, annotationType::getSimpleName).toConsumer()) //\n\t\t\t\t.filter(returnsPrimitiveVoid(issueReporter, __ -> annotationType.getSimpleName()).and(\n\t\t\t\t\tadditionalCondition).toPredicate()) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate static Condition<Method> isStatic(DiscoveryIssueReporter issueReporter,\n\t\t\tFunction<Method, String> annotationNameProvider) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isStatic, method -> {\n\t\t\tString message = \"@%s method '%s' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\".formatted(\n\t\t\t\tannotationNameProvider.apply(method), method.toGenericString());\n\t\t\treturn createIssue(Severity.ERROR, message, method);\n\t\t});\n\t}\n\n\tprivate static Condition<Method> isNotStatic(DiscoveryIssueReporter issueReporter,\n\t\t\tFunction<Method, String> annotationNameProvider) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotStatic, method -> {\n\t\t\tString message = \"@%s method '%s' must not be static.\".formatted(annotationNameProvider.apply(method),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createIssue(Severity.ERROR, message, method);\n\t\t});\n\t}\n\n\tprivate static Condition<Method> isNotPrivateError(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotPrivate, method -> {\n\t\t\tString message = \"@%s method '%s' must not be private.\".formatted(\n\t\t\t\tclassTemplateInvocationLifecycleMethodAnnotationName(method), method.toGenericString());\n\t\t\treturn createIssue(Severity.ERROR, message, method);\n\t\t});\n\t}\n\n\tprivate static Condition<Method> isNotPrivateWarning(DiscoveryIssueReporter issueReporter,\n\t\t\tSupplier<String> annotationNameProvider) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotPrivate, method -> {\n\t\t\tString message = \"@%s method '%s' should not be private. This will be disallowed in a future release.\".formatted(\n\t\t\t\tannotationNameProvider.get(), method.toGenericString());\n\t\t\treturn createIssue(Severity.WARNING, message, method);\n\t\t});\n\t}\n\n\tprivate static Condition<Method> returnsPrimitiveVoid(DiscoveryIssueReporter issueReporter,\n\t\t\tFunction<Method, String> annotationNameProvider) {\n\t\treturn issueReporter.createReportingCondition(method -> getReturnType(method) == void.class, method -> {\n\t\t\tString message = \"@%s method '%s' must not return a value.\".formatted(annotationNameProvider.apply(method),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createIssue(Severity.ERROR, message, method);\n\t\t});\n\t}\n\n\tprivate static String classTemplateInvocationLifecycleMethodAnnotationName(Method method) {\n\t\treturn findClassTemplateInvocationLifecycleMethodAnnotation(method) //\n\t\t\t\t.map(ClassTemplateInvocationLifecycleMethod::lifecycleMethodAnnotation) //\n\t\t\t\t.map(Class::getSimpleName) //\n\t\t\t\t.orElseGet(ClassTemplateInvocationLifecycleMethod.class::getSimpleName);\n\t}\n\n\tprivate static Optional<ClassTemplateInvocationLifecycleMethod> findClassTemplateInvocationLifecycleMethodAnnotation(\n\t\t\tMethod method) {\n\t\treturn findAnnotation(method, ClassTemplateInvocationLifecycleMethod.class);\n\t}\n\n\tprivate static DiscoveryIssue createIssue(Severity severity, String message, Method method) {\n\t\treturn DiscoveryIssue.builder(severity, message).source(MethodSource.from(method)).build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.parallel.ResourceLockTarget.CHILDREN;\nimport static org.junit.jupiter.engine.descriptor.DisplayNameUtils.determineDisplayNameForMethod;\nimport static org.junit.jupiter.engine.descriptor.ResourceLockAware.enclosingInstanceTypesDependentResourceLocksProviderEvaluator;\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestWatcher;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Base class for {@link TestDescriptor TestDescriptors} based on Java methods.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic abstract class MethodBasedTestDescriptor extends JupiterTestDescriptor\n\t\timplements ResourceLockAware, TestClassAware, Validatable {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(MethodBasedTestDescriptor.class);\n\n\tprivate final MethodInfo methodInfo;\n\n\tMethodBasedTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method testMethod,\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration) {\n\t\tthis(uniqueId, determineDisplayNameForMethod(enclosingInstanceTypes, testClass, testMethod, configuration),\n\t\t\ttestClass, testMethod, configuration);\n\t}\n\n\tMethodBasedTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, displayName, MethodSource.from(testClass, testMethod), configuration);\n\t\tthis.methodInfo = new MethodInfo(testClass, testMethod);\n\t}\n\n\tpublic final Method getTestMethod() {\n\t\treturn this.methodInfo.testMethod;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic final Set<TestTag> getTags() {\n\t\t// return modifiable copy\n\t\tSet<TestTag> allTags = new LinkedHashSet<>(this.methodInfo.tags);\n\t\tgetParent().ifPresent(parentDescriptor -> allTags.addAll(parentDescriptor.getTags()));\n\t\treturn allTags;\n\t}\n\n\t@Override\n\tprotected final String getLegacyReportingBaseName() {\n\t\treturn \"%s(%s)\".formatted(getTestMethod().getName(),\n\t\t\tClassUtils.nullSafeToString(Class::getSimpleName, getTestMethod().getParameterTypes()));\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic final Class<?> getTestClass() {\n\t\treturn this.methodInfo.testClass;\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getParent() //\n\t\t\t\t.filter(TestClassAware.class::isInstance) //\n\t\t\t\t.map(TestClassAware.class::cast) //\n\t\t\t\t.map(TestClassAware::getEnclosingTestClasses) //\n\t\t\t\t.orElseGet(Collections::emptyList);\n\t}\n\n\t// --- Validatable ---------------------------------------------------------\n\n\t@Override\n\tpublic void validate(DiscoveryIssueReporter reporter) {\n\t\tValidatable.reportAndClear(this.methodInfo.discoveryIssues, reporter);\n\t\tDisplayNameUtils.validateAnnotation(getTestMethod(), //\n\t\t\t() -> \"method '%s'\".formatted(getTestMethod().toGenericString()), //\n\t\t\t// Use _declaring_ class here because that's where the `@DisplayName` annotation is declared\n\t\t\t() -> MethodSource.from(getTestMethod()), //\n\t\t\treporter);\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic ExclusiveResourceCollector getExclusiveResourceCollector() {\n\t\t// There's no need to cache this as this method should only be called once\n\t\tExclusiveResourceCollector collector = ExclusiveResourceCollector.from(getTestMethod());\n\n\t\tif (collector.getStaticResourcesFor(CHILDREN).findAny().isPresent()) {\n\t\t\tString message = \"'ResourceLockTarget.CHILDREN' is not supported for methods.\" + //\n\t\t\t\t\t\" Invalid method: \" + getTestMethod();\n\t\t\tthrow new JUnitException(message);\n\t\t}\n\n\t\treturn collector;\n\t}\n\n\t@Override\n\tpublic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {\n\t\treturn enclosingInstanceTypesDependentResourceLocksProviderEvaluator(this::getEnclosingTestClasses,\n\t\t\t(provider, enclosingInstanceTypes) -> provider.provideForMethod(enclosingInstanceTypes, getTestClass(),\n\t\t\t\tgetTestMethod()));\n\t}\n\n\t@Override\n\tprotected Optional<ExecutionMode> getExplicitExecutionMode() {\n\t\treturn getExecutionModeFromAnnotation(getTestMethod());\n\t}\n\n\t/**\n\t * Invoke {@link TestWatcher#testDisabled(ExtensionContext, Optional)} on each\n\t * registered {@link TestWatcher}, in registration order.\n\t *\n\t * @since 5.4\n\t */\n\t@Override\n\tpublic void nodeSkipped(JupiterEngineExecutionContext context, TestDescriptor descriptor, SkipResult result) {\n\t\tinvokeTestWatchers(context, false,\n\t\t\twatcher -> watcher.testDisabled(context.getExtensionContext(), result.getReason()));\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\tprotected void invokeTestWatchers(JupiterEngineExecutionContext context, boolean reverseOrder,\n\t\t\tConsumer<TestWatcher> callback) {\n\n\t\tList<TestWatcher> watchers = context.getExtensionRegistry().getExtensions(TestWatcher.class);\n\n\t\tConsumer<TestWatcher> action = watcher -> {\n\t\t\ttry {\n\t\t\t\tcallback.accept(watcher);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\t\t\tlogger.warn(throwable,\n\t\t\t\t\t() -> \"Failed to invoke TestWatcher [%s] for method [%s] with display name [%s]\".formatted(\n\t\t\t\t\t\twatcher.getClass().getName(),\n\t\t\t\t\t\tReflectionUtils.getFullyQualifiedMethodName(extensionContext.getRequiredTestClass(),\n\t\t\t\t\t\t\textensionContext.getRequiredTestMethod()),\n\t\t\t\t\t\tgetDisplayName()));\n\t\t\t}\n\t\t};\n\t\tif (reverseOrder) {\n\t\t\tforEachInReverseOrder(watchers, action);\n\t\t}\n\t\telse {\n\t\t\twatchers.forEach(action);\n\t\t}\n\t}\n\n\tprivate static class MethodInfo {\n\n\t\tprivate final List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\n\t\tprivate final Class<?> testClass;\n\t\tprivate final Method testMethod;\n\n\t\t/**\n\t\t * Set of method-level tags; does not contain tags from parent.\n\t\t */\n\t\tprivate final Set<TestTag> tags;\n\n\t\tMethodInfo(Class<?> testClass, Method testMethod) {\n\t\t\tthis.testClass = Preconditions.notNull(testClass, \"Class must not be null\");\n\t\t\tthis.testMethod = testMethod;\n\t\t\tthis.tags = getTags(testMethod, //\n\t\t\t\t() -> \"method '%s'\".formatted(testMethod.toGenericString()), //\n\t\t\t\t// Use _declaring_ class here because that's where the `@Tag` annotation is declared\n\t\t\t\t() -> MethodSource.from(testMethod.getDeclaringClass(), testMethod), //\n\t\t\t\tdiscoveryIssues::add);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * @since 5.0\n */\nfinal class MethodExtensionContext extends AbstractExtensionContext<TestMethodTestDescriptor> {\n\n\tprivate final ThrowableCollector throwableCollector;\n\n\tprivate @Nullable TestInstances testInstances;\n\n\tMethodExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tTestMethodTestDescriptor testDescriptor, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade,\n\t\t\tThrowableCollector throwableCollector) {\n\n\t\tsuper(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\n\t\tthis.throwableCollector = throwableCollector;\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.of(getTestDescriptor().getTestMethod());\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getTestDescriptor().getEnclosingTestClasses();\n\t}\n\n\t@Override\n\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\treturn getParent().flatMap(ExtensionContext::getTestInstanceLifecycle);\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn getTestInstances().map(TestInstances::getInnermostInstance);\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn Optional.ofNullable(this.testInstances);\n\t}\n\n\tvoid setTestInstances(TestInstances testInstances) {\n\t\tthis.testInstances = testInstances;\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.of(getTestDescriptor().getTestMethod());\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.ofNullable(this.throwableCollector.getThrowable());\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodSourceSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.net.URI;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * Jupiter internal support for creating {@link MethodSource} from {@link URI}.\n *\n * @since 5.5\n * @see MethodSource\n * @see MethodSelector\n */\nclass MethodSourceSupport {\n\n\tstatic final String METHOD_SCHEME = \"method\";\n\n\t/**\n\t * Create a new {@code MethodSource} from the supplied {@link URI}.\n\t *\n\t * <p>The supplied {@link URI} should be of the form {@code method:<FQMN>}\n\t * where FQMN is the fully qualified method name. See\n\t * {@link DiscoverySelectors#selectMethod(String)} for the supported formats.\n\t *\n\t * <p></p>The {@link URI#getSchemeSpecificPart() scheme-specific part}\n\t * component of the {@code URI} will be used as fully qualified class name.\n\t * The {@linkplain URI#getFragment() fragment} component of the {@code URI}\n\t * will be used to retrieve the method name and method parameter types.\n\t *\n\t * @param uri the {@code URI} for the method; never {@code null}\n\t * @return a new {@code MethodSource}; never {@code null}\n\t * @since 5.5\n\t * @see #METHOD_SCHEME\n\t * @see DiscoverySelectors#selectMethod(String)\n\t */\n\tstatic MethodSource from(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t\tPreconditions.condition(METHOD_SCHEME.equals(uri.getScheme()),\n\t\t\t() -> \"URI [\" + uri + \"] must have [\" + METHOD_SCHEME + \"] scheme\");\n\t\tString schemeSpecificPart = Preconditions.notNull(uri.getSchemeSpecificPart(),\n\t\t\t() -> \"Invalid method URI (scheme-specific part must not be null). Please consult the Javadoc of \"\n\t\t\t\t\t+ DiscoverySelectors.class.getName()\n\t\t\t\t\t+ \"#selectMethod(String) for details on the supported formats.\");\n\t\tString fragment = Preconditions.notNull(uri.getFragment(),\n\t\t\t() -> \"Invalid method URI (fragment must not be null). Please consult the Javadoc of \"\n\t\t\t\t\t+ DiscoverySelectors.class.getName()\n\t\t\t\t\t+ \"#selectMethod(String) for details on the supported formats.\");\n\n\t\tString fullyQualifiedMethodName = schemeSpecificPart + \"#\" + fragment;\n\t\tString[] methodSpec = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName);\n\t\treturn MethodSource.from(methodSpec[0], methodSpec[1], methodSpec[2]);\n\t}\n\n\tprivate MethodSourceSupport() {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.DisplayNameUtils.createDisplayNameSupplierForNestedClass;\nimport static org.junit.jupiter.engine.descriptor.ResourceLockAware.enclosingInstanceTypesDependentResourceLocksProviderEvaluator;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.ExtensionContextSupplier;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@link TestDescriptor} for tests based on nested (but not static) Java classes.\n *\n * <h2>Default Display Names</h2>\n *\n * <p>The default display name for a non-static nested test class is the simple\n * name of the class.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class NestedClassTestDescriptor extends ClassBasedTestDescriptor {\n\n\tpublic static final String SEGMENT_TYPE = \"nested-class\";\n\n\tpublic NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass,\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass,\n\t\t\tcreateDisplayNameSupplierForNestedClass(enclosingInstanceTypes, testClass, configuration), configuration);\n\t}\n\n\tprivate NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, String displayName,\n\t\t\tJupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, displayName, configuration);\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected NestedClassTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new NestedClassTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getTestClass(), getDisplayName(),\n\t\t\tconfiguration);\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic final Set<TestTag> getTags() {\n\t\t// return modifiable copy\n\t\tSet<TestTag> allTags = new LinkedHashSet<>(this.classInfo.tags);\n\t\tgetParent().ifPresent(parentDescriptor -> allTags.addAll(parentDescriptor.getTags()));\n\t\treturn allTags;\n\t}\n\n\t// --- TestClassAware ------------------------------------------------------\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getEnclosingTestClasses(getParent().orElse(null));\n\t}\n\n\t@API(status = INTERNAL, since = \"5.12\")\n\tpublic static List<Class<?>> getEnclosingTestClasses(@Nullable TestDescriptor parent) {\n\t\tif (parent instanceof TestClassAware testClassAwareParent) {\n\t\t\tList<Class<?>> result = new ArrayList<>(testClassAwareParent.getEnclosingTestClasses());\n\t\t\tresult.add(testClassAwareParent.getTestClass());\n\t\t\treturn List.copyOf(result);\n\t\t}\n\t\treturn List.of();\n\t}\n\n\t// --- ClassBasedTestDescriptor --------------------------------------------\n\n\t@Override\n\tprotected TestInstances instantiateTestClass(JupiterEngineExecutionContext parentExecutionContext,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry registry,\n\t\t\tJupiterEngineExecutionContext context) {\n\n\t\t// Extensions registered for nested classes and below are not to be used for instantiating and initializing outer classes\n\t\tExtensionRegistry extensionRegistryForOuterInstanceCreation = parentExecutionContext.getExtensionRegistry();\n\t\tTestInstances outerInstances = parentExecutionContext.getTestInstancesProvider().getTestInstances(\n\t\t\textensionRegistryForOuterInstanceCreation, context);\n\t\treturn instantiateTestClass(Optional.of(outerInstances), registry, extensionContext);\n\t}\n\n\t// --- ResourceLockAware ---------------------------------------------------\n\n\t@Override\n\tpublic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator() {\n\t\treturn enclosingInstanceTypesDependentResourceLocksProviderEvaluator(this::getEnclosingTestClasses, (provider,\n\t\t\t\tenclosingInstanceTypes) -> provider.provideForNestedClass(enclosingInstanceTypes, getTestClass()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ResourceLockAware.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.api.parallel.ResourceLockTarget.CHILDREN;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\n\n/**\n * @since 5.12\n */\ninterface ResourceLockAware extends TestDescriptor {\n\n\tdefault Stream<ExclusiveResource> determineExclusiveResources() {\n\n\t\tDeque<ResourceLockAware> ancestors = new ArrayDeque<>();\n\t\tTestDescriptor parent = this.getParent().orElse(null);\n\t\twhile (parent instanceof ResourceLockAware resourceLockAwareParent) {\n\t\t\tancestors.addFirst(resourceLockAwareParent);\n\t\t\tparent = parent.getParent().orElse(null);\n\t\t}\n\n\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> evaluator = getResourceLocksProviderEvaluator();\n\n\t\tif (ancestors.isEmpty()) {\n\t\t\treturn determineOwnExclusiveResources(evaluator);\n\t\t}\n\n\t\tStream<ExclusiveResource> parentStaticResourcesForChildren = ancestors.getLast() //\n\t\t\t\t.getExclusiveResourceCollector().getStaticResourcesFor(CHILDREN);\n\n\t\tStream<ExclusiveResource> ancestorDynamicResources = ancestors.stream() //\n\t\t\t\t.map(ResourceLockAware::getExclusiveResourceCollector) //\n\t\t\t\t.flatMap(collector -> collector.getDynamicResources(evaluator));\n\n\t\treturn Stream.of(ancestorDynamicResources, parentStaticResourcesForChildren,\n\t\t\tdetermineOwnExclusiveResources(evaluator))//\n\t\t\t\t.flatMap(s -> s);\n\t}\n\n\tdefault Stream<ExclusiveResource> determineOwnExclusiveResources(\n\t\t\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> providerToLocks) {\n\t\treturn this.getExclusiveResourceCollector().getAllExclusiveResources(providerToLocks);\n\t}\n\n\tExclusiveResourceCollector getExclusiveResourceCollector();\n\n\tFunction<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> getResourceLocksProviderEvaluator();\n\n\tstatic Function<ResourceLocksProvider, Set<ResourceLocksProvider.Lock>> enclosingInstanceTypesDependentResourceLocksProviderEvaluator(\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypesSupplier,\n\t\t\tBiFunction<ResourceLocksProvider, List<Class<?>>, Set<ResourceLocksProvider.Lock>> evaluator) {\n\t\treturn new Function<>() {\n\n\t\t\t@Nullable\n\t\t\tprivate List<Class<?>> enclosingInstanceTypes;\n\n\t\t\t@Override\n\t\t\tpublic Set<ResourceLocksProvider.Lock> apply(ResourceLocksProvider provider) {\n\t\t\t\tif (this.enclosingInstanceTypes == null) {\n\t\t\t\t\tthis.enclosingInstanceTypes = List.copyOf(enclosingInstanceTypesSupplier.get());\n\t\t\t\t}\n\t\t\t\treturn evaluator.apply(provider, this.enclosingInstanceTypes);\n\t\t\t}\n\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TemplateExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TemplateInvocationValidationException;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\nabstract class TemplateExecutor<P extends Extension, C> {\n\n\tprivate final TestDescriptor parent;\n\tprivate final Class<P> providerType;\n\tprivate final DynamicDescendantFilter dynamicDescendantFilter;\n\n\t<T extends TestDescriptor & Filterable> TemplateExecutor(T parent, Class<P> providerType) {\n\t\tthis.parent = parent;\n\t\tthis.providerType = providerType;\n\t\tthis.dynamicDescendantFilter = parent.getDynamicDescendantFilter();\n\t}\n\n\tvoid execute(JupiterEngineExecutionContext context, Node.DynamicTestExecutor dynamicTestExecutor) {\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tList<P> providers = validateProviders(extensionContext, context.getExtensionRegistry());\n\t\tAtomicInteger invocationIndex = new AtomicInteger();\n\t\tfor (P provider : providers) {\n\t\t\texecuteForProvider(provider, invocationIndex, dynamicTestExecutor, extensionContext);\n\t\t}\n\t}\n\n\tprivate void executeForProvider(P provider, AtomicInteger invocationIndex,\n\t\t\tNode.DynamicTestExecutor dynamicTestExecutor, ExtensionContext extensionContext) {\n\n\t\tint initialValue = invocationIndex.get();\n\n\t\tStream<? extends C> stream = provideContexts(provider, extensionContext);\n\t\ttry {\n\t\t\tstream.forEach(invocationContext -> createInvocationTestDescriptor(invocationContext,\n\t\t\t\tinvocationIndex.incrementAndGet()) //\n\t\t\t\t\t\t.ifPresent(testDescriptor -> execute(dynamicTestExecutor, testDescriptor)));\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\ttry {\n\t\t\t\tstream.close();\n\t\t\t}\n\t\t\tcatch (TemplateInvocationValidationException ignore) {\n\t\t\t\t// ignore exceptions from close() to avoid masking the original failure\n\t\t\t}\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t}\n\t\tfinally {\n\t\t\tstream.close();\n\t\t}\n\n\t\tPreconditions.condition(\n\t\t\tinvocationIndex.get() != initialValue || mayReturnZeroContexts(provider, extensionContext),\n\t\t\tgetZeroContextsProvidedErrorMessage(provider));\n\t}\n\n\tprivate List<P> validateProviders(ExtensionContext extensionContext, ExtensionRegistry extensionRegistry) {\n\t\tList<P> providers = extensionRegistry.stream(providerType) //\n\t\t\t\t.filter(provider -> supports(provider, extensionContext)) //\n\t\t\t\t.toList();\n\t\treturn Preconditions.notEmpty(providers, this::getNoRegisteredProviderErrorMessage);\n\t}\n\n\tprivate Optional<TestDescriptor> createInvocationTestDescriptor(C invocationContext, int index) {\n\t\tUniqueId invocationUniqueId = createInvocationUniqueId(parent.getUniqueId(), index);\n\t\tif (this.dynamicDescendantFilter.test(invocationUniqueId, index - 1)) {\n\t\t\treturn Optional.of(createInvocationTestDescriptor(invocationUniqueId, invocationContext, index));\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate void execute(Node.DynamicTestExecutor dynamicTestExecutor, TestDescriptor testDescriptor) {\n\t\ttestDescriptor.setParent(parent);\n\t\tdynamicTestExecutor.execute(testDescriptor);\n\t}\n\n\tabstract boolean supports(P provider, ExtensionContext extensionContext);\n\n\tprotected abstract String getNoRegisteredProviderErrorMessage();\n\n\tabstract Stream<? extends C> provideContexts(P provider, ExtensionContext extensionContext);\n\n\tabstract boolean mayReturnZeroContexts(P provider, ExtensionContext extensionContext);\n\n\tprotected abstract String getZeroContextsProvidedErrorMessage(P provider);\n\n\tabstract UniqueId createInvocationUniqueId(UniqueId parentUniqueId, int index);\n\n\tabstract TestDescriptor createInvocationTestDescriptor(UniqueId uniqueId, C invocationContext, int index);\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestClassAware.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\npublic interface TestClassAware {\n\n\tClass<?> getTestClass();\n\n\tList<Class<?>> getEnclosingTestClasses();\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.MethodSourceSupport.METHOD_SCHEME;\nimport static org.junit.platform.engine.support.descriptor.ClassSource.CLASS_SCHEME;\nimport static org.junit.platform.engine.support.descriptor.ClasspathResourceSource.CLASSPATH_SCHEME;\n\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DynamicContainer;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.ClasspathResourceSource;\nimport org.junit.platform.engine.support.descriptor.UriSource;\n\n/**\n * {@link org.junit.platform.engine.TestDescriptor TestDescriptor} for\n * {@link org.junit.jupiter.api.TestFactory @TestFactory} methods.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class TestFactoryTestDescriptor extends TestMethodTestDescriptor implements Filterable {\n\n\tpublic static final String SEGMENT_TYPE = \"test-factory\";\n\tpublic static final String DYNAMIC_CONTAINER_SEGMENT_TYPE = \"dynamic-container\";\n\tpublic static final String DYNAMIC_TEST_SEGMENT_TYPE = \"dynamic-test\";\n\n\tprivate static final ReflectiveInterceptorCall<Method, @Nullable Object> interceptorCall = InvocationInterceptor::interceptTestFactoryMethod;\n\tprivate static final InterceptingExecutableInvoker executableInvoker = new InterceptingExecutableInvoker();\n\n\tprivate final DynamicDescendantFilter dynamicDescendantFilter;\n\n\tpublic TestFactoryTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method testMethod,\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, testMethod, enclosingInstanceTypes, configuration);\n\t\tthis.dynamicDescendantFilter = new DynamicDescendantFilter();\n\t}\n\n\tprivate TestFactoryTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,\n\t\t\tJupiterConfiguration configuration, DynamicDescendantFilter dynamicDescendantFilter) {\n\t\tsuper(uniqueId, displayName, testClass, testMethod, configuration);\n\t\tthis.dynamicDescendantFilter = dynamicDescendantFilter;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected TestFactoryTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new TestFactoryTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getDisplayName(), getTestClass(),\n\t\t\tgetTestMethod(), this.configuration, this.dynamicDescendantFilter.copy(uniqueIdTransformer));\n\t}\n\n\t// --- Filterable ----------------------------------------------------------\n\n\t@Override\n\tpublic DynamicDescendantFilter getDynamicDescendantFilter() {\n\t\treturn dynamicDescendantFilter;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tpublic boolean mayRegisterTests() {\n\t\treturn true;\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tprotected void invokeTestMethod(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) {\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\n\t\tcontext.getThrowableCollector().execute(() -> {\n\t\t\tObject instance = extensionContext.getRequiredTestInstance();\n\t\t\tObject testFactoryMethodResult = executableInvoker.<@Nullable Object> invoke(getTestMethod(), instance,\n\t\t\t\textensionContext, context.getExtensionRegistry(), interceptorCall);\n\t\t\tTestSource defaultTestSource = getSource().orElseThrow(\n\t\t\t\t() -> new JUnitException(\"Illegal state: TestSource must be present\"));\n\t\t\ttry (Stream<DynamicNode> dynamicNodeStream = toDynamicNodeStream(testFactoryMethodResult)) {\n\t\t\t\tint index = 1;\n\t\t\t\tIterator<DynamicNode> iterator = dynamicNodeStream.iterator();\n\t\t\t\twhile (iterator.hasNext()) {\n\t\t\t\t\tDynamicNode dynamicNode = iterator.next();\n\t\t\t\t\tOptional<JupiterTestDescriptor> descriptor = createDynamicDescriptor(this, dynamicNode, index,\n\t\t\t\t\t\tdefaultTestSource, getDynamicDescendantFilter(), configuration);\n\t\t\t\t\tdescriptor.ifPresent(dynamicTestExecutor::execute);\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (ClassCastException ex) {\n\t\t\t\tthrow invalidReturnTypeException(ex);\n\t\t\t}\n\t\t\tdynamicTestExecutor.awaitFinished();\n\t\t});\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate Stream<DynamicNode> toDynamicNodeStream(@Nullable Object testFactoryMethodResult) {\n\t\tif (testFactoryMethodResult == null) {\n\t\t\tthrow new JUnitException(\"@TestFactory method must not return null\");\n\t\t}\n\t\tif (testFactoryMethodResult instanceof DynamicNode node) {\n\t\t\treturn Stream.of(node);\n\t\t}\n\t\treturn (Stream<DynamicNode>) CollectionUtils.toStream(testFactoryMethodResult);\n\t}\n\n\tprivate JUnitException invalidReturnTypeException(Throwable cause) {\n\t\tString message = \"Objects produced by @TestFactory method '%s' must be of type %s.\".formatted(\n\t\t\tgetTestMethod().toGenericString(), DynamicNode.class.getName());\n\t\treturn new JUnitException(message, cause);\n\t}\n\n\tstatic Optional<JupiterTestDescriptor> createDynamicDescriptor(JupiterTestDescriptor parent, DynamicNode node,\n\t\t\tint index, TestSource defaultTestSource, DynamicDescendantFilter dynamicDescendantFilter,\n\t\t\tJupiterConfiguration configuration) {\n\n\t\tUniqueId uniqueId;\n\t\tSupplier<JupiterTestDescriptor> descriptorCreator;\n\t\tOptional<TestSource> customTestSource = node.getTestSourceUri().map(TestFactoryTestDescriptor::fromUri);\n\t\tTestSource source = customTestSource.orElse(defaultTestSource);\n\n\t\tif (node instanceof DynamicTest test) {\n\t\t\tuniqueId = parent.getUniqueId().append(DYNAMIC_TEST_SEGMENT_TYPE, \"#\" + index);\n\t\t\tdescriptorCreator = () -> new DynamicTestTestDescriptor(uniqueId, index, test, source, configuration);\n\t\t}\n\t\telse {\n\t\t\tDynamicContainer container = (DynamicContainer) node;\n\t\t\tuniqueId = parent.getUniqueId().append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#\" + index);\n\t\t\tdescriptorCreator = () -> new DynamicContainerTestDescriptor(uniqueId, index, container, source,\n\t\t\t\tdynamicDescendantFilter.withoutIndexFiltering(), configuration);\n\t\t}\n\t\tif (dynamicDescendantFilter.test(uniqueId, index - 1)) {\n\t\t\tJupiterTestDescriptor descriptor = descriptorCreator.get();\n\t\t\tdescriptor.setParent(parent);\n\t\t\treturn Optional.of(descriptor);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * @since 5.3\n\t */\n\tstatic TestSource fromUri(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t\tif (CLASSPATH_SCHEME.equals(uri.getScheme())) {\n\t\t\treturn ClasspathResourceSource.from(uri);\n\t\t}\n\t\tif (CLASS_SCHEME.equals(uri.getScheme())) {\n\t\t\treturn ClassSource.from(uri);\n\t\t}\n\t\tif (METHOD_SCHEME.equals(uri.getScheme())) {\n\t\t\treturn MethodSourceSupport.from(uri);\n\t\t}\n\t\treturn UriSource.from(uri);\n\t}\n\n\t/**\n\t * Override {@link TestMethodTestDescriptor#nodeSkipped} as a no-op, since\n\t * the {@code TestWatcher} API is not supported for {@code @TestFactory}\n\t * containers.\n\t *\n\t * @since 5.4\n\t */\n\t@Override\n\tpublic void nodeSkipped(JupiterEngineExecutionContext context, TestDescriptor descriptor, SkipResult result) {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Override {@link TestMethodTestDescriptor#nodeFinished} as a no-op, since\n\t * the {@code TestWatcher} API is not supported for {@code @TestFactory}\n\t * containers.\n\t *\n\t * @since 5.4\n\t */\n\t@Override\n\tpublic void nodeFinished(JupiterEngineExecutionContext context, TestDescriptor descriptor,\n\t\t\tTestExecutionResult result) {\n\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Collection of utilities for retrieving the test instance lifecycle mode.\n *\n * @since 5.0\n * @see TestInstance\n * @see TestInstance.Lifecycle\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic final class TestInstanceLifecycleUtils {\n\n\tprivate TestInstanceLifecycleUtils() {\n\t\t/* no-op */\n\t}\n\n\tstatic TestInstance.Lifecycle getTestInstanceLifecycle(Class<?> testClass, JupiterConfiguration configuration) {\n\t\tPreconditions.notNull(testClass, \"testClass must not be null\");\n\t\tPreconditions.notNull(configuration, \"configuration must not be null\");\n\n\t\t// @formatter:off\n\t\treturn AnnotationSupport.findAnnotation(testClass, TestInstance.class)\n\t\t\t\t.map(TestInstance::value)\n\t\t\t\t.orElseGet(configuration::getDefaultTestInstanceLifecycle);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestMethodTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeAfterCallbacks;\nimport static org.junit.jupiter.engine.descriptor.CallbackSupport.invokeBeforeCallbacks;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromExecutableParameters;\nimport static org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory.createThrowableCollector;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.extension.TestWatcher;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.AfterEachMethodAdapter;\nimport org.junit.jupiter.engine.execution.BeforeEachMethodAdapter;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall.VoidMethodInterceptorCall;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * {@link TestDescriptor} for {@link org.junit.jupiter.api.Test @Test} methods.\n *\n * <h2>Default Display Names</h2>\n *\n * <p>The default display name for a test method is the name of the method\n * concatenated with a comma-separated list of parameter types in parentheses.\n * The names of parameter types are retrieved using {@link Class#getSimpleName()}.\n * For example, the default display name for the following test method is\n * {@code testUser(TestInfo, User)}.\n *\n * <pre class=\"code\">\n *   {@literal @}Test\n *   void testUser(TestInfo testInfo, {@literal @}Mock User user) { ... }\n * </pre>\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class TestMethodTestDescriptor extends MethodBasedTestDescriptor {\n\n\tpublic static final String SEGMENT_TYPE = \"method\";\n\tprivate static final InterceptingExecutableInvoker executableInvoker = new InterceptingExecutableInvoker();\n\tprivate static final VoidMethodInterceptorCall defaultInterceptorCall = InvocationInterceptor::interceptTestMethod;\n\tprivate final VoidMethodInterceptorCall interceptorCall;\n\n\tpublic TestMethodTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method testMethod,\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, testMethod, enclosingInstanceTypes, configuration);\n\t\tthis.interceptorCall = defaultInterceptorCall;\n\t}\n\n\tTestMethodTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,\n\t\t\tJupiterConfiguration configuration) {\n\t\tthis(uniqueId, displayName, testClass, testMethod, configuration, defaultInterceptorCall);\n\t}\n\n\tTestMethodTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method testMethod,\n\t\t\tJupiterConfiguration configuration, VoidMethodInterceptorCall interceptorCall) {\n\t\tsuper(uniqueId, displayName, testClass, testMethod, configuration);\n\t\tthis.interceptorCall = interceptorCall;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected TestMethodTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new TestMethodTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getDisplayName(), getTestClass(),\n\t\t\tgetTestMethod(), this.configuration, interceptorCall);\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.TEST;\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = populateNewExtensionRegistry(context);\n\t\tThrowableCollector throwableCollector = createThrowableCollector();\n\t\tMethodExtensionContext extensionContext = new MethodExtensionContext(context.getExtensionContext(),\n\t\t\tcontext.getExecutionListener(), this, context.getConfiguration(), registry,\n\t\t\tcontext.getLauncherStoreFacade(), throwableCollector);\n\t\t// @formatter:off\n\t\tJupiterEngineExecutionContext newContext = context.extend()\n\t\t\t\t.withExtensionRegistry(registry)\n\t\t\t\t.withExtensionContext(extensionContext)\n\t\t\t\t.withThrowableCollector(throwableCollector)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthrowableCollector.execute(() -> {\n\t\t\tTestInstances testInstances = newContext.getTestInstancesProvider().getTestInstances(newContext);\n\t\t\textensionContext.setTestInstances(testInstances);\n\t\t\tprepareExtensionContext(extensionContext);\n\t\t});\n\t\treturn newContext;\n\t}\n\n\tprotected void prepareExtensionContext(ExtensionContext extensionContext) {\n\t\t// nothing to do by default\n\t}\n\n\tprotected MutableExtensionRegistry populateNewExtensionRegistry(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(\n\t\t\tcontext.getExtensionRegistry(), getTestMethod());\n\t\tregisterExtensionsFromExecutableParameters(registry, getTestMethod());\n\t\treturn registry;\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) {\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\n\t\t// @formatter:off\n\t\tinvokeBeforeEachCallbacks(context);\n\t\t\tif (throwableCollector.isEmpty()) {\n\t\t\t\tinvokeBeforeEachMethods(context);\n\t\t\t\tif (throwableCollector.isEmpty()) {\n\t\t\t\t\tinvokeBeforeTestExecutionCallbacks(context);\n\t\t\t\t\tif (throwableCollector.isEmpty()) {\n\t\t\t\t\t\tinvokeTestMethod(context, dynamicTestExecutor);\n\t\t\t\t\t}\n\t\t\t\t\tinvokeAfterTestExecutionCallbacks(context);\n\t\t\t\t}\n\t\t\t\tinvokeAfterEachMethods(context);\n\t\t\t}\n\t\tinvokeAfterEachCallbacks(context);\n\t\t// @formatter:on\n\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic void cleanUp(JupiterEngineExecutionContext context) throws Exception {\n\t\tif (isPerMethodLifecycle(context) && context.getExtensionContext().getTestInstance().isPresent()) {\n\t\t\tinvokeTestInstancePreDestroyCallbacks(context);\n\t\t}\n\t\tcontext.getThrowableCollector().execute(() -> super.cleanUp(context));\n\t\tcontext.getThrowableCollector().assertEmpty();\n\t}\n\n\tprivate boolean isPerMethodLifecycle(JupiterEngineExecutionContext context) {\n\t\treturn context.getExtensionContext().getTestInstanceLifecycle().orElse(\n\t\t\tLifecycle.PER_CLASS) == Lifecycle.PER_METHOD;\n\t}\n\n\tprivate void invokeBeforeEachCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeBeforeCallbacks(BeforeEachCallback.class, context, BeforeEachCallback::beforeEach);\n\t}\n\n\tprivate void invokeBeforeEachMethods(JupiterEngineExecutionContext context) {\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tinvokeBeforeCallbacks(BeforeEachMethodAdapter.class, context, (adapter, extensionContext) -> {\n\t\t\ttry {\n\t\t\t\tadapter.invokeBeforeEachMethod(extensionContext, registry);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tinvokeBeforeEachExecutionExceptionHandlers(extensionContext, registry, throwable);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void invokeBeforeEachExecutionExceptionHandlers(ExtensionContext context, ExtensionRegistry registry,\n\t\t\tThrowable throwable) {\n\n\t\tinvokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,\n\t\t\t(handler, handledThrowable) -> handler.handleBeforeEachMethodExecutionException(context, handledThrowable));\n\t}\n\n\tprivate void invokeBeforeTestExecutionCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeBeforeCallbacks(BeforeTestExecutionCallback.class, context,\n\t\t\tBeforeTestExecutionCallback::beforeTestExecution);\n\t}\n\n\tprotected void invokeTestMethod(JupiterEngineExecutionContext context, DynamicTestExecutor dynamicTestExecutor) {\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tThrowableCollector throwableCollector = context.getThrowableCollector();\n\n\t\tthrowableCollector.execute(() -> {\n\t\t\ttry {\n\t\t\t\tMethod testMethod = getTestMethod();\n\t\t\t\tObject instance = extensionContext.getRequiredTestInstance();\n\t\t\t\texecutableInvoker.invokeVoid(testMethod, instance, extensionContext, context.getExtensionRegistry(),\n\t\t\t\t\tinterceptorCall);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\t\tinvokeTestExecutionExceptionHandlers(context.getExtensionRegistry(), extensionContext, throwable);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void invokeTestExecutionExceptionHandlers(ExtensionRegistry registry, ExtensionContext context,\n\t\t\tThrowable throwable) {\n\n\t\tinvokeExecutionExceptionHandlers(TestExecutionExceptionHandler.class, registry, throwable,\n\t\t\t(handler, handledThrowable) -> handler.handleTestExecutionException(context, handledThrowable));\n\t}\n\n\tprivate void invokeAfterTestExecutionCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeAfterCallbacks(AfterTestExecutionCallback.class, context, AfterTestExecutionCallback::afterTestExecution);\n\t}\n\n\tprivate void invokeAfterEachMethods(JupiterEngineExecutionContext context) {\n\t\tExtensionRegistry registry = context.getExtensionRegistry();\n\t\tinvokeAfterCallbacks(AfterEachMethodAdapter.class, context, (adapter, extensionContext) -> {\n\t\t\ttry {\n\t\t\t\tadapter.invokeAfterEachMethod(extensionContext, registry);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tinvokeAfterEachExecutionExceptionHandlers(extensionContext, registry, throwable);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void invokeAfterEachExecutionExceptionHandlers(ExtensionContext context, ExtensionRegistry registry,\n\t\t\tThrowable throwable) {\n\n\t\tinvokeExecutionExceptionHandlers(LifecycleMethodExecutionExceptionHandler.class, registry, throwable,\n\t\t\t(handler, handledThrowable) -> handler.handleAfterEachMethodExecutionException(context, handledThrowable));\n\t}\n\n\tprivate void invokeAfterEachCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeAfterCallbacks(AfterEachCallback.class, context, AfterEachCallback::afterEach);\n\t}\n\n\tprivate void invokeTestInstancePreDestroyCallbacks(JupiterEngineExecutionContext context) {\n\t\tinvokeAfterCallbacks(TestInstancePreDestroyCallback.class, context,\n\t\t\tTestInstancePreDestroyCallback::preDestroyTestInstance);\n\t}\n\n\t/**\n\t * Invoke {@link TestWatcher#testSuccessful testSuccessful()},\n\t * {@link TestWatcher#testAborted testAborted()}, or\n\t * {@link TestWatcher#testFailed testFailed()} on each\n\t * registered {@link TestWatcher} according to the status of the supplied\n\t * {@link TestExecutionResult}, in reverse registration order.\n\t *\n\t * @since 5.4\n\t */\n\t@Override\n\tpublic void nodeFinished(JupiterEngineExecutionContext context, TestDescriptor descriptor,\n\t\t\tTestExecutionResult result) {\n\n\t\tExtensionContext extensionContext = context.getExtensionContext();\n\t\tTestExecutionResult.Status status = result.getStatus();\n\n\t\tinvokeTestWatchers(context, true, watcher -> {\n\t\t\tswitch (status) {\n\t\t\t\tcase SUCCESSFUL -> watcher.testSuccessful(extensionContext);\n\t\t\t\tcase ABORTED -> watcher.testAborted(extensionContext, result.getThrowable().orElse(null));\n\t\t\t\tcase FAILED -> watcher.testFailed(extensionContext, result.getThrowable().orElse(null));\n\t\t\t}\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateExtensionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.Node;\n\n/**\n * @since 5.0\n */\nfinal class TestTemplateExtensionContext extends AbstractExtensionContext<TestTemplateTestDescriptor> {\n\n\tprivate final @Nullable TestInstances testInstances;\n\n\tTestTemplateExtensionContext(ExtensionContext parent, EngineExecutionListener engineExecutionListener,\n\t\t\tTestTemplateTestDescriptor testDescriptor, JupiterConfiguration configuration,\n\t\t\tExtensionRegistry extensionRegistry, LauncherStoreFacade launcherStoreFacade,\n\t\t\t@Nullable TestInstances testInstances) {\n\n\t\tsuper(parent, engineExecutionListener, testDescriptor, configuration, extensionRegistry, launcherStoreFacade);\n\t\tthis.testInstances = testInstances;\n\t}\n\n\t@Override\n\tpublic Optional<AnnotatedElement> getElement() {\n\t\treturn Optional.of(getTestDescriptor().getTestMethod());\n\t}\n\n\t@Override\n\tpublic Optional<Class<?>> getTestClass() {\n\t\treturn Optional.of(getTestDescriptor().getTestClass());\n\t}\n\n\t@Override\n\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\treturn getTestDescriptor().getEnclosingTestClasses();\n\t}\n\n\t@Override\n\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\treturn getParent().flatMap(ExtensionContext::getTestInstanceLifecycle);\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTestInstance() {\n\t\treturn getTestInstances().map(TestInstances::getInnermostInstance);\n\t}\n\n\t@Override\n\tpublic Optional<TestInstances> getTestInstances() {\n\t\treturn Optional.ofNullable(this.testInstances);\n\t}\n\n\t@Override\n\tpublic Optional<Method> getTestMethod() {\n\t\treturn Optional.of(getTestDescriptor().getTestMethod());\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> getExecutionException() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tprotected Node.ExecutionMode getPlatformExecutionMode() {\n\t\treturn getTestDescriptor().getExecutionMode();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.reflect.Method;\nimport java.util.OptionalInt;\nimport java.util.Set;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall.VoidMethodInterceptorCall;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\n\n/**\n * {@link TestDescriptor} for a {@link org.junit.jupiter.api.TestTemplate @TestTemplate}\n * invocation.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class TestTemplateInvocationTestDescriptor extends TestMethodTestDescriptor {\n\n\tpublic static final String SEGMENT_TYPE = \"test-template-invocation\";\n\tprivate static final VoidMethodInterceptorCall interceptorCall = InvocationInterceptor::interceptTestTemplateMethod;\n\n\tprivate @Nullable TestTemplateInvocationContext invocationContext;\n\n\tprivate final int index;\n\n\tTestTemplateInvocationTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method templateMethod,\n\t\t\tTestTemplateInvocationContext invocationContext, int index, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, invocationContext.getDisplayName(index), testClass, templateMethod, configuration,\n\t\t\tinterceptorCall);\n\t\tthis.invocationContext = invocationContext;\n\t\tthis.index = index;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected TestTemplateInvocationTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new TestTemplateInvocationTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getTestClass(),\n\t\t\tgetTestMethod(), requiredInvocationContext(), this.index, this.configuration);\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Set<ExclusiveResource> getExclusiveResources() {\n\t\t// Resources are already collected and returned by the enclosing container\n\t\treturn emptySet();\n\t}\n\n\t@Override\n\tprotected OptionalInt getLegacyReportingIndex() {\n\t\treturn OptionalInt.of(index);\n\t}\n\n\t@Override\n\tprotected MutableExtensionRegistry populateNewExtensionRegistry(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = super.populateNewExtensionRegistry(context);\n\t\tvar invocationContext = requiredInvocationContext();\n\t\tinvocationContext.getAdditionalExtensions().forEach(\n\t\t\textension -> registry.registerExtension(extension, invocationContext));\n\t\treturn registry;\n\t}\n\n\t@Override\n\tprotected void prepareExtensionContext(ExtensionContext extensionContext) {\n\t\trequiredInvocationContext().prepareInvocation(extensionContext);\n\t}\n\n\t@Override\n\tpublic void after(JupiterEngineExecutionContext context) {\n\t\t// forget invocationContext so it can be garbage collected\n\t\tthis.invocationContext = null;\n\t}\n\n\tprivate TestTemplateInvocationContext requiredInvocationContext() {\n\t\treturn requireNonNull(this.invocationContext);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.descriptor.ExtensionUtils.populateNewExtensionRegistryFromExtendWithAnnotation;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@link TestDescriptor} for {@link org.junit.jupiter.api.TestTemplate @TestTemplate}\n * methods.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class TestTemplateTestDescriptor extends MethodBasedTestDescriptor implements Filterable {\n\n\tpublic static final String SEGMENT_TYPE = \"test-template\";\n\tprivate final DynamicDescendantFilter dynamicDescendantFilter;\n\n\tpublic TestTemplateTestDescriptor(UniqueId uniqueId, Class<?> testClass, Method templateMethod,\n\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration) {\n\t\tsuper(uniqueId, testClass, templateMethod, enclosingInstanceTypes, configuration);\n\t\tthis.dynamicDescendantFilter = new DynamicDescendantFilter();\n\t}\n\n\tprivate TestTemplateTestDescriptor(UniqueId uniqueId, String displayName, Class<?> testClass, Method templateMethod,\n\t\t\tJupiterConfiguration configuration, DynamicDescendantFilter dynamicDescendantFilter) {\n\t\tsuper(uniqueId, displayName, testClass, templateMethod, configuration);\n\t\tthis.dynamicDescendantFilter = dynamicDescendantFilter;\n\t}\n\n\t// --- JupiterTestDescriptor -----------------------------------------------\n\n\t@Override\n\tprotected TestTemplateTestDescriptor withUniqueId(UnaryOperator<UniqueId> uniqueIdTransformer) {\n\t\treturn new TestTemplateTestDescriptor(uniqueIdTransformer.apply(getUniqueId()), getDisplayName(),\n\t\t\tgetTestClass(), getTestMethod(), this.configuration,\n\t\t\tthis.dynamicDescendantFilter.copy(uniqueIdTransformer));\n\t}\n\n\t// --- Filterable ----------------------------------------------------------\n\n\t@Override\n\tpublic DynamicDescendantFilter getDynamicDescendantFilter() {\n\t\treturn dynamicDescendantFilter;\n\t}\n\n\t// --- TestDescriptor ------------------------------------------------------\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tpublic boolean mayRegisterTests() {\n\t\treturn true;\n\t}\n\n\t// --- Node ----------------------------------------------------------------\n\n\t@Override\n\tpublic JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext context) {\n\t\tMutableExtensionRegistry registry = populateNewExtensionRegistryFromExtendWithAnnotation(\n\t\t\tcontext.getExtensionRegistry(), getTestMethod());\n\n\t\t// The test instance should be properly maintained by the enclosing class's ExtensionContext.\n\t\tTestInstances testInstances = context.getExtensionContext().getTestInstances().orElse(null);\n\n\t\tExtensionContext extensionContext = new TestTemplateExtensionContext(context.getExtensionContext(),\n\t\t\tcontext.getExecutionListener(), this, context.getConfiguration(), registry,\n\t\t\tcontext.getLauncherStoreFacade(), testInstances);\n\n\t\t// @formatter:off\n\t\treturn context.extend()\n\t\t\t\t.withExtensionRegistry(registry)\n\t\t\t\t.withExtensionContext(extensionContext)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic JupiterEngineExecutionContext execute(JupiterEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) throws Exception {\n\n\t\tnew TestTemplateExecutor().execute(context, dynamicTestExecutor);\n\t\treturn context;\n\t}\n\n\tprivate class TestTemplateExecutor\n\t\t\textends TemplateExecutor<TestTemplateInvocationContextProvider, TestTemplateInvocationContext> {\n\n\t\tTestTemplateExecutor() {\n\t\t\tsuper(TestTemplateTestDescriptor.this, TestTemplateInvocationContextProvider.class);\n\t\t}\n\n\t\t@Override\n\t\tboolean supports(TestTemplateInvocationContextProvider provider, ExtensionContext extensionContext) {\n\t\t\treturn provider.supportsTestTemplate(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tprotected String getNoRegisteredProviderErrorMessage() {\n\t\t\treturn \"You must register at least one %s that supports @%s method [%s]\".formatted(\n\t\t\t\tTestTemplateInvocationContextProvider.class.getSimpleName(), TestTemplate.class.getSimpleName(),\n\t\t\t\tgetTestMethod());\n\t\t}\n\n\t\t@Override\n\t\tStream<? extends TestTemplateInvocationContext> provideContexts(TestTemplateInvocationContextProvider provider,\n\t\t\t\tExtensionContext extensionContext) {\n\t\t\treturn provider.provideTestTemplateInvocationContexts(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tboolean mayReturnZeroContexts(TestTemplateInvocationContextProvider provider,\n\t\t\t\tExtensionContext extensionContext) {\n\t\t\treturn provider.mayReturnZeroTestTemplateInvocationContexts(extensionContext);\n\t\t}\n\n\t\t@Override\n\t\tprotected String getZeroContextsProvidedErrorMessage(TestTemplateInvocationContextProvider provider) {\n\t\t\treturn \"\"\"\n\t\t\t\t\tProvider [%s] did not provide any invocation contexts, but was expected to do so. \\\n\t\t\t\t\tYou may override mayReturnZeroTestTemplateInvocationContexts() to allow this.\"\"\".formatted(\n\t\t\t\tprovider.getClass().getSimpleName());\n\t\t}\n\n\t\t@Override\n\t\tUniqueId createInvocationUniqueId(UniqueId parentUniqueId, int index) {\n\t\t\treturn parentUniqueId.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#\" + index);\n\t\t}\n\n\t\t@Override\n\t\tTestDescriptor createInvocationTestDescriptor(UniqueId uniqueId,\n\t\t\t\tTestTemplateInvocationContext invocationContext, int index) {\n\t\t\treturn new TestTemplateInvocationTestDescriptor(uniqueId, getTestClass(), getTestMethod(),\n\t\t\t\tinvocationContext, index, TestTemplateTestDescriptor.this.configuration);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/UniqueIdPrefixTransformer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.util.List;\nimport java.util.function.UnaryOperator;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 5.13\n */\nclass UniqueIdPrefixTransformer implements UnaryOperator<UniqueId> {\n\n\tprivate final UniqueId oldPrefix;\n\tprivate final UniqueId newPrefix;\n\tprivate final int oldPrefixLength;\n\n\tUniqueIdPrefixTransformer(UniqueId oldPrefix, UniqueId newPrefix) {\n\t\tthis.oldPrefix = oldPrefix;\n\t\tthis.newPrefix = newPrefix;\n\t\tthis.oldPrefixLength = oldPrefix.getSegments().size();\n\t}\n\n\t@Override\n\tpublic UniqueId apply(UniqueId uniqueId) {\n\t\tPreconditions.condition(uniqueId.hasPrefix(oldPrefix),\n\t\t\t() -> \"Unique ID %s does not have the expected prefix %s\".formatted(uniqueId, oldPrefix));\n\t\tList<UniqueId.Segment> oldSegments = uniqueId.getSegments();\n\t\tList<UniqueId.Segment> suffix = oldSegments.subList(oldPrefixLength, oldSegments.size());\n\t\tUniqueId newValue = newPrefix;\n\t\tfor (UniqueId.Segment segment : suffix) {\n\t\t\tnewValue = newValue.append(segment);\n\t\t}\n\t\treturn newValue;\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/Validatable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Interface for descriptors that can be validated during discovery.\n *\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\npublic interface Validatable {\n\n\t/**\n\t * Validate the state of this descriptor and report any issues found to the\n\t * supplied {@link DiscoveryIssueReporter}.\n\t */\n\tvoid validate(DiscoveryIssueReporter reporter);\n\n\t/**\n\t * Report and clear the given list of {@link DiscoveryIssue}s using the\n\t * supplied {@link DiscoveryIssueReporter}.\n\t */\n\tstatic void reportAndClear(List<DiscoveryIssue> issues, DiscoveryIssueReporter reporter) {\n\t\tissues.forEach(reporter::reportIssue);\n\t\tissues.clear();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Test descriptors used within the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.descriptor;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractAnnotatedDescriptorWrapper.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * Abstract base class for wrappers for test descriptors based on annotated\n * elements.\n *\n * @since 5.8\n */\nabstract class AbstractAnnotatedDescriptorWrapper<E extends AnnotatedElement> {\n\n\tprivate final TestDescriptor testDescriptor;\n\tprivate final E annotatedElement;\n\n\tAbstractAnnotatedDescriptorWrapper(TestDescriptor testDescriptor, E annotatedElement) {\n\t\tthis.testDescriptor = testDescriptor;\n\t\tthis.annotatedElement = annotatedElement;\n\t}\n\n\tE getAnnotatedElement() {\n\t\treturn this.annotatedElement;\n\t}\n\n\tTestDescriptor getTestDescriptor() {\n\t\treturn this.testDescriptor;\n\t}\n\n\tpublic final String getDisplayName() {\n\t\treturn this.testDescriptor.getDisplayName();\n\t}\n\n\tpublic final boolean isAnnotated(Class<? extends Annotation> annotationType) {\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\treturn AnnotationSupport.isAnnotated(getAnnotatedElement(), annotationType);\n\t}\n\n\tpublic final <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\treturn AnnotationSupport.findAnnotation(getAnnotatedElement(), annotationType);\n\t}\n\n\tpublic final <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\treturn AnnotationSupport.findRepeatableAnnotations(getAnnotatedElement(), annotationType);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/AbstractOrderingVisitor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Collectors.toCollection;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Abstract base class for {@linkplain TestDescriptor.Visitor visitors} that\n * order children nodes.\n *\n * @since 5.8\n */\nabstract class AbstractOrderingVisitor implements TestDescriptor.Visitor {\n\n\tprivate final DiscoveryIssueReporter issueReporter;\n\n\tAbstractOrderingVisitor(DiscoveryIssueReporter issueReporter) {\n\t\tthis.issueReporter = issueReporter;\n\t}\n\n\t/**\n\t * @param <PARENT> the parent container type to search in for matching children\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprotected <PARENT extends TestDescriptor> void doWithMatchingDescriptor(Class<PARENT> parentTestDescriptorType,\n\t\t\tTestDescriptor testDescriptor, Consumer<PARENT> action, Function<PARENT, String> errorMessageBuilder) {\n\n\t\tif (parentTestDescriptorType.isInstance(testDescriptor)) {\n\t\t\tPARENT parentTestDescriptor = (PARENT) testDescriptor;\n\t\t\ttry {\n\t\t\t\taction.accept(parentTestDescriptor);\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\tString message = errorMessageBuilder.apply(parentTestDescriptor);\n\t\t\t\tthis.issueReporter.reportIssue(DiscoveryIssue.builder(Severity.ERROR, message) //\n\t\t\t\t\t\t.source(parentTestDescriptor.getSource()) //\n\t\t\t\t\t\t.cause(t));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @param <CHILD> the type of children (containers or tests) to order\n\t */\n\tprotected <PARENT extends TestDescriptor, CHILD extends TestDescriptor, WRAPPER extends AbstractAnnotatedDescriptorWrapper<?>> void orderChildrenTestDescriptors(\n\t\t\tPARENT parentTestDescriptor, Class<CHILD> matchingChildrenType, Optional<Consumer<CHILD>> validationAction,\n\t\t\tFunction<CHILD, WRAPPER> descriptorWrapperFactory,\n\t\t\tDescriptorWrapperOrderer<PARENT, ?, WRAPPER> descriptorWrapperOrderer) {\n\n\t\tStream<CHILD> matchingChildren = parentTestDescriptor.getChildren()//\n\t\t\t\t.stream()//\n\t\t\t\t.filter(matchingChildrenType::isInstance)//\n\t\t\t\t.map(matchingChildrenType::cast);\n\n\t\tif (!descriptorWrapperOrderer.canOrderWrappers()) {\n\t\t\tvalidationAction.ifPresent(matchingChildren::forEach);\n\t\t\treturn;\n\t\t}\n\n\t\tif (validationAction.isPresent()) {\n\t\t\tmatchingChildren = matchingChildren.peek(validationAction.get());\n\t\t}\n\n\t\tList<WRAPPER> matchingDescriptorWrappers = matchingChildren//\n\t\t\t\t.map(descriptorWrapperFactory)//\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\n\t\t// If there are no children to order, abort early.\n\t\tif (matchingDescriptorWrappers.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tparentTestDescriptor.orderChildren(children -> {\n\t\t\tStream<TestDescriptor> nonMatchingTestDescriptors = children.stream()//\n\t\t\t\t\t.filter(childTestDescriptor -> !matchingChildrenType.isInstance(childTestDescriptor));\n\n\t\t\tdescriptorWrapperOrderer.orderWrappers(parentTestDescriptor, matchingDescriptorWrappers,\n\t\t\t\tmessage -> reportWarning(parentTestDescriptor, message));\n\n\t\t\tStream<TestDescriptor> orderedTestDescriptors = matchingDescriptorWrappers.stream()//\n\t\t\t\t\t.map(AbstractAnnotatedDescriptorWrapper::getTestDescriptor);\n\n\t\t\tif (shouldNonMatchingDescriptorsComeBeforeOrderedOnes()) {\n\t\t\t\treturn Stream.concat(nonMatchingTestDescriptors, orderedTestDescriptors)//\n\t\t\t\t\t\t.toList();\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn Stream.concat(orderedTestDescriptors, nonMatchingTestDescriptors)//\n\t\t\t\t\t\t.toList();\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void reportWarning(TestDescriptor parentTestDescriptor, String message) {\n\t\tissueReporter.reportIssue(DiscoveryIssue.builder(Severity.WARNING, message) //\n\t\t\t\t.source(parentTestDescriptor.getSource()));\n\t}\n\n\tprotected abstract boolean shouldNonMatchingDescriptorsComeBeforeOrderedOnes();\n\n\t/**\n\t * @param <WRAPPER> the wrapper type for the children to order\n\t */\n\tprotected static class DescriptorWrapperOrderer<PARENT extends TestDescriptor, ORDERER, WRAPPER> {\n\n\t\tprivate static final DescriptorWrapperOrderer<?, ?, ?> NOOP = new DescriptorWrapperOrderer<>(null, null,\n\t\t\t(__, ___) -> \"\", (__, ___) -> \"\");\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tstatic <PARENT extends TestDescriptor, ORDERER, WRAPPER extends AbstractAnnotatedDescriptorWrapper<?>> DescriptorWrapperOrderer<PARENT, ORDERER, WRAPPER> noop() {\n\t\t\treturn (DescriptorWrapperOrderer<PARENT, ORDERER, WRAPPER>) NOOP;\n\t\t}\n\n\t\t@Nullable\n\t\tprivate final ORDERER orderer;\n\t\t@Nullable\n\t\tprivate final OrderingAction<PARENT, WRAPPER> orderingAction;\n\n\t\tprivate final MessageGenerator<PARENT> descriptorsAddedMessageGenerator;\n\t\tprivate final MessageGenerator<PARENT> descriptorsRemovedMessageGenerator;\n\n\t\tDescriptorWrapperOrderer(@Nullable ORDERER orderer, @Nullable OrderingAction<PARENT, WRAPPER> orderingAction,\n\t\t\t\tMessageGenerator<PARENT> descriptorsAddedMessageGenerator,\n\t\t\t\tMessageGenerator<PARENT> descriptorsRemovedMessageGenerator) {\n\n\t\t\tthis.orderer = orderer;\n\t\t\tthis.orderingAction = orderingAction;\n\t\t\tthis.descriptorsAddedMessageGenerator = descriptorsAddedMessageGenerator;\n\t\t\tthis.descriptorsRemovedMessageGenerator = descriptorsRemovedMessageGenerator;\n\t\t}\n\n\t\t@Nullable\n\t\tORDERER getOrderer() {\n\t\t\treturn orderer;\n\t\t}\n\n\t\tprivate boolean canOrderWrappers() {\n\t\t\treturn this.orderingAction != null;\n\t\t}\n\n\t\tprivate void orderWrappers(PARENT parentTestDescriptor, List<WRAPPER> wrappers, Consumer<String> errorHandler) {\n\t\t\tList<WRAPPER> orderedWrappers = new ArrayList<>(wrappers);\n\t\t\trequireNonNull(this.orderingAction).order(parentTestDescriptor, orderedWrappers);\n\t\t\tMap<Object, Integer> distinctWrappersToIndex = distinctWrappersToIndex(orderedWrappers);\n\n\t\t\tint difference = orderedWrappers.size() - wrappers.size();\n\t\t\tint distinctDifference = distinctWrappersToIndex.size() - wrappers.size();\n\t\t\tif (difference > 0) { // difference >= distinctDifference\n\t\t\t\treportDescriptorsAddedWarning(difference, errorHandler, parentTestDescriptor);\n\t\t\t}\n\t\t\tif (distinctDifference < 0) { // distinctDifference <= difference\n\t\t\t\treportDescriptorsRemovedWarning(distinctDifference, errorHandler, parentTestDescriptor);\n\t\t\t}\n\n\t\t\twrappers.sort(comparing(wrapper -> distinctWrappersToIndex.getOrDefault(wrapper, -1)));\n\t\t}\n\n\t\tprivate Map<Object, Integer> distinctWrappersToIndex(List<?> wrappers) {\n\t\t\tMap<Object, Integer> toIndex = new HashMap<>();\n\t\t\tfor (int i = 0; i < wrappers.size(); i++) {\n\t\t\t\t// Avoid ClassCastException if a misbehaving ordering action added a non-WRAPPER\n\t\t\t\tObject wrapper = wrappers.get(i);\n\t\t\t\tif (!toIndex.containsKey(wrapper)) {\n\t\t\t\t\ttoIndex.put(wrapper, i);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn toIndex;\n\t\t}\n\n\t\tprivate void reportDescriptorsAddedWarning(int number, Consumer<String> errorHandler,\n\t\t\t\tPARENT parentTestDescriptor) {\n\t\t\terrorHandler.accept(this.descriptorsAddedMessageGenerator.generateMessage(parentTestDescriptor, number));\n\t\t}\n\n\t\tprivate void reportDescriptorsRemovedWarning(int number, Consumer<String> errorHandler,\n\t\t\t\tPARENT parentTestDescriptor) {\n\t\t\terrorHandler.accept(\n\t\t\t\tthis.descriptorsRemovedMessageGenerator.generateMessage(parentTestDescriptor, Math.abs(number)));\n\t\t}\n\n\t}\n\n\t@FunctionalInterface\n\tprotected interface OrderingAction<PARENT extends TestDescriptor, WRAPPER> {\n\n\t\tvoid order(PARENT testDescriptor, List<WRAPPER> wrappers);\n\t}\n\n\t@FunctionalInterface\n\tprotected interface MessageGenerator<PARENT extends TestDescriptor> {\n\n\t\tString generateMessage(PARENT testDescriptor, int number);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassOrderingVisitor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\n\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.TestClassOrder;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestClassAware;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.LruCache;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\n\n/**\n * @since 5.8\n */\nclass ClassOrderingVisitor extends AbstractOrderingVisitor {\n\n\tprivate final LruCache<ClassBasedTestDescriptor, DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor>> ordererCache = new LruCache<>(\n\t\t10);\n\tprivate final JupiterConfiguration configuration;\n\tprivate final DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> globalOrderer;\n\tprivate final Condition<ClassBasedTestDescriptor> noOrderAnnotation;\n\n\tClassOrderingVisitor(JupiterConfiguration configuration, DiscoveryIssueReporter issueReporter) {\n\t\tsuper(issueReporter);\n\t\tthis.configuration = configuration;\n\t\tthis.globalOrderer = createGlobalOrderer(configuration);\n\t\tthis.noOrderAnnotation = issueReporter.createReportingCondition(\n\t\t\ttestDescriptor -> !isAnnotated(testDescriptor.getTestClass(), Order.class), testDescriptor -> {\n\t\t\t\tString message = \"\"\"\n\t\t\t\t\t\tIneffective @Order annotation on class '%s'. \\\n\t\t\t\t\t\tIt will not be applied because ClassOrderer.OrderAnnotation is not in use. \\\n\t\t\t\t\t\tNote that the annotation may be either directly present or meta-present on the class.\"\"\"//\n\t\t\t\t\t\t.formatted(testDescriptor.getTestClass().getName());\n\t\t\t\treturn DiscoveryIssue.builder(Severity.INFO, message) //\n\t\t\t\t\t\t.source(testDescriptor.getSource()) //\n\t\t\t\t\t\t.build();\n\t\t\t});\n\t}\n\n\t@Override\n\tpublic void visit(TestDescriptor testDescriptor) {\n\t\tdoWithMatchingDescriptor(JupiterEngineDescriptor.class, testDescriptor, this::orderTopLevelClasses,\n\t\t\tdescriptor -> \"Failed to order top-level classes\");\n\t\tdoWithMatchingDescriptor(ClassBasedTestDescriptor.class, testDescriptor, this::orderNestedClasses,\n\t\t\tdescriptor -> \"Failed to order nested classes for \" + descriptor.getTestClass());\n\t}\n\n\t@Override\n\tprotected boolean shouldNonMatchingDescriptorsComeBeforeOrderedOnes() {\n\t\t// Non-matching descriptors can only occur when ordering nested classes in which\n\t\t// case they contain only local test methods (for @Nested classes) which must be\n\t\t// executed before tests in @Nested test classes. So we add the test methods before\n\t\t// adding the @Nested test classes.\n\t\treturn true;\n\t}\n\n\tprivate void orderTopLevelClasses(JupiterEngineDescriptor engineDescriptor) {\n\t\torderChildrenTestDescriptors(//\n\t\t\tengineDescriptor, //\n\t\t\tClassBasedTestDescriptor.class, //\n\t\t\ttoValidationAction(globalOrderer.getOrderer()), //\n\t\t\tDefaultClassDescriptor::new, //\n\t\t\tglobalOrderer);\n\t}\n\n\tprivate void orderNestedClasses(ClassBasedTestDescriptor descriptor) {\n\t\tvar wrapperOrderer = createAndCacheClassLevelOrderer(descriptor);\n\t\torderChildrenTestDescriptors(//\n\t\t\tdescriptor, //\n\t\t\tClassBasedTestDescriptor.class, //\n\t\t\ttoValidationAction(wrapperOrderer.getOrderer()), //\n\t\t\tDefaultClassDescriptor::new, //\n\t\t\twrapperOrderer);\n\t}\n\n\tprivate DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> createGlobalOrderer(\n\t\t\tJupiterConfiguration configuration) {\n\t\tClassOrderer classOrderer = configuration.getDefaultTestClassOrderer().orElse(null);\n\t\treturn classOrderer == null ? DescriptorWrapperOrderer.noop() : createDescriptorWrapperOrderer(classOrderer);\n\t}\n\n\tprivate DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> createAndCacheClassLevelOrderer(\n\t\t\tClassBasedTestDescriptor classBasedTestDescriptor) {\n\t\tvar orderer = createClassLevelOrderer(classBasedTestDescriptor);\n\t\tordererCache.put(classBasedTestDescriptor, orderer);\n\t\treturn orderer;\n\t}\n\n\tprivate DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> createClassLevelOrderer(\n\t\t\tClassBasedTestDescriptor classBasedTestDescriptor) {\n\t\treturn AnnotationSupport.findAnnotation(classBasedTestDescriptor.getTestClass(), TestClassOrder.class)//\n\t\t\t\t.map(TestClassOrder::value)//\n\t\t\t\t.map(this::createDescriptorWrapperOrderer)//\n\t\t\t\t.orElseGet(() -> {\n\t\t\t\t\tObject parent = classBasedTestDescriptor.getParent().orElse(null);\n\t\t\t\t\tif (parent instanceof ClassBasedTestDescriptor parentClassTestDescriptor) {\n\t\t\t\t\t\tvar cacheEntry = ordererCache.get(parentClassTestDescriptor);\n\t\t\t\t\t\treturn cacheEntry != null ? cacheEntry : createClassLevelOrderer(parentClassTestDescriptor);\n\t\t\t\t\t}\n\t\t\t\t\treturn globalOrderer;\n\t\t\t\t});\n\t}\n\n\tprivate DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> createDescriptorWrapperOrderer(\n\t\t\tClass<? extends ClassOrderer> ordererClass) {\n\t\tif (ordererClass == ClassOrderer.Default.class) {\n\t\t\treturn globalOrderer;\n\t\t}\n\t\treturn createDescriptorWrapperOrderer(ReflectionSupport.newInstance(ordererClass));\n\t}\n\n\tprivate DescriptorWrapperOrderer<TestDescriptor, ClassOrderer, DefaultClassDescriptor> createDescriptorWrapperOrderer(\n\t\t\tClassOrderer classOrderer) {\n\t\tOrderingAction<TestDescriptor, DefaultClassDescriptor> orderingAction = (__,\n\t\t\t\tclassDescriptors) -> classOrderer.orderClasses(\n\t\t\t\t\tnew DefaultClassOrdererContext(classDescriptors, this.configuration));\n\n\t\tMessageGenerator<TestDescriptor> descriptorsAddedMessageGenerator = (parent,\n\t\t\t\tnumber) -> \"ClassOrderer [%s] added %d %s which will be ignored.\".formatted(\n\t\t\t\t\tclassOrderer.getClass().getName(), number, describeClassDescriptors(parent));\n\t\tMessageGenerator<TestDescriptor> descriptorsRemovedMessageGenerator = (parent,\n\t\t\t\tnumber) -> \"ClassOrderer [%s] removed %d %s which will be retained with arbitrary ordering.\".formatted(\n\t\t\t\t\tclassOrderer.getClass().getName(), number, describeClassDescriptors(parent));\n\n\t\treturn new DescriptorWrapperOrderer<>(classOrderer, orderingAction, descriptorsAddedMessageGenerator,\n\t\t\tdescriptorsRemovedMessageGenerator);\n\t}\n\n\tprivate static String describeClassDescriptors(TestDescriptor parent) {\n\t\treturn parent instanceof TestClassAware testClassAware //\n\t\t\t\t? \"nested ClassDescriptor(s) for test class [%s]\".formatted(testClassAware.getTestClass().getName()) //\n\t\t\t\t: \"top-level ClassDescriptor(s)\";\n\t}\n\n\tprivate Optional<Consumer<ClassBasedTestDescriptor>> toValidationAction(@Nullable ClassOrderer orderer) {\n\t\tif (orderer instanceof ClassOrderer.OrderAnnotation) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.of(noOrderAnnotation::check);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/ClassSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.function.Predicate.isEqual;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor.getEnclosingTestClasses;\nimport static org.junit.jupiter.engine.discovery.predicates.TestClassPredicates.NestedClassInvalidityReason.NOT_INNER;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\nimport static org.junit.platform.commons.util.ReflectionUtils.isNotAbstract;\nimport static org.junit.platform.commons.util.ReflectionUtils.streamNestedClasses;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.Filterable;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestClassAware;\nimport org.junit.jupiter.engine.discovery.predicates.TestClassPredicates;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.ReflectionUtils.CycleErrorHandling;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.NestedClassSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.SelectorResolver;\n\n/**\n * @since 5.5\n */\nclass ClassSelectorResolver implements SelectorResolver {\n\n\tprivate final Predicate<String> classNameFilter;\n\tprivate final JupiterConfiguration configuration;\n\tprivate final TestClassPredicates predicates;\n\tprivate final DiscoveryIssueReporter issueReporter;\n\n\tClassSelectorResolver(Predicate<String> classNameFilter, JupiterConfiguration configuration,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\tthis.classNameFilter = classNameFilter;\n\t\tthis.configuration = configuration;\n\t\tthis.predicates = new TestClassPredicates(issueReporter);\n\t\tthis.issueReporter = issueReporter;\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ClassSelector selector, Context context) {\n\t\tClass<?> testClass = selector.getJavaClass();\n\n\t\tif (this.predicates.isAnnotatedWithNested.test(testClass)) {\n\t\t\t// Class name filter is not applied to nested test classes\n\t\t\tvar invalidityReason = this.predicates.validateNestedTestClass(testClass);\n\t\t\tif (invalidityReason == null) {\n\t\t\t\treturn toResolution(\n\t\t\t\t\tcontext.addToParent(() -> DiscoverySelectors.selectClass(testClass.getEnclosingClass()),\n\t\t\t\t\t\tparent -> Optional.of(newMemberClassTestDescriptor(parent, testClass))));\n\t\t\t}\n\t\t\tif (invalidityReason == NOT_INNER) {\n\t\t\t\treturn resolveStandaloneTestClass(context, testClass);\n\t\t\t}\n\t\t\treturn unresolved();\n\t\t}\n\t\treturn resolveStandaloneTestClass(context, testClass);\n\t}\n\n\tprivate Resolution resolveStandaloneTestClass(Context context, Class<?> testClass) {\n\t\tif (isAcceptedStandaloneTestClass(testClass)) {\n\t\t\treturn toResolution(\n\t\t\t\tcontext.addToParent(parent -> Optional.of(newStandaloneClassTestDescriptor(parent, testClass))));\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate boolean isAcceptedStandaloneTestClass(Class<?> testClass) {\n\t\treturn this.classNameFilter.test(testClass.getName()) //\n\t\t\t\t&& this.predicates.looksLikeIntendedTestClass(testClass) //\n\t\t\t\t&& this.predicates.isValidStandaloneTestClass(testClass);\n\t}\n\n\t@Override\n\tpublic Resolution resolve(NestedClassSelector selector, Context context) {\n\t\tClass<?> nestedClass = selector.getNestedClass();\n\t\tif (this.predicates.isAnnotatedWithNested.test(nestedClass)) {\n\t\t\tif (this.predicates.isValidNestedTestClass(nestedClass)) {\n\t\t\t\treturn toResolution(context.addToParent(() -> selectClass(selector.getEnclosingClasses()),\n\t\t\t\t\tparent -> Optional.of(newMemberClassTestDescriptor(parent, nestedClass))));\n\t\t\t}\n\t\t}\n\t\telse if (isInnerClass(nestedClass) && isNotAbstract(nestedClass)\n\t\t\t\t&& predicates.looksLikeIntendedTestClass(nestedClass)) {\n\t\t\tString message = \"Inner class '%s' looks like it was intended to be a test class but will not be executed. It must be static or annotated with @Nested.\".formatted(\n\t\t\t\tnestedClass.getName());\n\t\t\tissueReporter.reportIssue(DiscoveryIssue.builder(Severity.WARNING, message) //\n\t\t\t\t\t.source(ClassSource.from(nestedClass)));\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\t@Override\n\tpublic Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\tUniqueId uniqueId = selector.getUniqueId();\n\t\tUniqueId.Segment lastSegment = uniqueId.getLastSegment();\n\t\treturn switch (lastSegment.getType()) {\n\t\t\tcase ClassTestDescriptor.SEGMENT_TYPE -> //\n\t\t\t\t\tresolveStandaloneClassUniqueId(context, lastSegment, __ -> true, this::newClassTestDescriptor);\n\t\t\tcase ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE -> //\n\t\t\t\t\tresolveStandaloneClassUniqueId(context, lastSegment, this.predicates.isAnnotatedWithClassTemplate,\n\t\t\t\t\t\tthis::newClassTemplateTestDescriptor);\n\t\t\tcase NestedClassTestDescriptor.SEGMENT_TYPE -> //\n\t\t\t\t\tresolveNestedClassUniqueId(context, uniqueId, __ -> true, this::newNestedClassTestDescriptor);\n\t\t\tcase ClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE -> //\n\t\t\t\t\tresolveNestedClassUniqueId(context, uniqueId, this.predicates.isAnnotatedWithClassTemplate,\n\t\t\t\t\t\tthis::newNestedClassTemplateTestDescriptor);\n\t\t\tcase ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE -> {\n\t\t\t\tOptional<ClassTemplateInvocationTestDescriptor> testDescriptor = context.addToParent(\n\t\t\t\t\t() -> selectUniqueId(uniqueId.removeLastSegment()), parent -> {\n\t\t\t\t\t\tint index = Integer.parseInt(lastSegment.getValue().substring(1));\n\t\t\t\t\t\treturn Optional.of(newDummyClassTemplateInvocationTestDescriptor(parent, index));\n\t\t\t\t\t});\n\t\t\t\tyield toInvocationMatch(testDescriptor) //\n\t\t\t\t\t\t.map(Resolution::match) //\n\t\t\t\t\t\t.orElse(unresolved());\n\t\t\t}\n\t\t\tdefault -> unresolved();\n\t\t};\n\t}\n\n\t@Override\n\tpublic Resolution resolve(IterationSelector selector, Context context) {\n\t\tDiscoverySelector parentSelector = selector.getParentSelector();\n\t\tif (parentSelector instanceof ClassSelector classSelector\n\t\t\t\t&& this.predicates.isAnnotatedWithClassTemplate.test(classSelector.getJavaClass())) {\n\t\t\treturn resolveIterations(selector, context);\n\t\t}\n\t\tif (parentSelector instanceof NestedClassSelector nestedClassSelector\n\t\t\t\t&& this.predicates.isAnnotatedWithClassTemplate.test(nestedClassSelector.getNestedClass())) {\n\t\t\treturn resolveIterations(selector, context);\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate Resolution resolveIterations(IterationSelector selector, Context context) {\n\t\tDiscoverySelector parentSelector = selector.getParentSelector();\n\t\tSet<Match> matches = selector.getIterationIndices().stream() //\n\t\t\t\t.map(index -> context.addToParent(() -> parentSelector,\n\t\t\t\t\tparent -> Optional.of(newDummyClassTemplateInvocationTestDescriptor(parent, index + 1)))) //\n\t\t\t\t.map(this::toInvocationMatch) //\n\t\t\t\t.flatMap(Optional::stream) //\n\t\t\t\t.collect(toSet());\n\t\treturn matches.isEmpty() ? unresolved() : Resolution.matches(matches);\n\t}\n\n\tprivate Resolution resolveStandaloneClassUniqueId(Context context, UniqueId.Segment lastSegment,\n\t\t\tPredicate<? super Class<?>> condition,\n\t\t\tBiFunction<TestDescriptor, Class<?>, ClassBasedTestDescriptor> factory) {\n\n\t\tString className = lastSegment.getValue();\n\t\treturn ReflectionSupport.tryToLoadClass(className).toOptional() //\n\t\t\t\t.filter(this.predicates::isValidStandaloneTestClass) //\n\t\t\t\t.filter(condition) //\n\t\t\t\t.map(testClass -> toResolution(\n\t\t\t\t\tcontext.addToParent(parent -> Optional.of(factory.apply(parent, testClass))))) //\n\t\t\t\t.orElse(unresolved());\n\t}\n\n\tprivate Resolution resolveNestedClassUniqueId(Context context, UniqueId uniqueId,\n\t\t\tPredicate<? super Class<?>> condition,\n\t\t\tBiFunction<TestDescriptor, Class<?>, ClassBasedTestDescriptor> factory) {\n\n\t\tString simpleClassName = uniqueId.getLastSegment().getValue();\n\t\treturn toResolution(context.addToParent(() -> selectUniqueId(uniqueId.removeLastSegment()), parent -> {\n\t\t\tClass<?> parentTestClass = ((TestClassAware) parent).getTestClass();\n\t\t\treturn ReflectionSupport.findNestedClasses(parentTestClass,\n\t\t\t\tthis.predicates.isAnnotatedWithNestedAndValid.and(\n\t\t\t\t\twhere(Class::getSimpleName, isEqual(simpleClassName)))).stream() //\n\t\t\t\t\t.findFirst() //\n\t\t\t\t\t.filter(condition) //\n\t\t\t\t\t.map(testClass -> factory.apply(parent, testClass));\n\t\t}));\n\t}\n\n\tprivate ClassTemplateInvocationTestDescriptor newDummyClassTemplateInvocationTestDescriptor(TestDescriptor parent,\n\t\t\tint index) {\n\t\tUniqueId uniqueId = parent.getUniqueId().append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#\" + index);\n\t\treturn new ClassTemplateInvocationTestDescriptor(uniqueId, (ClassTemplateTestDescriptor) parent,\n\t\t\tDummyClassTemplateInvocationContext.INSTANCE, index, parent.getSource().orElse(null), configuration);\n\t}\n\n\tprivate ClassBasedTestDescriptor newStandaloneClassTestDescriptor(TestDescriptor parent, Class<?> testClass) {\n\t\treturn this.predicates.isAnnotatedWithClassTemplate.test(testClass) //\n\t\t\t\t? newClassTemplateTestDescriptor(parent, testClass) //\n\t\t\t\t: newClassTestDescriptor(parent, testClass);\n\t}\n\n\tprivate ClassTemplateTestDescriptor newClassTemplateTestDescriptor(TestDescriptor parent, Class<?> testClass) {\n\t\treturn newClassTemplateTestDescriptor(parent, ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tnewClassTestDescriptor(parent, testClass));\n\t}\n\n\tprivate ClassTestDescriptor newClassTestDescriptor(TestDescriptor parent, Class<?> testClass) {\n\t\treturn new ClassTestDescriptor(\n\t\t\tparent.getUniqueId().append(ClassTestDescriptor.SEGMENT_TYPE, testClass.getName()), testClass,\n\t\t\tconfiguration);\n\t}\n\n\tprivate ClassBasedTestDescriptor newMemberClassTestDescriptor(TestDescriptor parent, Class<?> testClass) {\n\t\treturn this.predicates.isAnnotatedWithClassTemplate.test(testClass) //\n\t\t\t\t? newNestedClassTemplateTestDescriptor(parent, testClass) //\n\t\t\t\t: newNestedClassTestDescriptor(parent, testClass);\n\t}\n\n\tprivate ClassTemplateTestDescriptor newNestedClassTemplateTestDescriptor(TestDescriptor parent,\n\t\t\tClass<?> testClass) {\n\t\treturn newClassTemplateTestDescriptor(parent, ClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE,\n\t\t\tnewNestedClassTestDescriptor(parent, testClass));\n\t}\n\n\tprivate NestedClassTestDescriptor newNestedClassTestDescriptor(TestDescriptor parent, Class<?> testClass) {\n\t\tUniqueId uniqueId = parent.getUniqueId().append(NestedClassTestDescriptor.SEGMENT_TYPE,\n\t\t\ttestClass.getSimpleName());\n\t\treturn new NestedClassTestDescriptor(uniqueId, testClass, () -> getEnclosingTestClasses(parent), configuration);\n\t}\n\n\tprivate ClassTemplateTestDescriptor newClassTemplateTestDescriptor(TestDescriptor parent, String segmentType,\n\t\t\tClassBasedTestDescriptor delegate) {\n\n\t\tdelegate.setParent(parent);\n\t\tString segmentValue = delegate.getUniqueId().getLastSegment().getValue();\n\t\tUniqueId uniqueId = parent.getUniqueId().append(segmentType, segmentValue);\n\t\treturn new ClassTemplateTestDescriptor(uniqueId, delegate);\n\t}\n\n\tprivate Optional<Match> toInvocationMatch(Optional<ClassTemplateInvocationTestDescriptor> testDescriptor) {\n\t\treturn testDescriptor //\n\t\t\t\t.map(it -> Match.exact(it, expansionCallback(it,\n\t\t\t\t\t() -> it.getParent().map(parent -> getTestClasses((TestClassAware) parent)).orElse(emptyList()))));\n\t}\n\n\tprivate Resolution toResolution(Optional<? extends ClassBasedTestDescriptor> testDescriptor) {\n\t\treturn testDescriptor //\n\t\t\t\t.map(it -> Resolution.match(Match.exact(it, expansionCallback(it)))) //\n\t\t\t\t.orElse(unresolved());\n\t}\n\n\tprivate Supplier<Set<? extends DiscoverySelector>> expansionCallback(ClassBasedTestDescriptor testDescriptor) {\n\t\treturn expansionCallback(testDescriptor, () -> getTestClasses(testDescriptor));\n\t}\n\n\tprivate static List<Class<?>> getTestClasses(TestClassAware testDescriptor) {\n\t\tList<Class<?>> testClasses = new ArrayList<>(testDescriptor.getEnclosingTestClasses());\n\t\ttestClasses.add(testDescriptor.getTestClass());\n\t\treturn testClasses;\n\t}\n\n\tprivate Supplier<Set<? extends DiscoverySelector>> expansionCallback(TestDescriptor testDescriptor,\n\t\t\tSupplier<List<Class<?>>> testClassesSupplier) {\n\t\treturn () -> {\n\t\t\tif (testDescriptor instanceof Filterable filterable) {\n\t\t\t\tfilterable.getDynamicDescendantFilter().allowAll();\n\t\t\t}\n\t\t\tList<Class<?>> testClasses = testClassesSupplier.get();\n\t\t\tClass<?> testClass = testClasses.get(testClasses.size() - 1);\n\t\t\tStream<DiscoverySelector> methods = findMethods(testClass,\n\t\t\t\tthis.predicates.isTestOrTestFactoryOrTestTemplateMethod, TOP_DOWN).stream() //\n\t\t\t\t\t\t.map(method -> selectMethod(testClasses, method));\n\t\t\tStream<Class<?>> annotatedNestedClasses = streamNestedClasses(testClass,\n\t\t\t\tthis.predicates.isAnnotatedWithNested);\n\t\t\tStream<Class<?>> notAnnotatedInnerClasses = streamNestedClasses(testClass,\n\t\t\t\tthis.predicates.isAnnotatedWithNested.negate().and(ReflectionUtils::isInnerClass),\n\t\t\t\tCycleErrorHandling.ABORT_VISIT);\n\t\t\tvar nestedClasses = Stream.concat(annotatedNestedClasses, notAnnotatedInnerClasses) //\n\t\t\t\t\t.map(nestedClass -> DiscoverySelectors.selectNestedClass(testClasses, nestedClass));\n\t\t\treturn Stream.concat(methods, nestedClasses).collect(\n\t\t\t\ttoCollection((Supplier<Set<DiscoverySelector>>) LinkedHashSet::new));\n\t\t};\n\t}\n\n\tprivate DiscoverySelector selectClass(List<Class<?>> classes) {\n\t\tif (classes.size() == 1) {\n\t\t\treturn DiscoverySelectors.selectClass(classes.get(0));\n\t\t}\n\t\tint lastIndex = classes.size() - 1;\n\t\treturn DiscoverySelectors.selectNestedClass(classes.subList(0, lastIndex), classes.get(lastIndex));\n\t}\n\n\tprivate DiscoverySelector selectMethod(List<Class<?>> classes, Method method) {\n\t\treturn new DeclaredMethodSelector(classes, method);\n\t}\n\n\tstatic class DummyClassTemplateInvocationContext implements ClassTemplateInvocationContext {\n\t\tprivate static final DummyClassTemplateInvocationContext INSTANCE = new DummyClassTemplateInvocationContext();\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DeclaredMethodSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\n\n/**\n * Jupiter-specific selector for methods, potentially in nested classes.\n *\n * <p>The important difference to {@link MethodSelector} is that this selector's\n * {@link #equals(Object)} method takes into account the selected method's\n * {@linkplain Method#getDeclaringClass() declaring class} to support cases\n * where a package-private method is declared in a super class in a different\n * package and a method with the same signature is declared in a subclass. In\n * that case both methods should be discovered because the one declared in the\n * subclass does <em>not</em> override the one in the super class.\n *\n * @since 5.14.1\n */\nrecord DeclaredMethodSelector(List<Class<?>> testClasses, Method method) implements DiscoverySelector {\n\tDeclaredMethodSelector {\n\t\tPreconditions.notEmpty(testClasses, \"testClasses must not be empty\");\n\t\tPreconditions.containsNoNullElements(testClasses, \"testClasses must not contain null elements\");\n\t\tPreconditions.notNull(method, \"method must not be null\");\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport org.junit.jupiter.api.ClassDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link ClassDescriptor}, backed by\n * a {@link ClassBasedTestDescriptor}.\n *\n * @since 5.8\n */\nclass DefaultClassDescriptor extends AbstractAnnotatedDescriptorWrapper<Class<?>> implements ClassDescriptor {\n\n\tDefaultClassDescriptor(ClassBasedTestDescriptor testDescriptor) {\n\t\tsuper(testDescriptor, testDescriptor.getTestClass());\n\t}\n\n\t@Override\n\tpublic final Class<?> getTestClass() {\n\t\treturn getAnnotatedElement();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"class\", getTestClass().toGenericString()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultClassOrdererContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.ClassDescriptor;\nimport org.junit.jupiter.api.ClassOrdererContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\n\n/**\n * Default implementation of {@link ClassOrdererContext}.\n *\n * @since 5.8\n */\nclass DefaultClassOrdererContext implements ClassOrdererContext {\n\n\tprivate final List<? extends ClassDescriptor> classDescriptors;\n\tprivate final JupiterConfiguration configuration;\n\n\tDefaultClassOrdererContext(List<? extends ClassDescriptor> classDescriptors, JupiterConfiguration configuration) {\n\t\tthis.classDescriptors = classDescriptors;\n\t\tthis.configuration = configuration;\n\t}\n\n\t@Override\n\tpublic List<? extends ClassDescriptor> getClassDescriptors() {\n\t\treturn this.classDescriptors;\n\t}\n\n\t@Override\n\tpublic Optional<String> getConfigurationParameter(String key) {\n\t\treturn this.configuration.getRawConfigurationParameter(key);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.MethodDescriptor;\nimport org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link MethodDescriptor}, backed by\n * a {@link MethodBasedTestDescriptor}.\n *\n * @since 5.4\n */\nclass DefaultMethodDescriptor extends AbstractAnnotatedDescriptorWrapper<Method> implements MethodDescriptor {\n\n\tDefaultMethodDescriptor(MethodBasedTestDescriptor testDescriptor) {\n\t\tsuper(testDescriptor, testDescriptor.getTestMethod());\n\t}\n\n\t@Override\n\tpublic final Method getMethod() {\n\t\treturn getAnnotatedElement();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"method\", getMethod().toGenericString()).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DefaultMethodOrdererContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.MethodDescriptor;\nimport org.junit.jupiter.api.MethodOrdererContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link MethodOrdererContext}.\n *\n * @since 5.4\n */\nclass DefaultMethodOrdererContext implements MethodOrdererContext {\n\n\tprivate final Class<?> testClass;\n\tprivate final List<? extends MethodDescriptor> methodDescriptors;\n\tprivate final JupiterConfiguration configuration;\n\n\tDefaultMethodOrdererContext(Class<?> testClass, List<? extends MethodDescriptor> methodDescriptors,\n\t\t\tJupiterConfiguration configuration) {\n\n\t\tthis.testClass = testClass;\n\t\tthis.methodDescriptors = methodDescriptors;\n\t\tthis.configuration = configuration;\n\t}\n\n\t@Override\n\tpublic final Class<?> getTestClass() {\n\t\treturn this.testClass;\n\t}\n\n\t@Override\n\tpublic List<? extends MethodDescriptor> getMethodDescriptors() {\n\t\treturn this.methodDescriptors;\n\t}\n\n\t@Override\n\tpublic Optional<String> getConfigurationParameter(String key) {\n\t\treturn this.configuration.getRawConfigurationParameter(key);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"methodDescriptors\", methodDescriptors).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.Validatable;\nimport org.junit.jupiter.engine.discovery.predicates.TestClassPredicates;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver;\nimport org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.InitializationContext;\n\n/**\n * {@code DiscoverySelectorResolver} resolves {@link TestDescriptor TestDescriptors}\n * for containers and tests selected by {@link org.junit.platform.engine.DiscoverySelector\n * DiscoverySelectors}, with the help of an {@link EngineDiscoveryRequestResolver}.\n *\n * <p>This is an internal utility which is only {@code public} in order to provide\n * the {@link org.junit.jupiter.engine.JupiterTestEngine JupiterTestEngine} access\n * to the functionality of the {@code discovery} package.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class DiscoverySelectorResolver {\n\n\tprivate static final EngineDiscoveryRequestResolver<JupiterEngineDescriptor> resolver = EngineDiscoveryRequestResolver.<JupiterEngineDescriptor> builder() //\n\t\t\t.addClassContainerSelectorResolverWithContext(\n\t\t\t\tctx -> new TestClassPredicates(ctx.getIssueReporter()).looksLikeNestedOrStandaloneTestClass) //\n\t\t\t.addSelectorResolver(ctx -> new ClassSelectorResolver(ctx.getClassNameFilter(), getConfiguration(ctx),\n\t\t\t\tctx.getIssueReporter())) //\n\t\t\t.addSelectorResolver(ctx -> new MethodSelectorResolver(getConfiguration(ctx), ctx.getIssueReporter())) //\n\t\t\t.addTestDescriptorVisitor(ctx -> TestDescriptor.Visitor.composite( //\n\t\t\t\tnew ClassOrderingVisitor(getConfiguration(ctx), ctx.getIssueReporter()), //\n\t\t\t\tnew MethodOrderingVisitor(getConfiguration(ctx), ctx.getIssueReporter()), //\n\t\t\t\tdescriptor -> {\n\t\t\t\t\tif (descriptor instanceof Validatable validatable) {\n\t\t\t\t\t\tvalidatable.validate(ctx.getIssueReporter());\n\t\t\t\t\t}\n\t\t\t\t})) //\n\t\t\t.build();\n\n\tprivate static JupiterConfiguration getConfiguration(InitializationContext<JupiterEngineDescriptor> context) {\n\t\treturn context.getEngineDescriptor().getConfiguration();\n\t}\n\n\tpublic static void resolveSelectors(EngineDiscoveryRequest request, JupiterEngineDescriptor engineDescriptor,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\tresolver.resolve(request, engineDescriptor, issueReporter);\n\t}\n\n\tprivate DiscoverySelectorResolver() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodOrderingVisitor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Comparator.comparing;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.function.UnaryOperator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.LruCache;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\n\n/**\n * @since 5.5\n */\nclass MethodOrderingVisitor extends AbstractOrderingVisitor {\n\n\tprivate final LruCache<ClassBasedTestDescriptor, DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor>> ordererCache = new LruCache<>(\n\t\t10);\n\tprivate final JupiterConfiguration configuration;\n\tprivate final DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> globalOrderer;\n\tprivate final Condition<MethodBasedTestDescriptor> noOrderAnnotation;\n\n\t// Not a static field to avoid initialization at build time for GraalVM\n\tprivate final UnaryOperator<List<TestDescriptor>> methodsBeforeNestedClassesOrderer;\n\n\tMethodOrderingVisitor(JupiterConfiguration configuration, DiscoveryIssueReporter issueReporter) {\n\t\tsuper(issueReporter);\n\t\tthis.configuration = configuration;\n\t\tthis.globalOrderer = createGlobalOrderer(configuration);\n\t\tthis.noOrderAnnotation = issueReporter.createReportingCondition(\n\t\t\ttestDescriptor -> !isAnnotated(testDescriptor.getTestMethod(), Order.class), testDescriptor -> {\n\t\t\t\tString message = \"\"\"\n\t\t\t\t\t\tIneffective @Order annotation on method '%s'. \\\n\t\t\t\t\t\tIt will not be applied because MethodOrderer.OrderAnnotation is not in use. \\\n\t\t\t\t\t\tNote that the annotation may be either directly present or meta-present on the method.\"\"\"//\n\t\t\t\t\t\t.formatted(testDescriptor.getTestMethod().toGenericString());\n\t\t\t\treturn DiscoveryIssue.builder(Severity.INFO, message) //\n\t\t\t\t\t\t.source(testDescriptor.getSource()) //\n\t\t\t\t\t\t.build();\n\t\t\t});\n\t\tthis.methodsBeforeNestedClassesOrderer = createMethodsBeforeNestedClassesOrderer();\n\t}\n\n\t@Override\n\tpublic void visit(TestDescriptor testDescriptor) {\n\t\tdoWithMatchingDescriptor(ClassBasedTestDescriptor.class, testDescriptor, this::orderContainedMethods,\n\t\t\tdescriptor -> \"Failed to order methods for \" + descriptor.getTestClass());\n\t}\n\n\t@Override\n\tprotected boolean shouldNonMatchingDescriptorsComeBeforeOrderedOnes() {\n\t\t// Non-matching descriptors can only contain @Nested test classes which should be\n\t\t// added after local test methods.\n\t\treturn false;\n\t}\n\n\tprivate void orderContainedMethods(ClassBasedTestDescriptor descriptor) {\n\t\tvar wrapperOrderer = createAndCacheClassLevelOrderer(descriptor);\n\t\tvar methodOrderer = wrapperOrderer.getOrderer();\n\n\t\torderChildrenTestDescriptors(descriptor, //\n\t\t\tMethodBasedTestDescriptor.class, //\n\t\t\ttoValidationAction(methodOrderer), //\n\t\t\tDefaultMethodDescriptor::new, //\n\t\t\twrapperOrderer);\n\n\t\tif (methodOrderer == null) {\n\t\t\t// If there is an orderer, this is ensured by the call above\n\t\t\tdescriptor.orderChildren(methodsBeforeNestedClassesOrderer);\n\t\t}\n\t\telse {\n\t\t\t// Note: MethodOrderer#getDefaultExecutionMode() is guaranteed\n\t\t\t// to be invoked after MethodOrderer#orderMethods().\n\t\t\tmethodOrderer.getDefaultExecutionMode() //\n\t\t\t\t\t.map(JupiterTestDescriptor::toExecutionMode) //\n\t\t\t\t\t.ifPresent(descriptor::setDefaultChildExecutionMode);\n\t\t}\n\t}\n\n\tprivate DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> createGlobalOrderer(\n\t\t\tJupiterConfiguration configuration) {\n\t\tMethodOrderer methodOrderer = configuration.getDefaultTestMethodOrderer().orElse(null);\n\t\treturn methodOrderer == null ? DescriptorWrapperOrderer.noop() : createDescriptorWrapperOrderer(methodOrderer);\n\t}\n\n\tprivate DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> createAndCacheClassLevelOrderer(\n\t\t\tClassBasedTestDescriptor classBasedTestDescriptor) {\n\t\tvar orderer = createClassLevelOrderer(classBasedTestDescriptor);\n\t\tordererCache.put(classBasedTestDescriptor, orderer);\n\t\treturn orderer;\n\t}\n\n\tprivate DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> createClassLevelOrderer(\n\t\t\tClassBasedTestDescriptor classBasedTestDescriptor) {\n\t\treturn AnnotationSupport.findAnnotation(classBasedTestDescriptor.getTestClass(), TestMethodOrder.class)//\n\t\t\t\t.map(TestMethodOrder::value)//\n\t\t\t\t.map(this::createDescriptorWrapperOrderer)//\n\t\t\t\t.orElseGet(() -> {\n\t\t\t\t\tObject parent = classBasedTestDescriptor.getParent().orElse(null);\n\t\t\t\t\tif (parent instanceof ClassBasedTestDescriptor parentClassTestDescriptor) {\n\t\t\t\t\t\tvar cacheEntry = ordererCache.get(parentClassTestDescriptor);\n\t\t\t\t\t\treturn cacheEntry != null ? cacheEntry : createClassLevelOrderer(parentClassTestDescriptor);\n\t\t\t\t\t}\n\t\t\t\t\treturn globalOrderer;\n\t\t\t\t});\n\t}\n\n\tprivate DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> createDescriptorWrapperOrderer(\n\t\t\tClass<? extends MethodOrderer> ordererClass) {\n\t\tif (ordererClass == MethodOrderer.Default.class) {\n\t\t\treturn globalOrderer;\n\t\t}\n\t\treturn createDescriptorWrapperOrderer(ReflectionSupport.newInstance(ordererClass));\n\t}\n\n\tprivate DescriptorWrapperOrderer<ClassBasedTestDescriptor, MethodOrderer, DefaultMethodDescriptor> createDescriptorWrapperOrderer(\n\t\t\tMethodOrderer methodOrderer) {\n\t\tOrderingAction<ClassBasedTestDescriptor, DefaultMethodDescriptor> orderingAction = (parent,\n\t\t\t\tmethodDescriptors) -> methodOrderer.orderMethods(\n\t\t\t\t\tnew DefaultMethodOrdererContext(parent.getTestClass(), methodDescriptors, this.configuration));\n\n\t\tMessageGenerator<ClassBasedTestDescriptor> descriptorsAddedMessageGenerator = (parent,\n\t\t\t\tnumber) -> \"MethodOrderer [%s] added %d MethodDescriptor(s) for test class [%s] which will be ignored.\".formatted(\n\t\t\t\t\tmethodOrderer.getClass().getName(), number, parent.getTestClass().getName());\n\t\tMessageGenerator<ClassBasedTestDescriptor> descriptorsRemovedMessageGenerator = (parent,\n\t\t\t\tnumber) -> \"MethodOrderer [%s] removed %d MethodDescriptor(s) for test class [%s] which will be retained with arbitrary ordering.\".formatted(\n\t\t\t\t\tmethodOrderer.getClass().getName(), number, parent.getTestClass().getName());\n\n\t\treturn new DescriptorWrapperOrderer<>(methodOrderer, orderingAction, descriptorsAddedMessageGenerator,\n\t\t\tdescriptorsRemovedMessageGenerator);\n\t}\n\n\tprivate Optional<Consumer<MethodBasedTestDescriptor>> toValidationAction(@Nullable MethodOrderer methodOrderer) {\n\t\tif (methodOrderer instanceof MethodOrderer.OrderAnnotation) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.of(noOrderAnnotation::check);\n\t}\n\n\tprivate static UnaryOperator<List<TestDescriptor>> createMethodsBeforeNestedClassesOrderer() {\n\t\tvar methodsFirst = comparing(MethodBasedTestDescriptor.class::isInstance).reversed();\n\t\treturn children -> {\n\t\t\tchildren.sort(methodsFirst);\n\t\t\treturn children;\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSegmentResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static org.junit.platform.commons.util.ReflectionUtils.isDeclaredInSamePackage;\nimport static org.junit.platform.commons.util.ReflectionUtils.isPackagePrivate;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.0\n */\nclass MethodSegmentResolver {\n\n\t// Pattern: [declaringClassName#]methodName(comma-separated list of parameter type names)\n\tprivate static final Pattern METHOD_PATTERN = Pattern.compile(\n\t\t\"(?:(?<declaringClass>.+)#)?(?<method>.+)\\\\((?<parameters>.*)\\\\)\");\n\n\t/**\n\t * If the {@code method} is package-private and declared a class in a\n\t * different package than {@code testClass}, the declaring class name is\n\t * included in the method's unique ID segment. Otherwise, it only\n\t * consists of the method name and its parameter types.\n\t */\n\tString formatMethodSpecPart(Method method, Class<?> testClass) {\n\t\tvar parameterTypes = ClassUtils.nullSafeToString(method.getParameterTypes());\n\t\tif (isPackagePrivate(method) && !isDeclaredInSamePackage(method.getDeclaringClass(), testClass)) {\n\t\t\treturn \"%s#%s(%s)\".formatted(method.getDeclaringClass().getName(), method.getName(), parameterTypes);\n\t\t}\n\t\treturn \"%s(%s)\".formatted(method.getName(), parameterTypes);\n\t}\n\n\tOptional<Method> findMethod(String methodSpecPart, Class<?> testClass) {\n\t\tMatcher matcher = METHOD_PATTERN.matcher(methodSpecPart);\n\n\t\tPreconditions.condition(matcher.matches(),\n\t\t\t() -> \"Method [%s] does not match pattern [%s]\".formatted(methodSpecPart, METHOD_PATTERN));\n\n\t\tClass<?> targetClass = testClass;\n\t\tString declaringClass = matcher.group(\"declaringClass\");\n\t\tif (declaringClass != null) {\n\t\t\ttargetClass = ReflectionUtils.tryToLoadClass(declaringClass).getNonNullOrThrow(\n\t\t\t\tcause -> new PreconditionViolationException(\n\t\t\t\t\t\"Could not load declaring class with name: \" + declaringClass, cause));\n\t\t}\n\t\tString methodName = matcher.group(\"method\");\n\t\tString parameterTypeNames = matcher.group(\"parameters\");\n\t\treturn ReflectionSupport.findMethod(targetClass, methodName, parameterTypeNames);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/MethodSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptySet;\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.matches;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.Filterable;\nimport org.junit.jupiter.engine.descriptor.TestClassAware;\nimport org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor;\nimport org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod;\nimport org.junit.jupiter.engine.discovery.predicates.IsTestMethod;\nimport org.junit.jupiter.engine.discovery.predicates.IsTestTemplateMethod;\nimport org.junit.jupiter.engine.discovery.predicates.TestClassPredicates;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.NestedMethodSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.SelectorResolver;\n\n/**\n * @since 5.5\n */\nclass MethodSelectorResolver implements SelectorResolver {\n\n\tprivate static final MethodSegmentResolver methodSegmentResolver = new MethodSegmentResolver();\n\tprivate final Predicate<Class<?>> testClassPredicate;\n\n\tprivate final JupiterConfiguration configuration;\n\tprivate final DiscoveryIssueReporter issueReporter;\n\tprivate final List<MethodType> methodTypes;\n\n\tMethodSelectorResolver(JupiterConfiguration configuration, DiscoveryIssueReporter issueReporter) {\n\t\tthis.configuration = configuration;\n\t\tthis.issueReporter = issueReporter;\n\t\tthis.methodTypes = MethodType.allPossibilities(issueReporter);\n\t\tthis.testClassPredicate = new TestClassPredicates(issueReporter).looksLikeNestedOrStandaloneTestClass;\n\t}\n\n\t@Override\n\tpublic Resolution resolve(MethodSelector selector, Context context) {\n\t\treturn resolve(context, emptyList(), selector.getJavaClass(), selector::getJavaMethod, Match::exact);\n\t}\n\n\t@Override\n\tpublic Resolution resolve(NestedMethodSelector selector, Context context) {\n\t\treturn resolve(context, selector.getEnclosingClasses(), selector.getNestedClass(), selector::getMethod,\n\t\t\tMatch::exact);\n\t}\n\n\t@Override\n\tpublic Resolution resolve(DiscoverySelector selector, Context context) {\n\t\tif (selector instanceof DeclaredMethodSelector methodSelector) {\n\t\t\tvar testClasses = methodSelector.testClasses();\n\t\t\tif (testClasses.size() == 1) {\n\t\t\t\treturn resolve(context, emptyList(), testClasses.get(0), methodSelector::method, Match::exact);\n\t\t\t}\n\t\t\tint lastIndex = testClasses.size() - 1;\n\t\t\treturn resolve(context, testClasses.subList(0, lastIndex), testClasses.get(lastIndex),\n\t\t\t\tmethodSelector::method, Match::exact);\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate Resolution resolve(Context context, List<Class<?>> enclosingClasses, Class<?> testClass,\n\t\t\tSupplier<Method> methodSupplier,\n\t\t\tBiFunction<TestDescriptor, Supplier<Set<? extends DiscoverySelector>>, Match> matchFactory) {\n\t\tif (!testClassPredicate.test(testClass)) {\n\t\t\treturn unresolved();\n\t\t}\n\t\tMethod method = methodSupplier.get();\n\t\t// @formatter:off\n\t\tSet<Match> matches = methodTypes.stream()\n\t\t\t\t.map(methodType -> methodType.resolve(enclosingClasses, testClass, method, context, configuration))\n\t\t\t\t.flatMap(Optional::stream)\n\t\t\t\t.map(testDescriptor -> matchFactory.apply(testDescriptor, expansionCallback(testDescriptor)))\n\t\t\t\t.collect(toSet());\n\t\t// @formatter:on\n\t\tif (matches.size() > 1) {\n\t\t\tStream<TestDescriptor> testDescriptors = matches.stream().map(Match::getTestDescriptor);\n\t\t\tString message = \"\"\"\n\t\t\t\t\tPossible configuration error: method [%s] resulted in multiple TestDescriptors %s. \\\n\t\t\t\t\tThis is typically the result of annotating a method with multiple competing annotations \\\n\t\t\t\t\tsuch as @Test, @RepeatedTest, @ParameterizedTest, @TestFactory, etc.\"\"\".formatted(\n\t\t\t\tmethod.toGenericString(), testDescriptors.map(d -> d.getClass().getName()).toList());\n\t\t\tissueReporter.reportIssue(\n\t\t\t\tDiscoveryIssue.builder(Severity.WARNING, message).source(MethodSource.from(method)));\n\t\t}\n\t\treturn matches.isEmpty() ? unresolved() : matches(matches);\n\t}\n\n\t@Override\n\tpublic Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\tUniqueId uniqueId = selector.getUniqueId();\n\t\t// @formatter:off\n\t\treturn methodTypes.stream()\n\t\t\t\t.map(methodType -> methodType.resolveUniqueIdIntoTestDescriptor(uniqueId, context, configuration))\n\t\t\t\t.flatMap(Optional::stream)\n\t\t\t\t.map(testDescriptor -> {\n\t\t\t\t\tboolean exactMatch = uniqueId.equals(testDescriptor.getUniqueId());\n\t\t\t\t\tif (testDescriptor instanceof Filterable filterable) {\n\t\t\t\t\t\tif (exactMatch) {\n\t\t\t\t\t\t\tfilterable.getDynamicDescendantFilter().allowAll();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tfilterable.getDynamicDescendantFilter().allowUniqueIdPrefix(uniqueId);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn Resolution.match(exactMatch ? Match.exact(testDescriptor) : Match.partial(testDescriptor, expansionCallback(testDescriptor)));\n\t\t\t\t})\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(unresolved());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Resolution resolve(IterationSelector selector, Context context) {\n\t\tif (selector.getParentSelector() instanceof MethodSelector methodSelector) {\n\t\t\treturn resolve(context, emptyList(), methodSelector.getJavaClass(), methodSelector::getJavaMethod,\n\t\t\t\t(testDescriptor, childSelectorsSupplier) -> {\n\t\t\t\t\tif (testDescriptor instanceof Filterable filterable) {\n\t\t\t\t\t\tfilterable.getDynamicDescendantFilter().allowIndex(selector.getIterationIndices());\n\t\t\t\t\t}\n\t\t\t\t\treturn Match.partial(testDescriptor, childSelectorsSupplier);\n\t\t\t\t});\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate Supplier<Set<? extends DiscoverySelector>> expansionCallback(TestDescriptor testDescriptor) {\n\t\treturn () -> {\n\t\t\tif (testDescriptor instanceof Filterable filterable) {\n\t\t\t\tfilterable.getDynamicDescendantFilter().allowAll();\n\t\t\t}\n\t\t\treturn emptySet();\n\t\t};\n\t}\n\n\tprivate static class MethodType {\n\n\t\tstatic List<MethodType> allPossibilities(DiscoveryIssueReporter issueReporter) {\n\t\t\treturn Arrays.asList( //\n\t\t\t\tnew MethodType(new IsTestMethod(issueReporter), TestMethodTestDescriptor::new,\n\t\t\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE), //\n\t\t\t\tnew MethodType(new IsTestFactoryMethod(issueReporter), TestFactoryTestDescriptor::new,\n\t\t\t\t\tTestFactoryTestDescriptor.SEGMENT_TYPE, TestFactoryTestDescriptor.DYNAMIC_CONTAINER_SEGMENT_TYPE,\n\t\t\t\t\tTestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE), //\n\t\t\t\tnew MethodType(new IsTestTemplateMethod(issueReporter), TestTemplateTestDescriptor::new,\n\t\t\t\t\tTestTemplateTestDescriptor.SEGMENT_TYPE, TestTemplateInvocationTestDescriptor.SEGMENT_TYPE) //\n\t\t\t);\n\t\t}\n\n\t\tprivate final Predicate<Method> methodPredicate;\n\t\tprivate final TestDescriptorFactory testDescriptorFactory;\n\t\tprivate final String segmentType;\n\t\tprivate final Set<String> dynamicDescendantSegmentTypes;\n\n\t\tprivate MethodType(Predicate<Method> methodPredicate, TestDescriptorFactory testDescriptorFactory,\n\t\t\t\tString segmentType, String... dynamicDescendantSegmentTypes) {\n\t\t\tthis.methodPredicate = methodPredicate;\n\t\t\tthis.testDescriptorFactory = testDescriptorFactory;\n\t\t\tthis.segmentType = segmentType;\n\t\t\tthis.dynamicDescendantSegmentTypes = new LinkedHashSet<>(Arrays.asList(dynamicDescendantSegmentTypes));\n\t\t}\n\n\t\tOptional<TestDescriptor> resolve(List<Class<?>> enclosingClasses, Class<?> testClass, Method method,\n\t\t\t\tContext context, JupiterConfiguration configuration) {\n\t\t\tif (!methodPredicate.test(method)) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t\treturn context.addToParent(() -> selectClass(enclosingClasses, testClass), //\n\t\t\t\tparent -> Optional.of(createTestDescriptor(parent, testClass, method, configuration)));\n\t\t}\n\n\t\tprivate DiscoverySelector selectClass(List<Class<?>> enclosingClasses, Class<?> testClass) {\n\t\t\tif (enclosingClasses.isEmpty()) {\n\t\t\t\treturn DiscoverySelectors.selectClass(testClass);\n\t\t\t}\n\t\t\treturn DiscoverySelectors.selectNestedClass(enclosingClasses, testClass);\n\t\t}\n\n\t\tOptional<TestDescriptor> resolveUniqueIdIntoTestDescriptor(UniqueId uniqueId, Context context,\n\t\t\t\tJupiterConfiguration configuration) {\n\t\t\tUniqueId.Segment lastSegment = uniqueId.getLastSegment();\n\t\t\tif (segmentType.equals(lastSegment.getType())) {\n\t\t\t\treturn context.addToParent(() -> selectUniqueId(uniqueId.removeLastSegment()), parent -> {\n\t\t\t\t\tString methodSpecPart = lastSegment.getValue();\n\t\t\t\t\tClass<?> testClass = ((TestClassAware) parent).getTestClass();\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\treturn methodSegmentResolver.findMethod(methodSpecPart, testClass)\n\t\t\t\t\t\t\t.filter(methodPredicate)\n\t\t\t\t\t\t\t.map(method -> createTestDescriptor(parent, testClass, method, configuration));\n\t\t\t\t\t// @formatter:on\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (dynamicDescendantSegmentTypes.contains(lastSegment.getType())) {\n\t\t\t\treturn resolveUniqueIdIntoTestDescriptor(uniqueId.removeLastSegment(), context, configuration);\n\t\t\t}\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\tprivate TestDescriptor createTestDescriptor(TestDescriptor parent, Class<?> testClass, Method method,\n\t\t\t\tJupiterConfiguration configuration) {\n\t\t\tUniqueId uniqueId = createUniqueId(method, parent, testClass);\n\t\t\treturn testDescriptorFactory.create(uniqueId, testClass, method,\n\t\t\t\t((TestClassAware) parent)::getEnclosingTestClasses, configuration);\n\t\t}\n\n\t\tprivate UniqueId createUniqueId(Method method, TestDescriptor parent, Class<?> testClass) {\n\t\t\treturn parent.getUniqueId().append(segmentType,\n\t\t\t\tmethodSegmentResolver.formatMethodSpecPart(method, testClass));\n\t\t}\n\n\t\tinterface TestDescriptorFactory {\n\t\t\tTestDescriptor create(UniqueId uniqueId, Class<?> testClass, Method method,\n\t\t\t\t\tSupplier<List<Class<?>>> enclosingInstanceTypes, JupiterConfiguration configuration);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal classes for test discovery within the JUnit Jupiter test engine.\n * Contains resolvers for Java elements.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.discovery;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.support.MethodReflectionUtils.getGenericReturnType;\nimport static org.junit.jupiter.engine.support.MethodReflectionUtils.getReturnType;\nimport static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.WildcardType;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Test if a method is a JUnit Jupiter {@link TestFactory @TestFactory} method.\n *\n * <p>NOTE: this predicate does <strong>not</strong> check if a candidate method\n * has an appropriate return type for a {@code @TestFactory} method.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class IsTestFactoryMethod extends IsTestableMethod {\n\n\tprivate static final String EXPECTED_RETURN_TYPE_MESSAGE = \"must return a single %1$s or a Stream, Collection, Iterable, Iterator, Iterator provider, or array of %1$s\".formatted(\n\t\tDynamicNode.class.getName());\n\n\tpublic IsTestFactoryMethod(DiscoveryIssueReporter issueReporter) {\n\t\tsuper(TestFactory.class, IsTestFactoryMethod::hasCompatibleReturnType, issueReporter);\n\t}\n\n\tprivate static DiscoveryIssueReporter.Condition<Method> hasCompatibleReturnType(\n\t\t\tClass<? extends Annotation> annotationType, DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(method -> isCompatible(method, issueReporter),\n\t\t\tmethod -> createIssue(annotationType, method, EXPECTED_RETURN_TYPE_MESSAGE));\n\t}\n\n\tprivate static boolean isCompatible(Method method, DiscoveryIssueReporter issueReporter) {\n\t\tClass<?> returnType = getReturnType(method);\n\t\tif (DynamicNode.class.isAssignableFrom(returnType) || DynamicNode[].class.isAssignableFrom(returnType)) {\n\t\t\treturn true;\n\t\t}\n\t\tif (returnType == Object.class || returnType == Object[].class) {\n\t\t\tissueReporter.reportIssue(createTooGenericReturnTypeIssue(method));\n\t\t\treturn true;\n\t\t}\n\t\tboolean validContainerType = !returnType.isArray() && isConvertibleToStream(returnType);\n\t\treturn validContainerType && isCompatibleContainerType(method, issueReporter);\n\t}\n\n\tprivate static boolean isCompatibleContainerType(Method method, DiscoveryIssueReporter issueReporter) {\n\t\tType genericReturnType = getGenericReturnType(method);\n\n\t\tif (genericReturnType instanceof ParameterizedType type) {\n\t\t\tType[] typeArguments = type.getActualTypeArguments();\n\t\t\tif (typeArguments.length == 1) {\n\t\t\t\tType typeArgument = typeArguments[0];\n\t\t\t\tif (typeArgument instanceof Class<?> clazz) {\n\t\t\t\t\t// Stream<DynamicNode> etc.\n\t\t\t\t\treturn DynamicNode.class.isAssignableFrom(clazz);\n\t\t\t\t}\n\t\t\t\tif (typeArgument instanceof WildcardType wildcardType) {\n\t\t\t\t\tType[] upperBounds = wildcardType.getUpperBounds();\n\t\t\t\t\tType[] lowerBounds = wildcardType.getLowerBounds();\n\t\t\t\t\tif (upperBounds.length == 1 && lowerBounds.length == 0\n\t\t\t\t\t\t\t&& upperBounds[0] instanceof Class<?> upperBound) {\n\t\t\t\t\t\tif (Object.class.equals(upperBound)) { // Stream<?> etc.\n\t\t\t\t\t\t\tissueReporter.reportIssue(createTooGenericReturnTypeIssue(method));\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Stream<? extends DynamicNode> etc.\n\t\t\t\t\t\treturn DynamicNode.class.isAssignableFrom(upperBound);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t// Raw Stream etc. without type argument\n\t\tissueReporter.reportIssue(createTooGenericReturnTypeIssue(method));\n\t\treturn true;\n\t}\n\n\tprivate static DiscoveryIssue.Builder createTooGenericReturnTypeIssue(Method method) {\n\t\tString message = (\"The declared return type of @TestFactory method '%s' does not support static validation. It \"\n\t\t\t\t+ EXPECTED_RETURN_TYPE_MESSAGE + \".\").formatted(method.toGenericString());\n\t\treturn DiscoveryIssue.builder(Severity.INFO, message) //\n\t\t\t\t.source(MethodSource.from(method));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Test if a method is a JUnit Jupiter {@link Test @Test} method.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class IsTestMethod extends IsTestableMethod {\n\n\tpublic IsTestMethod(DiscoveryIssueReporter issueReporter) {\n\t\tsuper(Test.class, IsTestableMethod::hasVoidReturnType, issueReporter);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Test if a method is a JUnit Jupiter {@link TestTemplate @TestTemplate} method.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class IsTestTemplateMethod extends IsTestableMethod {\n\n\tpublic IsTestTemplateMethod(DiscoveryIssueReporter issueReporter) {\n\t\tsuper(TestTemplate.class, IsTestableMethod::hasVoidReturnType, issueReporter);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.junit.jupiter.engine.support.MethodReflectionUtils.getReturnType;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotAbstract;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\n\n/**\n * @since 5.0\n */\nabstract class IsTestableMethod implements Predicate<Method> {\n\n\tprivate final Class<? extends Annotation> annotationType;\n\tprivate final Condition<Method> condition;\n\n\tIsTestableMethod(Class<? extends Annotation> annotationType,\n\t\t\tBiFunction<Class<? extends Annotation>, DiscoveryIssueReporter, Condition<Method>> returnTypeConditionFactory,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\tthis.annotationType = annotationType;\n\t\tthis.condition = isNotStatic(annotationType, issueReporter) //\n\t\t\t\t.and(isNotPrivate(annotationType, issueReporter)) //\n\t\t\t\t.and(returnTypeConditionFactory.apply(annotationType, issueReporter));\n\t}\n\n\t@Override\n\tpublic boolean test(Method candidate) {\n\t\tif (!candidate.isSynthetic() && isAnnotated(candidate, this.annotationType)) {\n\t\t\treturn condition.check(candidate) && isNotAbstract(candidate);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static Condition<Method> isNotStatic(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotStatic,\n\t\t\tmethod -> createIssue(annotationType, method, \"must not be static\"));\n\t}\n\n\tprivate static Condition<Method> isNotPrivate(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotPrivate,\n\t\t\tmethod -> createIssue(annotationType, method, \"must not be private\"));\n\t}\n\n\tprotected static Condition<Method> hasVoidReturnType(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(method -> getReturnType(method) == void.class,\n\t\t\tmethod -> createIssue(annotationType, method, \"must not return a value\"));\n\t}\n\n\tprotected static DiscoveryIssue createIssue(Class<? extends Annotation> annotationType, Method method,\n\t\t\tString condition) {\n\t\tString message = \"@%s method '%s' %s. It will not be executed.\".formatted(annotationType.getSimpleName(),\n\t\t\tmethod.toGenericString(), condition);\n\t\treturn DiscoveryIssue.builder(Severity.WARNING, message) //\n\t\t\t\t.source(MethodSource.from(method)) //\n\t\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/TestClassPredicates.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.support.ModifierSupport.isAbstract;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotAbstract;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotPrivate;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinInterfaceDefaultImplsClass;\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\nimport static org.junit.platform.commons.util.ReflectionUtils.isMethodPresent;\nimport static org.junit.platform.commons.util.ReflectionUtils.isNestedClassPresent;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.ReflectionUtils.CycleErrorHandling;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\n\n/**\n * Predicates for determining whether a class is a JUnit Jupiter test class.\n *\n * @since 5.13\n */\n@API(status = INTERNAL, since = \"5.13\")\npublic class TestClassPredicates {\n\n\tpublic final Predicate<Class<?>> isAnnotatedWithNested = candidate -> isAnnotatedButNotComposed(candidate,\n\t\tNested.class);\n\tpublic final Predicate<Class<?>> isAnnotatedWithClassTemplate = candidate -> isAnnotatedButNotComposed(candidate,\n\t\tClassTemplate.class);\n\n\tpublic final Predicate<Class<?>> isAnnotatedWithNestedAndValid = candidate -> this.isAnnotatedWithNested.test(\n\t\tcandidate) && isValidNestedTestClass(candidate);\n\tpublic final Predicate<Class<?>> looksLikeNestedOrStandaloneTestClass = candidate -> this.isAnnotatedWithNested.test(\n\t\tcandidate) || looksLikeIntendedTestClass(candidate);\n\tpublic final Predicate<Method> isTestOrTestFactoryOrTestTemplateMethod;\n\n\tprivate final Condition<Class<?>> isNotPrivateUnlessAbstractNestedClass;\n\tprivate final Condition<Class<?>> isInnerNestedClass;\n\tprivate final Condition<Class<?>> isValidStandaloneTestClass;\n\n\tpublic TestClassPredicates(DiscoveryIssueReporter issueReporter) {\n\t\tthis.isTestOrTestFactoryOrTestTemplateMethod = new IsTestMethod(issueReporter) //\n\t\t\t\t.or(new IsTestFactoryMethod(issueReporter)) //\n\t\t\t\t.or(new IsTestTemplateMethod(issueReporter));\n\t\tthis.isNotPrivateUnlessAbstractNestedClass = isNotPrivateUnlessAbstract(\"@Nested\", issueReporter);\n\t\tthis.isInnerNestedClass = isInner(issueReporter);\n\t\tthis.isValidStandaloneTestClass = isNotPrivateUnlessAbstract(\"Test\", issueReporter) //\n\t\t\t\t.and(isNotLocal(issueReporter)) //\n\t\t\t\t.and(isNotInnerUnlessAbstract(issueReporter)) //\n\t\t\t\t.and(isNotAnonymous(issueReporter));\n\t}\n\n\tpublic boolean looksLikeIntendedTestClass(Class<?> candidate) {\n\t\treturn looksLikeIntendedTestClass(candidate, new HashSet<>());\n\t}\n\n\tprivate boolean looksLikeIntendedTestClass(Class<?> candidate, Set<Class<?>> seen) {\n\t\tif (seen.add(candidate) && !isKotlinInterfaceDefaultImplsClass(candidate)) {\n\t\t\treturn this.isAnnotatedWithClassTemplate.test(candidate) //\n\t\t\t\t\t|| hasTestOrTestFactoryOrTestTemplateMethods(candidate) //\n\t\t\t\t\t|| hasNestedTests(candidate, seen);\n\t\t}\n\t\treturn false;\n\t}\n\n\tpublic boolean isValidNestedTestClass(Class<?> candidate) {\n\t\treturn validateNestedTestClass(candidate) == null;\n\t}\n\n\tpublic @Nullable NestedClassInvalidityReason validateNestedTestClass(Class<?> candidate) {\n\t\tboolean isInner = this.isInnerNestedClass.check(candidate);\n\t\tboolean isNotPrivateUnlessAbstract = this.isNotPrivateUnlessAbstractNestedClass.check(candidate);\n\t\tif (isNotPrivateUnlessAbstract && isNotAbstract(candidate)) {\n\t\t\treturn isInner ? null : NestedClassInvalidityReason.NOT_INNER;\n\t\t}\n\t\treturn NestedClassInvalidityReason.OTHER;\n\t}\n\n\tpublic boolean isValidStandaloneTestClass(Class<?> candidate) {\n\t\treturn this.isValidStandaloneTestClass.check(candidate) //\n\t\t\t\t&& isNotAbstract(candidate);\n\t}\n\n\tprivate boolean hasTestOrTestFactoryOrTestTemplateMethods(Class<?> candidate) {\n\t\treturn isMethodPresent(candidate, this.isTestOrTestFactoryOrTestTemplateMethod);\n\t}\n\n\tprivate boolean hasNestedTests(Class<?> candidate, Set<Class<?>> seen) {\n\t\tvar hasAnnotatedClass = isNestedClassPresent(candidate, this.isAnnotatedWithNested,\n\t\t\tCycleErrorHandling.THROW_EXCEPTION);\n\t\tif (hasAnnotatedClass) {\n\t\t\treturn true;\n\t\t}\n\t\treturn isNestedClassPresent( //\n\t\t\tcandidate, //\n\t\t\tit -> isInnerClass(it) && looksLikeIntendedTestClass(it, seen), //\n\t\t\tCycleErrorHandling.ABORT_VISIT //\n\t\t);\n\t}\n\n\tprivate static Condition<Class<?>> isNotPrivateUnlessAbstract(String prefix, DiscoveryIssueReporter issueReporter) {\n\t\t// Allow abstract test classes to be private because subclasses may widen access.\n\t\treturn issueReporter.createReportingCondition(testClass -> isNotPrivate(testClass) || isAbstract(testClass),\n\t\t\ttestClass -> createIssue(prefix, testClass, \"must not be private\"));\n\t}\n\n\tprivate static Condition<Class<?>> isNotLocal(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(testClass -> !testClass.isLocalClass(),\n\t\t\ttestClass -> createIssue(\"Test\", testClass, \"must not be a local class\"));\n\t}\n\n\tprivate static Condition<Class<?>> isInner(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ReflectionUtils::isInnerClass, testClass -> {\n\t\t\tif (testClass.getEnclosingClass() == null) {\n\t\t\t\treturn createIssue(\"Top-level\", testClass, \"must not be annotated with @Nested\",\n\t\t\t\t\t\"It will be executed anyway for backward compatibility. \"\n\t\t\t\t\t\t\t+ \"You should remove the @Nested annotation to resolve this warning.\");\n\t\t\t}\n\t\t\treturn createIssue(\"@Nested\", testClass, \"must not be static\",\n\t\t\t\t\"It will only be executed if discovered as a standalone test class. \"\n\t\t\t\t\t\t+ \"You should remove the annotation or make it non-static to resolve this warning.\");\n\t\t});\n\t}\n\n\tprivate static Condition<Class<?>> isNotInnerUnlessAbstract(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(testClass -> !isInnerClass(testClass) || isAbstract(testClass),\n\t\t\ttestClass -> createIssue(\"Test\", testClass, \"must not be an inner class unless annotated with @Nested\"));\n\t}\n\n\tprivate static Condition<Class<?>> isNotAnonymous(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(testClass -> !testClass.isAnonymousClass(),\n\t\t\ttestClass -> createIssue(\"Test\", testClass, \"must not be anonymous\"));\n\t}\n\n\tprivate static DiscoveryIssue createIssue(String prefix, Class<?> testClass, String detailMessage) {\n\t\treturn createIssue(prefix, testClass, detailMessage, \"It will not be executed.\");\n\t}\n\n\tprivate static DiscoveryIssue createIssue(String prefix, Class<?> testClass, String detailMessage, String effect) {\n\t\tString message = \"%s class '%s' %s. %s\".formatted(prefix, testClass.getName(), detailMessage, effect);\n\t\treturn DiscoveryIssue.builder(DiscoveryIssue.Severity.WARNING, message) //\n\t\t\t\t.source(ClassSource.from(testClass)) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static boolean isAnnotatedButNotComposed(Class<?> candidate, Class<? extends Annotation> annotationType) {\n\t\treturn !candidate.isAnnotation() && isAnnotated(candidate, annotationType);\n\t}\n\n\t@API(status = INTERNAL, since = \"5.13.3\")\n\tpublic enum NestedClassInvalidityReason {\n\t\tNOT_INNER, OTHER\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal predicate classes used by test discovery within the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/AfterEachMethodAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\n\n/**\n * Functional interface for registering an {@link AfterEach @AfterEach} method\n * as a pseudo-extension.\n *\n * @since 5.0\n */\n@FunctionalInterface\n@API(status = INTERNAL, since = \"5.0\")\npublic interface AfterEachMethodAdapter extends Extension {\n\n\tvoid invokeAfterEachMethod(ExtensionContext context, ExtensionRegistry registry) throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/BeforeEachMethodAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\n\n/**\n * Functional interface for registering a {@link BeforeEach @BeforeEach} method\n * as a pseudo-extension.\n *\n * @since 5.0\n */\n@FunctionalInterface\n@API(status = INTERNAL, since = \"5.0\")\npublic interface BeforeEachMethodAdapter extends Extension {\n\n\tvoid invokeBeforeEachMethod(ExtensionContext context, ExtensionRegistry registry) throws Throwable;\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport java.io.Serial;\n\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Thrown if an error is encountered while evaluating an\n * {@link ExecutionCondition}.\n *\n * @since 5.0\n * @see ConditionEvaluator\n */\nclass ConditionEvaluationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tConditionEvaluationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConditionEvaluator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * {@code ConditionEvaluator} evaluates {@link ExecutionCondition} extensions.\n *\n * @since 5.0\n * @see ExecutionCondition\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class ConditionEvaluator {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ConditionEvaluator.class);\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"No 'disabled' conditions encountered\");\n\n\t/**\n\t * Evaluate all {@link ExecutionCondition} extensions registered for the\n\t * supplied {@link ExtensionContext}.\n\t *\n\t * @param context the current {@code ExtensionContext}\n\t * @return the first <em>disabled</em> {@code ConditionEvaluationResult},\n\t * or a default <em>enabled</em> {@code ConditionEvaluationResult} if no\n\t * disabled conditions are encountered\n\t */\n\tpublic ConditionEvaluationResult evaluate(ExtensionRegistry extensionRegistry, JupiterConfiguration configuration,\n\t\t\tExtensionContext context) {\n\n\t\t// @formatter:off\n\t\treturn extensionRegistry.stream(ExecutionCondition.class)\n\t\t\t\t.filter(configuration.getExecutionConditionFilter())\n\t\t\t\t.map(condition -> evaluate(condition, context))\n\t\t\t\t.filter(ConditionEvaluationResult::isDisabled)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(ENABLED);\n\t\t// @formatter:on\n\t}\n\n\tprivate ConditionEvaluationResult evaluate(ExecutionCondition condition, ExtensionContext context) {\n\t\ttry {\n\t\t\tConditionEvaluationResult result = condition.evaluateExecutionCondition(context);\n\t\t\tlogResult(condition.getClass(), result, context);\n\t\t\treturn result;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow evaluationException(condition.getClass(), ex);\n\t\t}\n\t}\n\n\tprivate void logResult(Class<?> conditionType, ConditionEvaluationResult result, ExtensionContext context) {\n\t\tlogger.trace(() -> \"Evaluation of condition [%s] on [%s] resulted in: %s\".formatted(conditionType.getName(),\n\t\t\tcontext.getElement().orElse(null), result));\n\t}\n\n\tprivate ConditionEvaluationException evaluationException(Class<?> conditionType, Exception ex) {\n\t\tString cause = StringUtils.isNotBlank(ex.getMessage()) ? \": \" + ex.getMessage() : \"\";\n\t\treturn new ConditionEvaluationException(\n\t\t\t\"Failed to evaluate condition [%s]%s\".formatted(conditionType.getName(), cause), ex);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ConstructorInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Collections.unmodifiableList;\n\nimport java.lang.reflect.Constructor;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\nclass ConstructorInvocation<T> implements Invocation<T>, ReflectiveInvocationContext<Constructor<T>> {\n\n\tprivate final Constructor<T> constructor;\n\tprivate final @Nullable Object[] arguments;\n\n\tConstructorInvocation(Constructor<T> constructor, @Nullable Object[] arguments) {\n\t\tthis.constructor = constructor;\n\t\tthis.arguments = arguments;\n\t}\n\n\t@Override\n\tpublic Class<?> getTargetClass() {\n\t\treturn this.constructor.getDeclaringClass();\n\t}\n\n\t@Override\n\tpublic Constructor<T> getExecutable() {\n\t\treturn this.constructor;\n\t}\n\n\t@Override\n\tpublic List<Object> getArguments() {\n\t\treturn unmodifiableList(Arrays.asList(this.arguments));\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTarget() {\n\t\treturn Optional.empty();\n\t}\n\n\t@Override\n\tpublic T proceed() {\n\t\treturn ReflectionUtils.newInstance(this.constructor, this.arguments);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultExecutableInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameters;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExecutableInvoker;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.jupiter.engine.support.MethodReflectionUtils;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.9\n */\n@API(status = INTERNAL, since = \"5.9\")\npublic class DefaultExecutableInvoker implements ExecutableInvoker {\n\n\tprivate final ExtensionContext extensionContext;\n\tprivate final ExtensionRegistry extensionRegistry;\n\n\tpublic DefaultExecutableInvoker(ExtensionContext extensionContext, ExtensionRegistry extensionRegistry) {\n\t\tthis.extensionContext = extensionContext;\n\t\tthis.extensionRegistry = extensionRegistry;\n\t}\n\n\t@Override\n\tpublic <T> T invoke(Constructor<T> constructor, @Nullable Object outerInstance) {\n\t\t@Nullable\n\t\tObject[] arguments = resolveParameters(constructor, Optional.empty(), Optional.ofNullable(outerInstance),\n\t\t\textensionContext, extensionRegistry);\n\t\treturn ReflectionUtils.newInstance(constructor, arguments);\n\t}\n\n\t@Override\n\tpublic @Nullable Object invoke(Method method, @Nullable Object target) {\n\t\t@Nullable\n\t\tObject[] arguments = resolveParameters(method, Optional.ofNullable(target), extensionContext,\n\t\t\textensionRegistry);\n\t\treturn MethodReflectionUtils.invoke(method, target, arguments);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultParameterContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport java.lang.reflect.Parameter;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * @since 5.0\n */\nrecord DefaultParameterContext(Parameter parameter, int index, Optional<Object> target) implements ParameterContext {\n\n\tDefaultParameterContext {\n\t\tPreconditions.condition(index >= 0, \"index must be greater than or equal to zero\");\n\t\tPreconditions.notNull(parameter, \"parameter must not be null\");\n\t\tPreconditions.notNull(target, \"target must not be null\");\n\t}\n\n\t@Override\n\tpublic Parameter getParameter() {\n\t\treturn this.parameter;\n\t}\n\n\t@Override\n\tpublic int getIndex() {\n\t\treturn this.index;\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTarget() {\n\t\treturn this.target;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"parameter\", this.parameter)\n\t\t\t\t.append(\"index\", this.index)\n\t\t\t\t.append(\"target\", this.target)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/DefaultTestInstances.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.platform.commons.util.Preconditions;\n\n@API(status = INTERNAL, since = \"5.4\")\npublic class DefaultTestInstances implements TestInstances {\n\n\tpublic static DefaultTestInstances of(Object instance) {\n\t\treturn new DefaultTestInstances(List.of(instance));\n\t}\n\n\tpublic static DefaultTestInstances of(TestInstances testInstances, Object instance) {\n\t\tList<Object> allInstances = new ArrayList<>(testInstances.getAllInstances());\n\t\tallInstances.add(instance);\n\t\treturn new DefaultTestInstances(Collections.unmodifiableList(allInstances));\n\t}\n\n\tprivate final List<Object> instances;\n\n\tprivate DefaultTestInstances(List<Object> instances) {\n\t\tthis.instances = Preconditions.notEmpty(instances, \"instances must not be empty\");\n\t}\n\n\t@Override\n\tpublic Object getInnermostInstance() {\n\t\treturn instances.get(instances.size() - 1);\n\t}\n\n\t@Override\n\tpublic List<Object> getEnclosingInstances() {\n\t\treturn instances.subList(0, instances.size() - 1);\n\t}\n\n\t@Override\n\tpublic List<Object> getAllInstances() {\n\t\treturn instances;\n\t}\n\n\t@Override\n\tpublic <T> Optional<T> findInstance(Class<T> requiredType) {\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tListIterator<Object> iterator = instances.listIterator(instances.size());\n\t\twhile (iterator.hasPrevious()) {\n\t\t\tObject instance = iterator.previous();\n\t\t\tif (requiredType.isInstance(instance)) {\n\t\t\t\treturn Optional.of(requiredType.cast(instance));\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ExtensionContextSupplier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstantiationAwareExtension;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\n\n/**\n * Container of two instances of {@link ExtensionContext} to simplify the legacy for\n * <a href=\"https://github.com/junit-team/junit-framework/issues/3445\">#3445</a>.\n *\n * @since 5.12\n * @see TestInstantiationAwareExtension\n */\n@FunctionalInterface\n@API(status = INTERNAL, since = \"5.12\")\npublic interface ExtensionContextSupplier {\n\n\tstatic ExtensionContextSupplier create(ExtensionContext currentExtensionContext,\n\t\t\tExtensionContext legacyExtensionContext, JupiterConfiguration configuration) {\n\t\tif (currentExtensionContext == legacyExtensionContext\n\t\t\t\t|| configuration.getDefaultTestInstantiationExtensionContextScope() == TEST_METHOD) {\n\t\t\treturn __ -> currentExtensionContext;\n\t\t}\n\t\treturn new ScopeBasedExtensionContextSupplier(currentExtensionContext, legacyExtensionContext);\n\t}\n\n\tExtensionContext get(TestInstantiationAwareExtension extension);\n\n\tclass ScopeBasedExtensionContextSupplier implements ExtensionContextSupplier {\n\n\t\tprivate final ExtensionContext currentExtensionContext;\n\t\tprivate final ExtensionContext legacyExtensionContext;\n\n\t\tprivate ScopeBasedExtensionContextSupplier(ExtensionContext currentExtensionContext,\n\t\t\t\tExtensionContext legacyExtensionContext) {\n\t\t\tthis.currentExtensionContext = currentExtensionContext;\n\t\t\tthis.legacyExtensionContext = legacyExtensionContext;\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContext get(TestInstantiationAwareExtension extension) {\n\t\t\treturn isTestScoped(extension) ? currentExtensionContext : legacyExtensionContext;\n\t\t}\n\n\t\tprivate boolean isTestScoped(TestInstantiationAwareExtension extension) {\n\t\t\tExtensionContext rootContext = legacyExtensionContext.getRoot();\n\t\t\treturn extension.getTestInstantiationExtensionContextScope(rootContext) == TEST_METHOD;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.jupiter.engine.execution.ParameterResolutionUtils.resolveParameters;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall.VoidMethodInterceptorCall;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\n\n/**\n * {@code InterceptingExecutableInvoker} encapsulates the invocation of a\n * {@link java.lang.reflect.Executable} (i.e., method or constructor),\n * including support for dynamic resolution of method parameters via\n * {@link ParameterResolver ParameterResolvers}.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class InterceptingExecutableInvoker {\n\n\tprivate static final InvocationInterceptorChain interceptorChain = new InvocationInterceptorChain();\n\n\t/**\n\t * Invoke the supplied constructor with the supplied outer instance and\n\t * dynamic parameter resolution.\n\t *\n\t * @param constructor the constructor to invoke and resolve parameters for\n\t * @param outerInstance the outer instance to supply as the first argument\n\t * to the constructor; empty, for top-level classes\n\t * @param extensionContext the current {@code ExtensionContext}\n\t * @param extensionRegistry the {@code ExtensionRegistry} to retrieve\n\t * {@code ParameterResolvers} from\n\t * @param interceptorCall the call for intercepting this constructor\n\t * invocation via all registered {@linkplain InvocationInterceptor\n\t * interceptors}\n\t */\n\tpublic <T> T invoke(Constructor<T> constructor, Optional<Object> outerInstance,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry extensionRegistry,\n\t\t\tReflectiveInterceptorCall<Constructor<T>, T> interceptorCall) {\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveParameters(constructor, Optional.empty(), outerInstance, extensionContext,\n\t\t\textensionRegistry);\n\t\tConstructorInvocation<T> invocation = new ConstructorInvocation<>(constructor, arguments);\n\t\treturn invoke(invocation, invocation, extensionContext, extensionRegistry, interceptorCall);\n\t}\n\n\tpublic void invokeVoid(Method method, @Nullable Object target, ExtensionContext extensionContext,\n\t\t\tExtensionRegistry extensionRegistry, VoidMethodInterceptorCall interceptorCall) {\n\t\tthis.<@Nullable Void> invoke(method, target, extensionContext, extensionRegistry,\n\t\t\tReflectiveInterceptorCall.ofVoidMethod(interceptorCall));\n\t}\n\n\t/**\n\t * Invoke the supplied method with dynamic parameter resolution.\n\t *\n\t * @param method the method to invoke and resolve parameters for\n\t * @param target the target on which the executable will be invoked,\n\t * potentially wrapped in an {@link Optional}; can be {@code null} or an\n\t * empty {@code Optional} for a {@code static} method\n\t * @param extensionContext the current {@code ExtensionContext}\n\t * @param extensionRegistry the {@code ExtensionRegistry} to retrieve\n\t * {@code ParameterResolvers} from\n\t * @param interceptorCall the call for intercepting this method invocation\n\t * via all registered {@linkplain InvocationInterceptor interceptors}\n\t */\n\tpublic <T extends @Nullable Object> T invoke(Method method, @Nullable Object target,\n\t\t\tExtensionContext extensionContext, ExtensionRegistry extensionRegistry,\n\t\t\tReflectiveInterceptorCall<Method, T> interceptorCall) {\n\n\t\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\t\tOptional<Object> optionalTarget = (target instanceof Optional optional ? optional\n\t\t\t\t: Optional.ofNullable(target));\n\t\t@Nullable\n\t\tObject[] arguments = resolveParameters(method, optionalTarget, extensionContext, extensionRegistry);\n\t\tMethodInvocation<T> invocation = new MethodInvocation<>(method, optionalTarget, arguments);\n\t\treturn invoke(invocation, invocation, extensionContext, extensionRegistry, interceptorCall);\n\t}\n\n\tprivate <E extends Executable, T> T invoke(Invocation<T> originalInvocation,\n\t\t\tReflectiveInvocationContext<E> invocationContext, ExtensionContext extensionContext,\n\t\t\tExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<E, T> call) {\n\t\treturn interceptorChain.invoke(originalInvocation, extensionRegistry, (interceptor,\n\t\t\t\twrappedInvocation) -> call.apply(interceptor, wrappedInvocation, invocationContext, extensionContext));\n\t}\n\n\tprivate <E extends Executable, T> T invoke(Invocation<T> originalInvocation,\n\t\t\tReflectiveInvocationContext<E> invocationContext, ExtensionContextSupplier extensionContext,\n\t\t\tExtensionRegistry extensionRegistry, ReflectiveInterceptorCall<E, T> call) {\n\t\treturn interceptorChain.invoke(originalInvocation, extensionRegistry,\n\t\t\t(interceptor, wrappedInvocation) -> call.apply(interceptor, wrappedInvocation, invocationContext,\n\t\t\t\textensionContext.get(interceptor)));\n\t}\n\n\tpublic interface ReflectiveInterceptorCall<E extends Executable, T extends @Nullable Object> {\n\n\t\tT apply(InvocationInterceptor interceptor, Invocation<T> invocation,\n\t\t\t\tReflectiveInvocationContext<E> invocationContext, ExtensionContext extensionContext) throws Throwable;\n\n\t\t@SuppressWarnings(\"NullAway\") // for JDK 26 and earlier\n\t\tstatic ReflectiveInterceptorCall<Method, @Nullable Void> ofVoidMethod(VoidMethodInterceptorCall call) {\n\t\t\treturn (interceptorChain, invocation, invocationContext, extensionContext) -> {\n\t\t\t\tcall.apply(interceptorChain, invocation, invocationContext, extensionContext);\n\t\t\t\treturn null;\n\t\t\t};\n\t\t}\n\n\t\tinterface VoidMethodInterceptorCall {\n\t\t\tvoid apply(InvocationInterceptor interceptor, Invocation<@Nullable Void> invocation,\n\t\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Throwable;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/InvocationInterceptorChain.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ExceptionUtils;\n\n@API(status = INTERNAL, since = \"5.5\")\npublic class InvocationInterceptorChain {\n\n\tpublic <T extends @Nullable Object> T invoke(Invocation<T> invocation, ExtensionRegistry extensionRegistry,\n\t\t\tInterceptorCall<T> call) {\n\t\tList<InvocationInterceptor> interceptors = extensionRegistry.getExtensions(InvocationInterceptor.class);\n\t\tif (interceptors.isEmpty()) {\n\t\t\treturn proceed(invocation);\n\t\t}\n\t\treturn chainAndInvoke(invocation, call, interceptors);\n\t}\n\n\tprivate <T> T chainAndInvoke(Invocation<T> invocation, InterceptorCall<T> call,\n\t\t\tList<InvocationInterceptor> interceptors) {\n\n\t\tValidatingInvocation<T> validatingInvocation = new ValidatingInvocation<>(invocation, interceptors);\n\t\tInvocation<T> chainedInvocation = chainInterceptors(validatingInvocation, call, interceptors);\n\t\tT result = proceed(chainedInvocation);\n\t\tvalidatingInvocation.verifyInvokedAtLeastOnce();\n\t\treturn result;\n\t}\n\n\tprivate <T> Invocation<T> chainInterceptors(Invocation<T> invocation, InterceptorCall<T> call,\n\t\t\tList<InvocationInterceptor> interceptors) {\n\n\t\tInvocation<T> result = invocation;\n\t\tListIterator<InvocationInterceptor> iterator = interceptors.listIterator(interceptors.size());\n\t\twhile (iterator.hasPrevious()) {\n\t\t\tInvocationInterceptor interceptor = iterator.previous();\n\t\t\tresult = new InterceptedInvocation<>(result, call, interceptor);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate <T> T proceed(Invocation<T> invocation) {\n\t\ttry {\n\t\t\treturn invocation.proceed();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t}\n\t}\n\n\t@FunctionalInterface\n\tpublic interface InterceptorCall<T extends @Nullable Object> {\n\n\t\tT apply(InvocationInterceptor interceptor, Invocation<T> invocation) throws Throwable;\n\n\t\tstatic InterceptorCall<@Nullable Void> ofVoid(VoidInterceptorCall call) {\n\t\t\treturn (InvocationInterceptor interceptorChain, Invocation<@Nullable Void> invocation) -> {\n\t\t\t\tcall.apply(interceptorChain, invocation);\n\t\t\t\treturn null;\n\t\t\t};\n\t\t}\n\n\t}\n\n\t@FunctionalInterface\n\tpublic interface VoidInterceptorCall {\n\n\t\tvoid apply(InvocationInterceptor interceptor, Invocation<@Nullable Void> invocation) throws Throwable;\n\n\t}\n\n\tprivate record InterceptedInvocation<T>(Invocation<T> invocation, InterceptorCall<T> call,\n\t\t\tInvocationInterceptor interceptor) implements Invocation<T> {\n\n\t\t@Override\n\t\tpublic T proceed() throws Throwable {\n\t\t\treturn call.apply(interceptor, invocation);\n\t\t}\n\n\t\t@Override\n\t\tpublic void skip() {\n\t\t\tinvocation.skip();\n\t\t}\n\t}\n\n\tprivate static class ValidatingInvocation<T extends @Nullable Object> implements Invocation<T> {\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(ValidatingInvocation.class);\n\n\t\tprivate final AtomicBoolean invokedOrSkipped = new AtomicBoolean();\n\t\tprivate final Invocation<T> delegate;\n\t\tprivate final List<InvocationInterceptor> interceptors;\n\n\t\tValidatingInvocation(Invocation<T> delegate, List<InvocationInterceptor> interceptors) {\n\t\t\tthis.delegate = delegate;\n\t\t\tthis.interceptors = interceptors;\n\t\t}\n\n\t\t@Override\n\t\tpublic T proceed() throws Throwable {\n\t\t\tmarkInvokedOrSkipped();\n\t\t\treturn delegate.proceed();\n\t\t}\n\n\t\t@Override\n\t\tpublic void skip() {\n\t\t\tlogger.debug(() -> \"The invocation is skipped\");\n\t\t\tmarkInvokedOrSkipped();\n\t\t\tdelegate.skip();\n\t\t}\n\n\t\tprivate void markInvokedOrSkipped() {\n\t\t\tif (!invokedOrSkipped.compareAndSet(false, true)) {\n\t\t\t\tfail(\"Chain of InvocationInterceptors called invocation multiple times instead of just once\");\n\t\t\t}\n\t\t}\n\n\t\tvoid verifyInvokedAtLeastOnce() {\n\t\t\tif (!invokedOrSkipped.get()) {\n\t\t\t\tfail(\"Chain of InvocationInterceptors never called invocation\");\n\t\t\t}\n\t\t}\n\n\t\tprivate void fail(String prefix) {\n\t\t\tString commaSeparatedInterceptorClasses = interceptors.stream().map(Object::getClass).map(\n\t\t\t\tClass::getName).collect(joining(\", \"));\n\t\t\tthrow new JUnitException(prefix + \": \" + commaSeparatedInterceptorClasses);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.support.hierarchical.EngineExecutionContext;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class JupiterEngineExecutionContext implements EngineExecutionContext {\n\n\tprivate final State state;\n\n\t// The following is not \"cloneable\" State.\n\tprivate boolean beforeAllCallbacksExecuted = false;\n\tprivate boolean beforeAllMethodsExecuted = false;\n\n\tpublic JupiterEngineExecutionContext(EngineExecutionListener executionListener, JupiterConfiguration configuration,\n\t\t\tLauncherStoreFacade launcherStoreFacade) {\n\t\tthis(new State(executionListener, configuration, launcherStoreFacade));\n\t}\n\n\tprivate JupiterEngineExecutionContext(State state) {\n\t\tthis.state = state;\n\t}\n\n\tpublic void close() throws Exception {\n\t\tExtensionContext extensionContext = getExtensionContext();\n\t\tif (extensionContext instanceof @SuppressWarnings(\"resource\") AutoCloseable closeable) {\n\t\t\ttry {\n\t\t\t\tcloseable.close();\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new JUnitException(\"Failed to close extension context\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic EngineExecutionListener getExecutionListener() {\n\t\treturn this.state.executionListener;\n\t}\n\n\tpublic JupiterConfiguration getConfiguration() {\n\t\treturn this.state.configuration;\n\t}\n\n\tpublic LauncherStoreFacade getLauncherStoreFacade() {\n\t\treturn this.state.launcherStoreFacade;\n\t}\n\n\tpublic TestInstancesProvider getTestInstancesProvider() {\n\t\treturn requireNonNull(this.state.testInstancesProvider);\n\t}\n\n\tpublic MutableExtensionRegistry getExtensionRegistry() {\n\t\treturn requireNonNull(this.state.extensionRegistry);\n\t}\n\n\tpublic ExtensionContext getExtensionContext() {\n\t\treturn requireNonNull(this.state.extensionContext);\n\t}\n\n\tpublic ThrowableCollector getThrowableCollector() {\n\t\treturn requireNonNull(this.state.throwableCollector);\n\t}\n\n\t/**\n\t * Track that an attempt was made to execute {@code BeforeAllCallback} extensions.\n\t *\n\t * @since 5.3\n\t */\n\tpublic void beforeAllCallbacksExecuted(boolean beforeAllCallbacksExecuted) {\n\t\tthis.beforeAllCallbacksExecuted = beforeAllCallbacksExecuted;\n\t}\n\n\t/**\n\t * @return {@code true} if an attempt was made to execute {@code BeforeAllCallback}\n\t * extensions\n\t * @since 5.3\n\t */\n\tpublic boolean beforeAllCallbacksExecuted() {\n\t\treturn beforeAllCallbacksExecuted;\n\t}\n\n\t/**\n\t * Track that an attempt was made to execute {@code @BeforeAll} methods.\n\t */\n\tpublic void beforeAllMethodsExecuted(boolean beforeAllMethodsExecuted) {\n\t\tthis.beforeAllMethodsExecuted = beforeAllMethodsExecuted;\n\t}\n\n\t/**\n\t * @return {@code true} if an attempt was made to execute {@code @BeforeAll}\n\t * methods\n\t */\n\tpublic boolean beforeAllMethodsExecuted() {\n\t\treturn this.beforeAllMethodsExecuted;\n\t}\n\n\tpublic Builder extend() {\n\t\treturn new Builder(this.state);\n\t}\n\n\tprivate static final class State implements Cloneable {\n\n\t\tfinal EngineExecutionListener executionListener;\n\t\tfinal JupiterConfiguration configuration;\n\t\tfinal LauncherStoreFacade launcherStoreFacade;\n\n\t\t@Nullable\n\t\tTestInstancesProvider testInstancesProvider;\n\n\t\t@Nullable\n\t\tMutableExtensionRegistry extensionRegistry;\n\n\t\t@Nullable\n\t\tExtensionContext extensionContext;\n\n\t\t@Nullable\n\t\tThrowableCollector throwableCollector;\n\n\t\tState(EngineExecutionListener executionListener, JupiterConfiguration configuration,\n\t\t\t\tLauncherStoreFacade launcherStoreFacade) {\n\t\t\tthis.executionListener = executionListener;\n\t\t\tthis.configuration = configuration;\n\t\t\tthis.launcherStoreFacade = launcherStoreFacade;\n\t\t}\n\n\t\t@Override\n\t\tpublic State clone() {\n\t\t\ttry {\n\t\t\t\treturn (State) super.clone();\n\t\t\t}\n\t\t\tcatch (CloneNotSupportedException e) {\n\t\t\t\tthrow new JUnitException(\"State could not be cloned\", e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tpublic static class Builder {\n\n\t\tprivate State originalState;\n\n\t\t@Nullable\n\t\tprivate State newState = null;\n\n\t\tprivate Builder(State originalState) {\n\t\t\tthis.originalState = originalState;\n\t\t}\n\n\t\tpublic Builder withTestInstancesProvider(TestInstancesProvider testInstancesProvider) {\n\t\t\tnewState().testInstancesProvider = testInstancesProvider;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withExtensionRegistry(MutableExtensionRegistry extensionRegistry) {\n\t\t\tnewState().extensionRegistry = extensionRegistry;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withExtensionContext(ExtensionContext extensionContext) {\n\t\t\tnewState().extensionContext = extensionContext;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic Builder withThrowableCollector(ThrowableCollector throwableCollector) {\n\t\t\tnewState().throwableCollector = throwableCollector;\n\t\t\treturn this;\n\t\t}\n\n\t\tpublic JupiterEngineExecutionContext build() {\n\t\t\tif (newState != null) {\n\t\t\t\toriginalState = newState;\n\t\t\t\tnewState = null;\n\t\t\t}\n\t\t\treturn new JupiterEngineExecutionContext(originalState);\n\t\t}\n\n\t\tprivate State newState() {\n\t\t\tif (newState == null) {\n\t\t\t\tthis.newState = originalState.clone();\n\t\t\t}\n\t\t\treturn newState;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/LauncherStoreFacade.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n@API(status = INTERNAL, since = \"5.14\")\npublic class LauncherStoreFacade {\n\n\tprivate final NamespacedHierarchicalStore<Namespace> requestLevelStore;\n\tprivate final NamespacedHierarchicalStore<Namespace> sessionLevelStore;\n\n\tpublic LauncherStoreFacade(NamespacedHierarchicalStore<Namespace> requestLevelStore) {\n\t\tthis.requestLevelStore = requestLevelStore;\n\t\tthis.sessionLevelStore = requestLevelStore.getParent().orElseThrow(\n\t\t\t() -> new JUnitException(\"Request-level store must have a parent\"));\n\t}\n\n\tpublic NamespacedHierarchicalStore<Namespace> getRequestLevelStore() {\n\t\treturn this.requestLevelStore;\n\t}\n\n\tpublic ExtensionContext.Store getRequestLevelStore(ExtensionContext.Namespace namespace) {\n\t\treturn getStoreAdapter(this.requestLevelStore, namespace);\n\t}\n\n\tpublic ExtensionContext.Store getSessionLevelStore(ExtensionContext.Namespace namespace) {\n\t\treturn getStoreAdapter(this.sessionLevelStore, namespace);\n\t}\n\n\tpublic NamespaceAwareStore getStoreAdapter(NamespacedHierarchicalStore<Namespace> valuesStore,\n\t\t\tExtensionContext.Namespace namespace) {\n\t\tPreconditions.notNull(namespace, \"Namespace must not be null\");\n\t\treturn new NamespaceAwareStore(valuesStore, convert(namespace));\n\t}\n\n\tprivate Namespace convert(ExtensionContext.Namespace namespace) {\n\t\treturn namespace.equals(ExtensionContext.Namespace.GLOBAL) //\n\t\t\t\t? Namespace.GLOBAL //\n\t\t\t\t: Namespace.create(namespace.getParts());\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/MethodInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Collections.unmodifiableList;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.jupiter.engine.support.MethodReflectionUtils;\n\nclass MethodInvocation<T extends @Nullable Object> implements Invocation<T>, ReflectiveInvocationContext<Method> {\n\n\tprivate final Method method;\n\tprivate final Optional<Object> target;\n\tprivate final @Nullable Object[] arguments;\n\n\tMethodInvocation(Method method, Optional<Object> target, @Nullable Object[] arguments) {\n\t\tthis.method = method;\n\t\tthis.target = target;\n\t\tthis.arguments = arguments;\n\t}\n\n\t@Override\n\tpublic Class<?> getTargetClass() {\n\t\treturn this.target.<Class<?>> map(Object::getClass).orElseGet(this.method::getDeclaringClass);\n\t}\n\n\t@Override\n\tpublic Optional<Object> getTarget() {\n\t\treturn this.target;\n\t}\n\n\t@Override\n\tpublic Method getExecutable() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic List<Object> getArguments() {\n\t\treturn unmodifiableList(Arrays.asList(this.arguments));\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"unchecked\", \"NullAway\" })\n\tpublic T proceed() {\n\t\tvar actualTarget = this.target.orElse(null);\n\t\treturn (T) MethodReflectionUtils.invoke(this.method, actualTarget, this.arguments);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/NamespaceAwareStore.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.ExtensionContextException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStoreException;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class NamespaceAwareStore implements Store {\n\n\tprivate final NamespacedHierarchicalStore<Namespace> valuesStore;\n\tprivate final Namespace namespace;\n\n\tpublic NamespaceAwareStore(NamespacedHierarchicalStore<Namespace> valuesStore, Namespace namespace) {\n\t\tthis.valuesStore = valuesStore;\n\t\tthis.namespace = namespace;\n\t}\n\n\t@Override\n\tpublic @Nullable Object get(Object key) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tSupplier<@Nullable Object> action = () -> this.valuesStore.get(this.namespace, key);\n\t\treturn this.<@Nullable Object> accessStore(action);\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T get(Object key, Class<T> requiredType) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tSupplier<@Nullable T> action = () -> this.valuesStore.get(this.namespace, key, requiredType);\n\t\treturn this.<@Nullable T> accessStore(action);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Override\n\tpublic <K, V extends @Nullable Object> @Nullable Object getOrComputeIfAbsent(K key,\n\t\t\tFunction<? super K, ? extends V> defaultCreator) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator function must not be null\");\n\t\tSupplier<@Nullable Object> action = () -> this.valuesStore.getOrComputeIfAbsent(this.namespace, key,\n\t\t\tdefaultCreator);\n\t\treturn this.<@Nullable Object> accessStore(action);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Override\n\tpublic <K, V extends @Nullable Object> @Nullable V getOrComputeIfAbsent(K key,\n\t\t\tFunction<? super K, ? extends V> defaultCreator, Class<V> requiredType) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator function must not be null\");\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tSupplier<@Nullable V> action = () -> this.valuesStore.getOrComputeIfAbsent(this.namespace, key, defaultCreator,\n\t\t\trequiredType);\n\t\treturn this.<@Nullable V> accessStore(action);\n\t}\n\n\t@Override\n\tpublic <K, V> Object computeIfAbsent(K key, Function<? super K, ? extends V> defaultCreator) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator function must not be null\");\n\t\tSupplier<Object> action = () -> this.valuesStore.computeIfAbsent(this.namespace, key, defaultCreator);\n\t\treturn accessStore(action);\n\t}\n\n\t@Override\n\tpublic <K, V> V computeIfAbsent(K key, Function<? super K, ? extends V> defaultCreator, Class<V> requiredType) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator function must not be null\");\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tSupplier<V> action = () -> this.valuesStore.computeIfAbsent(this.namespace, key, defaultCreator, requiredType);\n\t\treturn accessStore(action);\n\t}\n\n\t@Override\n\tpublic void put(Object key, @Nullable Object value) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tSupplier<@Nullable Object> action = () -> this.valuesStore.put(this.namespace, key, value);\n\t\tthis.<@Nullable Object> accessStore(action);\n\t}\n\n\t@Override\n\tpublic @Nullable Object remove(Object key) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tSupplier<@Nullable Object> action = () -> this.valuesStore.remove(this.namespace, key);\n\t\treturn this.<@Nullable Object> accessStore(action);\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T remove(Object key, Class<T> requiredType) {\n\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tSupplier<@Nullable T> action = () -> this.valuesStore.remove(this.namespace, key, requiredType);\n\t\treturn this.<@Nullable T> accessStore(action);\n\t}\n\n\tprivate <T extends @Nullable Object> T accessStore(Supplier<T> action) {\n\t\ttry {\n\t\t\treturn action.get();\n\t\t}\n\t\tcatch (NamespacedHierarchicalStoreException e) {\n\t\t\tthrow new ExtensionContextException(e.getMessage(), e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/ParameterResolutionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.getKotlinSuspendingFunctionParameters;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinSuspendingFunction;\nimport static org.junit.platform.commons.util.ReflectionUtils.isAssignableTo;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * {@code ParameterResolutionUtils} provides support for dynamic resolution\n * of executable parameters via {@link ParameterResolver ParameterResolvers}.\n *\n * @since 5.9\n */\n@API(status = INTERNAL, since = \"5.9\")\npublic class ParameterResolutionUtils {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ParameterResolutionUtils.class);\n\n\t/**\n\t * Resolve the array of parameters for the supplied method and target.\n\t *\n\t * @param method the method for which to resolve parameters\n\t * @param target an {@code Optional} containing the target on which the\n\t * executable will be invoked; never {@code null} but should be empty for\n\t * static methods and constructors\n\t * @param extensionContext the current {@code ExtensionContext}\n\t * @param extensionRegistry the {@code ExtensionRegistry} to retrieve\n\t * {@code ParameterResolvers} from\n\t * @return the array of Objects to be used as parameters in the executable\n\t * invocation; never {@code null} though potentially empty\n\t */\n\tpublic static @Nullable Object[] resolveParameters(Method method, Optional<Object> target,\n\t\t\tExtensionContext extensionContext, ExtensionRegistry extensionRegistry) {\n\n\t\treturn resolveParameters(method, target, Optional.empty(), __ -> extensionContext, extensionRegistry,\n\t\t\tisKotlinSuspendingFunction(method) //\n\t\t\t\t\t? getKotlinSuspendingFunctionParameters(method) //\n\t\t\t\t\t: method.getParameters());\n\t}\n\n\t/**\n\t * Resolve the array of parameters for the supplied executable, target, and\n\t * outer instance.\n\t *\n\t * @param executable the executable for which to resolve parameters\n\t * @param target an {@code Optional} containing the target on which the\n\t * executable will be invoked; never {@code null} but should be empty for\n\t * static methods and constructors\n\t * @param outerInstance the outer instance that will be supplied as the\n\t * first argument to a constructor for an inner class; should be {@code null}\n\t * for methods and constructors for top-level or static classes\n\t * @param extensionContext the current {@code ExtensionContext}\n\t * @param extensionRegistry the {@code ExtensionRegistry} to retrieve\n\t * {@code ParameterResolvers} from\n\t * @return the array of Objects to be used as parameters in the executable\n\t * invocation; never {@code null} though potentially empty\n\t */\n\tpublic static @Nullable Object[] resolveParameters(Executable executable, Optional<Object> target,\n\t\t\tOptional<Object> outerInstance, ExtensionContext extensionContext, ExtensionRegistry extensionRegistry) {\n\t\treturn resolveParameters(executable, target, outerInstance, __ -> extensionContext, extensionRegistry);\n\t}\n\n\tpublic static @Nullable Object[] resolveParameters(Executable executable, Optional<Object> target,\n\t\t\tOptional<Object> outerInstance, ExtensionContextSupplier extensionContext,\n\t\t\tExtensionRegistry extensionRegistry) {\n\n\t\treturn resolveParameters(executable, target, outerInstance, extensionContext, extensionRegistry,\n\t\t\texecutable.getParameters());\n\t}\n\n\tprivate static @Nullable Object[] resolveParameters(Executable executable, Optional<Object> target,\n\t\t\tOptional<Object> outerInstance, ExtensionContextSupplier extensionContext,\n\t\t\tExtensionRegistry extensionRegistry, Parameter[] parameters) {\n\n\t\tPreconditions.notNull(target, \"target must not be null\");\n\n\t\t@Nullable\n\t\tObject[] values = new Object[parameters.length];\n\t\tint start = 0;\n\n\t\t// Ensure that the outer instance is resolved as the first parameter if\n\t\t// the executable is a constructor for an inner class.\n\t\tif (outerInstance.isPresent()) {\n\t\t\tvalues[0] = outerInstance.get();\n\t\t\tstart = 1;\n\t\t}\n\n\t\t// Resolve remaining parameters dynamically\n\t\tfor (int i = start; i < parameters.length; i++) {\n\t\t\tParameterContext parameterContext = new DefaultParameterContext(parameters[i], i, target);\n\t\t\tvalues[i] = resolveParameter(parameterContext, executable, extensionContext, extensionRegistry);\n\t\t}\n\t\treturn values;\n\t}\n\n\tprivate static @Nullable Object resolveParameter(ParameterContext parameterContext, Executable executable,\n\t\t\tExtensionContextSupplier extensionContext, ExtensionRegistry extensionRegistry) {\n\n\t\ttry {\n\t\t\t// @formatter:off\n\t\t\tList<ParameterResolver> matchingResolvers = extensionRegistry.stream(ParameterResolver.class)\n\t\t\t\t\t.filter(resolver -> resolver.supportsParameter(parameterContext, extensionContext.get(resolver)))\n\t\t\t\t\t.toList();\n\t\t\t// @formatter:on\n\n\t\t\tif (matchingResolvers.isEmpty()) {\n\t\t\t\tthrow new ParameterResolutionException(\n\t\t\t\t\t\"No ParameterResolver registered for parameter [%s] in %s [%s].\".formatted(\n\t\t\t\t\t\tparameterContext.getParameter(), asLabel(executable), executable.toGenericString()));\n\t\t\t}\n\n\t\t\tif (matchingResolvers.size() > 1) {\n\t\t\t\t// @formatter:off\n\t\t\t\tString resolvers = matchingResolvers.stream()\n\t\t\t\t\t\t.map(StringUtils::defaultToString)\n\t\t\t\t\t\t.collect(joining(\", \"));\n\t\t\t\t// @formatter:on\n\t\t\t\tthrow new ParameterResolutionException(\n\t\t\t\t\t\"Discovered multiple competing ParameterResolvers for parameter [%s] in %s [%s]: %s\".formatted(\n\t\t\t\t\t\tparameterContext.getParameter(), asLabel(executable), executable.toGenericString(), resolvers));\n\t\t\t}\n\n\t\t\tParameterResolver resolver = matchingResolvers.get(0);\n\t\t\tObject value = resolver.resolveParameter(parameterContext, extensionContext.get(resolver));\n\t\t\tvalidateResolvedType(parameterContext.getParameter(), value, executable, resolver);\n\n\t\t\tlogger.trace(\n\t\t\t\t() -> \"ParameterResolver [%s] resolved a value of type [%s] for parameter [%s] in %s [%s].\".formatted(\n\t\t\t\t\tresolver.getClass().getName(), (value != null ? value.getClass().getTypeName() : null),\n\t\t\t\t\tparameterContext.getParameter(), asLabel(executable), executable.toGenericString()));\n\n\t\t\treturn value;\n\t\t}\n\t\tcatch (ParameterResolutionException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\n\t\t\tString message = \"Failed to resolve parameter [%s] in %s [%s]\".formatted(parameterContext.getParameter(),\n\t\t\t\tasLabel(executable), executable.toGenericString());\n\n\t\t\tif (StringUtils.isNotBlank(throwable.getMessage())) {\n\t\t\t\tmessage += \": \" + throwable.getMessage();\n\t\t\t}\n\n\t\t\tthrow new ParameterResolutionException(message, throwable);\n\t\t}\n\t}\n\n\tprivate static void validateResolvedType(Parameter parameter, @Nullable Object value, Executable executable,\n\t\t\tParameterResolver resolver) {\n\n\t\tClass<?> type = parameter.getType();\n\n\t\t// Note: null is permissible as a resolved value but only for non-primitive types.\n\t\tif (!isAssignableTo(value, type)) {\n\t\t\tString message;\n\t\t\tif (value == null && type.isPrimitive()) {\n\t\t\t\tmessage = \"\"\"\n\t\t\t\t\t\tParameterResolver [%s] resolved a null value for parameter [%s] \\\n\t\t\t\t\t\tin %s [%s], but a primitive of type [%s] is required.\"\"\".formatted(\n\t\t\t\t\tresolver.getClass().getName(), parameter, asLabel(executable), executable.toGenericString(),\n\t\t\t\t\ttype.getName());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tmessage = \"\"\"\n\t\t\t\t\t\tParameterResolver [%s] resolved a value of type [%s] for parameter [%s] \\\n\t\t\t\t\t\tin %s [%s], but a value assignment compatible with [%s] is required.\"\"\".formatted(\n\t\t\t\t\tresolver.getClass().getName(), (value != null ? value.getClass().getTypeName() : null), parameter,\n\t\t\t\t\tasLabel(executable), executable.toGenericString(), type.getTypeName());\n\t\t\t}\n\n\t\t\tthrow new ParameterResolutionException(message);\n\t\t}\n\t}\n\n\tprivate static String asLabel(Executable executable) {\n\t\treturn executable instanceof Constructor ? \"constructor\" : \"method\";\n\t}\n\n\tprivate ParameterResolutionUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/TestInstancesProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\n\n/**\n * @since 5.0\n */\n@FunctionalInterface\n@API(status = INTERNAL, since = \"5.0\")\npublic interface TestInstancesProvider {\n\n\tdefault TestInstances getTestInstances(JupiterEngineExecutionContext context) {\n\t\treturn getTestInstances(context.getExtensionRegistry(), context);\n\t}\n\n\tTestInstances getTestInstances(ExtensionRegistry extensionRegistry, JupiterEngineExecutionContext executionContext);\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/execution/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal classes for test execution within the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.execution;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/AutoCloseExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.BOTTOM_UP;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AutoClose;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * {@code AutoCloseExtension} is a JUnit Jupiter extension that closes resources\n * if a field in a test class is annotated with {@link AutoClose @AutoClose}.\n *\n * <p>Consult the Javadoc for {@code @AutoClose} for details on the contract.\n *\n * @since 5.11\n * @see AutoClose\n */\nclass AutoCloseExtension implements TestInstancePreDestroyCallback, AfterAllCallback {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(AutoCloseExtension.class);\n\n\t@Override\n\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\tThrowableCollector throwableCollector = new ThrowableCollector(__ -> false);\n\t\tTestInstancePreDestroyCallback.preDestroyTestInstances(context,\n\t\t\ttestInstance -> closeFields(testInstance.getClass(), testInstance, throwableCollector));\n\t\tthrowableCollector.assertEmpty();\n\t}\n\n\t@Override\n\tpublic void afterAll(ExtensionContext context) {\n\t\tThrowableCollector throwableCollector = new ThrowableCollector(__ -> false);\n\t\tcloseFields(context.getRequiredTestClass(), null, throwableCollector);\n\t\tthrowableCollector.assertEmpty();\n\t}\n\n\tprivate static void closeFields(Class<?> testClass, @Nullable Object testInstance,\n\t\t\tThrowableCollector throwableCollector) {\n\n\t\tPredicate<Field> predicate = (testInstance == null ? ModifierSupport::isStatic : ModifierSupport::isNotStatic);\n\t\tAnnotationSupport.findAnnotatedFields(testClass, AutoClose.class, predicate, BOTTOM_UP)//\n\t\t\t\t.forEach(field -> throwableCollector.execute(() -> closeField(field, testInstance)));\n\t}\n\n\tprivate static void closeField(Field field, @Nullable Object testInstance) throws Exception {\n\t\tString methodName = AnnotationSupport.findAnnotation(field, AutoClose.class).orElseThrow().value();\n\t\tClass<?> fieldType = field.getType();\n\n\t\tcheckCondition(StringUtils.isNotBlank(methodName), \"@AutoClose on field %s must specify a method name.\", field);\n\t\tcheckCondition(!fieldType.isPrimitive(), \"@AutoClose is not supported on primitive field %s.\", field);\n\t\tcheckCondition(!fieldType.isArray(), \"@AutoClose is not supported on array field %s.\", field);\n\n\t\tObject fieldValue = ReflectionSupport.tryToReadFieldValue(field, testInstance).get();\n\t\tif (fieldValue == null) {\n\t\t\tlogger.warn(() -> \"Cannot @AutoClose field %s because it is null.\".formatted(getQualifiedName(field)));\n\t\t}\n\t\telse {\n\t\t\tinvokeCloseMethod(field, fieldValue, methodName.strip());\n\t\t}\n\t}\n\n\tprivate static void invokeCloseMethod(Field field, Object target, String methodName) throws Exception {\n\t\t// Avoid reflection if we can directly invoke close() via AutoCloseable.\n\t\tif (target instanceof @SuppressWarnings(\"resource\") AutoCloseable closeable && \"close\".equals(methodName)) {\n\t\t\tcloseable.close();\n\t\t\treturn;\n\t\t}\n\n\t\tClass<?> targetType = target.getClass();\n\t\tMethod closeMethod = ReflectionSupport.findMethod(targetType, methodName).orElseThrow(\n\t\t\t() -> new ExtensionConfigurationException(\n\t\t\t\t\"Cannot @AutoClose field %s because %s does not define method %s().\"//\n\t\t\t\t\t\t.formatted(getQualifiedName(field), targetType.getName(), methodName)));\n\n\t\tcloseMethod = ReflectionUtils.getInterfaceMethodIfPossible(closeMethod, targetType);\n\t\tReflectionSupport.invokeMethod(closeMethod, target);\n\t}\n\n\tprivate static void checkCondition(boolean condition, String messageFormat, Field field) {\n\t\tPreconditions.condition(condition, () -> messageFormat.formatted(getQualifiedName(field)));\n\t}\n\n\tprivate static String getQualifiedName(Field field) {\n\t\tString typeName = field.getDeclaringClass().getCanonicalName();\n\t\tif (typeName == null) {\n\t\t\ttypeName = field.getDeclaringClass().getTypeName();\n\t\t}\n\t\treturn typeName + \".\" + field.getName();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultPreInterruptContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport org.junit.jupiter.api.extension.PreInterruptContext;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * @since 5.12\n */\nrecord DefaultPreInterruptContext(Thread threadToInterrupt) implements PreInterruptContext {\n\n\tDefaultPreInterruptContext {\n\t\tPreconditions.notNull(threadToInterrupt, \"threadToInterrupt must not be null\");\n\t}\n\n\t@Override\n\tpublic Thread getThreadToInterrupt() {\n\t\treturn threadToInterrupt;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"threadToInterrupt\", this.threadToInterrupt)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultRepetitionInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.RepetitionInfo;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link RepetitionInfo}.\n */\nrecord DefaultRepetitionInfo(int currentRepetition, int totalRepetitions, AtomicInteger failureCount,\n\t\tint failureThreshold) implements RepetitionInfo {\n\n\t@Override\n\tpublic int getCurrentRepetition() {\n\t\treturn this.currentRepetition;\n\t}\n\n\t@Override\n\tpublic int getTotalRepetitions() {\n\t\treturn this.totalRepetitions;\n\t}\n\n\t@Override\n\tpublic int getFailureCount() {\n\t\treturn this.failureCount.get();\n\t}\n\n\t@Override\n\tpublic int getFailureThreshold() {\n\t\treturn this.failureThreshold;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"currentRepetition\", this.currentRepetition)\n\t\t\t\t.append(\"totalRepetitions\", this.totalRepetitions)\n\t\t\t\t.append(\"failureCount\", this.failureCount)\n\t\t\t\t.append(\"failureThreshold\", this.failureThreshold)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DefaultTestReporter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.nio.file.Path;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\n\n/**\n * @since 5.12\n */\nclass DefaultTestReporter implements TestReporter {\n\n\tprivate final ExtensionContext extensionContext;\n\n\tDefaultTestReporter(ExtensionContext extensionContext) {\n\t\tthis.extensionContext = extensionContext;\n\t}\n\n\t@Override\n\tpublic void publishEntry(Map<String, String> map) {\n\t\tthis.extensionContext.publishReportEntry(map);\n\t}\n\n\t@Override\n\tpublic void publishFile(String name, MediaType mediaType, ThrowingConsumer<Path> action) {\n\t\tthis.extensionContext.publishFile(name, mediaType, action);\n\t}\n\n\t@Override\n\tpublic void publishDirectory(String name, ThrowingConsumer<Path> action) {\n\t\tthis.extensionContext.publishDirectory(name, action);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/DisabledCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.reflect.AnnotatedElement;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * {@link ExecutionCondition} that supports the {@code @Disabled} annotation.\n *\n * @since 5.0\n * @see Disabled\n * @see #evaluateExecutionCondition(ExtensionContext)\n */\nclass DisabledCondition implements ExecutionCondition {\n\n\tprivate static final ConditionEvaluationResult ENABLED = ConditionEvaluationResult.enabled(\n\t\t\"@Disabled is not present\");\n\n\t/**\n\t * Containers/tests are disabled if {@code @Disabled} is present on the test\n\t * class or method.\n\t */\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tAnnotatedElement element = context.getElement().orElse(null);\n\t\treturn findAnnotation(element, Disabled.class) //\n\t\t\t\t.map(annotation -> toResult(element, annotation)) //\n\t\t\t\t.orElse(ENABLED);\n\t}\n\n\tprivate ConditionEvaluationResult toResult(@Nullable AnnotatedElement element, Disabled annotation) {\n\t\tString value = annotation.value();\n\t\tString reason = StringUtils.isNotBlank(value) ? value : element + \" is @Disabled\";\n\t\treturn ConditionEvaluationResult.disabled(reason);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionContextInternal.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * {@code ExtensionContextInternal} extends the {@link ExtensionContext} with internal API.\n *\n * @since 5.12\n * @see ExtensionContext\n */\n@API(status = INTERNAL, since = \"5.12\")\npublic interface ExtensionContextInternal extends ExtensionContext {\n\n\t/**\n\t * Returns a list of registered extension at this context of the passed {@code extensionType}.\n\t *\n\t * @param <E> the extension type\n\t * @param extensionType the extension type\n\t * @return the list of extensions\n\t */\n\t<E extends Extension> List<E> getExtensions(Class<E> extensionType);\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistrar.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.reflect.Field;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n/**\n * An {@code ExtensionRegistrar} is used to register extensions.\n *\n * @since 5.5\n */\n@API(status = INTERNAL, since = \"5.5\")\npublic interface ExtensionRegistrar {\n\n\t/**\n\t * Instantiate an extension of the given type using its default constructor\n\t * and register it in the registry.\n\t *\n\t * <p>A new {@link Extension} should not be registered if an extension of the\n\t * given type already exists in the registry or a parent registry.\n\t *\n\t * @param extensionType the type of extension to register\n\t * @since 5.8\n\t */\n\tvoid registerExtension(Class<? extends Extension> extensionType);\n\n\t/**\n\t * Register the supplied {@link Extension}, without checking if an extension\n\t * of that type has already been registered.\n\t *\n\t * <h4>Semantics for Source</h4>\n\t *\n\t * <p>If an extension is registered <em>declaratively</em> via\n\t * {@link org.junit.jupiter.api.extension.ExtendWith @ExtendWith}, the\n\t * {@code source} and the {@code extension} should be the same object.\n\t * However, if an extension is registered <em>programmatically</em> via\n\t * {@link RegisterExtension @RegisterExtension}, the {@code source} object\n\t * should be the {@link java.lang.reflect.Field} that is annotated with\n\t * {@code @RegisterExtension}. Similarly, if an extension is registered\n\t * <em>programmatically</em> as a lambda expression or method reference, the\n\t * {@code source} object should be the underlying\n\t * {@link java.lang.reflect.Method} that implements the extension API.\n\t *\n\t * @param extension the extension to register; never {@code null}\n\t * @param source the source of the extension; never {@code null}\n\t */\n\tvoid registerExtension(Extension extension, Object source);\n\n\t/**\n\t * Register the supplied {@link Extension} as a <em>synthetic</em> extension,\n\t * without checking if an extension of that type has already been registered.\n\t *\n\t * @param extension the extension to register; never {@code null}\n\t * @param source the source of the extension; never {@code null}\n\t * @since 5.8\n\t * @see #registerExtension(Extension, Object)\n\t */\n\tvoid registerSyntheticExtension(Extension extension, Object source);\n\n\t/**\n\t * Register an uninitialized extension for the supplied {@code testClass} to\n\t * be initialized using the supplied {@code initializer} when an instance of\n\t * the test class is created.\n\t *\n\t * <p>Uninitialized extensions are typically registered for fields annotated\n\t * with {@link RegisterExtension @RegisterExtension} that cannot be\n\t * initialized until an instance of the test class is created. Until they\n\t * are initialized, such extensions are not available for use.\n\t *\n\t * @param testClass the test class for which the extension is registered;\n\t * never {@code null}\n\t * @param source the source of the extension; never {@code null}\n\t * @param initializer the initializer function to be used to create the\n\t * extension; never {@code null}\n\t */\n\tvoid registerUninitializedExtension(Class<?> testClass, Field source,\n\t\t\tFunction<Object, ? extends Extension> initializer);\n\n\t/**\n\t * Initialize all registered extensions for the supplied {@code testClass}\n\t * using the supplied {@code testInstance}.\n\t *\n\t * @param testClass the test class for which the extensions are initialized;\n\t * never {@code null}\n\t * @param testInstance the test instance to be used to initialize the\n\t * extensions; never {@code null}\n\t */\n\tvoid initializeExtensions(Class<?> testClass, Object testInstance);\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/ExtensionRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.stream.Collectors.toCollection;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.Extension;\n\n/**\n * An {@code ExtensionRegistry} holds all registered extensions (i.e.\n * instances of {@link Extension}) for a given\n * {@link org.junit.platform.engine.support.hierarchical.Node}.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic interface ExtensionRegistry {\n\n\t/**\n\t * Stream all {@code Extensions} of the specified type that are present\n\t * in this registry or one of its ancestors.\n\t *\n\t * @param extensionType the type of {@link Extension} to stream\n\t * @see #getExtensions(Class)\n\t */\n\t<E extends Extension> Stream<E> stream(Class<E> extensionType);\n\n\t/**\n\t * Get all {@code Extensions} of the specified type that are present\n\t * in this registry or one of its ancestors.\n\t *\n\t * @param extensionType the type of {@link Extension} to get\n\t * @see #stream(Class)\n\t */\n\tdefault <E extends Extension> List<E> getExtensions(Class<E> extensionType) {\n\t\treturn stream(extensionType).collect(toCollection(ArrayList::new));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/MutableExtensionRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptySet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.ServiceLoader;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ServiceLoaderUtils;\n\n/**\n * Default, mutable implementation of {@link ExtensionRegistry}.\n *\n * @since 5.5\n */\n@API(status = INTERNAL, since = \"5.5\")\npublic class MutableExtensionRegistry implements ExtensionRegistry, ExtensionRegistrar {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(MutableExtensionRegistry.class);\n\n\tprivate static final List<Extension> DEFAULT_STATELESS_EXTENSIONS = List.of( //\n\t\tnew DisabledCondition(), //\n\t\tnew AutoCloseExtension(), //\n\t\tnew TimeoutExtension(), //\n\t\tnew RepeatedTestExtension(), //\n\t\tnew TestInfoParameterResolver(), //\n\t\tnew TestReporterParameterResolver() //\n\t);\n\n\t/**\n\t * Factory for creating and populating a new root registry with the default\n\t * extensions.\n\t *\n\t * <p>If the {@link Constants#EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME}\n\t * configuration parameter has been set to {@code true}, extensions will be\n\t * auto-detected using Java's {@link ServiceLoader} mechanism and automatically\n\t * registered after the default extensions.\n\t *\n\t * <p>If the\n\t * {@value Constants#EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME}\n\t * configuration parameter has been set to {@code true}, the\n\t * {@link PreInterruptThreadDumpPrinter} will be registered.\n\t *\n\t * @param configuration configuration parameters used to retrieve the extension\n\t * auto-detection flag; never {@code null}\n\t * @return a new {@code ExtensionRegistry}; never {@code null}\n\t */\n\tpublic static MutableExtensionRegistry createRegistryWithDefaultExtensions(JupiterConfiguration configuration) {\n\t\tMutableExtensionRegistry extensionRegistry = new MutableExtensionRegistry();\n\n\t\tDEFAULT_STATELESS_EXTENSIONS.forEach(extensionRegistry::registerDefaultExtension);\n\n\t\textensionRegistry.registerDefaultExtension(new TempDirectory(configuration));\n\n\t\tif (configuration.isExtensionAutoDetectionEnabled()) {\n\t\t\tregisterAutoDetectedExtensions(extensionRegistry, configuration);\n\t\t}\n\n\t\tif (configuration.isThreadDumpOnTimeoutEnabled()) {\n\t\t\textensionRegistry.registerDefaultExtension(new PreInterruptThreadDumpPrinter());\n\t\t}\n\n\t\treturn extensionRegistry;\n\t}\n\n\tprivate static void registerAutoDetectedExtensions(MutableExtensionRegistry extensionRegistry,\n\t\t\tJupiterConfiguration configuration) {\n\n\t\tPredicate<Class<? extends Extension>> filter = configuration.getFilterForAutoDetectedExtensions();\n\t\tList<Class<? extends Extension>> excludedExtensions = new ArrayList<>();\n\n\t\tServiceLoader<Extension> serviceLoader = ServiceLoader.load(Extension.class,\n\t\t\tClassLoaderUtils.getDefaultClassLoader());\n\t\tServiceLoaderUtils.filter(serviceLoader, clazz -> {\n\t\t\tboolean included = filter.test(clazz);\n\t\t\tif (!included) {\n\t\t\t\texcludedExtensions.add(clazz);\n\t\t\t}\n\t\t\treturn included;\n\t\t}) //\n\t\t\t\t.forEach(extensionRegistry::registerAutoDetectedExtension);\n\n\t\tlogExcludedExtensions(excludedExtensions);\n\t}\n\n\tprivate static void logExcludedExtensions(List<Class<? extends Extension>> excludedExtensions) {\n\t\tif (!excludedExtensions.isEmpty()) {\n\t\t\t// @formatter:off\n\t\t\tList<String> excludeExtensionNames = excludedExtensions\n\t\t\t\t\t.stream()\n\t\t\t\t\t.map(Class::getName)\n\t\t\t\t\t.toList();\n\t\t\t// @formatter:on\n\t\t\tlogger.config(() -> \"Excluded auto-detected extensions due to configured includes/excludes: %s\".formatted(\n\t\t\t\texcludeExtensionNames));\n\t\t}\n\t}\n\n\t/**\n\t * Factory for creating and populating a new registry from a list of\n\t * extension types and a parent registry.\n\t *\n\t * @param parentRegistry the parent registry\n\t * @param extensionTypes the types of extensions to be registered in\n\t * the new registry\n\t * @return a new {@code ExtensionRegistry}; never {@code null}\n\t */\n\tpublic static MutableExtensionRegistry createRegistryFrom(MutableExtensionRegistry parentRegistry,\n\t\t\tStream<Class<? extends Extension>> extensionTypes) {\n\n\t\tPreconditions.notNull(parentRegistry, \"parentRegistry must not be null\");\n\n\t\tMutableExtensionRegistry registry = new MutableExtensionRegistry(parentRegistry);\n\t\textensionTypes.forEach(registry::registerExtension);\n\t\treturn registry;\n\t}\n\n\tprivate final Set<Class<? extends Extension>> registeredExtensionTypes;\n\tprivate final List<Entry> registeredExtensions;\n\tprivate final Map<Class<?>, LateInitExtensions> lateInitExtensions;\n\n\tprivate MutableExtensionRegistry() {\n\t\tthis(emptySet(), emptyList());\n\t}\n\n\tprivate MutableExtensionRegistry(MutableExtensionRegistry parent) {\n\t\tthis(parent.registeredExtensionTypes, parent.registeredExtensions);\n\t}\n\n\tprivate MutableExtensionRegistry(Set<Class<? extends Extension>> registeredExtensionTypes,\n\t\t\tList<Entry> registeredExtensions) {\n\t\tthis.registeredExtensionTypes = new LinkedHashSet<>(registeredExtensionTypes);\n\t\tthis.registeredExtensions = new ArrayList<>(registeredExtensions.size());\n\t\tthis.lateInitExtensions = new LinkedHashMap<>();\n\t\tregisteredExtensions.forEach(entry -> {\n\t\t\tEntry newEntry = entry;\n\t\t\tif (entry instanceof LateInitEntry lateInitEntry) {\n\t\t\t\tnewEntry = lateInitEntry.getExtension() //\n\t\t\t\t\t\t.map(Entry::of) //\n\t\t\t\t\t\t.orElseGet(() -> getLateInitExtensions(lateInitEntry.getTestClass()).add(lateInitEntry.copy()));\n\t\t\t}\n\t\t\tthis.registeredExtensions.add(newEntry);\n\t\t});\n\t}\n\n\t@Override\n\tpublic <E extends Extension> Stream<E> stream(Class<E> extensionType) {\n\t\treturn this.registeredExtensions.stream() //\n\t\t\t\t.map(p -> p.getExtension().orElse(null)) //\n\t\t\t\t.filter(extensionType::isInstance) //\n\t\t\t\t.map(extensionType::cast);\n\t}\n\n\t@Override\n\tpublic void registerExtension(Class<? extends Extension> extensionType) {\n\t\tif (!isAlreadyRegistered(extensionType)) {\n\t\t\tregisterLocalExtension(ReflectionSupport.newInstance(extensionType));\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the supplied type is already registered in this registry or in a\n\t * parent registry.\n\t */\n\tprivate boolean isAlreadyRegistered(Class<? extends Extension> extensionType) {\n\t\treturn this.registeredExtensionTypes.contains(extensionType);\n\t}\n\n\t@Override\n\tpublic void registerExtension(Extension extension, Object source) {\n\t\tPreconditions.notNull(source, \"source must not be null\");\n\t\tregisterExtension(\"local\", extension, source);\n\t}\n\n\t@Override\n\tpublic void registerSyntheticExtension(Extension extension, Object source) {\n\t\tregisterExtension(\"synthetic\", extension, source);\n\t}\n\n\t@Override\n\tpublic void registerUninitializedExtension(Class<?> testClass, Field source,\n\t\t\tFunction<Object, ? extends Extension> initializer) {\n\t\tPreconditions.notNull(testClass, \"testClass must not be null\");\n\t\tPreconditions.notNull(source, \"source must not be null\");\n\t\tPreconditions.notNull(initializer, \"initializer must not be null\");\n\n\t\tlogger.trace(() -> \"Registering local extension (late-init) for [%s]%s\".formatted(source.getType().getName(),\n\t\t\tbuildSourceInfo(source)));\n\n\t\tLateInitEntry entry = getLateInitExtensions(testClass) //\n\t\t\t\t.add(new LateInitEntry(testClass, initializer));\n\t\tthis.registeredExtensions.add(entry);\n\t}\n\n\t@Override\n\tpublic void initializeExtensions(Class<?> testClass, Object testInstance) {\n\t\tPreconditions.notNull(testClass, \"testClass must not be null\");\n\t\tPreconditions.notNull(testInstance, \"testInstance must not be null\");\n\n\t\tLateInitExtensions extensions = lateInitExtensions.remove(testClass);\n\t\tif (extensions != null) {\n\t\t\textensions.initialize(testInstance);\n\t\t}\n\t}\n\n\tprivate LateInitExtensions getLateInitExtensions(Class<?> testClass) {\n\t\treturn this.lateInitExtensions.computeIfAbsent(testClass, __ -> new LateInitExtensions());\n\t}\n\n\tprivate void registerDefaultExtension(Extension extension) {\n\t\tregisterExtension(\"default\", extension);\n\t}\n\n\tprivate void registerAutoDetectedExtension(Extension extension) {\n\t\tregisterExtension(\"auto-detected\", extension);\n\t}\n\n\tprivate void registerLocalExtension(Extension extension) {\n\t\tregisterExtension(\"local\", extension);\n\t}\n\n\tprivate void registerExtension(String category, Extension extension) {\n\t\tregisterExtension(category, extension, null);\n\t}\n\n\tprivate void registerExtension(String category, Extension extension, @Nullable Object source) {\n\t\tPreconditions.notBlank(category, \"category must not be null or blank\");\n\t\tPreconditions.notNull(extension, \"extension must not be null\");\n\n\t\tlogger.trace(() -> \"Registering %s extension [%s]%s\".formatted(category, extension, buildSourceInfo(source)));\n\n\t\tthis.registeredExtensions.add(Entry.of(extension));\n\t\tthis.registeredExtensionTypes.add(extension.getClass());\n\t}\n\n\tprivate String buildSourceInfo(@Nullable Object source) {\n\t\tif (source == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (source instanceof Member member) {\n\t\t\tObject type = (member instanceof Method ? \"method\" : \"field\");\n\t\t\tsource = \"%s %s.%s\".formatted(type, member.getDeclaringClass().getName(), member.getName());\n\t\t}\n\t\treturn \" from source [\" + source + \"]\";\n\t}\n\n\tprivate interface Entry {\n\n\t\tstatic Entry of(Extension extension) {\n\t\t\tOptional<Extension> value = Optional.of(extension);\n\t\t\treturn () -> value;\n\t\t}\n\n\t\tOptional<Extension> getExtension();\n\t}\n\n\tprivate static class LateInitEntry implements Entry {\n\n\t\tprivate final Class<?> testClass;\n\t\tprivate final Function<Object, ? extends Extension> initializer;\n\n\t\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n\t\tprivate Optional<Extension> extension = Optional.empty();\n\n\t\tLateInitEntry(Class<?> testClass, Function<Object, ? extends Extension> initializer) {\n\t\t\tthis.testClass = testClass;\n\t\t\tthis.initializer = initializer;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Extension> getExtension() {\n\t\t\treturn extension;\n\t\t}\n\n\t\tprivate Class<?> getTestClass() {\n\t\t\treturn testClass;\n\t\t}\n\n\t\tvoid initialize(Object testInstance) {\n\t\t\tPreconditions.condition(extension.isEmpty(), \"Extension already initialized\");\n\t\t\textension = Optional.of(initializer.apply(testInstance));\n\t\t}\n\n\t\tLateInitEntry copy() {\n\t\t\tPreconditions.condition(extension.isEmpty(), \"Extension already initialized\");\n\t\t\treturn new LateInitEntry(testClass, initializer);\n\t\t}\n\t}\n\n\tprivate static class LateInitExtensions {\n\n\t\tprivate final List<LateInitEntry> entries = new ArrayList<>();\n\n\t\tLateInitEntry add(LateInitEntry entry) {\n\t\t\tentries.add(entry);\n\t\t\treturn entry;\n\t\t}\n\n\t\tvoid initialize(Object testInstance) {\n\t\t\tentries.forEach(entry -> entry.initialize(testInstance));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/PreInterruptCallbackInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.function.Consumer;\n\n/**\n * @since 5.12\n */\n@FunctionalInterface\ninterface PreInterruptCallbackInvocation {\n\tPreInterruptCallbackInvocation NOOP = (t, e) -> {\n\t};\n\n\tvoid executePreInterruptCallback(Thread threadToInterrupt, Consumer<Throwable> errorHandler);\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/PreInterruptCallbackInvocationFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.extension.PreInterruptCallback;\nimport org.junit.jupiter.api.extension.PreInterruptContext;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * @since 5.12\n * @see PreInterruptCallbackInvocation\n */\nfinal class PreInterruptCallbackInvocationFactory {\n\n\tprivate PreInterruptCallbackInvocationFactory() {\n\t}\n\n\tstatic PreInterruptCallbackInvocation create(ExtensionContextInternal extensionContext) {\n\t\tfinal List<PreInterruptCallback> callbacks = extensionContext.getExtensions(PreInterruptCallback.class);\n\t\tif (callbacks.isEmpty()) {\n\t\t\treturn PreInterruptCallbackInvocation.NOOP;\n\t\t}\n\t\treturn (thread, errorHandler) -> {\n\t\t\tPreInterruptContext preInterruptContext = new DefaultPreInterruptContext(thread);\n\t\t\tfor (PreInterruptCallback callback : callbacks) {\n\t\t\t\ttry {\n\t\t\t\t\tcallback.beforeThreadInterrupt(preInterruptContext, extensionContext);\n\t\t\t\t}\n\t\t\t\tcatch (Throwable ex) {\n\t\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(ex);\n\t\t\t\t\terrorHandler.accept(ex);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/PreInterruptThreadDumpPrinter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.PreInterruptCallback;\nimport org.junit.jupiter.api.extension.PreInterruptContext;\n\n/**\n * Default implementation of {@link PreInterruptCallback}, which prints the stacks\n * of all {@link Thread}s to {@code System.out}.\n *\n * <p>Note: This is disabled by default and must be enabled via\n * {@link Constants#EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME}.\n *\n * @since 5.12\n */\nfinal class PreInterruptThreadDumpPrinter implements PreInterruptCallback {\n\n\tprivate static final String NL = \"\\n\";\n\n\t@Override\n\tpublic void beforeThreadInterrupt(PreInterruptContext preInterruptContext, ExtensionContext extensionContext) {\n\t\tMap<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();\n\n\t\tStringBuilder sb = new StringBuilder(\"Thread \");\n\t\tappendThreadName(sb, preInterruptContext.getThreadToInterrupt());\n\t\tsb.append(\" will be interrupted.\");\n\t\tsb.append(NL);\n\n\t\tfor (Map.Entry<Thread, StackTraceElement[]> entry : stackTraces.entrySet()) {\n\t\t\tThread thread = entry.getKey();\n\t\t\tStackTraceElement[] stack = entry.getValue();\n\t\t\tif (stack.length > 0) {\n\t\t\t\tsb.append(NL);\n\t\t\t\tappendThreadName(sb, thread);\n\t\t\t\tfor (StackTraceElement stackTraceElement : stack) {\n\t\t\t\t\tsb.append(NL);\n\t\t\t\t\t// Use the same prefix as java.lang.Throwable.printStackTrace(PrintStreamOrWriter)\n\t\t\t\t\tsb.append(\"\\tat \");\n\t\t\t\t\tsb.append(stackTraceElement);\n\t\t\t\t}\n\t\t\t\tsb.append(NL);\n\t\t\t}\n\t\t}\n\n\t\tSystem.out.println(sb);\n\t}\n\n\t/**\n\t * Append the {@link Thread} name and ID in a similar fashion as {@code jstack}.\n\t * @param builder the builder to append to\n\t * @param thread the thread whose information should be appended\n\t */\n\t@SuppressWarnings(\"deprecation\") // Thread.getId() is deprecated on JDK 19+\n\tprivate static void appendThreadName(StringBuilder builder, Thread thread) {\n\t\t// Use same format as java.lang.management.ThreadInfo.toString()\n\t\tbuilder.append(\"\\\"\");\n\t\tbuilder.append(thread.getName());\n\t\tbuilder.append(\"\\\"\");\n\t\tif (thread.isDaemon()) {\n\t\t\tbuilder.append(\" daemon\");\n\t\t}\n\t\tbuilder.append(\" prio=\");\n\t\tbuilder.append(thread.getPriority());\n\t\tbuilder.append(\" Id=\");\n\t\tbuilder.append(thread.getId());\n\t\tbuilder.append(\" \");\n\t\tbuilder.append(thread.getState());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestDisplayNameFormatter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.RepeatedTest.CURRENT_REPETITION_PLACEHOLDER;\nimport static org.junit.jupiter.api.RepeatedTest.DISPLAY_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.api.RepeatedTest.TOTAL_REPETITIONS_PLACEHOLDER;\n\nimport org.junit.jupiter.api.RepeatedTest;\n\n/**\n * Display name formatter for a {@link RepeatedTest @RepeatedTest}.\n *\n * @since 5.0\n */\nclass RepeatedTestDisplayNameFormatter {\n\n\tprivate final String pattern;\n\tprivate final String displayName;\n\n\tRepeatedTestDisplayNameFormatter(String pattern, String displayName) {\n\t\tthis.pattern = pattern;\n\t\tthis.displayName = displayName;\n\t}\n\n\tString format(int currentRepetition, int totalRepetitions) {\n\t\treturn this.pattern//\n\t\t\t\t.replace(DISPLAY_NAME_PLACEHOLDER, this.displayName)//\n\t\t\t\t.replace(CURRENT_REPETITION_PLACEHOLDER, String.valueOf(currentRepetition))//\n\t\t\t\t.replace(TOTAL_REPETITIONS_PLACEHOLDER, String.valueOf(totalRepetitions));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\n\nimport java.lang.reflect.Method;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code TestTemplateInvocationContextProvider} that supports the\n * {@link RepeatedTest @RepeatedTest} annotation.\n *\n * @since 5.0\n */\nclass RepeatedTestExtension implements TestTemplateInvocationContextProvider {\n\n\t@Override\n\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\treturn isAnnotated(context.getTestMethod(), RepeatedTest.class);\n\t}\n\n\t@Override\n\tpublic Stream<RepeatedTestInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\tMethod testMethod = context.getRequiredTestMethod();\n\t\tString displayName = context.getDisplayName();\n\t\tRepeatedTest repeatedTest = findAnnotation(testMethod, RepeatedTest.class).get();\n\t\tint totalRepetitions = totalRepetitions(repeatedTest, testMethod);\n\t\tAtomicInteger failureCount = new AtomicInteger();\n\t\tint failureThreshold = failureThreshold(repeatedTest, testMethod);\n\t\tRepeatedTestDisplayNameFormatter formatter = displayNameFormatter(repeatedTest, testMethod, displayName);\n\n\t\t// @formatter:off\n\t\treturn IntStream\n\t\t\t\t.rangeClosed(1, totalRepetitions)\n\t\t\t\t.mapToObj(repetition -> new DefaultRepetitionInfo(repetition, totalRepetitions, failureCount, failureThreshold))\n\t\t\t\t.map(repetitionInfo -> new RepeatedTestInvocationContext(repetitionInfo, formatter));\n\t\t// @formatter:on\n\t}\n\n\tprivate int totalRepetitions(RepeatedTest repeatedTest, Method method) {\n\t\tint repetitions = repeatedTest.value();\n\t\tPreconditions.condition(repetitions > 0,\n\t\t\t() -> \"Configuration error: @RepeatedTest on method [%s] must be declared with a positive 'value'.\".formatted(\n\t\t\t\tmethod));\n\t\treturn repetitions;\n\t}\n\n\tprivate int failureThreshold(RepeatedTest repeatedTest, Method method) {\n\t\tint failureThreshold = repeatedTest.failureThreshold();\n\t\tif (failureThreshold != Integer.MAX_VALUE) {\n\t\t\tint repetitions = repeatedTest.value();\n\t\t\tPreconditions.condition((failureThreshold > 0) && (failureThreshold < repetitions),\n\t\t\t\t() -> \"\"\"\n\t\t\t\t\t\tConfiguration error: @RepeatedTest on method [%s] must declare a \\\n\t\t\t\t\t\t'failureThreshold' greater than zero and less than the total number of repetitions [%d].\"\"\".formatted(\n\t\t\t\t\tmethod, repetitions));\n\t\t}\n\t\treturn failureThreshold;\n\t}\n\n\tprivate RepeatedTestDisplayNameFormatter displayNameFormatter(RepeatedTest repeatedTest, Method method,\n\t\t\tString displayName) {\n\t\tString pattern = Preconditions.notBlank(repeatedTest.name().strip(),\n\t\t\t() -> \"Configuration error: @RepeatedTest on method [%s] must be declared with a non-empty name.\".formatted(\n\t\t\t\tmethod));\n\t\treturn new RepeatedTestDisplayNameFormatter(pattern, displayName);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepeatedTestInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\n\n/**\n * {@link TestTemplateInvocationContext} for a {@link org.junit.jupiter.api.RepeatedTest @RepeatedTest}.\n *\n * @since 5.0\n */\nclass RepeatedTestInvocationContext implements TestTemplateInvocationContext {\n\n\tprivate final DefaultRepetitionInfo repetitionInfo;\n\tprivate final RepeatedTestDisplayNameFormatter formatter;\n\n\tRepeatedTestInvocationContext(DefaultRepetitionInfo repetitionInfo, RepeatedTestDisplayNameFormatter formatter) {\n\t\tthis.repetitionInfo = repetitionInfo;\n\t\tthis.formatter = formatter;\n\t}\n\n\t@Override\n\tpublic String getDisplayName(int invocationIndex) {\n\t\treturn this.formatter.format(this.repetitionInfo.currentRepetition(), this.repetitionInfo.totalRepetitions());\n\t}\n\n\t@Override\n\tpublic List<Extension> getAdditionalExtensions() {\n\t\treturn List.of(new RepetitionExtension(this.repetitionInfo));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/RepetitionExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled;\nimport static org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.RepetitionInfo;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestWatcher;\n\n/**\n * {@code RepetitionExtension} implements the following extension APIs to support\n * repetitions of a {@link RepeatedTest @RepeatedTest} method.\n *\n * <ul>\n * <li>{@link ParameterResolver} to resolve {@link RepetitionInfo} arguments</li>\n * <li>{@link TestWatcher} to track the {@linkplain RepetitionInfo#getFailureCount()\n * failure count}</li>\n * <li>{@link ExecutionCondition} to disable the repetition if the\n * {@linkplain RepetitionInfo#getFailureThreshold() failure threshold} has been\n * exceeded</li>\n * </ul>\n *\n * @since 5.0\n */\nclass RepetitionExtension implements ParameterResolver, TestWatcher, ExecutionCondition {\n\n\tprivate final DefaultRepetitionInfo repetitionInfo;\n\n\tRepetitionExtension(DefaultRepetitionInfo repetitionInfo) {\n\t\tthis.repetitionInfo = repetitionInfo;\n\t}\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn (parameterContext.getParameter().getType() == RepetitionInfo.class);\n\t}\n\n\t@Override\n\tpublic RepetitionInfo resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn this.repetitionInfo;\n\t}\n\n\t@Override\n\tpublic void testFailed(ExtensionContext context, @Nullable Throwable cause) {\n\t\tthis.repetitionInfo.failureCount().incrementAndGet();\n\t}\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tint failureThreshold = this.repetitionInfo.getFailureThreshold();\n\t\tif (this.repetitionInfo.getFailureCount() >= failureThreshold) {\n\t\t\treturn disabled(\"Failure threshold [\" + failureThreshold + \"] exceeded\");\n\t\t}\n\t\treturn enabled(\"Failure threshold not exceeded\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * @since 5.5\n */\nclass SameThreadTimeoutInvocation<T extends @Nullable Object> implements Invocation<T> {\n\n\tprivate final Invocation<T> delegate;\n\tprivate final TimeoutDuration timeout;\n\tprivate final ScheduledExecutorService executor;\n\tprivate final Supplier<String> descriptionSupplier;\n\tprivate final PreInterruptCallbackInvocation preInterruptCallback;\n\n\tSameThreadTimeoutInvocation(Invocation<T> delegate, TimeoutDuration timeout, ScheduledExecutorService executor,\n\t\t\tSupplier<String> descriptionSupplier, PreInterruptCallbackInvocation preInterruptCallback) {\n\t\tthis.delegate = delegate;\n\t\tthis.timeout = timeout;\n\t\tthis.executor = executor;\n\t\tthis.descriptionSupplier = descriptionSupplier;\n\t\tthis.preInterruptCallback = preInterruptCallback;\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@Override\n\tpublic T proceed() throws Throwable {\n\t\tInterruptTask interruptTask = new InterruptTask(Thread.currentThread(), preInterruptCallback);\n\t\tScheduledFuture<?> future = executor.schedule(interruptTask, timeout.value(), timeout.unit());\n\t\tThrowable failure = null;\n\t\tT result = null;\n\t\ttry {\n\t\t\tresult = delegate.proceed();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tfailure = t;\n\t\t}\n\t\tfinally {\n\t\t\tboolean cancelled = future.cancel(false);\n\t\t\tif (!cancelled) {\n\t\t\t\tfuture.get();\n\t\t\t}\n\t\t\tif (interruptTask.executed) {\n\t\t\t\tThread.interrupted();\n\t\t\t\tfailure = TimeoutExceptionFactory.create(descriptionSupplier.get(), timeout, failure);\n\t\t\t\tinterruptTask.attachSuppressedExceptions(failure);\n\t\t\t}\n\t\t}\n\t\tif (failure != null) {\n\t\t\tthrow failure;\n\t\t}\n\t\treturn result;\n\t}\n\n\tstatic class InterruptTask implements Runnable {\n\t\tprivate final PreInterruptCallbackInvocation preInterruptCallback;\n\t\tprivate final List<Throwable> exceptionsDuringInterruption = new CopyOnWriteArrayList<>();\n\t\tprivate final Thread thread;\n\t\tprivate volatile boolean executed;\n\n\t\tInterruptTask(Thread thread, PreInterruptCallbackInvocation preInterruptCallback) {\n\t\t\tthis.thread = thread;\n\t\t\tthis.preInterruptCallback = preInterruptCallback;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\texecuted = true;\n\t\t\tpreInterruptCallback.executePreInterruptCallback(thread, exceptionsDuringInterruption::add);\n\t\t\tthread.interrupt();\n\t\t}\n\n\t\tvoid attachSuppressedExceptions(Throwable outerException) {\n\t\t\tfor (Throwable throwable : exceptionsDuringInterruption) {\n\t\t\t\touterException.addSuppressed(throwable);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.timeout.PreemptiveTimeoutUtils.executeWithPreemptiveTimeout;\n\nimport java.util.concurrent.TimeoutException;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\n\n/**\n * @since 5.9\n */\nclass SeparateThreadTimeoutInvocation<T extends @Nullable Object> implements Invocation<T> {\n\n\tprivate final Invocation<T> delegate;\n\tprivate final TimeoutDuration timeout;\n\tprivate final Supplier<String> descriptionSupplier;\n\tprivate final PreInterruptCallbackInvocation preInterruptCallback;\n\n\tSeparateThreadTimeoutInvocation(Invocation<T> delegate, TimeoutDuration timeout,\n\t\t\tSupplier<String> descriptionSupplier, PreInterruptCallbackInvocation preInterruptCallback) {\n\t\tthis.delegate = delegate;\n\t\tthis.timeout = timeout;\n\t\tthis.descriptionSupplier = descriptionSupplier;\n\t\tthis.preInterruptCallback = preInterruptCallback;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"NullAway\")\n\tpublic T proceed() throws Throwable {\n\t\treturn executeWithPreemptiveTimeout(timeout.toDuration(), delegate::proceed, descriptionSupplier,\n\t\t\t(__, ___, cause, testThread) -> {\n\t\t\t\tTimeoutException exception = TimeoutExceptionFactory.create(descriptionSupplier.get(), timeout, null);\n\t\t\t\tif (testThread != null) {\n\t\t\t\t\tpreInterruptCallback.executePreInterruptCallback(testThread, exception::addSuppressed);\n\t\t\t\t}\n\t\t\t\texception.initCause(cause);\n\t\t\t\treturn exception;\n\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TempDirectory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;\nimport static org.junit.jupiter.api.io.CleanupMode.DEFAULT;\nimport static org.junit.jupiter.api.io.CleanupMode.NEVER;\nimport static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;\nimport static org.junit.jupiter.api.io.TempDirDeletionStrategy.IgnoreFailures.descriptionFor;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.ReflectionSupport.makeAccessible;\nimport static org.junit.platform.commons.util.ReflectionUtils.isRecordObject;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Parameter;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code TempDirectory} is a JUnit Jupiter extension that creates and cleans\n * up temporary directories if a field in a test class or a parameter in a\n * test class constructor, lifecycle method, or test method is annotated with\n * {@code @TempDir}.\n *\n * <p>Consult the Javadoc for {@link TempDir} for details on the contract.\n *\n * @since 5.4\n * @see TempDir @TempDir\n * @see Files#createTempDirectory\n */\nclass TempDirectory implements BeforeAllCallback, BeforeEachCallback, ParameterResolver {\n\n\tprivate static final Namespace NAMESPACE = Namespace.create(TempDirectory.class);\n\tprivate static final String KEY = \"temp.dir\";\n\tprivate static final String FAILURE_TRACKER = \"failure.tracker\";\n\tprivate static final String CHILD_FAILED = \"child.failed\";\n\n\tprivate final JupiterConfiguration configuration;\n\n\tTempDirectory(JupiterConfiguration configuration) {\n\t\tthis.configuration = configuration;\n\t}\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn TEST_METHOD;\n\t}\n\n\t/**\n\t * Perform field injection for non-private, {@code static} fields (i.e.,\n\t * class fields) of type {@link Path} or {@link File} that are annotated with\n\t * {@link TempDir @TempDir}.\n\t */\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\tinstallFailureTracker(context);\n\t\tinjectStaticFields(context, context.getRequiredTestClass());\n\t}\n\n\t/**\n\t * Perform field injection for non-private, non-static fields (i.e.,\n\t * instance fields) of type {@link Path} or {@link File} that are annotated\n\t * with {@link TempDir @TempDir}.\n\t */\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tinstallFailureTracker(context);\n\t\tcontext.getRequiredTestInstances().getAllInstances() //\n\t\t\t\t.forEach(instance -> injectInstanceFields(context, instance));\n\t}\n\n\tprivate static void installFailureTracker(ExtensionContext context) {\n\t\tcontext.getParent() //\n\t\t\t\t.filter(parentContext -> !context.getRoot().equals(parentContext)) //\n\t\t\t\t.ifPresent(parentContext -> installFailureTracker(context, parentContext));\n\t}\n\n\tprivate static void installFailureTracker(ExtensionContext context, ExtensionContext parentContext) {\n\t\tcontext.getStore(NAMESPACE).put(FAILURE_TRACKER, new FailureTracker(context, parentContext));\n\t}\n\n\tprivate void injectStaticFields(ExtensionContext context, Class<?> testClass) {\n\t\tinjectFields(context, null, testClass, ModifierSupport::isStatic);\n\t}\n\n\tprivate void injectInstanceFields(ExtensionContext context, Object instance) {\n\t\tif (!isRecordObject(instance)) {\n\t\t\tinjectFields(context, instance, instance.getClass(), ModifierSupport::isNotStatic);\n\t\t}\n\t}\n\n\tprivate void injectFields(ExtensionContext context, @Nullable Object testInstance, Class<?> testClass,\n\t\t\tPredicate<Field> predicate) {\n\n\t\tfindAnnotatedFields(testClass, TempDir.class, predicate).forEach(field -> {\n\t\t\tassertNonFinalField(field);\n\t\t\tassertSupportedType(\"field\", field.getType());\n\n\t\t\ttry {\n\t\t\t\tTempDir tempDir = findAnnotationOnField(field);\n\t\t\t\tmakeAccessible(field).set(testInstance,\n\t\t\t\t\tgetPathOrFile(field.getType(), new FieldContext(field), context, tempDir));\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Determine if the {@link Parameter} in the supplied {@link ParameterContext}\n\t * is annotated with {@link TempDir @TempDir}.\n\t */\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn parameterContext.isAnnotated(TempDir.class);\n\t}\n\n\t/**\n\t * Resolve the current temporary directory for the {@link Parameter} in the\n\t * supplied {@link ParameterContext}.\n\t */\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\tClass<?> parameterType = parameterContext.getParameter().getType();\n\t\tassertSupportedType(\"parameter\", parameterType);\n\t\tTempDir tempDir = findAnnotationOnParameter(parameterContext);\n\t\treturn getPathOrFile(parameterType, parameterContext, extensionContext, tempDir);\n\t}\n\n\tprivate static TempDir findAnnotationOnField(Field field) {\n\t\treturn findAnnotation(field, TempDir.class).orElseThrow(\n\t\t\t() -> new JUnitException(\"Field \" + field + \" must be annotated with @TempDir\"));\n\t}\n\n\tprivate static TempDir findAnnotationOnParameter(ParameterContext parameterContext) {\n\t\treturn parameterContext.findAnnotation(TempDir.class).orElseThrow(() -> new JUnitException(\n\t\t\t\"Parameter \" + parameterContext.getParameter() + \" must be annotated with @TempDir\"));\n\t}\n\n\tprivate CleanupMode determineCleanupMode(TempDir annotation) {\n\t\tvar cleanupMode = annotation.cleanup();\n\t\treturn cleanupMode == DEFAULT ? this.configuration.getDefaultTempDirCleanupMode() : cleanupMode;\n\t}\n\n\tprivate Supplier<TempDirDeletionStrategy> determineDeletionStrategy(TempDir annotation) {\n\t\tvar strategyClass = annotation.deletionStrategy();\n\t\treturn strategyClass == TempDirDeletionStrategy.class //\n\t\t\t\t? this.configuration.getDefaultTempDirDeletionStrategySupplier() //\n\t\t\t\t: () -> ReflectionSupport.newInstance(strategyClass);\n\t}\n\n\tprivate TempDirFactory determineTempDirFactory(TempDir tempDir) {\n\t\tClass<? extends TempDirFactory> factory = tempDir.factory();\n\n\t\treturn factory == TempDirFactory.class //\n\t\t\t\t? this.configuration.getDefaultTempDirFactorySupplier().get()\n\t\t\t\t: ReflectionSupport.newInstance(factory);\n\t}\n\n\tprivate static void assertNonFinalField(Field field) {\n\t\tif (ModifierSupport.isFinal(field)) {\n\t\t\tthrow new ExtensionConfigurationException(\"@TempDir field [\" + field + \"] must not be declared as final.\");\n\t\t}\n\t}\n\n\tprivate static void assertSupportedType(String target, Class<?> type) {\n\t\tif (type != Path.class && type != File.class) {\n\t\t\tthrow new ExtensionConfigurationException(\"Can only resolve @TempDir \" + target + \" of type \"\n\t\t\t\t\t+ Path.class.getName() + \" or \" + File.class.getName() + \" but was: \" + type.getName());\n\t\t}\n\t}\n\n\tprivate Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,\n\t\t\tExtensionContext extensionContext, TempDir tempDir) {\n\t\tTempDirFactory factory = determineTempDirFactory(tempDir);\n\t\tCleanup cleanup = new Cleanup(determineCleanupMode(tempDir), determineDeletionStrategy(tempDir));\n\t\treturn getPathOrFile(elementType, elementContext, factory, cleanup, extensionContext);\n\t}\n\n\tprivate static Object getPathOrFile(Class<?> elementType, AnnotatedElementContext elementContext,\n\t\t\tTempDirFactory factory, Cleanup cleanup, ExtensionContext extensionContext) {\n\n\t\tPath path = extensionContext.getStore(NAMESPACE.append(elementContext)) //\n\t\t\t\t.computeIfAbsent(KEY,\n\t\t\t\t\t__ -> createTempDir(factory, cleanup, elementType, elementContext, extensionContext),\n\t\t\t\t\tCloseablePath.class) //\n\t\t\t\t.get();\n\n\t\treturn (elementType == Path.class) ? path : path.toFile();\n\t}\n\n\tstatic CloseablePath createTempDir(TempDirFactory factory, Cleanup cleanup, Class<?> elementType,\n\t\t\tAnnotatedElementContext elementContext, ExtensionContext extensionContext) {\n\n\t\ttry {\n\t\t\treturn new CloseablePath(factory, cleanup, elementType, elementContext, extensionContext);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new ExtensionConfigurationException(\"Failed to create default temp directory\", ex);\n\t\t}\n\t}\n\n\tprivate static boolean selfOrChildFailed(ExtensionContext context) {\n\t\treturn context.getExecutionException().isPresent() //\n\t\t\t\t|| getContextSpecificStore(context).getOrDefault(CHILD_FAILED, Boolean.class, false);\n\t}\n\n\tprivate static ExtensionContext.Store getContextSpecificStore(ExtensionContext context) {\n\t\treturn context.getStore(NAMESPACE.append(context));\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tstatic class CloseablePath implements Store.CloseableResource, AutoCloseable {\n\n\t\tprivate final @Nullable Path dir;\n\t\tprivate final TempDirFactory factory;\n\t\tprivate final Cleanup cleanup;\n\t\tprivate final AnnotatedElementContext elementContext;\n\t\tprivate final ExtensionContext extensionContext;\n\n\t\tprivate CloseablePath(TempDirFactory factory, Cleanup cleanup, Class<?> elementType,\n\t\t\t\tAnnotatedElementContext elementContext, ExtensionContext extensionContext) throws Exception {\n\t\t\tthis.dir = factory.createTempDirectory(elementContext, extensionContext);\n\t\t\tthis.factory = factory;\n\t\t\tthis.cleanup = cleanup;\n\t\t\tthis.elementContext = elementContext;\n\t\t\tthis.extensionContext = extensionContext;\n\n\t\t\tif (this.dir == null || !Files.isDirectory(this.dir)) {\n\t\t\t\tclose();\n\t\t\t\tthrow new PreconditionViolationException(\"temp directory must be a directory\");\n\t\t\t}\n\n\t\t\tif (elementType == File.class && !this.dir.getFileSystem().equals(FileSystems.getDefault())) {\n\t\t\t\tclose();\n\t\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\t\"temp directory with non-default file system cannot be injected into \" + File.class.getName()\n\t\t\t\t\t\t\t+ \" target\");\n\t\t\t}\n\t\t}\n\n\t\tPath get() {\n\t\t\treturn requireNonNull(this.dir);\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws IOException {\n\t\t\ttry {\n\t\t\t\tif (this.dir != null) {\n\t\t\t\t\tthis.cleanup.run(this.dir, this.elementContext, this.extensionContext);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tthis.factory.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate record FieldContext(Field field) implements AnnotatedElementContext {\n\n\t\tprivate FieldContext(Field field) {\n\t\t\tthis.field = Preconditions.notNull(field, \"field must not be null\");\n\t\t}\n\n\t\t@Override\n\t\tpublic AnnotatedElement getAnnotatedElement() {\n\t\t\treturn this.field;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\t// @formatter:off\n\t\t\treturn new ToStringBuilder(this)\n\t\t\t\t\t.append(\"field\", this.field)\n\t\t\t\t\t.toString();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate record FailureTracker(ExtensionContext context, ExtensionContext parentContext)\n\t\t\timplements Store.CloseableResource, AutoCloseable {\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tif (selfOrChildFailed(context)) {\n\t\t\t\tgetContextSpecificStore(parentContext).put(CHILD_FAILED, true);\n\t\t\t}\n\t\t}\n\t}\n\n\trecord Cleanup(CleanupMode cleanupMode, Supplier<TempDirDeletionStrategy> deletionStrategy) {\n\n\t\tprivate static final Logger logger = LoggerFactory.getLogger(Cleanup.class);\n\n\t\tvoid run(Path dir, AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\tthrows IOException {\n\t\t\tif (cleanupMode == NEVER || (cleanupMode == ON_SUCCESS && selfOrChildFailed(extensionContext))) {\n\t\t\t\tlogger.info(() -> \"Skipping cleanup of temp dir %s for %s due to CleanupMode.%s.\".formatted(dir,\n\t\t\t\t\tdescriptionFor(elementContext.getAnnotatedElement()), cleanupMode.name()));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogger.trace(() -> \"Cleaning up temp dir \" + dir);\n\t\t\tif (Files.exists(dir)) {\n\t\t\t\tdeletionStrategy.get().delete(dir, elementContext, extensionContext) //\n\t\t\t\t\t\t.toException() //\n\t\t\t\t\t\t.ifPresent(exception -> {\n\t\t\t\t\t\t\tthrow exception;\n\t\t\t\t\t\t});\n\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestInfoParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@link ParameterResolver} that resolves the {@link TestInfo} for\n * the currently executing test.\n *\n * @since 5.0\n */\nclass TestInfoParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn (parameterContext.getParameter().getType() == TestInfo.class);\n\t}\n\n\t@Override\n\tpublic TestInfo resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn new DefaultTestInfo(extensionContext);\n\t}\n\n\tprivate static class DefaultTestInfo implements TestInfo {\n\n\t\tprivate final String displayName;\n\t\tprivate final Set<String> tags;\n\t\tprivate final Optional<Class<?>> testClass;\n\t\tprivate final Optional<Method> testMethod;\n\n\t\tDefaultTestInfo(ExtensionContext extensionContext) {\n\t\t\tthis.displayName = extensionContext.getDisplayName();\n\t\t\tthis.tags = extensionContext.getTags();\n\t\t\tthis.testClass = extensionContext.getTestClass();\n\t\t\tthis.testMethod = extensionContext.getTestMethod();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDisplayName() {\n\t\t\treturn this.displayName;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> getTags() {\n\t\t\treturn this.tags;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Class<?>> getTestClass() {\n\t\t\treturn this.testClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Method> getTestMethod() {\n\t\t\treturn this.testMethod;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\t// @formatter:off\n\t\t\treturn new ToStringBuilder(this)\n\t\t\t\t\t.append(\"displayName\", this.displayName)\n\t\t\t\t\t.append(\"tags\", this.tags)\n\t\t\t\t\t.append(\"testClass\", nullSafeGet(this.testClass))\n\t\t\t\t\t.append(\"testMethod\", nullSafeGet(this.testMethod))\n\t\t\t\t\t.toString();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@SuppressWarnings({ \"OptionalAssignedToNull\", \"NullableOptional\" })\n\t\tprivate static @Nullable Object nullSafeGet(@Nullable Optional<?> optional) {\n\t\t\treturn optional != null ? optional.orElse(null) : null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TestReporterParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * {@link ParameterResolver} that injects a {@link TestReporter}.\n *\n * @since 5.0\n */\nclass TestReporterParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn (parameterContext.getParameter().getType() == TestReporter.class);\n\t}\n\n\t@Override\n\tpublic TestReporter resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn new DefaultTestReporter(extensionContext);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Timeout.DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.TIMEOUT_MODE_PROPERTY_NAME;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Timeout.ThreadMode;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.EnumConfigurationParameterConverter;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.RuntimeUtils;\n\n/**\n * @since 5.5\n */\nclass TimeoutConfiguration {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(TimeoutConfiguration.class);\n\n\tprivate final TimeoutDurationParser parser = new TimeoutDurationParser();\n\tprivate final Map<String, Optional<TimeoutDuration>> cache = new ConcurrentHashMap<>();\n\tprivate final AtomicReference<Optional<ThreadMode>> threadMode = new AtomicReference<>();\n\tprivate final ExtensionContext extensionContext;\n\tprivate final boolean timeoutDisabled;\n\n\tTimeoutConfiguration(ExtensionContext extensionContext) {\n\t\tthis.extensionContext = extensionContext;\n\t\tthis.timeoutDisabled = new EnumConfigurationParameterConverter<>(TimeoutMode.class, \"timeout mode\") //\n\t\t\t\t.get(extensionContext, TIMEOUT_MODE_PROPERTY_NAME) //\n\t\t\t\t.map(TimeoutMode::isTimeoutDisabled) //\n\t\t\t\t.orElse(false);\n\t}\n\n\tboolean isTimeoutDisabled() {\n\t\treturn timeoutDisabled;\n\t}\n\n\tOptional<TimeoutDuration> getDefaultTestMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultTestableMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultTestTemplateMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME,\n\t\t\tthis::getDefaultTestableMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultTestFactoryMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultTestableMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultBeforeAllMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultLifecycleMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultBeforeEachMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultLifecycleMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultAfterEachMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultLifecycleMethodTimeout);\n\t}\n\n\tOptional<TimeoutDuration> getDefaultAfterAllMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultLifecycleMethodTimeout);\n\t}\n\n\tprivate Optional<TimeoutDuration> getDefaultTestableMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultTimeout);\n\t}\n\n\tprivate Optional<TimeoutDuration> getDefaultLifecycleMethodTimeout() {\n\t\treturn parseOrDefault(DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME, this::getDefaultTimeout);\n\t}\n\n\tprivate Optional<TimeoutDuration> getDefaultTimeout() {\n\t\treturn parseTimeoutDuration(DEFAULT_TIMEOUT_PROPERTY_NAME);\n\t}\n\n\tprivate Optional<TimeoutDuration> parseOrDefault(String propertyName,\n\t\t\tSupplier<Optional<TimeoutDuration>> defaultSupplier) {\n\t\tOptional<TimeoutDuration> timeoutConfiguration = parseTimeoutDuration(propertyName);\n\t\treturn timeoutConfiguration.isPresent() ? timeoutConfiguration : defaultSupplier.get();\n\t}\n\n\tprivate Optional<TimeoutDuration> parseTimeoutDuration(String propertyName) {\n\t\treturn cache.computeIfAbsent(propertyName, key -> extensionContext.getConfigurationParameter(key).map(value -> {\n\t\t\ttry {\n\t\t\t\treturn parser.parse(value);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tlogger.warn(e,\n\t\t\t\t\t() -> \"Ignored invalid timeout '%s' set via the '%s' configuration parameter.\".formatted(value,\n\t\t\t\t\t\tkey));\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}));\n\t}\n\n\tOptional<ThreadMode> getDefaultTimeoutThreadMode() {\n\t\tif (threadMode.get() != null) {\n\t\t\treturn threadMode.get();\n\t\t}\n\t\telse {\n\t\t\tOptional<ThreadMode> configuredThreadMode = parseTimeoutThreadModeConfiguration();\n\t\t\tthreadMode.set(configuredThreadMode);\n\t\t\treturn configuredThreadMode;\n\t\t}\n\t}\n\n\tprivate Optional<ThreadMode> parseTimeoutThreadModeConfiguration() {\n\t\treturn new EnumConfigurationParameterConverter<>(ThreadMode.class, \"timeout thread mode\") //\n\t\t\t\t.get(extensionContext, DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME);\n\t}\n\n\tprivate enum TimeoutMode {\n\n\t\tENABLED {\n\t\t\t@Override\n\t\t\tboolean isTimeoutDisabled() {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tDISABLED {\n\t\t\t@Override\n\t\t\tboolean isTimeoutDisabled() {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t},\n\n\t\tDISABLED_ON_DEBUG {\n\t\t\t@Override\n\t\t\tboolean isTimeoutDisabled() {\n\t\t\t\treturn RuntimeUtils.isDebugMode();\n\t\t\t}\n\t\t};\n\n\t\tabstract boolean isTimeoutDisabled();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDuration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.time.Duration;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Locale;\nimport java.util.concurrent.TimeUnit;\n\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.5\n */\nrecord TimeoutDuration(long value, TimeUnit unit) {\n\n\tstatic TimeoutDuration from(Timeout timeout) {\n\t\treturn new TimeoutDuration(timeout.value(), timeout.unit());\n\t}\n\n\tTimeoutDuration(long value, TimeUnit unit) {\n\t\tPreconditions.condition(value > 0, () -> \"timeout duration must be a positive number: \" + value);\n\t\tthis.value = value;\n\t\tthis.unit = Preconditions.notNull(unit, \"timeout unit must not be null\");\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tString label = unit.name().toLowerCase(Locale.ROOT);\n\t\tif (value == 1 && label.endsWith(\"s\")) {\n\t\t\tlabel = label.substring(0, label.length() - 1);\n\t\t}\n\t\treturn value + \" \" + label;\n\t}\n\n\tpublic Duration toDuration() {\n\t\treturn Duration.of(value, toChronoUnit());\n\t}\n\n\tprivate ChronoUnit toChronoUnit() {\n\t\treturn switch (unit) {\n\t\t\tcase NANOSECONDS -> ChronoUnit.NANOS;\n\t\t\tcase MICROSECONDS -> ChronoUnit.MICROS;\n\t\t\tcase MILLISECONDS -> ChronoUnit.MILLIS;\n\t\t\tcase SECONDS -> ChronoUnit.SECONDS;\n\t\t\tcase MINUTES -> ChronoUnit.MINUTES;\n\t\t\tcase HOURS -> ChronoUnit.HOURS;\n\t\t\tcase DAYS -> ChronoUnit.DAYS;\n\t\t\tdefault -> throw new JUnitException(\"Could not map TimeUnit \" + unit + \" to ChronoUnit\");\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutDurationParser.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.concurrent.TimeUnit.DAYS;\nimport static java.util.concurrent.TimeUnit.HOURS;\nimport static java.util.concurrent.TimeUnit.MICROSECONDS;\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static java.util.concurrent.TimeUnit.MINUTES;\nimport static java.util.concurrent.TimeUnit.NANOSECONDS;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static java.util.regex.Pattern.CASE_INSENSITIVE;\nimport static java.util.regex.Pattern.UNICODE_CASE;\n\nimport java.time.format.DateTimeParseException;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @since 5.5\n */\nclass TimeoutDurationParser {\n\n\tprivate static final Pattern PATTERN = Pattern.compile(\"([1-9]\\\\d*) ?((?:[nμm]?s)|m|h|d)?\",\n\t\tCASE_INSENSITIVE | UNICODE_CASE);\n\n\tprivate static final Map<String, TimeUnit> UNITS_BY_ABBREVIATION = Map.of( //\n\t\t\"ns\", NANOSECONDS, //\n\t\t\"μs\", MICROSECONDS, //\n\t\t\"ms\", MILLISECONDS, //\n\t\t\"s\", SECONDS, //\n\t\t\"m\", MINUTES, //\n\t\t\"h\", HOURS, //\n\t\t\"d\", DAYS //\n\t);\n\n\tTimeoutDuration parse(CharSequence text) throws DateTimeParseException {\n\t\tMatcher matcher = PATTERN.matcher(text);\n\t\tif (matcher.matches()) {\n\t\t\tlong value = Long.parseLong(matcher.group(1));\n\t\t\tString unitAbbreviation = matcher.group(2);\n\t\t\tTimeUnit unit = unitAbbreviation == null ? SECONDS\n\t\t\t\t\t: requireNonNull(UNITS_BY_ABBREVIATION.get(unitAbbreviation.toLowerCase(Locale.ENGLISH)));\n\t\t\treturn new TimeoutDuration(value, unit);\n\t\t}\n\t\tthrow new DateTimeParseException(\"Timeout duration is not in the expected format (<number> [ns|μs|ms|s|m|h|d])\",\n\t\t\ttext, 0);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.concurrent.TimeoutException;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.9\n */\nclass TimeoutExceptionFactory {\n\n\tprivate TimeoutExceptionFactory() {\n\t}\n\n\tstatic TimeoutException create(String methodSignature, TimeoutDuration timeoutDuration,\n\t\t\t@Nullable Throwable failure) {\n\t\tString message = \"%s timed out after %s\".formatted(\n\t\t\tPreconditions.notNull(methodSignature, \"method signature must not be null\"),\n\t\t\tPreconditions.notNull(timeoutDuration, \"timeout duration must not be null\"));\n\t\tTimeoutException timeoutException = new TimeoutException(message);\n\t\tif (failure != null) {\n\t\t\ttimeoutException.addSuppressed(failure);\n\t\t}\n\t\treturn timeoutException;\n\t}\n\n\tstatic TimeoutException create(String methodSignature, TimeoutDuration timeoutDuration) {\n\t\treturn create(methodSignature, timeoutDuration, null);\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Timeout.ThreadMode.SAME_THREAD;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.Timeout.ThreadMode;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.jupiter.engine.extension.TimeoutInvocationFactory.TimeoutInvocationParameters;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.5\n */\nclass TimeoutExtension implements BeforeAllCallback, BeforeEachCallback, InvocationInterceptor {\n\n\tprivate static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create(Timeout.class);\n\tprivate static final String TESTABLE_METHOD_TIMEOUT_KEY = \"testable_method_timeout_from_annotation\";\n\tprivate static final String TESTABLE_METHOD_TIMEOUT_THREAD_MODE_KEY = \"testable_method_timeout_thread_mode_from_annotation\";\n\tprivate static final String GLOBAL_TIMEOUT_CONFIG_KEY = \"global_timeout_config\";\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t\treadAndStoreTimeoutSoChildrenInheritIt(context);\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\treadAndStoreTimeoutSoChildrenInheritIt(context);\n\t}\n\n\tprivate void readAndStoreTimeoutSoChildrenInheritIt(ExtensionContext context) {\n\t\treadTimeoutFromAnnotation(context.getElement()).ifPresent(\n\t\t\ttimeout -> context.getStore(NAMESPACE).put(TESTABLE_METHOD_TIMEOUT_KEY, timeout));\n\t\treadTimeoutThreadModeFromAnnotation(context.getElement()).ifPresent(\n\t\t\ttimeoutThreadMode -> context.getStore(NAMESPACE).put(TESTABLE_METHOD_TIMEOUT_THREAD_MODE_KEY,\n\t\t\t\ttimeoutThreadMode));\n\t}\n\n\t@Override\n\tpublic void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tinterceptLifecycleMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultBeforeAllMethodTimeout);\n\t}\n\n\t@Override\n\tpublic void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tinterceptLifecycleMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultBeforeEachMethodTimeout);\n\t}\n\n\t@Override\n\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tthis.<@Nullable Void> interceptTestableMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultTestMethodTimeout);\n\t}\n\n\t@Override\n\tpublic void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tthis.<@Nullable Void> interceptTestableMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultTestTemplateMethodTimeout);\n\t}\n\n\t@Override\n\tpublic <T extends @Nullable Object> T interceptTestFactoryMethod(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\treturn this.<T> interceptTestableMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultTestFactoryMethodTimeout);\n\t}\n\n\t@Override\n\tpublic void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tinterceptLifecycleMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultAfterEachMethodTimeout);\n\t}\n\n\t@Override\n\tpublic void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\tinterceptLifecycleMethod(invocation, invocationContext, extensionContext,\n\t\t\tTimeoutConfiguration::getDefaultAfterAllMethodTimeout);\n\t}\n\n\tprivate void interceptLifecycleMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext,\n\t\t\tTimeoutProvider defaultTimeoutProvider) throws Throwable {\n\n\t\tTimeoutDuration timeout = readTimeoutFromAnnotation(Optional.of(invocationContext.getExecutable())).orElse(\n\t\t\tnull);\n\t\tthis.<@Nullable Void> intercept(invocation, invocationContext, extensionContext, timeout,\n\t\t\tdefaultTimeoutProvider);\n\t}\n\n\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n\tprivate Optional<TimeoutDuration> readTimeoutFromAnnotation(Optional<AnnotatedElement> element) {\n\t\treturn AnnotationSupport.findAnnotation(element, Timeout.class).map(TimeoutDuration::from);\n\t}\n\n\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n\tprivate Optional<ThreadMode> readTimeoutThreadModeFromAnnotation(Optional<AnnotatedElement> element) {\n\t\treturn AnnotationSupport.findAnnotation(element, Timeout.class).map(Timeout::threadMode);\n\t}\n\n\tprivate <T extends @Nullable Object> T interceptTestableMethod(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext,\n\t\t\tTimeoutProvider defaultTimeoutProvider) throws Throwable {\n\n\t\tTimeoutDuration timeout = extensionContext.getStore(NAMESPACE).get(TESTABLE_METHOD_TIMEOUT_KEY,\n\t\t\tTimeoutDuration.class);\n\t\treturn intercept(invocation, invocationContext, extensionContext, timeout, defaultTimeoutProvider);\n\t}\n\n\tprivate <T extends @Nullable Object> T intercept(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext,\n\t\t\t@Nullable TimeoutDuration explicitTimeout, TimeoutProvider defaultTimeoutProvider) throws Throwable {\n\n\t\tTimeoutConfiguration timeoutConfiguration = getGlobalTimeoutConfiguration(extensionContext);\n\t\tif (timeoutConfiguration.isTimeoutDisabled()) {\n\t\t\treturn invocation.proceed();\n\t\t}\n\n\t\tTimeoutDuration timeout = explicitTimeout == null\n\t\t\t\t? getDefaultTimeout(defaultTimeoutProvider, timeoutConfiguration)\n\t\t\t\t: explicitTimeout;\n\t\treturn decorate(invocation, invocationContext, extensionContext, timeout, timeoutConfiguration).proceed();\n\t}\n\n\tprivate @Nullable TimeoutDuration getDefaultTimeout(TimeoutProvider defaultTimeoutProvider,\n\t\t\tTimeoutConfiguration timeoutConfiguration) {\n\n\t\treturn defaultTimeoutProvider.apply(timeoutConfiguration).orElse(null);\n\t}\n\n\tprivate TimeoutConfiguration getGlobalTimeoutConfiguration(ExtensionContext extensionContext) {\n\t\tExtensionContext root = extensionContext.getRoot();\n\t\treturn root.getStore(NAMESPACE).computeIfAbsent(GLOBAL_TIMEOUT_CONFIG_KEY, __ -> new TimeoutConfiguration(root),\n\t\t\tTimeoutConfiguration.class);\n\t}\n\n\tprivate <T extends @Nullable Object> Invocation<T> decorate(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext,\n\t\t\t@Nullable TimeoutDuration timeout, TimeoutConfiguration timeoutConfiguration) {\n\n\t\tif (timeout == null) {\n\t\t\treturn invocation;\n\t\t}\n\n\t\tThreadMode threadMode = resolveTimeoutThreadMode(extensionContext, timeoutConfiguration);\n\t\treturn new TimeoutInvocationFactory(extensionContext.getRoot().getStore(NAMESPACE)).create(threadMode,\n\t\t\tnew TimeoutInvocationParameters<>(invocation, timeout, () -> describe(invocationContext, extensionContext),\n\t\t\t\tPreInterruptCallbackInvocationFactory.create((ExtensionContextInternal) extensionContext)));\n\t}\n\n\tprivate ThreadMode resolveTimeoutThreadMode(ExtensionContext extensionContext,\n\t\t\tTimeoutConfiguration timeoutConfiguration) {\n\t\tThreadMode annotationThreadMode = getAnnotationThreadMode(extensionContext);\n\t\tif (annotationThreadMode == null || annotationThreadMode == ThreadMode.INFERRED) {\n\t\t\treturn timeoutConfiguration.getDefaultTimeoutThreadMode().orElse(SAME_THREAD);\n\t\t}\n\t\treturn annotationThreadMode;\n\t}\n\n\tprivate @Nullable ThreadMode getAnnotationThreadMode(ExtensionContext extensionContext) {\n\t\treturn extensionContext.getStore(NAMESPACE).get(TESTABLE_METHOD_TIMEOUT_THREAD_MODE_KEY, ThreadMode.class);\n\t}\n\n\tprivate String describe(ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) {\n\t\tMethod method = invocationContext.getExecutable();\n\t\tOptional<Class<?>> testClass = extensionContext.getTestClass();\n\t\tif (testClass.isPresent() && invocationContext.getTargetClass().equals(testClass.get())) {\n\t\t\treturn \"%s(%s)\".formatted(method.getName(), ClassUtils.nullSafeToString(method.getParameterTypes()));\n\t\t}\n\t\treturn ReflectionUtils.getFullyQualifiedMethodName(invocationContext.getTargetClass(), method);\n\t}\n\n\t@FunctionalInterface\n\tprivate interface TimeoutProvider extends Function<TimeoutConfiguration, Optional<TimeoutDuration>> {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.Timeout.ThreadMode;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.9\n */\nclass TimeoutInvocationFactory {\n\n\tprivate final Store store;\n\n\tTimeoutInvocationFactory(Store store) {\n\t\tthis.store = Preconditions.notNull(store, \"store must not be null\");\n\t}\n\n\t<T> Invocation<T> create(ThreadMode threadMode, TimeoutInvocationParameters<T> timeoutInvocationParameters) {\n\t\tPreconditions.notNull(threadMode, \"thread mode must not be null\");\n\t\tPreconditions.condition(threadMode != ThreadMode.INFERRED, \"thread mode must not be INFERRED\");\n\t\tPreconditions.notNull(timeoutInvocationParameters, \"timeout invocation parameters must not be null\");\n\t\tif (threadMode == ThreadMode.SEPARATE_THREAD) {\n\t\t\treturn new SeparateThreadTimeoutInvocation<>(timeoutInvocationParameters.getInvocation(),\n\t\t\t\ttimeoutInvocationParameters.getTimeoutDuration(), timeoutInvocationParameters.getDescriptionSupplier(),\n\t\t\t\ttimeoutInvocationParameters.getPreInterruptCallback());\n\t\t}\n\t\treturn new SameThreadTimeoutInvocation<>(timeoutInvocationParameters.getInvocation(),\n\t\t\ttimeoutInvocationParameters.getTimeoutDuration(), getThreadExecutorForSameThreadInvocation(),\n\t\t\ttimeoutInvocationParameters.getDescriptionSupplier(),\n\t\t\ttimeoutInvocationParameters.getPreInterruptCallback());\n\t}\n\n\tprivate ScheduledExecutorService getThreadExecutorForSameThreadInvocation() {\n\t\treturn store.computeIfAbsent(SingleThreadExecutorResource.class).get();\n\t}\n\n\t@SuppressWarnings({ \"deprecation\", \"try\" })\n\tprivate abstract static class ExecutorResource implements Store.CloseableResource, AutoCloseable {\n\n\t\tprivate final ScheduledExecutorService executor;\n\n\t\tExecutorResource(ScheduledExecutorService executor) {\n\t\t\tthis.executor = executor;\n\t\t}\n\n\t\tScheduledExecutorService get() {\n\t\t\treturn executor;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\texecutor.shutdown();\n\t\t\tboolean terminated = executor.awaitTermination(5, TimeUnit.SECONDS);\n\t\t\tif (!terminated) {\n\t\t\t\texecutor.shutdownNow();\n\t\t\t\tthrow new JUnitException(\"Scheduled executor could not be stopped in an orderly manner\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"try\")\n\tstatic class SingleThreadExecutorResource extends ExecutorResource {\n\n\t\t@SuppressWarnings({ \"unused\", \"ThreadPriorityCheck\" })\n\t\tSingleThreadExecutorResource() {\n\t\t\tsuper(Executors.newSingleThreadScheduledExecutor(runnable -> {\n\t\t\t\tThread thread = new Thread(runnable, \"junit-jupiter-timeout-watcher\");\n\t\t\t\tthread.setPriority(Thread.MAX_PRIORITY);\n\t\t\t\treturn thread;\n\t\t\t}));\n\t\t}\n\t}\n\n\tstatic class TimeoutInvocationParameters<T> {\n\n\t\tprivate final Invocation<T> invocation;\n\t\tprivate final TimeoutDuration timeout;\n\t\tprivate final Supplier<String> descriptionSupplier;\n\t\tprivate final PreInterruptCallbackInvocation preInterruptCallback;\n\n\t\tTimeoutInvocationParameters(Invocation<T> invocation, TimeoutDuration timeout,\n\t\t\t\tSupplier<String> descriptionSupplier, PreInterruptCallbackInvocation preInterruptCallback) {\n\t\t\tthis.invocation = Preconditions.notNull(invocation, \"invocation must not be null\");\n\t\t\tthis.timeout = Preconditions.notNull(timeout, \"timeout must not be null\");\n\t\t\tthis.descriptionSupplier = Preconditions.notNull(descriptionSupplier,\n\t\t\t\t\"description supplier must not be null\");\n\t\t\tthis.preInterruptCallback = Preconditions.notNull(preInterruptCallback,\n\t\t\t\t\"preInterruptCallback must not be null\");\n\t\t}\n\n\t\tpublic Invocation<T> getInvocation() {\n\t\t\treturn invocation;\n\t\t}\n\n\t\tpublic TimeoutDuration getTimeoutDuration() {\n\t\t\treturn timeout;\n\t\t}\n\n\t\tpublic Supplier<String> getDescriptionSupplier() {\n\t\t\treturn descriptionSupplier;\n\t\t}\n\n\t\tpublic PreInterruptCallbackInvocation getPreInterruptCallback() {\n\t\t\treturn preInterruptCallback;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/extension/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Test extensions specific to the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.extension;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Core package for the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/JupiterThrowableCollectorFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.support;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\n\n/**\n * Factory for creating {@link ThrowableCollector ThrowableCollectors} within\n * the JUnit Jupiter test engine.\n *\n * @since 5.4\n * @see ThrowableCollector\n */\n@API(status = INTERNAL, since = \"5.4\")\npublic class JupiterThrowableCollectorFactory {\n\n\t/**\n\t * Create a new {@link ThrowableCollector} that treats instances of the\n\t * OTA's {@link org.opentest4j.TestAbortedException} and JUnit 4's\n\t * {@code org.junit.AssumptionViolatedException} as <em>aborting</em>.\n\t */\n\tpublic static ThrowableCollector createThrowableCollector() {\n\t\treturn new OpenTest4JAndJUnit4AwareThrowableCollector();\n\t}\n\n\tprivate JupiterThrowableCollectorFactory() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/MethodReflectionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.support;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.getKotlinSuspendingFunctionGenericReturnType;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.getKotlinSuspendingFunctionReturnType;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.invokeKotlinFunction;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.invokeKotlinSuspendingFunction;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinSuspendingFunction;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinType;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.KotlinReflectionUtils;\n\n@API(status = INTERNAL, since = \"6.0\")\npublic class MethodReflectionUtils {\n\n\tpublic static Class<?> getReturnType(Method method) {\n\t\treturn isKotlinSuspendingFunction(method) //\n\t\t\t\t? getKotlinSuspendingFunctionReturnType(method) //\n\t\t\t\t: method.getReturnType();\n\t}\n\n\tpublic static Type getGenericReturnType(Method method) {\n\t\treturn isKotlinSuspendingFunction(method) //\n\t\t\t\t? getKotlinSuspendingFunctionGenericReturnType(method) //\n\t\t\t\t: method.getGenericReturnType();\n\t}\n\n\tpublic static @Nullable Object invoke(Method method, @Nullable Object target, @Nullable Object[] arguments) {\n\t\tif (isKotlinSuspendingFunction(method)) {\n\t\t\treturn invokeKotlinSuspendingFunction(method, target, arguments);\n\t\t}\n\t\tif (isKotlinType(method.getDeclaringClass()) && KotlinReflectionUtils.isKotlinReflectPresent()\n\t\t\t\t&& hasInlineTypeArgument(arguments)) {\n\t\t\treturn invokeKotlinFunction(method, target, arguments);\n\t\t}\n\t\treturn ReflectionSupport.invokeMethod(method, target, arguments);\n\t}\n\n\tprivate static boolean hasInlineTypeArgument(@Nullable Object[] arguments) {\n\t\tif (!KotlinReflectionUtils.isKotlinReflectPresent()) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn arguments.length > 0 && Arrays.stream(arguments).anyMatch(KotlinReflectionUtils::isInstanceOfInlineType);\n\t}\n\n\tprivate MethodReflectionUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.support;\n\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Specialization of {@link ThrowableCollector} that treats instances of the\n * OTA's {@link org.opentest4j.TestAbortedException} and JUnit 4's\n * {@code org.junit.AssumptionViolatedException} as <em>aborting</em>.\n *\n * @since 5.4\n * @see ThrowableCollector\n * @see org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector\n */\nclass OpenTest4JAndJUnit4AwareThrowableCollector extends ThrowableCollector {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(OpenTest4JAndJUnit4AwareThrowableCollector.class);\n\n\tprivate static final String ASSUMPTION_VIOLATED_EXCEPTION = \"org.junit.internal.AssumptionViolatedException\";\n\n\tprivate static final String COMMON_FAILURE_MESSAGE = \"Failed to load class \" + ASSUMPTION_VIOLATED_EXCEPTION\n\t\t\t+ \": only supporting \" + TestAbortedException.class.getName() + \" for aborted execution.\";\n\n\tprivate static final Predicate<? super Throwable> abortedExecutionPredicate = createAbortedExecutionPredicate();\n\n\tOpenTest4JAndJUnit4AwareThrowableCollector() {\n\t\tsuper(abortedExecutionPredicate);\n\t}\n\n\tprivate static Predicate<? super Throwable> createAbortedExecutionPredicate() {\n\t\tPredicate<Throwable> otaPredicate = TestAbortedException.class::isInstance;\n\n\t\t// Additionally support JUnit 4's AssumptionViolatedException?\n\t\ttry {\n\t\t\tClass<?> clazz = ReflectionSupport.tryToLoadClass(ASSUMPTION_VIOLATED_EXCEPTION).get();\n\t\t\tif (clazz != null) {\n\t\t\t\treturn otaPredicate.or(clazz::isInstance);\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\tSupplier<String> messageSupplier = (throwable instanceof NoClassDefFoundError)\n\t\t\t\t\t? () -> COMMON_FAILURE_MESSAGE + \" Note that \" + ASSUMPTION_VIOLATED_EXCEPTION\n\t\t\t\t\t\t\t+ \" requires that Hamcrest is on the classpath.\"\n\t\t\t\t\t: () -> COMMON_FAILURE_MESSAGE;\n\t\t\tlogger.debug(throwable, messageSupplier);\n\t\t}\n\n\t\t// Else just OTA's TestAbortedException\n\t\treturn otaPredicate;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/support/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal support classes for the JUnit Jupiter test engine.\n */\n\n@NullMarked\npackage org.junit.jupiter.engine.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-engine/src/main/resources/META-INF/services/org.junit.platform.engine.TestEngine",
    "content": "org.junit.jupiter.engine.JupiterTestEngine"
  },
  {
    "path": "junit-jupiter-engine/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project.\n"
  },
  {
    "path": "junit-jupiter-engine/src/testFixtures/java/org/junit/jupiter/engine/discovery/JupiterUniqueIdBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\n\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Test data builder for unique IDs for JupiterTestEngine.\n *\n * Used to decouple tests from concrete unique ID strings.\n *\n * @since 5.0\n */\npublic class JupiterUniqueIdBuilder {\n\n\tpublic static UniqueId uniqueIdForClass(Class<?> clazz) {\n\t\tif (isInnerClass(clazz)) {\n\t\t\tvar segmentType = classSegmentType(clazz, NestedClassTestDescriptor.SEGMENT_TYPE,\n\t\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE);\n\t\t\treturn uniqueIdForClass(clazz.getEnclosingClass()).append(segmentType, clazz.getSimpleName());\n\t\t}\n\t\treturn uniqueIdForStaticClass(clazz.getName());\n\t}\n\n\tpublic static UniqueId uniqueIdForStaticClass(String className) {\n\t\treturn engineId().append(staticClassSegmentType(className), className);\n\t}\n\n\tprivate static String staticClassSegmentType(String className) {\n\t\treturn ReflectionSupport.tryToLoadClass(className).toOptional() //\n\t\t\t\t.map(it -> classSegmentType(it, ClassTestDescriptor.SEGMENT_TYPE,\n\t\t\t\t\tClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE)) //\n\t\t\t\t.orElse(ClassTestDescriptor.SEGMENT_TYPE);\n\t}\n\n\tprivate static String classSegmentType(Class<?> clazz, String regularSegmentType, String classTemplateSegmentType) {\n\t\treturn AnnotationSupport.isAnnotated(clazz, ClassTemplate.class) //\n\t\t\t\t? classTemplateSegmentType //\n\t\t\t\t: regularSegmentType;\n\t}\n\n\tpublic static UniqueId uniqueIdForMethod(Class<?> clazz, String methodPart) {\n\t\treturn uniqueIdForClass(clazz).append(TestMethodTestDescriptor.SEGMENT_TYPE, methodPart);\n\t}\n\n\tpublic static UniqueId uniqueIdForTestFactoryMethod(Class<?> clazz, String methodPart) {\n\t\treturn uniqueIdForClass(clazz).append(TestFactoryTestDescriptor.SEGMENT_TYPE, methodPart);\n\t}\n\n\tpublic static UniqueId uniqueIdForTestTemplateMethod(Class<?> clazz, String methodPart) {\n\t\treturn uniqueIdForClass(clazz).append(TestTemplateTestDescriptor.SEGMENT_TYPE, methodPart);\n\t}\n\n\tpublic static UniqueId appendTestTemplateInvocationSegment(UniqueId parentId, int index) {\n\t\treturn parentId.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#\" + index);\n\t}\n\n\tpublic static UniqueId appendClassTemplateInvocationSegment(UniqueId parentId, int index) {\n\t\treturn parentId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#\" + index);\n\t}\n\n\tpublic static UniqueId engineId() {\n\t\treturn UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t}\n\n\tprivate JupiterUniqueIdBuilder() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/README.md",
    "content": "# Module junit-jupiter-migrationsupport\n\nThis module provides support for JUnit 4 rules within JUnit Jupiter.\nCurrently, this support is limited to subclasses of the ```org.junit.rules.Verifier```\nand ```org.junit.rules.ExternalResource``` rules of JUnit 4, respectively.\n\nPlease note that a general support for arbitrary ```org.junit.rules.TestRule```\nimplementations is not possible within the JUnit Jupiter extension model.\n\nThe main purpose of this module is to facilitate the migration of large\nJUnit 4 codebases containing such JUnit 4 rules by minimizing the effort\nneeded to run such legacy tests under JUnit Platform.\nBy using one of the two provided class-level extensions on a test class\nsuch rules in legacy code bases can be left unchanged\nincluding the JUnit 4 rule import statements.\n\nHowever, if you intend to develop a *new* extension for\nJUnit please use the new extension model of JUnit Jupiter instead\nof the rule-based model of JUnit 4.\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/junit-jupiter-migrationsupport.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"junitbuild.junit4-compatibility\")\n}\n\ndescription = \"JUnit Jupiter Migration Support\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(libs.junit4)\n\tapi(projects.junitJupiterApi)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.compilerArgs.add(\"-Xlint:-requires-automatic,-requires-transitive-automatic\") // JUnit 4\n\t}\n\tjar {\n\t\tbundle {\n\t\t\tval importAPIGuardian: String by extra\n\t\t\tval importJSpecify: String by extra\n\t\t\tval importCommonsLogging: String by extra\n\t\t\tbnd(\"\"\"\n\t\t\t\t# Import JUnit4 packages with a version\n\t\t\t\tImport-Package: \\\n\t\t\t\t\t$importAPIGuardian,\\\n\t\t\t\t\t$importJSpecify,\\\n\t\t\t\t\t$importCommonsLogging,\\\n\t\t\t\t\torg.junit;version=\"[${libs.versions.junit4Min.get()},5)\",\\\n\t\t\t\t\torg.junit.rules;version=\"[${libs.versions.junit4Min.get()},5)\",\\\n\t\t\t\t\t*\n\t\t\t\"\"\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for migrating from JUnit 4 to JUnit Jupiter.\n *\n * @since 5.0\n * @deprecated Please migrate to the corresponding APIs and extensions provided\n * by JUnit Jupiter.\n */\n@Deprecated(forRemoval = true)\nmodule org.junit.jupiter.migrationsupport {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive junit; // 4\n\trequires transitive org.junit.jupiter.api;\n\trequires org.junit.platform.commons;\n\n\texports org.junit.jupiter.migrationsupport;\n\texports org.junit.jupiter.migrationsupport.conditions;\n\texports org.junit.jupiter.migrationsupport.rules;\n\texports org.junit.jupiter.migrationsupport.rules.adapter;\n\texports org.junit.jupiter.migrationsupport.rules.member;\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/EnableJUnit4MigrationSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.migrationsupport.conditions.IgnoreCondition;\nimport org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;\nimport org.junit.jupiter.migrationsupport.rules.ExpectedExceptionSupport;\nimport org.junit.jupiter.migrationsupport.rules.ExternalResourceSupport;\nimport org.junit.jupiter.migrationsupport.rules.VerifierSupport;\n\n/**\n * {@code EnableJUnit4MigrationSupport} is a class-level annotation that\n * enables all JUnit 4 migration support within JUnit Jupiter.\n *\n * <p>Specifically, this annotation registers all extensions supported by\n * {@link EnableRuleMigrationSupport @EnableRuleMigrationSupport} and provides\n * support for JUnit 4's {@link org.junit.Ignore @Ignore} annotation for\n * disabling test classes and test methods.\n *\n * <p>Technically speaking, {@code @EnableJUnit4MigrationSupport} is a composed\n * annotation which registers all of the following migration extensions:\n * {@link VerifierSupport}, {@link ExternalResourceSupport},\n * {@link ExpectedExceptionSupport}, and {@link IgnoreCondition}. Note, however,\n * that you can optionally register one or more of these extensions explicitly\n * without the use of this composed annotation.\n *\n * @since 5.4\n * @see ExternalResourceSupport\n * @see VerifierSupport\n * @see ExpectedExceptionSupport\n * @see IgnoreCondition\n * @see EnableRuleMigrationSupport\n * @deprecated Please migrate to Jupiter extensions and use\n * {@link org.junit.jupiter.api.Disabled @Disabled} instead.\n */\n@SuppressWarnings(\"removal\")\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\n@EnableRuleMigrationSupport\n@ExtendWith(IgnoreCondition.class)\npublic @interface EnableJUnit4MigrationSupport {\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/IgnoreCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.conditions;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.reflect.AnnotatedElement;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * {@link ExecutionCondition} that supports JUnit 4's {@link Ignore @Ignore}\n * annotation.\n *\n * @since 5.4\n * @see org.junit.Ignore @Ignore\n * @see org.junit.jupiter.api.Disabled @Disabled\n * @see #evaluateExecutionCondition(ExtensionContext)\n * @see org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport\n * @deprecated Please use {@link org.junit.jupiter.api.Disabled @Disabled} instead.\n */\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\npublic class IgnoreCondition implements ExecutionCondition {\n\n\tprivate static final ConditionEvaluationResult ENABLED = //\n\t\tConditionEvaluationResult.enabled(\"@org.junit.Ignore is not present\");\n\n\tpublic IgnoreCondition() {\n\t}\n\n\t/**\n\t * Containers/tests are disabled if {@link Ignore @Ignore} is present on\n\t * the test class or method.\n\t */\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tAnnotatedElement element = context.getElement().orElse(null);\n\t\treturn findAnnotation(element, Ignore.class) //\n\t\t\t\t.map(annotation -> toResult(element, annotation)) //\n\t\t\t\t.orElse(ENABLED);\n\t}\n\n\tprivate ConditionEvaluationResult toResult(@Nullable AnnotatedElement element, Ignore annotation) {\n\t\tString value = annotation.value();\n\t\tString reason = StringUtils.isNotBlank(value) ? value : element + \" is disabled via @org.junit.Ignore\";\n\t\treturn ConditionEvaluationResult.disabled(reason);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/conditions/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * <em>Deprecated</em> extensions that provide support for conditional test\n * execution features of JUnit 4 (e.g., the {@link org.junit.Ignore @Ignore}\n * annotation) within JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.migrationsupport.conditions;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * <em>Deprecated</em> support for migrating from JUnit 4 to JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.migrationsupport;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * This class-level annotation enables native JUnit 4 rule support\n * within JUnit Jupiter.\n *\n * <p>Currently, rules of type {@code Verifier}, {@code ExternalResource},\n * and {@code ExpectedException} rules are supported.\n *\n * <p>{@code @EnableRuleMigrationSupport} is a composed annotation which\n * enables all supported extensions: {@link VerifierSupport},\n * {@link ExternalResourceSupport}, and {@link ExpectedExceptionSupport}.\n *\n * @since 5.0\n * @see ExternalResourceSupport\n * @see VerifierSupport\n * @see ExpectedExceptionSupport\n * @see org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport\n * @deprecated Please migrate to Jupiter extensions.\n */\n@SuppressWarnings(\"removal\")\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\n@ExtendWith(ExternalResourceSupport.class)\n@ExtendWith(VerifierSupport.class)\n@ExtendWith(ExpectedExceptionSupport.class)\npublic @interface EnableRuleMigrationSupport {\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.migrationsupport.rules.adapter.ExpectedExceptionAdapter;\nimport org.junit.rules.ExpectedException;\n\n/**\n * This {@code Extension} provides native support for the\n * {@link ExpectedException} rule from JUnit 4.\n *\n * <p>By using this class-level extension on a test class,\n * {@code ExpectedException} can continue to be used.\n *\n * <p>However, you should rather switch to\n * {@link org.junit.jupiter.api.Assertions#assertThrows} for new code.\n *\n * @since 5.0\n * @see org.junit.jupiter.api.Assertions#assertThrows\n * @see org.junit.rules.ExpectedException\n * @see org.junit.rules.TestRule\n * @see org.junit.Rule\n * @deprecated Please use\n * {@link org.junit.jupiter.api.Assertions#assertThrows(Class, Executable)}\n * instead.\n */\n@SuppressWarnings(\"removal\")\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\npublic class ExpectedExceptionSupport implements AfterEachCallback, TestExecutionExceptionHandler {\n\n\tprivate static final String EXCEPTION_WAS_HANDLED = \"exceptionWasHandled\";\n\n\tprivate final TestRuleSupport support = new TestRuleSupport(ExpectedExceptionAdapter::new, ExpectedException.class);\n\n\tpublic ExpectedExceptionSupport() {\n\t}\n\n\t@Override\n\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\tgetStore(context).put(EXCEPTION_WAS_HANDLED, true);\n\t\tthis.support.handleTestExecutionException(context, throwable);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) throws Exception {\n\t\tboolean handled = getStore(context).computeIfAbsent(EXCEPTION_WAS_HANDLED, key -> false, Boolean.class);\n\t\tif (!handled) {\n\t\t\tthis.support.afterEach(context);\n\t\t}\n\t}\n\n\tprivate Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(Namespace.create(getClass(), context.getUniqueId()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.migrationsupport.rules.adapter.ExternalResourceAdapter;\nimport org.junit.rules.ExternalResource;\n\n/**\n * This {@code Extension} provides native support for subclasses of\n * the {@link ExternalResource} rule from JUnit 4.\n *\n * <p>{@code @Rule}-annotated fields as well as methods are supported.\n *\n * <p>By using this class-level extension on a test class such\n * {@code ExternalResource} implementations in legacy code bases\n * can be left unchanged including the JUnit 4 rule import statements.\n *\n * <p>However, if you intend to develop a <em>new</em> extension for\n * JUnit please use the new extension model of JUnit Jupiter instead\n * of the rule-based model of JUnit 4.\n *\n * @since 5.0\n * @see org.junit.rules.ExternalResource\n * @see org.junit.rules.TestRule\n * @see org.junit.Rule\n * @deprecated Please use {@link org.junit.jupiter.api.AutoClose @AutoClose} instead.\n */\n@SuppressWarnings(\"removal\")\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\npublic class ExternalResourceSupport implements BeforeEachCallback, AfterEachCallback {\n\n\tprivate final TestRuleSupport support = new TestRuleSupport(ExternalResourceAdapter::new, ExternalResource.class);\n\n\tpublic ExternalResourceSupport() {\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) throws Exception {\n\t\tthis.support.beforeEach(context);\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) throws Exception {\n\t\tthis.support.afterEach(context);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/TestRuleSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static java.util.Collections.unmodifiableList;\nimport static org.junit.platform.commons.support.AnnotationSupport.findPublicAnnotatedFields;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\nimport org.junit.jupiter.migrationsupport.rules.adapter.AbstractTestRuleAdapter;\nimport org.junit.jupiter.migrationsupport.rules.adapter.GenericBeforeAndAfterAdvice;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedField;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMethod;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\nclass TestRuleSupport implements BeforeEachCallback, TestExecutionExceptionHandler, AfterEachCallback {\n\n\tprivate final Class<? extends TestRule> ruleType;\n\tprivate final Function<TestRuleAnnotatedMember, AbstractTestRuleAdapter> adapterGenerator;\n\n\tTestRuleSupport(Function<TestRuleAnnotatedMember, AbstractTestRuleAdapter> adapterGenerator,\n\t\t\tClass<? extends TestRule> ruleType) {\n\n\t\tthis.adapterGenerator = adapterGenerator;\n\t\tthis.ruleType = ruleType;\n\t}\n\n\t/**\n\t * @see org.junit.runners.BlockJUnit4ClassRunner#withRules\n\t * @see org.junit.rules.RunRules\n\t */\n\t@SuppressWarnings(\"JavadocReference\")\n\tprivate List<TestRuleAnnotatedMember> findRuleAnnotatedMembers(Object testInstance) {\n\t\tList<TestRuleAnnotatedMember> result = new ArrayList<>();\n\t\t// @formatter:off\n\t\t// Instantiate rules from methods by calling them\n\t\tfindAnnotatedMethods(testInstance).stream()\n\t\t\t\t.map(method -> new TestRuleAnnotatedMethod(testInstance, method))\n\t\t\t\t.forEach(result::add);\n\t\t// Fields are already instantiated because we have a test instance\n\t\tfindAnnotatedFields(testInstance).stream()\n\t\t\t\t.map(field -> new TestRuleAnnotatedField(testInstance, field))\n\t\t\t\t.forEach(result::add);\n\t\t// @formatter:on\n\t\t// Due to how rules are applied (see RunRules), the last rule gets called first.\n\t\t// Rules from fields get called before those from methods.\n\t\t// Thus, we first add methods and then fields and reverse the list in the end.\n\t\tCollections.reverse(result);\n\t\treturn unmodifiableList(result);\n\t}\n\n\tprivate List<Method> findAnnotatedMethods(Object testInstance) {\n\t\tPredicate<Method> isRuleMethod = method -> isAnnotated(method, Rule.class);\n\t\tPredicate<Method> hasCorrectReturnType = method -> TestRule.class.isAssignableFrom(method.getReturnType());\n\n\t\treturn findMethods(testInstance.getClass(), isRuleMethod.and(hasCorrectReturnType), TOP_DOWN);\n\t}\n\n\tprivate List<Field> findAnnotatedFields(Object testInstance) {\n\t\treturn findPublicAnnotatedFields(testInstance.getClass(), TestRule.class, Rule.class);\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tinvokeAppropriateMethodOnRuleAnnotatedMembers(context, false, GenericBeforeAndAfterAdvice::before);\n\t}\n\n\t@Override\n\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\tint numRuleAnnotatedMembers = invokeAppropriateMethodOnRuleAnnotatedMembers(context, true,\n\t\t\tadvice -> advice.handleTestExecutionException(throwable));\n\n\t\t// If no appropriate @Rule annotated members were discovered, we then\n\t\t// have to rethrow the exception in order not to silently swallow it.\n\t\t// Fixes bug: https://github.com/junit-team/junit-framework/issues/1069\n\t\tif (numRuleAnnotatedMembers == 0) {\n\t\t\tthrow throwable;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t\tinvokeAppropriateMethodOnRuleAnnotatedMembers(context, true, GenericBeforeAndAfterAdvice::after);\n\t}\n\n\t/**\n\t * @return the number of appropriate rule-annotated members that were discovered\n\t */\n\tprivate int invokeAppropriateMethodOnRuleAnnotatedMembers(ExtensionContext context, boolean reverseOrder,\n\t\t\tAdviceInvoker adviceInvoker) {\n\n\t\tList<TestRuleAnnotatedMember> ruleAnnotatedMembers = getRuleAnnotatedMembers(context);\n\t\tif (reverseOrder) {\n\t\t\tCollections.reverse(ruleAnnotatedMembers);\n\t\t}\n\n\t\tAtomicInteger counter = new AtomicInteger();\n\n\t\t// @formatter:off\n\t\truleAnnotatedMembers.stream()\n\t\t\t\t.filter(annotatedMember -> this.ruleType.isInstance(annotatedMember.getTestRule()))\n\t\t\t\t.map(this.adapterGenerator)\n\t\t\t\t.forEach(advice -> {\n\t\t\t\t\tadviceInvoker.invokeAndMaskCheckedExceptions(advice);\n\t\t\t\t\tcounter.incrementAndGet();\n\t\t\t\t});\n\t\t// @formatter:on\n\n\t\treturn counter.get();\n\t}\n\n\t/**\n\t * @return a modifiable copy of the list of rule-annotated members\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tprivate List<TestRuleAnnotatedMember> getRuleAnnotatedMembers(ExtensionContext context) {\n\t\tObject testInstance = context.getRequiredTestInstance();\n\t\tNamespace namespace = Namespace.create(TestRuleSupport.class, context.getRequiredTestClass());\n\t\t// @formatter:off\n\t\treturn new ArrayList<>(context.getStore(namespace)\n\t\t\t\t.computeIfAbsent(\"rule-annotated-members\", key -> findRuleAnnotatedMembers(testInstance), List.class));\n\t\t// @formatter:on\n\t}\n\n\t@FunctionalInterface\n\tprivate interface AdviceInvoker {\n\n\t\tdefault void invokeAndMaskCheckedExceptions(GenericBeforeAndAfterAdvice advice) {\n\t\t\ttry {\n\t\t\t\tinvoke(advice);\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t}\n\t\t}\n\n\t\tvoid invoke(GenericBeforeAndAfterAdvice advice) throws Throwable;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/VerifierSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.migrationsupport.rules.adapter.VerifierAdapter;\nimport org.junit.rules.Verifier;\n\n/**\n * This {@code Extension} provides native support for subclasses of\n * the {@link Verifier} rule from JUnit 4.\n *\n * <p>{@code @Rule}-annotated fields as well as methods are supported.\n *\n * <p>By using this class-level extension on a test class such\n * {@code Verifier} implementations in legacy code bases\n * can be left unchanged including the JUnit 4 rule import statements.\n *\n * <p>However, if you intend to develop a <em>new</em> extension for\n * JUnit please use the new extension model of JUnit Jupiter instead\n * of the rule-based model of JUnit 4.\n *\n * @since 5.0\n * @see org.junit.rules.Verifier\n * @see org.junit.rules.TestRule\n * @see org.junit.Rule\n * @deprecated Please implement {@link org.junit.jupiter.api.extension.AfterTestExecutionCallback} instead.\n */\n@SuppressWarnings(\"removal\")\n@API(status = DEPRECATED, since = \"6.0\")\n@Deprecated(since = \"6.0\", forRemoval = true)\npublic class VerifierSupport implements AfterEachCallback {\n\n\tprivate final TestRuleSupport support = new TestRuleSupport(VerifierAdapter::new, Verifier.class);\n\n\tpublic VerifierSupport() {\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) throws Exception {\n\t\tthis.support.afterEach(context);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/AbstractTestRuleAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethod;\nimport static org.junit.platform.commons.support.ReflectionSupport.invokeMethod;\n\nimport java.lang.reflect.Method;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic abstract class AbstractTestRuleAdapter implements GenericBeforeAndAfterAdvice {\n\n\tprivate final TestRule target;\n\n\tpublic AbstractTestRuleAdapter(TestRuleAnnotatedMember annotatedMember, Class<? extends TestRule> adapteeClass) {\n\t\tthis.target = annotatedMember.getTestRule();\n\t\tPreconditions.condition(adapteeClass.isAssignableFrom(this.target.getClass()),\n\t\t\t() -> adapteeClass + \" is not assignable from \" + this.target.getClass());\n\t}\n\n\tprotected @Nullable Object executeMethod(String name) {\n\t\treturn executeMethod(name, new Class<?>[0]);\n\t}\n\n\tprotected @Nullable Object executeMethod(String methodName, Class<?>[] parameterTypes, Object... arguments) {\n\t\tMethod method = findMethod(this.target.getClass(), methodName, parameterTypes).orElseThrow(\n\t\t\t() -> new JUnitException(\"Failed to find method %s(%s) in class %s\".formatted(methodName,\n\t\t\t\tClassUtils.nullSafeToString(parameterTypes), this.target.getClass().getName())));\n\n\t\treturn invokeMethod(method, this.target, arguments);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExpectedExceptionAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport static java.lang.Boolean.TRUE;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.rules.ExpectedException;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class ExpectedExceptionAdapter extends AbstractTestRuleAdapter {\n\n\tpublic ExpectedExceptionAdapter(TestRuleAnnotatedMember annotatedMember) {\n\t\tsuper(annotatedMember, ExpectedException.class);\n\t}\n\n\t@Override\n\tpublic void handleTestExecutionException(Throwable cause) throws Throwable {\n\t\texecuteMethod(\"handleException\", new Class<?>[] { Throwable.class }, cause);\n\t}\n\n\t@Override\n\tpublic void after() {\n\t\tif (TRUE.equals(executeMethod(\"isAnyExceptionExpected\"))) {\n\t\t\texecuteMethod(\"failDueToMissingException\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/ExternalResourceAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.rules.ExternalResource;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class ExternalResourceAdapter extends AbstractTestRuleAdapter {\n\n\tpublic ExternalResourceAdapter(TestRuleAnnotatedMember annotatedMember) {\n\t\tsuper(annotatedMember, ExternalResource.class);\n\t}\n\n\t@Override\n\tpublic void before() {\n\t\texecuteMethod(\"before\");\n\t}\n\n\t@Override\n\tpublic void after() {\n\t\texecuteMethod(\"after\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/GenericBeforeAndAfterAdvice.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic interface GenericBeforeAndAfterAdvice {\n\n\tdefault void before() {\n\t}\n\n\tdefault void handleTestExecutionException(Throwable cause) throws Throwable {\n\t}\n\n\tdefault void after() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/VerifierAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.rules.Verifier;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class VerifierAdapter extends AbstractTestRuleAdapter {\n\n\tpublic VerifierAdapter(TestRuleAnnotatedMember annotatedMember) {\n\t\tsuper(annotatedMember, Verifier.class);\n\t}\n\n\t@Override\n\tpublic void after() {\n\t\texecuteMethod(\"verify\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/adapter/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * <em>Internal</em> wrappers for JUnit 4 rules to overcome visibility\n * limitations of the JUnit 4 implementations.\n */\n\n@NullMarked\npackage org.junit.jupiter.migrationsupport.rules.adapter;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/AbstractTestRuleAnnotatedMember.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.member;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\nabstract class AbstractTestRuleAnnotatedMember implements TestRuleAnnotatedMember {\n\n\tprivate final TestRule testRule;\n\n\tAbstractTestRuleAnnotatedMember(@Nullable TestRule testRule) {\n\t\tthis.testRule = Preconditions.notNull(testRule, \"TestRule must not be null\");\n\t}\n\n\t@Override\n\tpublic TestRule getTestRule() {\n\t\treturn this.testRule;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedField.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.member;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.ReflectionSupport.makeAccessible;\n\nimport java.lang.reflect.Field;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.1\")\npublic final class TestRuleAnnotatedField extends AbstractTestRuleAnnotatedMember {\n\n\tpublic TestRuleAnnotatedField(Object testInstance, Field field) {\n\t\tsuper(retrieveTestRule(testInstance, field));\n\t}\n\n\tprivate static TestRule retrieveTestRule(Object testInstance, Field field) {\n\t\ttry {\n\t\t\treturn (TestRule) makeAccessible(field).get(testInstance);\n\t\t}\n\t\tcatch (IllegalAccessException exception) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(exception);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMember.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.member;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.1\")\npublic interface TestRuleAnnotatedMember {\n\n\tTestRule getTestRule();\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/TestRuleAnnotatedMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules.member;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.reflect.Method;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.rules.TestRule;\n\n/**\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.1\")\npublic final class TestRuleAnnotatedMethod extends AbstractTestRuleAnnotatedMember {\n\n\tpublic TestRuleAnnotatedMethod(Object testInstance, Method method) {\n\t\tsuper((TestRule) ReflectionSupport.invokeMethod(method, testInstance));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/member/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * <em>Internal</em> abstractions for members which can be targets of JUnit 4 rule annotations.\n */\n\n@NullMarked\npackage org.junit.jupiter.migrationsupport.rules.member;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/main/java/org/junit/jupiter/migrationsupport/rules/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * <em>Deprecated</em> extensions which provide (limited) support for JUnit 4\n * rules within JUnit Jupiter.\n */\n\n@NullMarked\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-migrationsupport/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project.\n"
  },
  {
    "path": "junit-jupiter-params/junit-jupiter-params.gradle.kts",
    "content": "import junitbuild.extensions.javaModuleName\nimport net.ltgt.gradle.errorprone.errorprone\nimport net.ltgt.gradle.nullaway.nullaway\n\nplugins {\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.shadow-conventions\")\n\tid(\"junitbuild.jmh-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Jupiter Params\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitJupiterApi)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tshadowed(libs.fastcsv)\n\n\tcompileOnly(kotlin(\"stdlib\"))\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\ntasks {\n\tjar {\n\t\tbundle {\n\t\t\tval version = project.version\n\t\t\tbnd(\"\"\"\n\t\t\t\tRequire-Capability:\\\n\t\t\t\t\torg.junit.platform.engine;\\\n\t\t\t\t\t\tfilter:='(&(org.junit.platform.engine=junit-jupiter)(version>=${'$'}{version_cleanup;$version})(!(version>=${'$'}{versionmask;+;${'$'}{version_cleanup;$version}})))';\\\n\t\t\t\t\t\teffective:=active\n\t\t\t\"\"\")\n\t\t}\n\t}\n\tval extractFastCSVLicense by registering(Sync::class) {\n\t\tfrom(zipTree(configurations.shadowedClasspath.flatMap { it.elements }.map { it.single { file -> file.asFile.name.contains(\"fastcsv\") } })) {\n\t\t\tinclude(\"META-INF/LICENSE\")\n\t\t\trename { \"LICENSE-fastcsv\" }\n\t\t}\n\t\tinto(layout.buildDirectory.dir(\"fastcsv\"))\n\t}\n\tshadowJar {\n\t\trelocate(\"de.siegmar.fastcsv\", \"org.junit.jupiter.params.shadow.de.siegmar.fastcsv\")\n\t\texclude(\"META-INF/LICENSE\")\n\t\tfrom(extractFastCSVLicense)\n\t}\n\tcompileJava {\n\t\toptions.compilerArgs.addAll(listOf(\n\t\t\t\"--add-modules\", \"de.siegmar.fastcsv\",\n\t\t\t\"--add-reads\", \"${javaModuleName}=de.siegmar.fastcsv\"\n\t\t))\n\t}\n\tcompileJmhJava {\n\t\toptions.compilerArgs.add(\"-Xlint:-processing\")\n\t\toptions.errorprone.nullaway {\n\t\t\tcustomInitializerAnnotations.add(\n\t\t\t\t\"org.openjdk.jmh.annotations.Setup\",\n\t\t\t)\n\t\t}\n\t}\n\tjavadoc {\n\t\t(options as StandardJavadocDocletOptions).apply {\n\t\t\taddStringOption(\"-add-modules\", \"de.siegmar.fastcsv\")\n\t\t\taddStringOption(\"-add-reads\", \"${javaModuleName}=de.siegmar.fastcsv\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/jmh/java/org/junit/jupiter/params/ParameterizedInvocationNameFormatterBenchmarks.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DEFAULT_DISPLAY_NAME;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DISPLAY_NAME_PLACEHOLDER;\n\nimport java.util.List;\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.Fork;\nimport org.openjdk.jmh.annotations.Measurement;\nimport org.openjdk.jmh.annotations.Param;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.Setup;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.Warmup;\nimport org.openjdk.jmh.infra.Blackhole;\n\n@State(Scope.Benchmark)\n@Fork(1)\n@Warmup(iterations = 1, time = 2)\n@Measurement(iterations = 3, time = 2)\npublic class ParameterizedInvocationNameFormatterBenchmarks {\n\n\t@Param({ \"1\", \"2\", \"4\", \"10\", \"100\", \"1000\" })\n\tprivate int numberOfParameters;\n\n\tList<? extends Arguments> argumentsList;\n\n\t@Setup\n\tpublic void setUp() {\n\t\targumentsList = IntStream.range(0, numberOfParameters) //\n\t\t\t\t.mapToObj(i -> Arguments.argumentSet(String.valueOf(i), i)) //\n\t\t\t\t.toList();\n\t}\n\n\t@Benchmark\n\tpublic void formatTestNames(Blackhole blackhole) throws Exception {\n\t\tvar method = TestCase.class.getDeclaredMethod(\"parameterizedTest\", int.class);\n\t\tvar formatter = new ParameterizedInvocationNameFormatter(\n\t\t\tDISPLAY_NAME_PLACEHOLDER + \" \" + DEFAULT_DISPLAY_NAME + \" ({0})\", \"displayName\",\n\t\t\tnew ParameterizedTestContext(TestCase.class, method,\n\t\t\t\trequireNonNull(method.getAnnotation(ParameterizedTest.class))),\n\t\t\t512);\n\t\tfor (int i = 0; i < argumentsList.size(); i++) {\n\t\t\tArguments arguments = argumentsList.get(i);\n\t\t\tblackhole.consume(formatter.format(i, EvaluatedArgumentSet.allOf(arguments), false));\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCase {\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ParameterizedTest\n\t\tvoid parameterizedTest(int param) {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter extension for parameterized tests.\n *\n * @since 5.0\n */\nmodule org.junit.jupiter.params {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.junit.jupiter.api;\n\trequires transitive org.junit.platform.commons;\n\n\texports org.junit.jupiter.params;\n\texports org.junit.jupiter.params.aggregator;\n\texports org.junit.jupiter.params.converter;\n\texports org.junit.jupiter.params.provider;\n\texports org.junit.jupiter.params.support;\n\n\topens org.junit.jupiter.params to org.junit.platform.commons;\n\topens org.junit.jupiter.params.converter to org.junit.platform.commons;\n\topens org.junit.jupiter.params.provider to org.junit.platform.commons;\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/AbstractParameterizedClassInvocationLifecycleMethodInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Objects.requireNonNull;\n\nimport org.junit.jupiter.api.extension.ExecutableInvoker;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * @since 5.13\n */\nabstract class AbstractParameterizedClassInvocationLifecycleMethodInvoker implements ParameterResolver {\n\n\tprivate final ParameterizedClassContext declarationContext;\n\tprivate final EvaluatedArgumentSet arguments;\n\tprivate final int invocationIndex;\n\tprivate final ResolutionCache resolutionCache;\n\tprivate final ArgumentSetLifecycleMethod lifecycleMethod;\n\n\tAbstractParameterizedClassInvocationLifecycleMethodInvoker(ParameterizedClassContext declarationContext,\n\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache,\n\t\t\tArgumentSetLifecycleMethod lifecycleMethod) {\n\t\tthis.declarationContext = declarationContext;\n\t\tthis.arguments = arguments;\n\t\tthis.invocationIndex = invocationIndex;\n\t\tthis.resolutionCache = resolutionCache;\n\t\tthis.lifecycleMethod = lifecycleMethod;\n\t}\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\t\treturn parameterContext.getDeclaringExecutable().equals(this.lifecycleMethod.method) //\n\t\t\t\t&& this.lifecycleMethod.parameterResolver.supports(parameterContext);\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\t\treturn requireNonNull(this.lifecycleMethod.parameterResolver //\n\t\t\t\t.resolve(parameterContext, extensionContext, this.arguments, this.invocationIndex,\n\t\t\t\t\tthis.resolutionCache));\n\t}\n\n\tprotected void invoke(ExtensionContext context) {\n\t\tif (isCorrectTestClass(context)) {\n\t\t\tExecutableInvoker executableInvoker = context.getExecutableInvoker();\n\t\t\tObject testInstance = context.getTestInstance().orElse(null);\n\t\t\texecutableInvoker.invoke(this.lifecycleMethod.method, testInstance);\n\t\t}\n\t}\n\n\tprivate boolean isCorrectTestClass(ExtensionContext context) {\n\t\treturn this.declarationContext.getAnnotatedElement().equals(context.getTestClass().orElse(null));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/AfterParameterizedClassInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationLifecycleMethod;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\n\n/**\n * {@code @AfterParameterizedClassInvocation} is used to signal that the\n * annotated method should be executed <em>before</em> <strong>each</strong>\n * invocation of the current {@link ParameterizedClass @ParameterizedClass}.\n *\n * <p>Declaring {@code @AfterParameterizedClassInvocation} methods in a regular,\n * non-parameterized test class has no effect and will be ignored.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @AfterParameterizedClassInvocation} methods must have a\n * {@code void} return type, must not be private, and must be {@code static}\n * unless the test class is annotated with\n * {@link org.junit.jupiter.api.TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <h2>Method Arguments</h2>\n *\n * <p>{@code @AfterParameterizedClassInvocation} methods may optionally declare\n * parameters that are resolved depending on the setting of the\n * {@link #injectArguments()} attribute.\n *\n * <p>If {@link #injectArguments()} is set to {@code false}, the parameters must\n * be resolved by other registered\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>If {@link #injectArguments()} is set to {@code true} (the default), the\n * method must declare the same parameters, in the same order, as the\n * <em>indexed parameters</em> (see\n * {@link ParameterizedClass @ParameterizedClass}) of the parameterized test\n * class. It may declare a subset of the indexed parameters starting from the\n * first argument. Additionally, the method may declare custom <em>aggregator\n * parameters</em> (see {@link ParameterizedClass @ParameterizedClass}) at the\n * end of its parameter list. If the method declares additional parameters after\n * these aggregator parameters, or more parameters than the class has indexed\n * parameters, they may be resolved by other\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>For example, given a {@link ParameterizedClass @ParameterizedClass} with\n * <em>indexed parameters</em> of type {@code int} and {@code String}, the\n * following method signatures are valid:\n *\n * <pre>{@code\n * @AfterParameterizedClassInvocation\n * void afterInvocation() { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(int number) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(int number, String text) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(int number, String text, TestInfo testInfo) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(ArgumentsAccessor accessor) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(ArgumentsAccessor accessor, TestInfo testInfo) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(int number, String text, ArgumentsAccessor accessor) { ... }\n *\n * @AfterParameterizedClassInvocation\n * void afterInvocation(int number, String text, ArgumentsAccessor accessor, TestInfo testInfo) { ... }\n * }</pre>\n *\n * <p>In the snippet above,{@link ArgumentsAccessor} is used as an example of an\n * aggregator parameter but the same applies to any parameter annotated with\n * {@link AggregateWith @AggregateWith}. The parameter of type\n * {@link org.junit.jupiter.api.TestInfo TestInfo} is used as an example of a\n * parameter that is resolved by another\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolver}.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @AfterParameterizedClassInvocation} methods are inherited from\n * superclasses as long as they are not <em>overridden</em> according to the\n * visibility rules of the Java language. Furthermore,\n * {@code @AfterParameterizedClassInvocation} methods from superclasses will be\n * executed before {@code @AfterParameterizedClassInvocation} methods in\n * subclasses.\n *\n * <p>Similarly, {@code @AfterParameterizedClassInvocation} methods declared in\n * an interface are inherited as long as they are not overridden, and\n * {@code @AfterParameterizedClassInvocation} methods from an interface will be\n * executed before {@code @AfterParameterizedClassInvocation} methods in the\n * class that implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @AfterParameterizedClassInvocation} methods that are declared within a\n * single parameterized test class or test interface. While it may at times\n * appear that these methods are invoked in alphabetical order, they are in fact\n * sorted using an algorithm that is deterministic but intentionally\n * non-obvious.\n *\n * <p>In addition, {@code @AfterParameterizedClassInvocation} methods are in no\n * way linked to {@code @BeforeParameterizedClassInvocation} methods.\n * Consequently, there are no guarantees with regard to their <em>wrapping</em>\n * behavior. For example, given two {@code @AfterParameterizedClassInvocation}\n * methods {@code createA()} and {@code createB()} as well as two\n * {@code @BeforeParameterizedClassInvocation} methods {@code destroyA()} and\n * {@code destroyB()}, the order in which the\n * {@code @AfterParameterizedClassInvocation} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @BeforeParameterizedClassInvocation} methods.\n * In other words, {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @AfterParameterizedClassInvocation} method and at\n * most one {@code @BeforeParameterizedClassInvocation} method per test class or\n * test interface unless there are no dependencies between the\n * {@code @AfterParameterizedClassInvocation} methods or between the\n * {@code @BeforeParameterizedClassInvocation} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @AfterParameterizedClassInvocation} may be used as a\n * meta-annotation in order to create a custom <em>composed annotation</em> that\n * inherits the semantics of {@code @AfterParameterizedClassInvocation}.\n *\n * @since 5.13\n * @see ParameterizedClass\n * @see BeforeParameterizedClassInvocation\n * @see org.junit.jupiter.api.TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = EXPERIMENTAL, since = \"6.0\")\n@ClassTemplateInvocationLifecycleMethod(classTemplateAnnotation = ParameterizedClass.class, lifecycleMethodAnnotation = AfterParameterizedClassInvocation.class)\npublic @interface AfterParameterizedClassInvocation {\n\n\t/**\n\t * Whether the arguments of the parameterized test class should be injected\n\t * into the annotated method (defaults to {@code true}).\n\t */\n\tboolean injectArguments() default true;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/AfterParameterizedClassInvocationMethodInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.13\n */\nclass AfterParameterizedClassInvocationMethodInvoker extends AbstractParameterizedClassInvocationLifecycleMethodInvoker\n\t\timplements AfterClassTemplateInvocationCallback {\n\n\tAfterParameterizedClassInvocationMethodInvoker(ParameterizedClassContext declarationContext,\n\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache,\n\t\t\tArgumentSetLifecycleMethod lifecycleMethod) {\n\t\tsuper(declarationContext, arguments, invocationIndex, resolutionCache, lifecycleMethod);\n\t}\n\n\t@Override\n\tpublic void afterClassTemplateInvocation(ExtensionContext context) {\n\t\tinvoke(context);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\n\n/**\n * Enumeration of argument count validation modes for\n * {@link ParameterizedClass @ParameterizedClass} and\n * {@link ParameterizedTest @ParameterizedTest}.\n *\n * <p>When an {@link ArgumentsSource} provides more arguments than declared by\n * the parameterized class or method, there might be a bug in the class/method\n * or the {@link ArgumentsSource}. By default, the additional arguments are\n * ignored. {@link ArgumentCountValidationMode} allows you to control how\n * additional arguments are handled.\n *\n * @since 5.12\n * @see ParameterizedClass#argumentCountValidation()\n * @see ParameterizedTest#argumentCountValidation()\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic enum ArgumentCountValidationMode {\n\n\t/**\n\t * Use the default validation mode.\n\t *\n\t * <p>The default validation mode may be changed via the\n\t * {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY}\n\t * configuration parameter (see the User Guide for details on configuration\n\t * parameters).\n\t */\n\tDEFAULT,\n\n\t/**\n\t * Use the \"none\" argument count validation mode.\n\t *\n\t * <p>When there are more arguments provided than declared by the\n\t * parameterized class or method, these additional arguments are ignored.\n\t */\n\tNONE,\n\n\t/**\n\t * Use the strict argument count validation mode.\n\t *\n\t * <p>When there are more arguments provided than declared by the\n\t * parameterized class or method, this raises an error.\n\t */\n\tSTRICT\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\n\nclass ArgumentCountValidator {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ArgumentCountValidator.class);\n\n\tstatic final String ARGUMENT_COUNT_VALIDATION_KEY = \"junit.jupiter.params.argumentCountValidation\";\n\tprivate static final Namespace NAMESPACE = Namespace.create(ArgumentCountValidator.class);\n\n\tprivate final ParameterizedDeclarationContext<?> declarationContext;\n\tprivate final EvaluatedArgumentSet arguments;\n\n\tArgumentCountValidator(ParameterizedDeclarationContext<?> declarationContext, EvaluatedArgumentSet arguments) {\n\t\tthis.declarationContext = declarationContext;\n\t\tthis.arguments = arguments;\n\t}\n\n\tvoid validate(ExtensionContext extensionContext) {\n\t\tvalidateRequiredArgumentsArePresent();\n\t\tArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext);\n\t\tswitch (argumentCountValidationMode) {\n\t\t\tcase DEFAULT, NONE -> {\n\t\t\t}\n\t\t\tcase STRICT -> {\n\t\t\t\tint consumedCount = this.declarationContext.getResolverFacade().determineConsumedArgumentCount(\n\t\t\t\t\tthis.arguments);\n\t\t\t\tint totalCount = this.arguments.getTotalLength();\n\t\t\t\tPreconditions.condition(consumedCount == totalCount,\n\t\t\t\t\t() -> wrongNumberOfArgumentsMessages(\"consumes\", consumedCount, null, null));\n\t\t\t}\n\t\t\tdefault -> throw new ExtensionConfigurationException(\n\t\t\t\t\"Unsupported argument count validation mode: \" + argumentCountValidationMode);\n\t\t}\n\t}\n\n\tprivate void validateRequiredArgumentsArePresent() {\n\t\tvar requiredParameterCount = this.declarationContext.getResolverFacade().getRequiredParameterCount();\n\t\tif (requiredParameterCount != null) {\n\t\t\tvar totalCount = this.arguments.getTotalLength();\n\t\t\tPreconditions.condition(requiredParameterCount.value() <= totalCount,\n\t\t\t\t() -> wrongNumberOfArgumentsMessages(\"has\", requiredParameterCount.value(), \"required\",\n\t\t\t\t\trequiredParameterCount.reason()));\n\t\t}\n\t}\n\n\tprivate String wrongNumberOfArgumentsMessages(String verb, int actualCount, @Nullable String parameterAdjective,\n\t\t\t@Nullable String reason) {\n\t\tint totalCount = this.arguments.getTotalLength();\n\t\treturn \"Configuration error: @%s %s %s %s%s%s but there %s %s %s provided.%nNote: the provided arguments were %s\".formatted(\n\t\t\tthis.declarationContext.getAnnotationName(), verb, actualCount,\n\t\t\tparameterAdjective == null ? \"\" : parameterAdjective + \" \",\n\t\t\tpluralize(actualCount, \"parameter\", \"parameters\"), reason == null ? \"\" : \" (due to %s)\".formatted(reason),\n\t\t\tpluralize(totalCount, \"was\", \"were\"), totalCount, pluralize(totalCount, \"argument\", \"arguments\"),\n\t\t\tArrays.toString(this.arguments.getAllPayloads()));\n\t}\n\n\tprivate ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) {\n\t\tArgumentCountValidationMode mode = declarationContext.getArgumentCountValidationMode();\n\t\tif (mode != ArgumentCountValidationMode.DEFAULT) {\n\t\t\treturn mode;\n\t\t}\n\t\telse {\n\t\t\treturn getArgumentCountValidationModeConfiguration(extensionContext);\n\t\t}\n\t}\n\n\tprivate ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) {\n\t\tString key = ARGUMENT_COUNT_VALIDATION_KEY;\n\t\tArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE;\n\t\tExtensionContext.Store store = getStore(extensionContext);\n\t\treturn store.computeIfAbsent(key, __ -> {\n\t\t\tOptional<String> optionalConfigValue = extensionContext.getConfigurationParameter(key);\n\t\t\tif (optionalConfigValue.isPresent()) {\n\t\t\t\tString configValue = optionalConfigValue.get();\n\t\t\t\tOptional<ArgumentCountValidationMode> enumValue = Arrays.stream(\n\t\t\t\t\tArgumentCountValidationMode.values()).filter(\n\t\t\t\t\t\tmode -> mode.name().equalsIgnoreCase(configValue)).findFirst();\n\t\t\t\tif (enumValue.isPresent()) {\n\t\t\t\t\tlogger.config(\n\t\t\t\t\t\t() -> \"Using ArgumentCountValidationMode '%s' set via the '%s' configuration parameter.\".formatted(\n\t\t\t\t\t\t\tenumValue.get().name(), key));\n\t\t\t\t\treturn enumValue.get();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlogger.warn(() -> \"\"\"\n\t\t\t\t\t\t\tInvalid ArgumentCountValidationMode '%s' set via the '%s' configuration parameter. \\\n\t\t\t\t\t\t\tFalling back to the %s default value.\"\"\".formatted(configValue, key, fallback.name()));\n\t\t\t\t\treturn fallback;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn fallback;\n\t\t\t}\n\t\t}, ArgumentCountValidationMode.class);\n\t}\n\n\tprivate static String pluralize(int count, String singular, String plural) {\n\t\treturn count == 1 ? singular : plural;\n\t}\n\n\tprivate ExtensionContext.Store getStore(ExtensionContext context) {\n\t\treturn context.getRoot().getStore(NAMESPACE);\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentSetLifecycleMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Method;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.13\n */\nclass ArgumentSetLifecycleMethod {\n\n\tfinal Method method;\n\tfinal ParameterResolver parameterResolver;\n\n\tArgumentSetLifecycleMethod(Method method) {\n\t\tthis(method, ParameterResolver.DISABLED);\n\t}\n\n\tArgumentSetLifecycleMethod(Method method, ParameterResolver parameterResolver) {\n\t\tthis.method = Preconditions.notNull(method, \"method must not be null\");\n\t\tthis.parameterResolver = Preconditions.notNull(parameterResolver, \"parameterResolver must not be null\");\n\t}\n\n\tinterface ParameterResolver {\n\n\t\tParameterResolver DISABLED = new ParameterResolver() {\n\t\t\t@Override\n\t\t\tpublic boolean supports(ParameterContext parameterContext) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext,\n\t\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache) {\n\t\t\t\tthrow new JUnitException(\"Parameter resolution is disabled\");\n\t\t\t}\n\t\t};\n\n\t\tboolean supports(ParameterContext parameterContext);\n\n\t\t@Nullable\n\t\tObject resolve(ParameterContext parameterContext, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache);\n\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/BeforeClassTemplateInvocationFieldInjector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\nclass BeforeClassTemplateInvocationFieldInjector implements BeforeClassTemplateInvocationCallback {\n\n\tprivate final ResolverFacade resolverFacade;\n\tprivate final EvaluatedArgumentSet arguments;\n\tprivate final int invocationIndex;\n\tprivate final ResolutionCache resolutionCache;\n\n\tBeforeClassTemplateInvocationFieldInjector(ResolverFacade resolverFacade, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\t\tthis.resolverFacade = resolverFacade;\n\t\tthis.arguments = arguments;\n\t\tthis.invocationIndex = invocationIndex;\n\t\tthis.resolutionCache = resolutionCache;\n\t}\n\n\t@Override\n\tpublic void beforeClassTemplateInvocation(ExtensionContext extensionContext) {\n\t\textensionContext.getTestInstance() //\n\t\t\t\t.ifPresent(testInstance -> this.resolverFacade //\n\t\t\t\t\t\t.resolveAndInjectFields(testInstance, extensionContext, this.arguments, this.invocationIndex,\n\t\t\t\t\t\t\tthis.resolutionCache));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/BeforeParameterizedClassInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationLifecycleMethod;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\n\n/**\n * {@code @BeforeParameterizedClassInvocation} is used to signal that the\n * annotated method should be executed <em>before</em> <strong>each</strong>\n * invocation of the current {@link ParameterizedClass @ParameterizedClass}.\n *\n * <p>Declaring {@code @BeforeParameterizedClassInvocation} methods in a\n * regular, non-parameterized test class has no effect and will be ignored.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @BeforeParameterizedClassInvocation} methods must have a\n * {@code void} return type, must not be private, and must be {@code static}\n * unless the test class is annotated with\n * {@link org.junit.jupiter.api.TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <h2>Method Arguments</h2>\n *\n * <p>{@code @BeforeParameterizedClassInvocation} methods may optionally declare\n * parameters that are resolved depending on the setting of the\n * {@link #injectArguments()} attribute.\n *\n * <p>If {@link #injectArguments()} is set to {@code false}, the parameters must\n * be resolved by other registered\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>If {@link #injectArguments()} is set to {@code true} (the default), the\n * method must declare the same parameters, in the same order, as the\n * <em>indexed parameters</em> (see\n * {@link ParameterizedClass @ParameterizedClass}) of the parameterized test\n * class. It may declare a subset of the indexed parameters starting from the\n * first argument. Additionally, the method may declare custom <em>aggregator\n * parameters</em> (see {@link ParameterizedClass @ParameterizedClass}) at the\n * end of its parameter list. If the method declares additional parameters after\n * these aggregator parameters, or more parameters than the class has indexed\n * parameters, they may be resolved by other\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}.\n *\n * <p>For example, given a {@link ParameterizedClass @ParameterizedClass} with\n * <em>indexed parameters</em> of type {@code int} and {@code String}, the\n * following method signatures are valid:\n *\n * <pre>{@code\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation() { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(int number) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(int number, String text) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(int number, String text, TestInfo testInfo) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(ArgumentsAccessor accessor) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(ArgumentsAccessor accessor, TestInfo testInfo) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(int number, String text, ArgumentsAccessor accessor) { ... }\n *\n * @BeforeParameterizedClassInvocation\n * void beforeInvocation(int number, String text, ArgumentsAccessor accessor, TestInfo testInfo) { ... }\n * }</pre>\n *\n * <p>In the snippet above,{@link ArgumentsAccessor} is used as an example of an\n * aggregator parameter but the same applies to any parameter annotated with\n * {@link AggregateWith @AggregateWith}. The parameter of type\n * {@link org.junit.jupiter.api.TestInfo TestInfo} is used as an example of a\n * parameter that is resolved by another\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolver}.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @BeforeParameterizedClassInvocation} methods are inherited from\n * superclasses as long as they are not <em>overridden</em> according to the\n * visibility rules of the Java language. Furthermore,\n * {@code @BeforeParameterizedClassInvocation} methods from superclasses will be\n * executed before {@code @BeforeParameterizedClassInvocation} methods in\n * subclasses.\n *\n * <p>Similarly, {@code @BeforeParameterizedClassInvocation} methods declared in\n * an interface are inherited as long as they are not overridden, and\n * {@code @BeforeParameterizedClassInvocation} methods from an interface will be\n * executed before {@code @BeforeParameterizedClassInvocation} methods in the\n * class that implements the interface.\n *\n * <p>JUnit Jupiter does not guarantee the execution order of multiple\n * {@code @BeforeParameterizedClassInvocation} methods that are declared within\n * a single parameterized test class or test interface. While it may at times\n * appear that these methods are invoked in alphabetical order, they are in fact\n * sorted using an algorithm that is deterministic but intentionally\n * non-obvious.\n *\n * <p>In addition, {@code @BeforeParameterizedClassInvocation} methods are in no\n * way linked to {@code @AfterParameterizedClassInvocation} methods.\n * Consequently, there are no guarantees with regard to their <em>wrapping</em>\n * behavior. For example, given two {@code @BeforeParameterizedClassInvocation}\n * methods {@code createA()} and {@code createB()} as well as two\n * {@code @AfterParameterizedClassInvocation} methods {@code destroyA()} and\n * {@code destroyB()}, the order in which the\n * {@code @BeforeParameterizedClassInvocation} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterParameterizedClassInvocation} methods.\n * In other words, {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeParameterizedClassInvocation} method and at\n * most one {@code @AfterParameterizedClassInvocation} method per test class or\n * test interface unless there are no dependencies between the\n * {@code @BeforeParameterizedClassInvocation} methods or between the\n * {@code @AfterParameterizedClassInvocation} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @BeforeParameterizedClassInvocation} may be used as a\n * meta-annotation in order to create a custom <em>composed annotation</em> that\n * inherits the semantics of {@code @BeforeParameterizedClassInvocation}.\n *\n * @since 5.13\n * @see ParameterizedClass\n * @see AfterParameterizedClassInvocation\n * @see org.junit.jupiter.api.TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = EXPERIMENTAL, since = \"6.0\")\n@ClassTemplateInvocationLifecycleMethod(classTemplateAnnotation = ParameterizedClass.class, lifecycleMethodAnnotation = BeforeParameterizedClassInvocation.class)\npublic @interface BeforeParameterizedClassInvocation {\n\n\t/**\n\t * Whether the arguments of the parameterized test class should be injected\n\t * into the annotated method (defaults to {@code true}).\n\t */\n\tboolean injectArguments() default true;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/BeforeParameterizedClassInvocationMethodInvoker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.13\n */\nclass BeforeParameterizedClassInvocationMethodInvoker extends AbstractParameterizedClassInvocationLifecycleMethodInvoker\n\t\timplements BeforeClassTemplateInvocationCallback {\n\n\tBeforeParameterizedClassInvocationMethodInvoker(ParameterizedClassContext declarationContext,\n\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache,\n\t\t\tArgumentSetLifecycleMethod lifecycleMethod) {\n\t\tsuper(declarationContext, arguments, invocationIndex, resolutionCache, lifecycleMethod);\n\t}\n\n\t@Override\n\tpublic void beforeClassTemplateInvocation(ExtensionContext context) {\n\t\tinvoke(context);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ClassTemplateConstructorParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.13\n */\nclass ClassTemplateConstructorParameterResolver extends ParameterizedInvocationParameterResolver {\n\n\tprivate final Class<?> classTemplateClass;\n\n\tClassTemplateConstructorParameterResolver(ParameterizedClassContext classContext, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\t\tsuper(classContext.getResolverFacade(), arguments, invocationIndex, resolutionCache);\n\t\tthis.classTemplateClass = classContext.getAnnotatedElement();\n\t}\n\n\t@Override\n\tprotected boolean isSupportedOnConstructorOrMethod(Executable declaringExecutable,\n\t\t\tExtensionContext extensionContext) {\n\t\treturn declaringExecutable instanceof Constructor //\n\t\t\t\t&& this.classTemplateClass.equals(declaringExecutable.getDeclaringClass());\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/DefaultParameterInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\n/**\n * @since 5.13\n */\n@SuppressWarnings(\"removal\")\nrecord DefaultParameterInfo(ParameterDeclarations declarations, ArgumentsAccessor arguments)\n\t\timplements org.junit.jupiter.params.support.ParameterInfo {\n\n\t@Override\n\tpublic ParameterDeclarations getDeclarations() {\n\t\treturn this.declarations;\n\t}\n\n\t@Override\n\tpublic ArgumentsAccessor getArguments() {\n\t\treturn this.arguments;\n\t}\n\n\tvoid store(ExtensionContext context) {\n\t\tcontext.getStore(org.junit.jupiter.params.ParameterInfo.NAMESPACE) //\n\t\t\t\t.put(org.junit.jupiter.params.ParameterInfo.KEY, this);\n\t\tcontext.getStore(org.junit.jupiter.params.support.ParameterInfo.NAMESPACE) //\n\t\t\t\t.put(org.junit.jupiter.params.support.ParameterInfo.KEY, this);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/EvaluatedArgumentSet.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.IntUnaryOperator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.Arguments.ArgumentSet;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Encapsulates the evaluation of an {@link Arguments} instance (so it happens\n * only once) and access to the resulting argument values.\n *\n * <p>The provided accessor methods are focused on the different use cases and\n * make it less error-prone to access the argument values.\n *\n * @since 5.13\n */\nclass EvaluatedArgumentSet {\n\n\tstatic EvaluatedArgumentSet allOf(Arguments arguments) {\n\t\t@Nullable\n\t\tObject[] all = arguments.get();\n\t\treturn create(all, all, arguments);\n\t}\n\n\tstatic EvaluatedArgumentSet of(Arguments arguments, IntUnaryOperator consumedLengthComputer) {\n\t\t@Nullable\n\t\tObject[] all = arguments.get();\n\t\t@Nullable\n\t\tObject[] consumed = dropSurplus(all, consumedLengthComputer.applyAsInt(all.length));\n\t\treturn create(all, consumed, arguments);\n\t}\n\n\tprivate static EvaluatedArgumentSet create(@Nullable Object[] all, @Nullable Object[] consumed,\n\t\t\tArguments arguments) {\n\t\treturn new EvaluatedArgumentSet(all, consumed, determineName(arguments));\n\t}\n\n\tprivate final @Nullable Object[] all;\n\tprivate final @Nullable Object[] consumed;\n\tprivate final Optional<String> name;\n\n\tprivate EvaluatedArgumentSet(@Nullable Object[] all, @Nullable Object[] consumed, Optional<String> name) {\n\t\tthis.all = all;\n\t\tthis.consumed = consumed;\n\t\tthis.name = name;\n\t}\n\n\tint getTotalLength() {\n\t\treturn this.all.length;\n\t}\n\n\t@Nullable\n\tObject[] getAllPayloads() {\n\t\treturn extractFromNamed(this.all, Named::getPayload);\n\t}\n\n\tint getConsumedLength() {\n\t\treturn this.consumed.length;\n\t}\n\n\t@Nullable\n\tObject[] getConsumedArguments() {\n\t\treturn this.consumed;\n\t}\n\n\t@Nullable\n\tObject[] getConsumedPayloads() {\n\t\treturn extractFromNamed(this.consumed, Named::getPayload);\n\t}\n\n\t@Nullable\n\tObject getConsumedPayload(int index) {\n\t\treturn extractFromNamed(this.consumed[index], Named::getPayload);\n\t}\n\n\tOptional<String> getName() {\n\t\treturn this.name;\n\t}\n\n\tprivate static @Nullable Object[] dropSurplus(@Nullable Object[] arguments, int newLength) {\n\t\tPreconditions.condition(newLength <= arguments.length,\n\t\t\t() -> \"New length %d must be less than or equal to the total length %d\".formatted(newLength,\n\t\t\t\targuments.length));\n\t\treturn arguments.length > newLength ? Arrays.copyOf(arguments, newLength) : arguments;\n\t}\n\n\tprivate static Optional<String> determineName(Arguments arguments) {\n\t\tif (arguments instanceof ArgumentSet set) {\n\t\t\treturn Optional.of(set.getName());\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate static @Nullable Object[] extractFromNamed(@Nullable Object[] arguments,\n\t\t\tFunction<Named<?>, @Nullable Object> mapper) {\n\t\treturn Arrays.stream(arguments) //\n\t\t\t\t.map(argument -> extractFromNamed(argument, mapper)) //\n\t\t\t\t.toArray();\n\t}\n\n\tprivate static @Nullable Object extractFromNamed(@Nullable Object argument,\n\t\t\tFunction<Named<?>, @Nullable Object> mapper) {\n\t\treturn argument instanceof Named<?> named ? mapper.apply(named) : argument;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/InstancePostProcessingClassTemplateFieldInjector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\n\nclass InstancePostProcessingClassTemplateFieldInjector implements TestInstancePostProcessor {\n\n\tprivate final ResolverFacade resolverFacade;\n\tprivate final EvaluatedArgumentSet arguments;\n\tprivate final int invocationIndex;\n\tprivate final ResolutionCache resolutionCache;\n\n\tInstancePostProcessingClassTemplateFieldInjector(ResolverFacade resolverFacade, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\t\tthis.resolverFacade = resolverFacade;\n\t\tthis.arguments = arguments;\n\t\tthis.invocationIndex = invocationIndex;\n\t\tthis.resolutionCache = resolutionCache;\n\t}\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext) {\n\t\tthis.resolverFacade.resolveAndInjectFields(testInstance, extensionContext, this.arguments, this.invocationIndex,\n\t\t\tthis.resolutionCache);\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/Parameter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\n\n/**\n * {@code @Parameter} is used to signal that a field in a\n * {@code @ParameterizedClass} constitutes a <em>parameter</em> and marks it for\n * field injection.\n *\n * <p>{@code @Parameter} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @Parameter}.\n *\n * @since 5.13\n * @see ParameterizedClass\n * @see ArgumentsAccessor\n * @see AggregateWith\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n * @see org.junit.jupiter.params.converter.ConvertWith\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD })\n@Documented\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic @interface Parameter {\n\n\t/**\n\t * Constant that indicates that the index of the parameter is unset.\n\t */\n\tint UNSET_INDEX = -1;\n\n\t/**\n\t * {@return the index of the parameter in the list of parameters}\n\t *\n\t * <p>Must be {@value #UNSET_INDEX} (the default) for <em>aggregators</em>,\n\t * that is any field of type {@link ArgumentsAccessor} or any field\n\t * annotated with {@link AggregateWith @AggregateWith}.\n\t *\n\t * <p>May be omitted if there's a single <em>indexed parameter</em>.\n\t * Otherwise, must be unique among all <em>indexed parameters</em> of the\n\t * parameterized class and its superclasses.\n\t */\n\tint value() default UNSET_INDEX;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\n/**\n * {@code ParameterInfo} is used to provide information about the current\n * invocation of a parameterized class or test.\n *\n * <p>Registered {@link Extension} implementations may retrieve the current\n * {@code ParameterInfo} instance by calling\n * {@link ExtensionContext#getStore(Namespace)} with {@link #NAMESPACE} and\n * {@link ExtensionContext.Store#get(Object, Class) Store.get(...)} with\n * {@link #KEY}. Alternatively, the {@link #get(ExtensionContext)} method may\n * be used to retrieve the {@code ParameterInfo} instance for the supplied\n * {@code ExtensionContext}. Extensions must not modify any entries in the\n * {@link ExtensionContext.Store Store} for {@link #NAMESPACE}.\n *\n * <p>When a {@link ParameterizedTest @ParameterizedTest} method is declared\n * inside a {@link ParameterizedClass @ParameterizedClass} or a\n * {@link Nested @Nested} {@link ParameterizedClass @ParameterizedClass} is\n * declared inside an enclosing {@link ParameterizedClass @ParameterizedClass},\n * there will be multiple {@code ParameterInfo} instances available on different\n * levels of the {@link ExtensionContext} hierarchy. In such cases, please use\n * {@link ExtensionContext#getParent()} to navigate to the right level before\n * retrieving the {@code ParameterInfo} instance from the\n * {@link ExtensionContext.Store Store}.\n *\n * @since 5.14\n * @see ParameterizedClass\n * @see ParameterizedTest\n */\n@API(status = EXPERIMENTAL, since = \"5.14\")\npublic interface ParameterInfo {\n\n\t/**\n\t * The {@link Namespace} for accessing the\n\t * {@link ExtensionContext.Store Store} for {@code ParameterInfo}.\n\t */\n\tNamespace NAMESPACE = Namespace.create(ParameterInfo.class);\n\n\t/**\n\t * The key for retrieving the {@code ParameterInfo} instance from the\n\t * {@link ExtensionContext.Store Store}.\n\t */\n\tObject KEY = ParameterInfo.class;\n\n\t/**\n\t * {@return the closest {@code ParameterInfo} instance for the supplied\n\t * {@code ExtensionContext}; potentially {@code null}}\n\t */\n\tstatic @Nullable ParameterInfo get(ExtensionContext context) {\n\t\treturn context.getStore(NAMESPACE).get(KEY, ParameterInfo.class);\n\t}\n\n\t/**\n\t * {@return the declarations of all <em>indexed</em> parameters}\n\t */\n\tParameterDeclarations getDeclarations();\n\n\t/**\n\t * {@return an accessor for the arguments of the current invocation}\n\t */\n\tArgumentsAccessor getArguments();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\n\n/**\n * {@code @ParameterizedClass} is used to signal that the annotated class is\n * a <em>parameterized test class</em>.\n *\n * <h2>Arguments Providers and Sources</h2>\n *\n * <p>A {@code @ParameterizedClass} must specify at least one\n * {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider}\n * via {@link org.junit.jupiter.params.provider.ArgumentsSource @ArgumentsSource}\n * or a corresponding composed annotation (such as {@code @ValueSource},\n * {@code @CsvSource}, etc.). The provider is responsible for providing a\n * {@link java.util.stream.Stream Stream} of\n * {@link org.junit.jupiter.params.provider.Arguments Arguments} that will be\n * used to invoke the parameterized class.\n *\n * <h2>Field or Constructor Injection</h2>\n *\n * <p>The provided arguments can either be injected into fields annotated with\n * {@link Parameter @Parameter} or passed to the unique constructor of the\n * parameterized class. If a {@code @Parameter}-annotated field is declared in\n * the parameterized class or one of its superclasses, field injection will be\n * used. Otherwise, constructor injection will be used.\n *\n * <h3>Constructor Injection</h3>\n *\n * <p>A {@code @ParameterizedClass} constructor may declare additional\n * parameters at the end of its parameter list to be resolved by other\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}\n * (such as {@code TestInfo}, {@code TestReporter}, etc.). Specifically, such a\n * constructor must declare formal parameters according to the following rules.\n *\n * <ol>\n * <li>Zero or more <em>indexed parameters</em> must be declared first.</li>\n * <li>Zero or more <em>aggregators</em> must be declared next.</li>\n * <li>Zero or more parameters supplied by other {@code ParameterResolver}\n * implementations must be declared last.</li>\n * </ol>\n *\n * <p>In this context, an <em>indexed parameter</em> is an argument for a given\n * index in the {@code Arguments} provided by an {@code ArgumentsProvider} that\n * is supplied as an argument to the parameterized class at the same index in\n * the constructor's formal parameter list. An <em>aggregator</em> is any\n * parameter of type\n * {@link org.junit.jupiter.params.aggregator.ArgumentsAccessor ArgumentsAccessor}\n * or any parameter annotated with\n * {@link org.junit.jupiter.params.aggregator.AggregateWith @AggregateWith}.\n *\n * <h3>Field injection</h3>\n *\n * <p>Fields annotated with {@code @Parameter} must be declared according to the\n * following rules.\n *\n * <ol>\n * <li>Zero or more <em>indexed parameters</em> may be declared; each must have\n * a unique index specified in its {@code @Parameter(index)} annotation. The\n * index may be omitted if there is only one indexed parameter. If there are at\n * least two indexed parameter declarations, there must be declarations for all\n * indexes from 0 to the largest declared index.</li>\n * <li>Zero or more <em>aggregators</em> may be declared; each without\n * specifying an index in its {@code @Parameter} annotation.</li>\n * <li>Zero or more other fields may be declared as usual as long as they're not\n * annotated with {@code @Parameter}.</li>\n * </ol>\n *\n * <p>In this context, an <em>indexed parameter</em> is an argument for a given\n * index in the {@code Arguments} provided by an {@code ArgumentsProvider} that\n * is injected into a field annotated with {@code @Parameter(index)}. An\n * <em>aggregator</em> is any {@code @Parameter}-annotated field of type\n * {@link org.junit.jupiter.params.aggregator.ArgumentsAccessor ArgumentsAccessor}\n * or any field annotated with\n * {@link org.junit.jupiter.params.aggregator.AggregateWith @AggregateWith}.\n *\n * <h2>Argument Conversion</h2>\n *\n * <p>{@code @Parameter}-annotated fields or constructor parameters may be\n * annotated with\n * {@link org.junit.jupiter.params.converter.ConvertWith @ConvertWith}\n * or a corresponding composed annotation to specify an <em>explicit</em>\n * {@link org.junit.jupiter.params.converter.ArgumentConverter ArgumentConverter}.\n * Otherwise, JUnit Jupiter will attempt to perform an <em>implicit</em>\n * conversion to the target type automatically (see the User Guide for further\n * details).\n *\n * <h2>Lifecycle Methods</h2>\n *\n * <p>If you wish to execute custom code before or after each invocation of the\n * parameterized class, you may declare methods annotated with\n * {@link BeforeParameterizedClassInvocation @BeforeParameterizedClassInvocation}\n * or {@link AfterParameterizedClassInvocation @AfterParameterizedClassInvocation}.\n * This can, for example, be useful to initialize the arguments before they are\n * used.\n *\n * <h2>Composed Annotations</h2>\n *\n * <p>{@code @ParameterizedClass} may also be used as a meta-annotation in\n * order to create a custom <em>composed annotation</em> that inherits the\n * semantics of {@code @ParameterizedClass}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.13\n * @see Parameter\n * @see BeforeParameterizedClassInvocation\n * @see AfterParameterizedClassInvocation\n * @see ParameterizedTest\n * @see org.junit.jupiter.params.provider.Arguments\n * @see org.junit.jupiter.params.provider.ArgumentsProvider\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.provider.CsvFileSource\n * @see org.junit.jupiter.params.provider.CsvSource\n * @see org.junit.jupiter.params.provider.EnumSource\n * @see org.junit.jupiter.params.provider.MethodSource\n * @see org.junit.jupiter.params.provider.ValueSource\n * @see org.junit.jupiter.params.aggregator.ArgumentsAccessor\n * @see org.junit.jupiter.params.aggregator.AggregateWith\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n * @see org.junit.jupiter.params.converter.ConvertWith\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = EXPERIMENTAL, since = \"6.0\")\n@ClassTemplate\n@ExtendWith(ParameterizedClassExtension.class)\n@SuppressWarnings(\"exports\")\npublic @interface ParameterizedClass {\n\n\t/**\n\t * The display name to be used for individual invocations of the\n\t * parameterized class; never blank or consisting solely of whitespace.\n\t *\n\t * <p>Defaults to\n\t * <code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code>.\n\t *\n\t * <p>If the default display name flag\n\t * (<code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code>)\n\t * is not overridden, JUnit will:\n\t * <ul>\n\t * <li>Look up the {@value ParameterizedInvocationNameFormatter#DISPLAY_NAME_PATTERN_KEY}\n\t * <em>configuration parameter</em> and use it if available. The configuration\n\t * parameter can be supplied via the {@code Launcher} API, build tools (e.g.,\n\t * Gradle and Maven), a JVM system property, or the JUnit Platform configuration\n\t * file (i.e., a file named {@code junit-platform.properties} in the root of\n\t * the class path). Consult the User Guide for further information.</li>\n\t * <li>Otherwise,\n\t * <code>{@value ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME}</code>\n\t * will be used.</li>\n\t * </ul>\n\t *\n\t * <h4>Supported placeholders</h4>\n\t * <ul>\n\t * <li><code>{@value ParameterizedInvocationConstants#DISPLAY_NAME_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#INDEX_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>\n\t * <li><code>\"{0}\"</code>, <code>\"{1}\"</code>, etc.: an individual argument (0-based)</li>\n\t * </ul>\n\t *\n\t * <p>For the latter, you may use {@link java.text.MessageFormat} patterns\n\t * to customize formatting (for example, {@code {0,number,#.###}}). Please\n\t * note that the original arguments are passed when formatting, regardless\n\t * of any implicit or explicit argument conversions.\n\t *\n\t * <p>Note that\n\t * <code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code> is\n\t * a flag rather than a placeholder.\n\t *\n\t * @see java.text.MessageFormat\n\t * @see #quoteTextArguments()\n\t */\n\tString name() default ParameterizedInvocationNameFormatter.DEFAULT_DISPLAY_NAME;\n\n\t/**\n\t * Configure whether to enclose text-based argument values in quotes within\n\t * display names.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * <p>In this context, any {@link CharSequence} (such as a {@link String})\n\t * or {@link Character} is considered text. A {@code CharSequence} is wrapped\n\t * in double quotes (\"), and a {@code Character} is wrapped in single quotes\n\t * (').\n\t *\n\t * <p>Special characters in Java strings and characters will be escaped in the\n\t * quoted text &mdash; for example, carriage returns and line feeds will be\n\t * escaped as {@code \\\\r} and {@code \\\\n}, respectively. In addition, any\n\t * {@linkplain Character#isISOControl(char) ISO control character} will be\n\t * represented as a question mark (?) in the quoted text.\n\t *\n\t * <p>For example, given a string argument {@code \"line 1\\nline 2\"}, the\n\t * representation in the display name would be {@code \"\\\"line 1\\\\nline 2\\\"\"}\n\t * (printed as {@code \"line 1\\nline 2\"}) with the newline character escaped as\n\t * {@code \"\\\\n\"}. Similarly, given a string argument {@code \"\\t\"}, the\n\t * representation in the display name would be {@code \"\\\"\\\\t\\\"\"} (printed as\n\t * {@code \"\\t\"}) instead of a blank string or invisible tab\n\t * character. The same applies for a character argument {@code '\\t'}, whose\n\t * representation in the display name would be {@code \"'\\\\t'\"} (printed as\n\t * {@code '\\t'}).\n\t *\n\t * <p>Please note that original source arguments are quoted when generating\n\t * a display name, before any implicit or explicit argument conversion is\n\t * performed. For example, if a parameterized class accepts {@code 3.14} as a\n\t * {@code float} argument that was converted from {@code \"3.14\"} as an input\n\t * string, {@code \"3.14\"} will be present in the display name instead of\n\t * {@code 3.14}.\n\t *\n\t * @since 6.0\n\t * @see #name()\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tboolean quoteTextArguments() default true;\n\n\t/**\n\t * Configure whether all arguments of the parameterized class that implement\n\t * {@link AutoCloseable} will be closed after their corresponding\n\t * invocation.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * <p><strong>WARNING</strong>: if an argument that implements\n\t * {@code AutoCloseable} is reused for multiple invocations of the same\n\t * parameterized class, you must set {@code autoCloseArguments} to\n\t * {@code false} to ensure that the argument is not closed between\n\t * invocations.\n\t *\n\t * @see java.lang.AutoCloseable\n\t */\n\tboolean autoCloseArguments() default true;\n\n\t/**\n\t * Configure whether zero invocations are allowed for this\n\t * parameterized class.\n\t *\n\t * <p>Set this attribute to {@code true} if the absence of invocations is\n\t * expected in some cases and should not cause a test failure.\n\t *\n\t * <p>Defaults to {@code false}.\n\t */\n\tboolean allowZeroInvocations() default false;\n\n\t/**\n\t * Configure how the number of arguments provided by an\n\t * {@link ArgumentsSource} are validated.\n\t *\n\t * <p>Defaults to {@link ArgumentCountValidationMode#DEFAULT}.\n\t *\n\t * <p>When an {@link ArgumentsSource} provides more arguments than declared\n\t * by the parameterized class constructor or {@link Parameter}-annotated\n\t * fields, there might be a bug in the parameterized class or the\n\t * {@link ArgumentsSource}. By default, the additional arguments are\n\t * ignored. {@code argumentCountValidation} allows you to control how\n\t * additional arguments are handled. The default can be configured via the\n\t * {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY}\n\t * configuration parameter (see the User Guide for details on configuration\n\t * parameters).\n\t *\n\t * @see ArgumentCountValidationMode\n\t */\n\tArgumentCountValidationMode argumentCountValidation() default ArgumentCountValidationMode.DEFAULT;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.reverse;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedMethods;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findFields;\nimport static org.junit.platform.commons.util.ReflectionUtils.isRecordClass;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.HierarchyTraversalMode;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\nclass ParameterizedClassContext implements ParameterizedDeclarationContext<ParameterizedClassInvocationContext> {\n\n\tprivate final Class<?> testClass;\n\tprivate final ParameterizedClass annotation;\n\tprivate final TestInstance.Lifecycle testInstanceLifecycle;\n\tprivate final ResolverFacade resolverFacade;\n\tprivate final InjectionType injectionType;\n\tprivate final List<ArgumentSetLifecycleMethod> beforeMethods;\n\tprivate final List<ArgumentSetLifecycleMethod> afterMethods;\n\n\tParameterizedClassContext(Class<?> testClass, ParameterizedClass annotation,\n\t\t\tTestInstance.Lifecycle testInstanceLifecycle) {\n\t\tthis.testClass = testClass;\n\t\tthis.annotation = annotation;\n\t\tthis.testInstanceLifecycle = testInstanceLifecycle;\n\n\t\tList<Field> fields = findParameterAnnotatedFields(testClass);\n\t\tif (fields.isEmpty()) {\n\t\t\tthis.resolverFacade = ResolverFacade.create(ReflectionUtils.getDeclaredConstructor(testClass), annotation);\n\t\t\tthis.injectionType = InjectionType.CONSTRUCTOR;\n\t\t}\n\t\telse {\n\t\t\tthis.resolverFacade = ResolverFacade.create(testClass, fields);\n\t\t\tthis.injectionType = InjectionType.FIELDS;\n\t\t}\n\n\t\tthis.beforeMethods = findLifecycleMethodsAndAssertStaticAndNonPrivate(testClass, TOP_DOWN,\n\t\t\tBeforeParameterizedClassInvocation.class, BeforeParameterizedClassInvocation::injectArguments,\n\t\t\tthis.resolverFacade);\n\n\t\t// Make a local copy since findAnnotatedMethods() returns an immutable list.\n\t\tthis.afterMethods = new ArrayList<>(findLifecycleMethodsAndAssertStaticAndNonPrivate(testClass, BOTTOM_UP,\n\t\t\tAfterParameterizedClassInvocation.class, AfterParameterizedClassInvocation::injectArguments,\n\t\t\tthis.resolverFacade));\n\n\t\t// Since the bottom-up ordering of afterMethods will later be reversed when the\n\t\t// AfterParameterizedClassInvocationMethodInvoker extensions are executed within\n\t\t// ClassTemplateInvocationTestDescriptor, we have to reverse them to put them\n\t\t// in top-down order before we register them as extensions.\n\t\treverse(afterMethods);\n\t}\n\n\tprivate static List<Field> findParameterAnnotatedFields(Class<?> clazz) {\n\t\tif (isRecordClass(clazz)) {\n\t\t\treturn emptyList();\n\t\t}\n\t\treturn findFields(clazz, it -> isAnnotated(it, Parameter.class), BOTTOM_UP);\n\t}\n\n\t@Override\n\tpublic Class<?> getTestClass() {\n\t\treturn this.testClass;\n\t}\n\n\t@Override\n\tpublic ParameterizedClass getAnnotation() {\n\t\treturn this.annotation;\n\t}\n\n\t@Override\n\tpublic Class<?> getAnnotatedElement() {\n\t\treturn this.testClass;\n\t}\n\n\t@Override\n\tpublic String getDisplayNamePattern() {\n\t\treturn this.annotation.name();\n\t}\n\n\t@Override\n\tpublic boolean quoteTextArguments() {\n\t\treturn this.annotation.quoteTextArguments();\n\t}\n\n\t@Override\n\tpublic boolean isAutoClosingArguments() {\n\t\treturn this.annotation.autoCloseArguments();\n\t}\n\n\t@Override\n\tpublic boolean isAllowingZeroInvocations() {\n\t\treturn this.annotation.allowZeroInvocations();\n\t}\n\n\t@Override\n\tpublic ArgumentCountValidationMode getArgumentCountValidationMode() {\n\t\treturn this.annotation.argumentCountValidation();\n\t}\n\n\t@Override\n\tpublic ResolverFacade getResolverFacade() {\n\t\treturn this.resolverFacade;\n\t}\n\n\t@Override\n\tpublic ParameterizedClassInvocationContext createInvocationContext(ParameterizedInvocationNameFormatter formatter,\n\t\t\tArguments arguments, int invocationIndex) {\n\n\t\treturn new ParameterizedClassInvocationContext(this, formatter, arguments, invocationIndex);\n\t}\n\n\tTestInstance.Lifecycle getTestInstanceLifecycle() {\n\t\treturn testInstanceLifecycle;\n\t}\n\n\tInjectionType getInjectionType() {\n\t\treturn injectionType;\n\t}\n\n\tList<ArgumentSetLifecycleMethod> getBeforeMethods() {\n\t\treturn beforeMethods;\n\t}\n\n\tList<ArgumentSetLifecycleMethod> getAfterMethods() {\n\t\treturn afterMethods;\n\t}\n\n\tprivate static <A extends Annotation> List<ArgumentSetLifecycleMethod> findLifecycleMethodsAndAssertStaticAndNonPrivate(\n\t\t\tClass<?> testClass, HierarchyTraversalMode traversalMode, Class<A> annotationType,\n\t\t\tPredicate<A> injectArgumentsPredicate, ResolverFacade resolverFacade) {\n\n\t\tList<Method> methods = findAnnotatedMethods(testClass, annotationType, traversalMode);\n\n\t\treturn methods.stream() //\n\t\t\t\t.map(method -> {\n\t\t\t\t\tA annotation = getAnnotation(method, annotationType);\n\t\t\t\t\tif (injectArgumentsPredicate.test(annotation)) {\n\t\t\t\t\t\treturn new ArgumentSetLifecycleMethod(method,\n\t\t\t\t\t\t\tresolverFacade.createLifecycleMethodParameterResolver(method, annotation));\n\t\t\t\t\t}\n\t\t\t\t\treturn new ArgumentSetLifecycleMethod(method);\n\t\t\t\t}) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {\n\t\treturn findAnnotation(method, annotationType) //\n\t\t\t\t.orElseThrow(() -> new JUnitException(\"Method not annotated with @\" + annotationType.getSimpleName()));\n\t}\n\n\tenum InjectionType {\n\t\tCONSTRUCTOR, FIELDS\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.params.ParameterizedClassContext.InjectionType.CONSTRUCTOR;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\n\n/**\n * @since 5.13\n */\nclass ParameterizedClassExtension extends ParameterizedInvocationContextProvider<ParameterizedClassInvocationContext>\n\t\timplements ClassTemplateInvocationContextProvider, ParameterResolver {\n\n\tprivate static final String DECLARATION_CONTEXT_KEY = \"context\";\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\n\t\t// This method always returns `false` because it is not intended to be used as a parameter resolver.\n\t\t// Instead, it is used to provide a better error message when `TestInstance.Lifecycle.PER_CLASS` is\n\t\t// attempted to be combined with constructor injection of parameters.\n\n\t\tif (isDeclaredOnTestClassConstructor(parameterContext, extensionContext)) {\n\t\t\tvalidateAndStoreClassContext(extensionContext);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\n\t\t// Should never be called (see comment above).\n\n\t\tthrow new JUnitException(\"Unexpected call to resolveParameter\");\n\t}\n\n\t@Override\n\tpublic boolean supportsClassTemplate(ExtensionContext extensionContext) {\n\t\treturn validateAndStoreClassContext(extensionContext);\n\t}\n\n\t@Override\n\tpublic Stream<ParameterizedClassInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\tExtensionContext extensionContext) {\n\n\t\treturn provideInvocationContexts(extensionContext, getDeclarationContext(extensionContext));\n\t}\n\n\t@Override\n\tpublic boolean mayReturnZeroClassTemplateInvocationContexts(ExtensionContext extensionContext) {\n\t\treturn getDeclarationContext(extensionContext).isAllowingZeroInvocations();\n\t}\n\n\tprivate static boolean isDeclaredOnTestClassConstructor(ParameterContext parameterContext,\n\t\t\tExtensionContext extensionContext) {\n\n\t\tExecutable declaringExecutable = parameterContext.getDeclaringExecutable();\n\t\treturn declaringExecutable instanceof Constructor //\n\t\t\t\t&& declaringExecutable.getDeclaringClass().equals(extensionContext.getTestClass().orElse(null));\n\t}\n\n\tprivate boolean validateAndStoreClassContext(ExtensionContext extensionContext) {\n\n\t\tStore store = getStore(extensionContext);\n\t\tif (store.get(DECLARATION_CONTEXT_KEY) != null) {\n\t\t\treturn true;\n\t\t}\n\n\t\tOptional<ParameterizedClass> annotation = findAnnotation(extensionContext.getTestClass(),\n\t\t\tParameterizedClass.class);\n\t\tif (annotation.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tstore.put(DECLARATION_CONTEXT_KEY,\n\t\t\tcreateClassContext(extensionContext, extensionContext.getRequiredTestClass(), annotation.get()));\n\n\t\treturn true;\n\t}\n\n\tprivate static ParameterizedClassContext createClassContext(ExtensionContext extensionContext, Class<?> testClass,\n\t\t\tParameterizedClass annotation) {\n\n\t\tTestInstance.Lifecycle lifecycle = extensionContext.getTestInstanceLifecycle() //\n\t\t\t\t.orElseThrow(() -> new PreconditionViolationException(\"TestInstance.Lifecycle not present\"));\n\n\t\tParameterizedClassContext classContext = new ParameterizedClassContext(testClass, annotation, lifecycle);\n\n\t\tif (lifecycle == PER_CLASS && classContext.getInjectionType() == CONSTRUCTOR) {\n\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)\");\n\t\t}\n\n\t\treturn classContext;\n\t}\n\n\tprivate ParameterizedClassContext getDeclarationContext(ExtensionContext extensionContext) {\n\t\treturn requireNonNull(getStore(extensionContext)//\n\t\t\t\t.get(DECLARATION_CONTEXT_KEY, ParameterizedClassContext.class));\n\t}\n\n\tprivate Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(Namespace.create(ParameterizedClassExtension.class, context.getRequiredTestClass()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedClassInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.ParameterizedClassContext.InjectionType;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.platform.commons.util.Preconditions;\n\nclass ParameterizedClassInvocationContext extends ParameterizedInvocationContext<ParameterizedClassContext>\n\t\timplements ClassTemplateInvocationContext {\n\n\tprivate final ResolutionCache resolutionCache = ResolutionCache.enabled();\n\n\tParameterizedClassInvocationContext(ParameterizedClassContext classContext,\n\t\t\tParameterizedInvocationNameFormatter formatter, Arguments arguments, int invocationIndex) {\n\t\tsuper(classContext, formatter, arguments, invocationIndex);\n\t}\n\n\t@Override\n\tpublic String getDisplayName(int invocationIndex) {\n\t\treturn super.getDisplayName(invocationIndex);\n\t}\n\n\t@Override\n\tpublic List<Extension> getAdditionalExtensions() {\n\t\treturn Stream.concat(Stream.of(createParameterInjector()), createLifecycleMethodInvokers()) //\n\t\t\t\t.toList();\n\t}\n\n\t@Override\n\tpublic void prepareInvocation(ExtensionContext context) {\n\t\tsuper.prepareInvocation(context);\n\t}\n\n\tprivate Extension createParameterInjector() {\n\t\tInjectionType injectionType = this.declarationContext.getInjectionType();\n\t\treturn switch (injectionType) {\n\t\t\tcase CONSTRUCTOR -> createExtensionForConstructorInjection();\n\t\t\tcase FIELDS -> createExtensionForFieldInjection();\n\t\t};\n\t}\n\n\tprivate ClassTemplateConstructorParameterResolver createExtensionForConstructorInjection() {\n\t\tPreconditions.condition(this.declarationContext.getTestInstanceLifecycle() == PER_METHOD,\n\t\t\t\"Constructor injection is only supported for lifecycle PER_METHOD\");\n\t\treturn new ClassTemplateConstructorParameterResolver(this.declarationContext, this.arguments,\n\t\t\tthis.invocationIndex, this.resolutionCache);\n\t}\n\n\tprivate Extension createExtensionForFieldInjection() {\n\t\tResolverFacade resolverFacade = this.declarationContext.getResolverFacade();\n\t\tTestInstance.Lifecycle lifecycle = this.declarationContext.getTestInstanceLifecycle();\n\t\treturn switch (lifecycle) {\n\t\t\tcase PER_CLASS -> new BeforeClassTemplateInvocationFieldInjector(resolverFacade, this.arguments,\n\t\t\t\tthis.invocationIndex, this.resolutionCache);\n\t\t\tcase PER_METHOD -> new InstancePostProcessingClassTemplateFieldInjector(resolverFacade, this.arguments,\n\t\t\t\tthis.invocationIndex, this.resolutionCache);\n\t\t};\n\t}\n\n\tprivate Stream<Extension> createLifecycleMethodInvokers() {\n\t\treturn Stream.concat( //\n\t\t\tthis.declarationContext.getBeforeMethods().stream().map(\n\t\t\t\tthis::createBeforeParameterizedClassInvocationMethodInvoker), //\n\t\t\tthis.declarationContext.getAfterMethods().stream().map(\n\t\t\t\tthis::createAfterParameterizedClassInvocationMethodInvoker) //\n\t\t);\n\t}\n\n\tprivate BeforeParameterizedClassInvocationMethodInvoker createBeforeParameterizedClassInvocationMethodInvoker(\n\t\t\tArgumentSetLifecycleMethod method) {\n\t\treturn new BeforeParameterizedClassInvocationMethodInvoker(this.declarationContext, this.arguments,\n\t\t\tthis.invocationIndex, this.resolutionCache, method);\n\t}\n\n\tprivate AfterParameterizedClassInvocationMethodInvoker createAfterParameterizedClassInvocationMethodInvoker(\n\t\t\tArgumentSetLifecycleMethod method) {\n\t\treturn new AfterParameterizedClassInvocationMethodInvoker(this.declarationContext, this.arguments,\n\t\t\tthis.invocationIndex, this.resolutionCache, method);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedDeclarationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\n\nimport org.junit.jupiter.params.provider.Arguments;\n\n/**\n * @since 5.13\n */\ninterface ParameterizedDeclarationContext<C> {\n\n\tClass<?> getTestClass();\n\n\tAnnotation getAnnotation();\n\n\tAnnotatedElement getAnnotatedElement();\n\n\tString getDisplayNamePattern();\n\n\tboolean quoteTextArguments();\n\n\tboolean isAutoClosingArguments();\n\n\tboolean isAllowingZeroInvocations();\n\n\tArgumentCountValidationMode getArgumentCountValidationMode();\n\n\tdefault String getAnnotationName() {\n\t\treturn getAnnotation().annotationType().getSimpleName();\n\t}\n\n\tResolverFacade getResolverFacade();\n\n\tC createInvocationContext(ParameterizedInvocationNameFormatter formatter, Arguments arguments, int invocationIndex);\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedInvocationConstants.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * Constants for the use with the\n * {@link ParameterizedClass @ParameterizedClass} and\n * {@link ParameterizedTest @ParameterizedTest} annotations.\n *\n * @since 5.13\n */\n@API(status = MAINTAINED, since = \"5.13\")\npublic class ParameterizedInvocationConstants {\n\n\t/**\n\t * Placeholder for the {@linkplain org.junit.jupiter.api.TestInfo#getDisplayName\n\t * display name} of a {@code @ParameterizedTest} method: <code>{displayName}</code>\n\t *\n\t * @since 5.3\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t */\n\tpublic static final String DISPLAY_NAME_PLACEHOLDER = \"{displayName}\";\n\n\t/**\n\t * Placeholder for the current invocation index of a {@code @ParameterizedTest}\n\t * method (1-based): <code>{index}</code>\n\t *\n\t * @since 5.3\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t * @see #DEFAULT_DISPLAY_NAME\n\t */\n\tpublic static final String INDEX_PLACEHOLDER = \"{index}\";\n\n\t/**\n\t * Placeholder for the complete, comma-separated arguments list of the\n\t * current invocation of a {@code @ParameterizedTest} method:\n\t * <code>{arguments}</code>\n\t *\n\t * @since 5.3\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t */\n\tpublic static final String ARGUMENTS_PLACEHOLDER = \"{arguments}\";\n\n\t/**\n\t * Placeholder for the complete, comma-separated named arguments list\n\t * of the current invocation of a {@code @ParameterizedTest} method:\n\t * <code>{argumentsWithNames}</code>\n\t *\n\t * <p>Argument names will be retrieved via the {@link java.lang.reflect.Parameter#getName()}\n\t * API if the byte code contains parameter names &mdash; for example, if\n\t * the code was compiled with the {@code -parameters} command line argument\n\t * for {@code javac}.\n\t *\n\t * @since 5.6\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t */\n\tpublic static final String ARGUMENTS_WITH_NAMES_PLACEHOLDER = \"{argumentsWithNames}\";\n\n\t/**\n\t * Placeholder for the name of the argument set for the current invocation\n\t * of a {@code @ParameterizedTest} method: <code>{argumentSetName}</code>.\n\t *\n\t * <p>This placeholder can be used when the current set of arguments was created via\n\t * {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t * argumentSet()}.\n\t *\n\t * @since 5.11\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String ARGUMENT_SET_NAME_PLACEHOLDER = \"{argumentSetName}\";\n\n\t/**\n\t * Placeholder for either {@link #ARGUMENT_SET_NAME_PLACEHOLDER} or\n\t * {@link #ARGUMENTS_WITH_NAMES_PLACEHOLDER}, depending on whether the\n\t * current set of arguments was created via\n\t * {@link org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t * argumentSet()}: <code>{argumentSetNameOrArgumentsWithNames}</code>.\n\t *\n\t * @since 5.11\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t * @see #ARGUMENT_SET_NAME_PLACEHOLDER\n\t * @see #ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @see #DEFAULT_DISPLAY_NAME\n\t * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER = \"{argumentSetNameOrArgumentsWithNames}\";\n\n\t/**\n\t * Default display name pattern for the current invocation of a\n\t * {@code @ParameterizedTest} method: {@value}\n\t *\n\t * <p>Note that the default pattern does <em>not</em> include the\n\t * {@linkplain #DISPLAY_NAME_PLACEHOLDER display name} of the\n\t * {@code @ParameterizedTest} method.\n\t *\n\t * @since 5.3\n\t * @see ParameterizedClass#name()\n\t * @see ParameterizedTest#name()\n\t * @see #DISPLAY_NAME_PLACEHOLDER\n\t * @see #INDEX_PLACEHOLDER\n\t * @see #ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t */\n\tpublic static final String DEFAULT_DISPLAY_NAME = ParameterizedInvocationNameFormatter.DEFAULT_DISPLAY_NAME_PATTERN;\n\n\tprivate ParameterizedInvocationConstants() {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getClassLoader;\n\nimport java.util.Arrays;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.DefaultArgumentsAccessor;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\nclass ParameterizedInvocationContext<T extends ParameterizedDeclarationContext<?>> {\n\n\tprivate static final Namespace NAMESPACE = Namespace.create(ParameterizedTestInvocationContext.class);\n\n\tprotected final T declarationContext;\n\tprivate final ParameterizedInvocationNameFormatter formatter;\n\tprotected final EvaluatedArgumentSet arguments;\n\tprotected final int invocationIndex;\n\n\tParameterizedInvocationContext(T declarationContext, ParameterizedInvocationNameFormatter formatter,\n\t\t\tArguments arguments, int invocationIndex) {\n\n\t\tthis.declarationContext = declarationContext;\n\t\tthis.formatter = formatter;\n\t\tResolverFacade resolverFacade = this.declarationContext.getResolverFacade();\n\t\tthis.arguments = EvaluatedArgumentSet.of(arguments, resolverFacade::determineConsumedArgumentLength);\n\t\tthis.invocationIndex = invocationIndex;\n\t}\n\n\tpublic String getDisplayName(int invocationIndex) {\n\t\treturn this.formatter.format(invocationIndex, this.arguments, this.declarationContext.quoteTextArguments());\n\t}\n\n\tpublic void prepareInvocation(ExtensionContext context) {\n\t\tif (this.declarationContext.isAutoClosingArguments()) {\n\t\t\tregisterAutoCloseableArgumentsInStoreForClosing(context);\n\t\t}\n\t\tvalidateArgumentCount(context);\n\t\tstoreParameterInfo(context);\n\t}\n\n\tprivate void registerAutoCloseableArgumentsInStoreForClosing(ExtensionContext context) {\n\t\tExtensionContext.Store store = context.getStore(NAMESPACE);\n\t\tAtomicInteger argumentIndex = new AtomicInteger();\n\n\t\tArrays.stream(this.arguments.getAllPayloads()) //\n\t\t\t\t.filter(AutoCloseable.class::isInstance) //\n\t\t\t\t.map(AutoCloseable.class::cast) //\n\t\t\t\t.map(CloseableArgument::new) //\n\t\t\t\t.forEach(closeable -> store.put(argumentIndex.incrementAndGet(), closeable));\n\t}\n\n\tprivate void validateArgumentCount(ExtensionContext context) {\n\t\tnew ArgumentCountValidator(this.declarationContext, this.arguments).validate(context);\n\t}\n\n\tprivate void storeParameterInfo(ExtensionContext context) {\n\t\tParameterDeclarations declarations = this.declarationContext.getResolverFacade().getIndexedParameterDeclarations();\n\t\tClassLoader classLoader = getClassLoader(this.declarationContext.getTestClass());\n\t\t@Nullable\n\t\tObject[] arguments = this.arguments.getConsumedPayloads();\n\t\tArgumentsAccessor accessor = DefaultArgumentsAccessor.create(invocationIndex, classLoader, arguments);\n\t\tnew DefaultParameterInfo(declarations, accessor).store(context);\n\t}\n\n\t@SuppressWarnings({ \"deprecation\", \"try\" })\n\tprivate record CloseableArgument(AutoCloseable autoCloseable)\n\t\t\timplements ExtensionContext.Store.CloseableResource, AutoCloseable {\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\tthis.autoCloseable.close();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedInvocationContextProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TemplateInvocationValidationException;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.ArgumentsProvider;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\nimport org.junit.jupiter.params.support.AnnotationConsumerInitializer;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\n\nclass ParameterizedInvocationContextProvider<T> {\n\n\tprotected Stream<T> provideInvocationContexts(ExtensionContext extensionContext,\n\t\t\tParameterizedDeclarationContext<T> declarationContext) {\n\n\t\tList<ArgumentsSource> argumentsSources = collectArgumentSources(declarationContext);\n\t\tParameterDeclarations parameters = declarationContext.getResolverFacade().getIndexedParameterDeclarations();\n\t\tParameterizedInvocationNameFormatter formatter = ParameterizedInvocationNameFormatter.create(extensionContext,\n\t\t\tdeclarationContext);\n\t\tAtomicLong invocationCount = new AtomicLong(0);\n\n\t\treturn argumentsSources.stream() //\n\t\t\t\t.map(ArgumentsSource::value) //\n\t\t\t\t.map(clazz -> ParameterizedTestSpiInstantiator.instantiate(ArgumentsProvider.class, clazz,\n\t\t\t\t\textensionContext)) //\n\t\t\t\t.map(provider -> AnnotationConsumerInitializer.initialize(declarationContext.getAnnotatedElement(),\n\t\t\t\t\tprovider)) //\n\t\t\t\t.flatMap(provider -> arguments(provider, parameters, extensionContext)) //\n\t\t\t\t.map(arguments -> {\n\t\t\t\t\tinvocationCount.incrementAndGet();\n\t\t\t\t\treturn declarationContext.createInvocationContext(formatter, arguments, invocationCount.intValue());\n\t\t\t\t}) //\n\t\t\t\t.onClose(() -> validateInvokedAtLeastOnce(invocationCount.get(), declarationContext));\n\t}\n\n\tprivate static <T> void validateInvokedAtLeastOnce(long invocationCount,\n\t\t\tParameterizedDeclarationContext<T> declarationContext) {\n\t\tif (invocationCount == 0 && !declarationContext.isAllowingZeroInvocations()) {\n\t\t\tString message = \"Configuration error: You must configure at least one set of arguments for this @%s\".formatted(\n\t\t\t\tdeclarationContext.getAnnotationName());\n\t\t\tthrow new TemplateInvocationValidationException(message);\n\t\t}\n\t}\n\n\tprivate static List<ArgumentsSource> collectArgumentSources(ParameterizedDeclarationContext<?> declarationContext) {\n\t\tList<ArgumentsSource> argumentsSources = findRepeatableAnnotations(declarationContext.getAnnotatedElement(),\n\t\t\tArgumentsSource.class);\n\n\t\tPreconditions.notEmpty(argumentsSources,\n\t\t\t() -> \"Configuration error: You must configure at least one arguments source for this @%s\".formatted(\n\t\t\t\tdeclarationContext.getAnnotationName()));\n\n\t\treturn argumentsSources;\n\t}\n\n\tprotected static Stream<? extends Arguments> arguments(ArgumentsProvider provider, ParameterDeclarations parameters,\n\t\t\tExtensionContext context) {\n\t\ttry {\n\t\t\treturn provider.provideArguments(parameters, context);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedInvocationNameFormatter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_WITH_NAMES_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENT_SET_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DISPLAY_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.INDEX_PLACEHOLDER;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\n\nimport java.text.FieldPosition;\nimport java.text.Format;\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.StringJoiner;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterNameAndArgument;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * @since 5.0\n */\nclass ParameterizedInvocationNameFormatter {\n\n\t/**\n\t * Global cache for {arguments} pattern strings, keyed by the number of arguments.\n\t * @since 6.0\n\t */\n\tprivate static final Map<Integer, String> argumentsPatternCache = new ConcurrentHashMap<>(8);\n\n\tstatic final String DEFAULT_DISPLAY_NAME = \"{default_display_name}\";\n\tstatic final String DEFAULT_DISPLAY_NAME_PATTERN = \"[\" + INDEX_PLACEHOLDER + \"] \"\n\t\t\t+ ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;\n\tstatic final String DISPLAY_NAME_PATTERN_KEY = \"junit.jupiter.params.displayname.default\";\n\tstatic final String ARGUMENT_MAX_LENGTH_KEY = \"junit.jupiter.params.displayname.argument.maxlength\";\n\n\tstatic ParameterizedInvocationNameFormatter create(ExtensionContext extensionContext,\n\t\t\tParameterizedDeclarationContext<?> declarationContext) {\n\n\t\tString name = declarationContext.getDisplayNamePattern();\n\t\tString pattern = DEFAULT_DISPLAY_NAME.equals(name)\n\t\t\t\t? extensionContext.getConfigurationParameter(DISPLAY_NAME_PATTERN_KEY) //\n\t\t\t\t\t\t.orElse(DEFAULT_DISPLAY_NAME_PATTERN)\n\t\t\t\t: name;\n\t\tpattern = Preconditions.notBlank(pattern.strip(),\n\t\t\t() -> \"Configuration error: @%s on %s must be declared with a non-empty name.\".formatted(\n\t\t\t\tdeclarationContext.getAnnotationName(),\n\t\t\t\tdeclarationContext.getResolverFacade().getIndexedParameterDeclarations().getSourceElementDescription()));\n\n\t\tint argumentMaxLength = extensionContext.getConfigurationParameter(ARGUMENT_MAX_LENGTH_KEY, Integer::parseInt) //\n\t\t\t\t.orElse(512);\n\n\t\treturn new ParameterizedInvocationNameFormatter(pattern, extensionContext.getDisplayName(), declarationContext,\n\t\t\targumentMaxLength);\n\t}\n\n\tprivate final PartialFormatter[] partialFormatters;\n\n\tParameterizedInvocationNameFormatter(String pattern, String displayName,\n\t\t\tParameterizedDeclarationContext<?> declarationContext, int argumentMaxLength) {\n\t\ttry {\n\t\t\tthis.partialFormatters = parse(pattern, displayName, declarationContext, argumentMaxLength);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tString message = \"The display name pattern defined for the parameterized test is invalid. \"\n\t\t\t\t\t+ \"See nested exception for further details.\";\n\t\t\tthrow new JUnitException(message, ex);\n\t\t}\n\t}\n\n\tString format(int invocationIndex, EvaluatedArgumentSet arguments, boolean quoteTextArguments) {\n\t\ttry {\n\t\t\treturn formatSafely(invocationIndex, arguments, quoteTextArguments);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tString message = \"Failed to format display name for parameterized test. \"\n\t\t\t\t\t+ \"See nested exception for further details.\";\n\t\t\tthrow new JUnitException(message, ex);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JdkObsolete\")\n\tprivate String formatSafely(int invocationIndex, EvaluatedArgumentSet arguments, boolean quoteTextArguments) {\n\t\tArgumentsContext context = new ArgumentsContext(invocationIndex, arguments.getConsumedArguments(),\n\t\t\targuments.getName(), quoteTextArguments);\n\t\tStringBuffer result = new StringBuffer(); // used instead of StringBuilder so MessageFormat can append directly\n\t\tfor (PartialFormatter partialFormatter : this.partialFormatters) {\n\t\t\tpartialFormatter.append(context, result);\n\t\t}\n\t\treturn result.toString();\n\t}\n\n\tprivate PartialFormatter[] parse(String pattern, String displayName,\n\t\t\tParameterizedDeclarationContext<?> declarationContext, int argumentMaxLength) {\n\n\t\tList<PartialFormatter> result = new ArrayList<>();\n\t\tPartialFormatters formatters = createPartialFormatters(displayName, declarationContext, argumentMaxLength);\n\t\tString unparsedSegment = pattern;\n\n\t\twhile (isNotBlank(unparsedSegment)) {\n\t\t\tPlaceholderPosition position = findFirstPlaceholder(formatters, unparsedSegment);\n\t\t\tif (position == null) {\n\t\t\t\tresult.add(determineNonPlaceholderFormatter(unparsedSegment, argumentMaxLength));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (position.index > 0) {\n\t\t\t\tString before = unparsedSegment.substring(0, position.index);\n\t\t\t\tresult.add(determineNonPlaceholderFormatter(before, argumentMaxLength));\n\t\t\t}\n\t\t\tresult.add(formatters.get(position.placeholder));\n\t\t\tunparsedSegment = unparsedSegment.substring(position.index + position.placeholder.length());\n\t\t}\n\n\t\treturn result.toArray(new PartialFormatter[0]);\n\t}\n\n\tprivate static @Nullable PlaceholderPosition findFirstPlaceholder(PartialFormatters formatters, String segment) {\n\t\tif (segment.length() < formatters.minimumPlaceholderLength) {\n\t\t\treturn null;\n\t\t}\n\t\tPlaceholderPosition minimum = null;\n\t\tfor (String placeholder : formatters.placeholders()) {\n\t\t\tint index = segment.indexOf(placeholder);\n\t\t\tif (index >= 0) {\n\t\t\t\tif (index < formatters.minimumPlaceholderLength) {\n\t\t\t\t\treturn new PlaceholderPosition(index, placeholder);\n\t\t\t\t}\n\t\t\t\telse if (minimum == null || index < minimum.index) {\n\t\t\t\t\tminimum = new PlaceholderPosition(index, placeholder);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn minimum;\n\t}\n\n\tprivate static PartialFormatter determineNonPlaceholderFormatter(String segment, int argumentMaxLength) {\n\t\treturn segment.contains(\"{\") //\n\t\t\t\t? new MessageFormatPartialFormatter(segment, argumentMaxLength) //\n\t\t\t\t: (context, result) -> result.append(segment);\n\t}\n\n\tprivate PartialFormatters createPartialFormatters(String displayName,\n\t\t\tParameterizedDeclarationContext<?> declarationContext, int argumentMaxLength) {\n\n\t\tPartialFormatter argumentsWithNamesFormatter = new CachingByArgumentsLengthPartialFormatter(\n\t\t\tlength -> new MessageFormatPartialFormatter(argumentsPattern(length), argumentMaxLength, true,\n\t\t\t\tdeclarationContext.getResolverFacade()));\n\n\t\tPartialFormatter argumentSetNameFormatter = new ArgumentSetNameFormatter(\n\t\t\tdeclarationContext.getAnnotationName());\n\n\t\tPartialFormatters formatters = new PartialFormatters();\n\t\tformatters.put(INDEX_PLACEHOLDER, PartialFormatter.INDEX);\n\t\tformatters.put(DISPLAY_NAME_PLACEHOLDER, (context, result) -> result.append(displayName));\n\t\tformatters.put(ARGUMENT_SET_NAME_PLACEHOLDER, argumentSetNameFormatter);\n\t\tformatters.put(ARGUMENTS_WITH_NAMES_PLACEHOLDER, argumentsWithNamesFormatter);\n\t\tformatters.put(ARGUMENTS_PLACEHOLDER, new CachingByArgumentsLengthPartialFormatter(\n\t\t\tlength -> new MessageFormatPartialFormatter(argumentsPattern(length), argumentMaxLength)));\n\t\tformatters.put(ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER, (context, result) -> {\n\t\t\tPartialFormatter formatterToUse = context.argumentSetName.isPresent() //\n\t\t\t\t\t? argumentSetNameFormatter //\n\t\t\t\t\t: argumentsWithNamesFormatter;\n\t\t\tformatterToUse.append(context, result);\n\t\t});\n\t\treturn formatters;\n\t}\n\n\tprivate static String argumentsPattern(int length) {\n\t\treturn argumentsPatternCache.computeIfAbsent(length, //\n\t\t\tkey -> {\n\t\t\t\tStringJoiner sj = new StringJoiner(\", \");\n\t\t\t\tfor (int i = 0; i < length; i++) {\n\t\t\t\t\tsj.add(\"{\" + i + \"}\");\n\t\t\t\t}\n\t\t\t\treturn sj.toString();\n\t\t\t});\n\t}\n\n\tprivate record PlaceholderPosition(int index, String placeholder) {\n\t}\n\n\t@SuppressWarnings(\"ArrayRecordComponent\")\n\tprivate record ArgumentsContext(int invocationIndex, @Nullable Object[] consumedArguments,\n\t\t\tOptional<String> argumentSetName, boolean quoteTextArguments) {\n\t}\n\n\t@FunctionalInterface\n\tprivate interface PartialFormatter {\n\n\t\tPartialFormatter INDEX = (context, result) -> result.append(context.invocationIndex);\n\n\t\tvoid append(ArgumentsContext context, StringBuffer result);\n\n\t}\n\n\tprivate record ArgumentSetNameFormatter(String annotationName) implements PartialFormatter {\n\n\t\t@Override\n\t\tpublic void append(ArgumentsContext context, StringBuffer result) {\n\t\t\tif (context.argumentSetName.isPresent()) {\n\t\t\t\tresult.append(context.argumentSetName.get());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow new ExtensionConfigurationException(\n\t\t\t\t\"When the display name pattern for a @%s contains %s, the arguments must be supplied as an ArgumentSet.\".formatted(\n\t\t\t\t\tthis.annotationName, ARGUMENT_SET_NAME_PLACEHOLDER));\n\t\t}\n\t}\n\n\tprivate static class MessageFormatPartialFormatter implements PartialFormatter {\n\n\t\t@SuppressWarnings(\"UnnecessaryUnicodeEscape\")\n\t\tprivate static final char ELLIPSIS = '\\u2026';\n\n\t\tprivate final MessageFormat messageFormat;\n\t\tprivate final int argumentMaxLength;\n\t\tprivate final boolean generateNameValuePairs;\n\t\tprivate final @Nullable ResolverFacade resolverFacade;\n\n\t\tMessageFormatPartialFormatter(String pattern, int argumentMaxLength) {\n\t\t\tthis(pattern, argumentMaxLength, false, null);\n\t\t}\n\n\t\tMessageFormatPartialFormatter(String pattern, int argumentMaxLength, boolean generateNameValuePairs,\n\t\t\t\t@Nullable ResolverFacade resolverFacade) {\n\t\t\tthis.messageFormat = new MessageFormat(pattern);\n\t\t\tthis.argumentMaxLength = argumentMaxLength;\n\t\t\tthis.generateNameValuePairs = generateNameValuePairs;\n\t\t\tthis.resolverFacade = resolverFacade;\n\t\t}\n\n\t\t// synchronized because MessageFormat is not thread-safe\n\t\t@Override\n\t\tpublic synchronized void append(ArgumentsContext context, StringBuffer result) {\n\t\t\tthis.messageFormat.format(makeReadable(context.consumedArguments, context.quoteTextArguments), result,\n\t\t\t\tnew FieldPosition(0));\n\t\t}\n\n\t\tprivate @Nullable Object[] makeReadable(@Nullable Object[] arguments, boolean quoteTextArguments) {\n\t\t\t@Nullable\n\t\t\tFormat[] formats = messageFormat.getFormatsByArgumentIndex();\n\t\t\t@Nullable\n\t\t\tObject[] result = Arrays.copyOf(arguments, Math.min(arguments.length, formats.length), Object[].class);\n\t\t\tfor (int i = 0; i < result.length; i++) {\n\t\t\t\tif (formats[i] == null) {\n\t\t\t\t\tObject argument = arguments[i];\n\t\t\t\t\tString prefix = \"\";\n\n\t\t\t\t\tif (argument instanceof ParameterNameAndArgument parameterNameAndArgument) {\n\t\t\t\t\t\t// This supports the useHeadersInDisplayName attributes in @CsvSource and @CsvFileSource.\n\t\t\t\t\t\tprefix = parameterNameAndArgument.getName() + \" = \";\n\t\t\t\t\t\targument = parameterNameAndArgument.getPayload();\n\t\t\t\t\t}\n\t\t\t\t\telse if (this.generateNameValuePairs && this.resolverFacade != null) {\n\t\t\t\t\t\tOptional<String> parameterName = this.resolverFacade.getParameterName(i);\n\t\t\t\t\t\tif (parameterName.isPresent()) {\n\t\t\t\t\t\t\t// This supports the {argumentsWithNames} pattern.\n\t\t\t\t\t\t\tprefix = parameterName.get() + \" = \";\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (argument instanceof Character ch) {\n\t\t\t\t\t\tresult[i] = prefix + (quoteTextArguments ? QuoteUtils.quote(ch) : ch);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tString argumentText = (argument == null ? \"null\"\n\t\t\t\t\t\t\t\t: truncateIfExceedsMaxLength(StringUtils.nullSafeToString(argument)));\n\t\t\t\t\t\tresult[i] = prefix + (quoteTextArguments && argument instanceof CharSequence//\n\t\t\t\t\t\t\t\t? QuoteUtils.quote(argumentText)\n\t\t\t\t\t\t\t\t: argumentText);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\tprivate String truncateIfExceedsMaxLength(String argument) {\n\t\t\tif (argument.length() > this.argumentMaxLength) {\n\t\t\t\treturn argument.substring(0, this.argumentMaxLength - 1) + ELLIPSIS;\n\t\t\t}\n\t\t\treturn argument;\n\t\t}\n\t}\n\n\t/**\n\t * Caches formatters by the length of the consumed <em>arguments</em> which\n\t * may differ from the number of declared parameters.\n\t *\n\t * <p>For example, when using multiple providers or a provider that returns\n\t * argument arrays of different length, such as:\n\t *\n\t * <pre>\n\t * &#064;ParameterizedTest\n\t * &#064;CsvSource({\"a\", \"a,b\", \"a,b,c\"})\n\t * void test(ArgumentsAccessor accessor) {}\n\t * </pre>\n\t */\n\tprivate static class CachingByArgumentsLengthPartialFormatter implements PartialFormatter {\n\n\t\tprivate final ConcurrentMap<Integer, PartialFormatter> cache = new ConcurrentHashMap<>(1);\n\t\tprivate final Function<Integer, PartialFormatter> factory;\n\n\t\tCachingByArgumentsLengthPartialFormatter(Function<Integer, PartialFormatter> factory) {\n\t\t\tthis.factory = factory;\n\t\t}\n\n\t\t@Override\n\t\tpublic void append(ArgumentsContext context, StringBuffer result) {\n\t\t\tcache.computeIfAbsent(context.consumedArguments.length, factory).append(context, result);\n\t\t}\n\t}\n\n\tprivate static class PartialFormatters {\n\n\t\tprivate final Map<String, PartialFormatter> formattersByPlaceholder = new LinkedHashMap<>();\n\t\tprivate int minimumPlaceholderLength = Integer.MAX_VALUE;\n\n\t\tvoid put(String placeholder, PartialFormatter formatter) {\n\t\t\tformattersByPlaceholder.put(placeholder, formatter);\n\t\t\tint newPlaceholderLength = placeholder.length();\n\t\t\tif (newPlaceholderLength < minimumPlaceholderLength) {\n\t\t\t\tminimumPlaceholderLength = newPlaceholderLength;\n\t\t\t}\n\t\t}\n\n\t\tPartialFormatter get(String placeholder) {\n\t\t\treturn requireNonNull(formattersByPlaceholder.get(placeholder));\n\t\t}\n\n\t\tSet<String> placeholders() {\n\t\t\treturn formattersByPlaceholder.keySet();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedInvocationParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Executable;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * @since 5.13\n */\nabstract class ParameterizedInvocationParameterResolver implements ParameterResolver {\n\n\tprivate final ResolverFacade resolverFacade;\n\tprivate final EvaluatedArgumentSet arguments;\n\tprivate final int invocationIndex;\n\tprivate final ResolutionCache resolutionCache;\n\n\tParameterizedInvocationParameterResolver(ResolverFacade resolverFacade, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\n\t\tthis.resolverFacade = resolverFacade;\n\t\tthis.arguments = arguments;\n\t\tthis.invocationIndex = invocationIndex;\n\t\tthis.resolutionCache = resolutionCache;\n\t}\n\n\t@Override\n\tpublic final ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t@Override\n\tpublic final boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\n\t\treturn isSupportedOnConstructorOrMethod(parameterContext.getDeclaringExecutable(), extensionContext) //\n\t\t\t\t&& this.resolverFacade.isSupportedParameter(parameterContext, this.arguments);\n\n\t}\n\n\t@Override\n\tpublic final @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\n\t\treturn this.resolverFacade.resolve(parameterContext, extensionContext, this.arguments, this.invocationIndex,\n\t\t\tthis.resolutionCache);\n\t}\n\n\tprotected abstract boolean isSupportedOnConstructorOrMethod(Executable declaringExecutable,\n\t\t\tExtensionContext extensionContext);\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\n\n/**\n * {@code @ParameterizedTest} is used to signal that the annotated method is a\n * <em>parameterized test</em> method.\n *\n * <p>Such methods must not be {@code private} or {@code static}.\n *\n * <h2>Arguments Providers and Sources</h2>\n *\n * <p>{@code @ParameterizedTest} methods must specify at least one\n * {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider}\n * via {@link org.junit.jupiter.params.provider.ArgumentsSource @ArgumentsSource}\n * or a corresponding composed annotation (e.g., {@code @ValueSource},\n * {@code @CsvSource}, etc.). The provider is responsible for providing a\n * {@link java.util.stream.Stream Stream} of\n * {@link org.junit.jupiter.params.provider.Arguments Arguments} that will be\n * used to invoke the parameterized test method.\n *\n * <h2>Formal Parameter List</h2>\n *\n * <p>A {@code @ParameterizedTest} method may declare additional parameters at\n * the end of the method's parameter list to be resolved by other\n * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolvers}\n * (e.g., {@code TestInfo}, {@code TestReporter}, etc.). Specifically, a\n * parameterized test method must declare formal parameters according to the\n * following rules.\n *\n * <ol>\n * <li>Zero or more <em>indexed parameters</em> must be declared first.</li>\n * <li>Zero or more <em>aggregators</em> must be declared next.</li>\n * <li>Zero or more parameters supplied by other {@code ParameterResolver}\n * implementations must be declared last.</li>\n * </ol>\n *\n * <p>In this context, an <em>indexed parameter</em> is an argument for a given\n * index in the {@code Arguments} provided by an {@code ArgumentsProvider} that\n * is passed as an argument to the parameterized method at the same index in the\n * method's formal parameter list. An <em>aggregator</em> is any parameter of type\n * {@link org.junit.jupiter.params.aggregator.ArgumentsAccessor ArgumentsAccessor}\n * or any parameter annotated with\n * {@link org.junit.jupiter.params.aggregator.AggregateWith @AggregateWith}.\n *\n * <h2>Argument Conversion</h2>\n *\n * <p>Method parameters may be annotated with\n * {@link org.junit.jupiter.params.converter.ConvertWith @ConvertWith}\n * or a corresponding composed annotation to specify an <em>explicit</em>\n * {@link org.junit.jupiter.params.converter.ArgumentConverter ArgumentConverter}.\n * Otherwise, JUnit Jupiter will attempt to perform an <em>implicit</em>\n * conversion to the target type automatically (see the User Guide for further\n * details).\n *\n * <h2>Composed Annotations</h2>\n *\n * <p>{@code @ParameterizedTest} may also be used as a meta-annotation in order\n * to create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @ParameterizedTest}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>{@code @ParameterizedTest} methods are inherited from superclasses as long\n * as they are not <em>overridden</em> according to the visibility rules of the\n * Java language. Similarly, {@code @ParameterizedTest} methods declared as\n * <em>interface default methods</em> are inherited as long as they are not\n * overridden.\n *\n * <h2>Test Execution Order</h2>\n *\n * <p>By default, test methods will be ordered using an algorithm that is\n * deterministic but intentionally nonobvious. This ensures that subsequent runs\n * of a test suite execute test methods in the same order, thereby allowing for\n * repeatable builds. In this context, a <em>test method</em> is any instance\n * method that is directly annotated or meta-annotated with {@code @Test},\n * {@code @RepeatedTest}, {@code @ParameterizedTest}, {@code @TestFactory}, or\n * {@code @TestTemplate}.\n *\n * <p>Although true <em>unit tests</em> typically should not rely on the order\n * in which they are executed, there are times when it is necessary to enforce\n * a specific test method execution order &mdash; for example, when writing\n * <em>integration tests</em> or <em>functional tests</em> where the sequence of\n * the tests is important, especially in conjunction with\n * {@link org.junit.jupiter.api.TestInstance @TestInstance(Lifecycle.PER_CLASS)}.\n *\n * <p>To control the order in which test methods are executed, annotate your\n * test class or test interface with\n * {@link org.junit.jupiter.api.TestMethodOrder @TestMethodOrder} and specify\n * the desired {@link org.junit.jupiter.api.MethodOrderer MethodOrderer}\n * implementation.\n *\n * @since 5.0\n * @see ParameterizedClass\n * @see org.junit.jupiter.params.provider.Arguments\n * @see org.junit.jupiter.params.provider.ArgumentsProvider\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.provider.CsvFileSource\n * @see org.junit.jupiter.params.provider.CsvSource\n * @see org.junit.jupiter.params.provider.EnumSource\n * @see org.junit.jupiter.params.provider.MethodSource\n * @see org.junit.jupiter.params.provider.ValueSource\n * @see org.junit.jupiter.params.aggregator.ArgumentsAccessor\n * @see org.junit.jupiter.params.aggregator.AggregateWith\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n * @see org.junit.jupiter.params.converter.ConvertWith\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.7\")\n@TestTemplate\n@ExtendWith(ParameterizedTestExtension.class)\n@SuppressWarnings(\"exports\")\npublic @interface ParameterizedTest {\n\n\t/**\n\t * See {@link ParameterizedInvocationConstants#DISPLAY_NAME_PLACEHOLDER}.\n\t *\n\t * @since 5.3\n\t * @see #name\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#DISPLAY_NAME_PLACEHOLDER}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString DISPLAY_NAME_PLACEHOLDER = ParameterizedInvocationConstants.DISPLAY_NAME_PLACEHOLDER;\n\n\t/**\n\t * See {@link ParameterizedInvocationConstants#INDEX_PLACEHOLDER}.\n\t *\n\t * @since 5.3\n\t * @see #name\n\t * @see ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#INDEX_PLACEHOLDER} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString INDEX_PLACEHOLDER = ParameterizedInvocationConstants.INDEX_PLACEHOLDER;\n\n\t/**\n\t * See {@link ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER}.\n\t *\n\t * @since 5.3\n\t * @see #name\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString ARGUMENTS_PLACEHOLDER = ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;\n\n\t/**\n\t * See\n\t * {@link ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}.\n\t *\n\t * @since 5.6\n\t * @see #name\n\t * @see ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString ARGUMENTS_WITH_NAMES_PLACEHOLDER = ParameterizedInvocationConstants.ARGUMENTS_WITH_NAMES_PLACEHOLDER;\n\n\t/**\n\t * See\n\t * {@link ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER}.\n\t *\n\t * @since 5.11\n\t * @see #name\n\t * @see ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString ARGUMENT_SET_NAME_PLACEHOLDER = ParameterizedInvocationConstants.ARGUMENT_SET_NAME_PLACEHOLDER;\n\n\t/**\n\t * See\n\t * {@link ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}.\n\t *\n\t * @since 5.11\n\t * @see #name\n\t * @see ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER\n\t * @see ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @see ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME\n\t * @see org.junit.jupiter.params.provider.Arguments#argumentSet(String, Object...)\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER = //\n\t\tParameterizedInvocationConstants.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;\n\n\t/**\n\t * See\n\t * {@link ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME}.\n\t *\n\t * @since 5.3\n\t * @see #name\n\t * @see ParameterizedInvocationConstants#DISPLAY_NAME_PLACEHOLDER\n\t * @see ParameterizedInvocationConstants#INDEX_PLACEHOLDER\n\t * @see ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t * @deprecated Please reference\n\t * {@link ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"5.13\")\n\t@Deprecated(since = \"5.13\")\n\tString DEFAULT_DISPLAY_NAME = ParameterizedInvocationConstants.DEFAULT_DISPLAY_NAME;\n\n\t/**\n\t * The display name to be used for individual invocations of the\n\t * parameterized test; never blank or consisting solely of whitespace.\n\t *\n\t * <p>Defaults to <code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code>.\n\t *\n\t * <p>If the default display name flag\n\t * (<code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code>)\n\t * is not overridden, JUnit will:\n\t * <ul>\n\t * <li>Look up the {@value ParameterizedInvocationNameFormatter#DISPLAY_NAME_PATTERN_KEY}\n\t * <em>configuration parameter</em> and use it if available. The configuration\n\t * parameter can be supplied via the {@code Launcher} API, build tools (e.g.,\n\t * Gradle and Maven), a JVM system property, or the JUnit Platform configuration\n\t * file (i.e., a file named {@code junit-platform.properties} in the root of\n\t * the class path). Consult the User Guide for further information.</li>\n\t * <li>Otherwise, <code>{@value ParameterizedInvocationConstants#DEFAULT_DISPLAY_NAME}</code> will be used.</li>\n\t * </ul>\n\t *\n\t * <h4>Supported placeholders</h4>\n\t * <ul>\n\t * <li><code>{@value ParameterizedInvocationConstants#DISPLAY_NAME_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#INDEX_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>\n\t * <li><code>{@value ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER}</code></li>\n\t * <li><code>\"{0}\"</code>, <code>\"{1}\"</code>, etc.: an individual argument (0-based)</li>\n\t * </ul>\n\t *\n\t * <p>For the latter, you may use {@link java.text.MessageFormat} patterns\n\t * to customize formatting (for example, {@code {0,number,#.###}}). Please\n\t * note that the original arguments are passed when formatting, regardless\n\t * of any implicit or explicit argument conversions.\n\t *\n\t * <p>Note that\n\t * <code>{@value ParameterizedInvocationNameFormatter#DEFAULT_DISPLAY_NAME}</code> is\n\t * a flag rather than a placeholder.\n\t *\n\t * @see java.text.MessageFormat\n\t * @see #quoteTextArguments()\n\t */\n\tString name() default ParameterizedInvocationNameFormatter.DEFAULT_DISPLAY_NAME;\n\n\t/**\n\t * Configure whether to enclose text-based argument values in quotes within\n\t * display names.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * <p>In this context, any {@link CharSequence} (such as a {@link String})\n\t * or {@link Character} is considered text. A {@code CharSequence} is wrapped\n\t * in double quotes (\"), and a {@code Character} is wrapped in single quotes\n\t * (').\n\t *\n\t * <p>Special characters in Java strings and characters will be escaped in the\n\t * quoted text &mdash; for example, carriage returns and line feeds will be\n\t * escaped as {@code \\\\r} and {@code \\\\n}, respectively. In addition, any\n\t * {@linkplain Character#isISOControl(char) ISO control character} will be\n\t * represented as a question mark (?) in the quoted text.\n\t *\n\t * <p>For example, given a string argument {@code \"line 1\\nline 2\"}, the\n\t * representation in the display name would be {@code \"\\\"line 1\\\\nline 2\\\"\"}\n\t * (printed as {@code \"line 1\\nline 2\"}) with the newline character escaped as\n\t * {@code \"\\\\n\"}. Similarly, given a string argument {@code \"\\t\"}, the\n\t * representation in the display name would be {@code \"\\\"\\\\t\\\"\"} (printed as\n\t * {@code \"\\t\"}) instead of a blank string or invisible tab\n\t * character. The same applies for a character argument {@code '\\t'}, whose\n\t * representation in the display name would be {@code \"'\\\\t'\"} (printed as\n\t * {@code '\\t'}).\n\t *\n\t * <p>Please note that original source arguments are quoted when generating\n\t * a display name, before any implicit or explicit argument conversion is\n\t * performed. For example, if a parameterized test accepts {@code 3.14} as a\n\t * {@code float} argument that was converted from {@code \"3.14\"} as an input\n\t * string, {@code \"3.14\"} will be present in the display name instead of\n\t * {@code 3.14}.\n\t *\n\t * @since 6.0\n\t * @see #name()\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tboolean quoteTextArguments() default true;\n\n\t/**\n\t * Configure whether all arguments of the parameterized test that implement\n\t * {@link AutoCloseable} will be closed after their corresponding\n\t * invocation.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * <p><strong>WARNING</strong>: if an argument that implements\n\t * {@code AutoCloseable} is reused for multiple invocations of the same\n\t * parameterized test method, you must set {@code autoCloseArguments} to\n\t * {@code false} to ensure that the argument is not closed between\n\t * invocations.\n\t *\n\t * @since 5.8\n\t * @see java.lang.AutoCloseable\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tboolean autoCloseArguments() default true;\n\n\t/**\n\t * Configure whether zero invocations are allowed for this\n\t * parameterized test.\n\t *\n\t * <p>Set this attribute to {@code true} if the absence of invocations is\n\t * expected in some cases and should not cause a test failure.\n\t *\n\t * <p>Defaults to {@code false}.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tboolean allowZeroInvocations() default false;\n\n\t/**\n\t * Configure how the number of arguments provided by an\n\t * {@link ArgumentsSource} are validated.\n\t *\n\t * <p>Defaults to {@link ArgumentCountValidationMode#DEFAULT}.\n\t *\n\t * <p>When an {@link ArgumentsSource} provides more arguments than declared\n\t * by the parameterized test method, there might be a bug in the method or\n\t * the {@link ArgumentsSource}. By default, the additional arguments are\n\t * ignored. {@code argumentCountValidation} allows you to control how\n\t * additional arguments are handled. The default can be configured via the\n\t * {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY}\n\t * configuration parameter (see the User Guide for details on configuration\n\t * parameters).\n\t *\n\t * @since 5.12\n\t * @see ArgumentCountValidationMode\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tArgumentCountValidationMode argumentCountValidation() default ArgumentCountValidationMode.DEFAULT;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Encapsulates access to the parameters of a parameterized test method and\n * caches the converters and aggregators used to resolve them.\n *\n * @since 5.3\n */\nclass ParameterizedTestContext implements ParameterizedDeclarationContext<ParameterizedTestInvocationContext> {\n\n\tprivate final Class<?> testClass;\n\tprivate final Method method;\n\tprivate final ParameterizedTest annotation;\n\tprivate final ResolverFacade resolverFacade;\n\n\tParameterizedTestContext(Class<?> testClass, Method method, ParameterizedTest annotation) {\n\t\tthis.testClass = testClass;\n\t\tthis.method = Preconditions.notNull(method, \"method must not be null\");\n\t\tthis.annotation = Preconditions.notNull(annotation, \"annotation must not be null\");\n\t\tthis.resolverFacade = ResolverFacade.create(method, annotation);\n\t}\n\n\t@Override\n\tpublic Class<?> getTestClass() {\n\t\treturn this.testClass;\n\t}\n\n\t@Override\n\tpublic ParameterizedTest getAnnotation() {\n\t\treturn this.annotation;\n\t}\n\n\t@Override\n\tpublic Method getAnnotatedElement() {\n\t\treturn this.method;\n\t}\n\n\t@Override\n\tpublic String getDisplayNamePattern() {\n\t\treturn this.annotation.name();\n\t}\n\n\t@Override\n\tpublic boolean quoteTextArguments() {\n\t\treturn this.annotation.quoteTextArguments();\n\t}\n\n\t@Override\n\tpublic boolean isAutoClosingArguments() {\n\t\treturn this.annotation.autoCloseArguments();\n\t}\n\n\t@Override\n\tpublic boolean isAllowingZeroInvocations() {\n\t\treturn this.annotation.allowZeroInvocations();\n\t}\n\n\t@Override\n\tpublic ArgumentCountValidationMode getArgumentCountValidationMode() {\n\t\treturn this.annotation.argumentCountValidation();\n\t}\n\n\t@Override\n\tpublic ResolverFacade getResolverFacade() {\n\t\treturn this.resolverFacade;\n\t}\n\n\t@Override\n\tpublic ParameterizedTestInvocationContext createInvocationContext(ParameterizedInvocationNameFormatter formatter,\n\t\t\tArguments arguments, int invocationIndex) {\n\t\treturn new ParameterizedTestInvocationContext(this, formatter, arguments, invocationIndex);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\n\n/**\n * @since 5.0\n */\nclass ParameterizedTestExtension extends ParameterizedInvocationContextProvider<ParameterizedTestInvocationContext>\n\t\timplements TestTemplateInvocationContextProvider {\n\n\tstatic final String DECLARATION_CONTEXT_KEY = \"context\";\n\n\t@Override\n\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\tOptional<ParameterizedTest> annotation = findAnnotation(context.getTestMethod(), ParameterizedTest.class);\n\t\tif (annotation.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tParameterizedTestContext methodContext = new ParameterizedTestContext(context.getRequiredTestClass(),\n\t\t\tcontext.getRequiredTestMethod(), annotation.get());\n\n\t\tgetStore(context).put(DECLARATION_CONTEXT_KEY, methodContext);\n\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic Stream<ParameterizedTestInvocationContext> provideTestTemplateInvocationContexts(\n\t\t\tExtensionContext extensionContext) {\n\n\t\treturn provideInvocationContexts(extensionContext, getDeclarationContext(extensionContext));\n\t}\n\n\t@Override\n\tpublic boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext extensionContext) {\n\t\treturn getDeclarationContext(extensionContext).isAllowingZeroInvocations();\n\t}\n\n\tprivate ParameterizedTestContext getDeclarationContext(ExtensionContext extensionContext) {\n\t\treturn requireNonNull(getStore(extensionContext)//\n\t\t\t\t.get(DECLARATION_CONTEXT_KEY, ParameterizedTestContext.class));\n\t}\n\n\tprivate ExtensionContext.Store getStore(ExtensionContext context) {\n\t\treturn context.getStore(Namespace.create(ParameterizedTestExtension.class, context.getRequiredTestMethod()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.params.provider.Arguments;\n\n/**\n * @since 5.0\n */\nclass ParameterizedTestInvocationContext extends ParameterizedInvocationContext<ParameterizedTestContext>\n\t\timplements TestTemplateInvocationContext {\n\n\tParameterizedTestInvocationContext(ParameterizedTestContext methodContext,\n\t\t\tParameterizedInvocationNameFormatter formatter, Arguments arguments, int invocationIndex) {\n\t\tsuper(methodContext, formatter, arguments, invocationIndex);\n\t}\n\n\t@Override\n\tpublic List<Extension> getAdditionalExtensions() {\n\t\treturn List.of( //\n\t\t\tnew ParameterizedTestMethodParameterResolver(this.declarationContext, this.arguments, this.invocationIndex) //\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestMethodParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.0\n */\nclass ParameterizedTestMethodParameterResolver extends ParameterizedInvocationParameterResolver {\n\n\tprivate final Method testTemplateMethod;\n\n\tParameterizedTestMethodParameterResolver(ParameterizedTestContext methodContext, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex) {\n\t\tsuper(methodContext.getResolverFacade(), arguments, invocationIndex, ResolutionCache.DISABLED);\n\t\tthis.testTemplateMethod = methodContext.getAnnotatedElement();\n\t}\n\n\t@Override\n\tprotected boolean isSupportedOnConstructorOrMethod(Executable declaringExecutable,\n\t\t\tExtensionContext extensionContext) {\n\t\treturn this.testTemplateMethod.equals(declaringExecutable);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestSpiInstantiator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.lang.reflect.Constructor;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.12\n */\nclass ParameterizedTestSpiInstantiator {\n\n\tstatic <T> T instantiate(Class<T> spiInterface, Class<? extends T> implementationClass,\n\t\t\tExtensionContext extensionContext) {\n\n\t\treturn extensionContext.getExecutableInvoker() //\n\t\t\t\t.invoke(findConstructor(spiInterface, implementationClass));\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> Constructor<? extends T> findConstructor(Class<T> spiInterface,\n\t\t\tClass<? extends T> implementationClass) {\n\n\t\treturn (Constructor<? extends T>) findBestConstructor(spiInterface, implementationClass);\n\t}\n\n\t/**\n\t * Find the \"best\" constructor for the supplied implementation class.\n\t *\n\t * <p>For backward compatibility, it first checks for a single constructor\n\t * and returns that. If there are multiple constructors, it checks for a\n\t * default constructor which takes precedence over any other constructors.\n\t * Otherwise, this method throws an exception stating that it failed to\n\t * find a suitable constructor.\n\t */\n\tprivate static <T> Constructor<?> findBestConstructor(Class<T> spiInterface,\n\t\t\tClass<? extends T> implementationClass) {\n\n\t\tPreconditions.condition(!ReflectionUtils.isInnerClass(implementationClass),\n\t\t\t() -> \"The %s [%s] must be either a top-level class or a static nested class\".formatted(\n\t\t\t\tspiInterface.getSimpleName(), implementationClass.getName()));\n\n\t\tConstructor<?>[] constructors = implementationClass.getDeclaredConstructors();\n\n\t\t// Single constructor?\n\t\tif (constructors.length == 1) {\n\t\t\treturn constructors[0];\n\t\t}\n\t\t// Find default constructor.\n\t\tfor (Constructor<?> constructor : constructors) {\n\t\t\tif (constructor.getParameterCount() == 0) {\n\t\t\t\treturn constructor;\n\t\t\t}\n\t\t}\n\t\t// Otherwise...\n\t\tString message = \"\"\"\n\t\t\t\tFailed to find constructor for %s [%s]. \\\n\t\t\t\tPlease ensure that a no-argument or a single constructor exists.\"\"\".formatted(\n\t\t\tspiInterface.getSimpleName(), implementationClass.getName());\n\t\tthrow new JUnitException(message);\n\t}\n\n\tprivate ParameterizedTestSpiInstantiator() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/QuoteUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\n/**\n * Collection of utilities for quoting text.\n *\n * @since 6.0\n */\nfinal class QuoteUtils {\n\n\tprivate QuoteUtils() {\n\t\t/* no-op */\n\t}\n\n\tpublic static String quote(CharSequence text) {\n\t\tif (text.isEmpty()) {\n\t\t\treturn \"\\\"\\\"\";\n\t\t}\n\t\tStringBuilder builder = new StringBuilder();\n\t\tbuilder.append('\"');\n\t\tfor (int i = 0; i < text.length(); i++) {\n\t\t\tbuilder.append(escape(text.charAt(i), true));\n\t\t}\n\t\tbuilder.append('\"');\n\t\treturn builder.toString();\n\t}\n\n\tpublic static String quote(char ch) {\n\t\treturn '\\'' + escape(ch, false) + '\\'';\n\t}\n\n\tprivate static String escape(char ch, boolean withinString) {\n\t\treturn switch (ch) {\n\t\t\tcase '\"' -> withinString ? \"\\\\\\\"\" : \"\\\"\";\n\t\t\tcase '\\'' -> withinString ? \"'\" : \"\\\\'\";\n\t\t\tcase '\\\\' -> \"\\\\\\\\\";\n\t\t\tcase '\\b' -> \"\\\\b\";\n\t\t\tcase '\\f' -> \"\\\\f\";\n\t\t\tcase '\\t' -> \"\\\\t\";\n\t\t\tcase '\\r' -> \"\\\\r\";\n\t\t\tcase '\\n' -> \"\\\\n\";\n\t\t\tdefault -> String.valueOf(ch);\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ResolutionCache.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.params.support.ParameterDeclaration;\n\n/**\n * @since 5.13\n */\ninterface ResolutionCache {\n\n\tstatic ResolutionCache enabled() {\n\t\treturn new Concurrent();\n\t}\n\n\tResolutionCache DISABLED = (__, resolver) -> resolver.get();\n\n\t@Nullable\n\tObject resolve(ParameterDeclaration declaration, Supplier<?> resolver);\n\n\tclass Concurrent implements ResolutionCache {\n\n\t\tprivate final Map<ParameterDeclaration, Object> cache = new ConcurrentHashMap<>();\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(ParameterDeclaration declaration, Supplier<?> resolver) {\n\t\t\treturn cache.computeIfAbsent(declaration, __ -> resolver.get());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/ResolverFacade.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.lang.System.lineSeparator;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toMap;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.support.ReflectionSupport.makeAccessible;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.getKotlinSuspendingFunctionParameters;\nimport static org.junit.platform.commons.util.KotlinReflectionUtils.isKotlinSuspendingFunction;\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.NavigableMap;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.TreeMap;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.ArgumentsAggregationException;\nimport org.junit.jupiter.params.aggregator.ArgumentsAggregator;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.converter.DefaultArgumentConverter;\nimport org.junit.jupiter.params.support.AnnotationConsumerInitializer;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.jupiter.params.support.ParameterDeclaration;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\n\nclass ResolverFacade {\n\n\tstatic ResolverFacade create(Class<?> clazz, List<Field> fields) {\n\t\tPreconditions.notEmpty(fields, \"Fields must not be empty\");\n\n\t\tNavigableMap<Integer, List<FieldParameterDeclaration>> allIndexedParameters = new TreeMap<>();\n\t\tSet<FieldParameterDeclaration> aggregatorParameters = new LinkedHashSet<>();\n\n\t\tfor (Field field : fields) {\n\t\t\tParameter annotation = findAnnotation(field, Parameter.class) //\n\t\t\t\t\t.orElseThrow(() -> new JUnitException(\"No @Parameter annotation present\"));\n\t\t\tint index = annotation.value();\n\n\t\t\tFieldParameterDeclaration declaration = new FieldParameterDeclaration(field, annotation.value());\n\t\t\tif (declaration.isAggregator()) {\n\t\t\t\taggregatorParameters.add(declaration);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (fields.size() == 1 && index == Parameter.UNSET_INDEX) {\n\t\t\t\t\tindex = 0;\n\t\t\t\t\tdeclaration = new FieldParameterDeclaration(field, 0);\n\t\t\t\t}\n\t\t\t\tallIndexedParameters.computeIfAbsent(index, __ -> new ArrayList<>()) //\n\t\t\t\t\t\t.add(declaration);\n\t\t\t}\n\t\t}\n\n\t\tNavigableMap<Integer, FieldParameterDeclaration> uniqueIndexedParameters = validateFieldDeclarations(\n\t\t\tallIndexedParameters, aggregatorParameters);\n\n\t\tStream.concat(uniqueIndexedParameters.values().stream(), aggregatorParameters.stream()) //\n\t\t\t\t.forEach(declaration -> makeAccessible(declaration.getField()));\n\n\t\tvar requiredParameterCount = new RequiredParameterCount(uniqueIndexedParameters.size(), \"field injection\");\n\n\t\treturn new ResolverFacade(clazz, uniqueIndexedParameters, aggregatorParameters, 0, requiredParameterCount);\n\t}\n\n\tstatic ResolverFacade create(Constructor<?> constructor, ParameterizedClass annotation) {\n\t\t// Inner classes get the outer instance as first (implicit) parameter\n\t\tint implicitParameters = isInnerClass(constructor.getDeclaringClass()) ? 1 : 0;\n\t\treturn create(constructor, annotation, implicitParameters);\n\t}\n\n\tstatic ResolverFacade create(Method method, Annotation annotation) {\n\t\tif (isKotlinSuspendingFunction(method)) {\n\t\t\treturn create(method, annotation, 0, getKotlinSuspendingFunctionParameters(method));\n\t\t}\n\t\treturn create(method, annotation, 0);\n\t}\n\n\t/**\n\t * Create a new {@link ResolverFacade} for the supplied {@link Executable}.\n\t *\n\t * <p>This method takes a best-effort approach at enforcing the following\n\t * policy for parameterized class constructors and parameterized test\n\t * methods that accept aggregators as arguments.\n\t * <ol>\n\t * <li>zero or more <em>indexed arguments</em> come first.</li>\n\t * <li>zero or more <em>aggregators</em> come next.</li>\n\t * <li>zero or more arguments supplied by other {@code ParameterResolver}\n\t * implementations come last.</li>\n\t * </ol>\n\t */\n\tprivate static ResolverFacade create(Executable executable, Annotation annotation, int indexOffset) {\n\t\treturn create(executable, annotation, indexOffset, executable.getParameters());\n\t}\n\n\tprivate static ResolverFacade create(Executable executable, Annotation annotation, int indexOffset,\n\t\t\tjava.lang.reflect.Parameter[] parameters) {\n\t\tNavigableMap<Integer, ExecutableParameterDeclaration> indexedParameters = new TreeMap<>();\n\t\tNavigableMap<Integer, ExecutableParameterDeclaration> aggregatorParameters = new TreeMap<>();\n\t\tfor (int index = indexOffset; index < parameters.length; index++) {\n\t\t\tExecutableParameterDeclaration declaration = new ExecutableParameterDeclaration(parameters[index], index,\n\t\t\t\tindexOffset);\n\t\t\tif (declaration.isAggregator()) {\n\t\t\t\tPreconditions.condition(\n\t\t\t\t\taggregatorParameters.isEmpty()\n\t\t\t\t\t\t\t|| aggregatorParameters.lastKey() == declaration.getParameterIndex() - 1,\n\t\t\t\t\t() -> \"\"\"\n\t\t\t\t\t\t\t@%s %s declares formal parameters in an invalid order: \\\n\t\t\t\t\t\t\targument aggregators must be declared after any indexed arguments \\\n\t\t\t\t\t\t\tand before any arguments resolved by another ParameterResolver.\"\"\".formatted(\n\t\t\t\t\t\tannotation.annotationType().getSimpleName(),\n\t\t\t\t\t\tDefaultParameterDeclarations.describe(executable)));\n\t\t\t\taggregatorParameters.put(declaration.getParameterIndex(), declaration);\n\t\t\t}\n\t\t\telse if (aggregatorParameters.isEmpty()) {\n\t\t\t\tindexedParameters.put(declaration.getParameterIndex(), declaration);\n\t\t\t}\n\t\t}\n\t\treturn new ResolverFacade(executable, indexedParameters, new LinkedHashSet<>(aggregatorParameters.values()),\n\t\t\tindexOffset, null);\n\t}\n\n\tprivate final int parameterIndexOffset;\n\tprivate final Map<ParameterDeclaration, Resolver> resolvers;\n\tprivate final DefaultParameterDeclarations indexedParameterDeclarations;\n\tprivate final Set<? extends ResolvableParameterDeclaration> aggregatorParameters;\n\tprivate final @Nullable RequiredParameterCount requiredParameterCount;\n\n\tprivate ResolverFacade(AnnotatedElement sourceElement,\n\t\t\tNavigableMap<Integer, ? extends ResolvableParameterDeclaration> indexedParameters,\n\t\t\tSet<? extends ResolvableParameterDeclaration> aggregatorParameters, int parameterIndexOffset,\n\t\t\t@Nullable RequiredParameterCount requiredParameterCount) {\n\t\tthis.aggregatorParameters = aggregatorParameters;\n\t\tthis.parameterIndexOffset = parameterIndexOffset;\n\t\tthis.resolvers = new ConcurrentHashMap<>(indexedParameters.size() + aggregatorParameters.size());\n\t\tthis.indexedParameterDeclarations = new DefaultParameterDeclarations(sourceElement, indexedParameters);\n\t\tthis.requiredParameterCount = requiredParameterCount;\n\t}\n\n\tParameterDeclarations getIndexedParameterDeclarations() {\n\t\treturn this.indexedParameterDeclarations;\n\t}\n\n\t@Nullable\n\tRequiredParameterCount getRequiredParameterCount() {\n\t\treturn this.requiredParameterCount;\n\t}\n\n\tboolean isSupportedParameter(ParameterContext parameterContext, EvaluatedArgumentSet arguments) {\n\t\tint index = toLogicalIndex(parameterContext);\n\t\tif (this.indexedParameterDeclarations.get(index).isPresent()) {\n\t\t\treturn index < arguments.getConsumedLength();\n\t\t}\n\t\treturn !this.aggregatorParameters.isEmpty()\n\t\t\t\t&& this.aggregatorParameters.stream().anyMatch(it -> it.getParameterIndex() == index);\n\t}\n\n\t/**\n\t * Get the name of the parameter with the supplied index, if it is present\n\t * and declared before the aggregators.\n\t *\n\t * @return an {@code Optional} containing the name of the parameter\n\t */\n\tOptional<String> getParameterName(int parameterIndex) {\n\t\treturn this.indexedParameterDeclarations.get(parameterIndex) //\n\t\t\t\t.flatMap(ParameterDeclaration::getParameterName);\n\t}\n\n\t/**\n\t * Determine the length of the arguments array that is considered consumed\n\t * by the parameter declarations in this resolver.\n\t *\n\t * <p>If an aggregator is present, all arguments are considered consumed.\n\t * Otherwise, the consumed argument length is the minimum of the total\n\t * length and the number of indexed parameter declarations.\n\t */\n\tint determineConsumedArgumentLength(int totalLength) {\n\t\tNavigableMap<Integer, ? extends ParameterDeclaration> declarationsByIndex = this.indexedParameterDeclarations.declarationsByIndex;\n\t\treturn this.aggregatorParameters.isEmpty() //\n\t\t\t\t? Math.min(totalLength, declarationsByIndex.isEmpty() ? 0 : declarationsByIndex.lastKey() + 1) //\n\t\t\t\t: totalLength;\n\t}\n\n\t/**\n\t * Determine the number of arguments that are considered consumed by the\n\t * parameter declarations in this resolver.\n\t *\n\t * <p>If an aggregator is present, all arguments are considered consumed.\n\t * Otherwise, the consumed argument count, is the number of indexes that\n\t * correspond to indexed parameter declarations.\n\t */\n\tint determineConsumedArgumentCount(EvaluatedArgumentSet arguments) {\n\t\tif (this.aggregatorParameters.isEmpty()) {\n\t\t\treturn this.indexedParameterDeclarations.declarationsByIndex.subMap(0,\n\t\t\t\targuments.getConsumedLength()).size();\n\t\t}\n\t\treturn arguments.getTotalLength();\n\t}\n\n\tArgumentSetLifecycleMethod.ParameterResolver createLifecycleMethodParameterResolver(Method method,\n\t\t\tAnnotation annotation) {\n\t\tResolverFacade originalResolverFacade = this;\n\t\tResolverFacade lifecycleMethodResolverFacade = create(method, annotation);\n\n\t\tMap<ParameterDeclaration, ResolvableParameterDeclaration> parameterDeclarationMapping = new HashMap<>();\n\t\tList<String> errors = validateLifecycleMethodParameters(originalResolverFacade, lifecycleMethodResolverFacade,\n\t\t\tparameterDeclarationMapping);\n\n\t\treturn Try //\n\t\t\t\t.call(() -> configurationErrorOrSuccess(errors,\n\t\t\t\t\t() -> new DefaultArgumentSetLifecycleMethodParameterResolver(originalResolverFacade,\n\t\t\t\t\t\tlifecycleMethodResolverFacade, parameterDeclarationMapping))) //\n\t\t\t\t.getNonNullOrThrow(cause -> new ExtensionConfigurationException(\n\t\t\t\t\t\"Invalid @%s lifecycle method declaration: %s\".formatted(\n\t\t\t\t\t\tannotation.annotationType().getSimpleName(), method.toGenericString()),\n\t\t\t\t\tcause));\n\t}\n\n\t/**\n\t * Resolve the parameter for the supplied context using the supplied\n\t * arguments.\n\t */\n\t@Nullable\n\tObject resolve(ParameterContext parameterContext, ExtensionContext extensionContext, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\n\t\tint parameterIndex = toLogicalIndex(parameterContext);\n\t\tResolvableParameterDeclaration declaration = findDeclaration(parameterIndex) //\n\t\t\t\t.orElseThrow(\n\t\t\t\t\t() -> new ParameterResolutionException(\"Parameter index out of bounds: \" + parameterIndex));\n\n\t\treturn resolutionCache.resolve(declaration,\n\t\t\t() -> resolve(declaration, extensionContext, arguments, invocationIndex, Optional.of(parameterContext)));\n\t}\n\n\tprivate Optional<? extends ResolvableParameterDeclaration> findDeclaration(int parameterIndex) {\n\t\tResolvableParameterDeclaration declaration = this.indexedParameterDeclarations.declarationsByIndex //\n\t\t\t\t.get(parameterIndex);\n\t\tif (declaration == null) {\n\t\t\treturn this.aggregatorParameters.stream() //\n\t\t\t\t\t.filter(it -> it.getParameterIndex() == parameterIndex) //\n\t\t\t\t\t.findFirst();\n\t\t}\n\t\treturn Optional.of(declaration);\n\t}\n\n\tvoid resolveAndInjectFields(Object testInstance, ExtensionContext extensionContext, EvaluatedArgumentSet arguments,\n\t\t\tint invocationIndex, ResolutionCache resolutionCache) {\n\n\t\tif (this.indexedParameterDeclarations.sourceElement.equals(testInstance.getClass())) {\n\t\t\tgetAllParameterDeclarations() //\n\t\t\t\t\t.filter(FieldParameterDeclaration.class::isInstance) //\n\t\t\t\t\t.map(FieldParameterDeclaration.class::cast) //\n\t\t\t\t\t.forEach(declaration -> setField(testInstance, declaration, extensionContext, arguments,\n\t\t\t\t\t\tinvocationIndex, resolutionCache));\n\t\t}\n\t}\n\n\tprivate Stream<ParameterDeclaration> getAllParameterDeclarations() {\n\t\treturn Stream.concat(this.indexedParameterDeclarations.declarationsByIndex.values().stream(),\n\t\t\taggregatorParameters.stream());\n\t}\n\n\tprivate void setField(Object testInstance, FieldParameterDeclaration declaration, ExtensionContext extensionContext,\n\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache) {\n\n\t\tObject argument = resolutionCache.resolve(declaration,\n\t\t\t() -> resolve(declaration, extensionContext, arguments, invocationIndex, Optional.empty()));\n\t\ttry {\n\t\t\tdeclaration.getField().set(testInstance, argument);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new JUnitException(\"Failed to inject parameter value into field: \" + declaration.getField(), e);\n\t\t}\n\t}\n\n\tprivate @Nullable Object resolve(ResolvableParameterDeclaration parameterDeclaration,\n\t\t\tExtensionContext extensionContext, EvaluatedArgumentSet arguments, int invocationIndex,\n\t\t\tOptional<ParameterContext> parameterContext) {\n\t\tResolver resolver = getResolver(extensionContext, parameterDeclaration);\n\t\treturn parameterDeclaration.resolve(resolver, extensionContext, arguments, invocationIndex, parameterContext);\n\t}\n\n\tprivate Resolver getResolver(ExtensionContext extensionContext, ResolvableParameterDeclaration declaration) {\n\t\treturn this.resolvers.computeIfAbsent(declaration, __ -> this.aggregatorParameters.contains(declaration) //\n\t\t\t\t? createAggregator(declaration, extensionContext) //\n\t\t\t\t: createConverter(declaration, extensionContext));\n\t}\n\n\tprivate int toLogicalIndex(ParameterContext parameterContext) {\n\t\tint index = parameterContext.getIndex() - this.parameterIndexOffset;\n\t\tPreconditions.condition(index >= 0, () -> \"Parameter index must be greater than or equal to zero\");\n\t\treturn index;\n\t}\n\n\tprivate static NavigableMap<Integer, FieldParameterDeclaration> validateFieldDeclarations(\n\t\t\tNavigableMap<Integer, List<FieldParameterDeclaration>> indexedParameters,\n\t\t\tSet<FieldParameterDeclaration> aggregatorParameters) {\n\n\t\tList<String> errors = new ArrayList<>();\n\t\tvalidateIndexedParameters(indexedParameters, errors);\n\t\tvalidateAggregatorParameters(aggregatorParameters, errors);\n\n\t\treturn configurationErrorOrSuccess(errors, () -> indexedParameters.entrySet().stream() //\n\t\t\t\t.collect(toMap(Map.Entry::getKey, entry -> entry.getValue().get(0), (d, __) -> d, TreeMap::new)));\n\t}\n\n\tprivate static List<String> validateLifecycleMethodParameters(ResolverFacade originalResolverFacade,\n\t\t\tResolverFacade lifecycleMethodResolverFacade,\n\t\t\tMap<ParameterDeclaration, ResolvableParameterDeclaration> parameterDeclarationMapping) {\n\t\tList<ParameterDeclaration> actualDeclarations = lifecycleMethodResolverFacade.indexedParameterDeclarations.getAll();\n\t\tList<String> errors = new ArrayList<>();\n\t\tfor (int parameterIndex = 0; parameterIndex < actualDeclarations.size(); parameterIndex++) {\n\t\t\tParameterDeclaration actualDeclaration = actualDeclarations.get(parameterIndex);\n\t\t\tResolvableParameterDeclaration originalDeclaration = originalResolverFacade.indexedParameterDeclarations.declarationsByIndex //\n\t\t\t\t\t.get(parameterIndex);\n\t\t\tif (originalDeclaration == null) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!actualDeclaration.getParameterType().equals(originalDeclaration.getParameterType())) {\n\t\t\t\terrors.add(\n\t\t\t\t\t\"parameter%s with index %d is incompatible with the parameter declared on the parameterized class: expected type '%s' but found '%s'\".formatted(\n\t\t\t\t\t\tparameterName(actualDeclaration), parameterIndex, originalDeclaration.getParameterType(),\n\t\t\t\t\t\tactualDeclaration.getParameterType()));\n\t\t\t}\n\t\t\telse if (findAnnotation(actualDeclaration.getAnnotatedElement(), ConvertWith.class).isPresent()) {\n\t\t\t\terrors.add(\"parameter%s with index %d must not be annotated with @ConvertWith\".formatted(\n\t\t\t\t\tparameterName(actualDeclaration), parameterIndex));\n\t\t\t}\n\t\t\telse if (errors.isEmpty()) {\n\t\t\t\tparameterDeclarationMapping.put(actualDeclaration, originalDeclaration);\n\t\t\t}\n\t\t}\n\t\treturn errors;\n\t}\n\n\tprivate static String parameterName(ParameterDeclaration actualDeclaration) {\n\t\treturn actualDeclaration.getParameterName().map(name -> \" '\" + name + \"'\").orElse(\"\");\n\t}\n\n\tprivate static <T> T configurationErrorOrSuccess(List<String> errors, Supplier<T> successfulResult) {\n\t\tif (errors.isEmpty()) {\n\t\t\treturn successfulResult.get();\n\t\t}\n\t\telse if (errors.size() == 1) {\n\t\t\tthrow new PreconditionViolationException(\"Configuration error: \" + errors.get(0) + \".\");\n\t\t}\n\t\telse {\n\t\t\tthrow new PreconditionViolationException(\"%d configuration errors:%n%s\".formatted(errors.size(),\n\t\t\t\terrors.stream().collect(joining(lineSeparator() + \"- \", \"- \", \"\"))));\n\t\t}\n\t}\n\n\tprivate static void validateIndexedParameters(\n\t\t\tNavigableMap<Integer, List<FieldParameterDeclaration>> indexedParameters, List<String> errors) {\n\n\t\tif (indexedParameters.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\tindexedParameters.forEach(\n\t\t\t(index, declarations) -> validateIndexedParameterDeclarations(index, declarations, errors));\n\n\t\tfor (int index = 0; index <= indexedParameters.lastKey(); index++) {\n\t\t\tif (!indexedParameters.containsKey(index)) {\n\t\t\t\terrors.add(\"no field annotated with @Parameter(%d) declared\".formatted(index));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void validateIndexedParameterDeclarations(int index, List<FieldParameterDeclaration> declarations,\n\t\t\tList<String> errors) {\n\t\tList<Field> fields = declarations.stream().map(FieldParameterDeclaration::getField).toList();\n\t\tif (index < 0) {\n\t\t\tdeclarations.stream() //\n\t\t\t\t\t.map(\n\t\t\t\t\t\tdeclaration -> \"index must be greater than or equal to zero in @Parameter(%d) annotation on field [%s]\".formatted(\n\t\t\t\t\t\t\tindex, declaration.getField())) //\n\t\t\t\t\t.forEach(errors::add);\n\t\t}\n\t\telse if (declarations.size() > 1) {\n\t\t\terrors.add(\"duplicate index declared in @Parameter(%d) annotation on fields %s\".formatted(index, fields));\n\t\t}\n\t\tfields.stream() //\n\t\t\t\t.filter(ModifierSupport::isFinal) //\n\t\t\t\t.map(\"@Parameter field [%s] must not be declared as final\"::formatted) //\n\t\t\t\t.forEach(errors::add);\n\t}\n\n\tprivate static void validateAggregatorParameters(Set<FieldParameterDeclaration> aggregatorParameters,\n\t\t\tList<String> errors) {\n\t\taggregatorParameters.stream() //\n\t\t\t\t.filter(declaration -> declaration.getParameterIndex() != Parameter.UNSET_INDEX) //\n\t\t\t\t.map(\n\t\t\t\t\tdeclaration -> \"no index may be declared in @Parameter(%d) annotation on aggregator field [%s]\".formatted(\n\t\t\t\t\t\tdeclaration.getParameterIndex(), declaration.getField())) //\n\t\t\t\t.forEach(errors::add);\n\t}\n\n\tprivate static Converter createConverter(ParameterDeclaration declaration, ExtensionContext extensionContext) {\n\t\ttry { // @formatter:off\n\t\t\treturn findAnnotation(declaration.getAnnotatedElement(), ConvertWith.class)\n\t\t\t\t\t.map(ConvertWith::value)\n\t\t\t\t\t.map(clazz -> ParameterizedTestSpiInstantiator.instantiate(ArgumentConverter.class, clazz, extensionContext))\n\t\t\t\t\t.map(converter -> AnnotationConsumerInitializer.initialize(declaration.getAnnotatedElement(), converter))\n\t\t\t\t\t.map(Converter::new)\n\t\t\t\t\t.orElse(Converter.DEFAULT);\n\t\t} // @formatter:on\n\t\tcatch (Exception ex) {\n\t\t\tthrow parameterResolutionException(\"Error creating ArgumentConverter\", ex, declaration.getParameterIndex());\n\t\t}\n\t}\n\n\tprivate static Aggregator createAggregator(ParameterDeclaration declaration, ExtensionContext extensionContext) {\n\t\ttry { // @formatter:off\n\t\t\treturn findAnnotation(declaration.getAnnotatedElement(), AggregateWith.class)\n\t\t\t\t\t.map(AggregateWith::value)\n\t\t\t\t\t.map(clazz -> ParameterizedTestSpiInstantiator.instantiate(ArgumentsAggregator.class, clazz, extensionContext))\n\t\t\t\t\t.map(Aggregator::new)\n\t\t\t\t\t.orElse(Aggregator.DEFAULT);\n\t\t} // @formatter:on\n\t\tcatch (Exception ex) {\n\t\t\tthrow parameterResolutionException(\"Error creating ArgumentsAggregator\", ex,\n\t\t\t\tdeclaration.getParameterIndex());\n\t\t}\n\t}\n\n\tprivate static ParameterResolutionException parameterResolutionException(String message, Exception cause,\n\t\t\tint index) {\n\t\tString fullMessage = message + \" at index \" + index;\n\t\tif (StringUtils.isNotBlank(cause.getMessage())) {\n\t\t\tfullMessage += \": \" + cause.getMessage();\n\t\t}\n\t\treturn new ParameterResolutionException(fullMessage, cause);\n\t}\n\n\tprivate interface Resolver {\n\n\t\t@Nullable\n\t\tObject resolve(ParameterContext parameterContext, int parameterIndex, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex);\n\n\t\t@Nullable\n\t\tObject resolve(FieldContext fieldContext, ExtensionContext extensionContext, EvaluatedArgumentSet arguments,\n\t\t\t\tint invocationIndex);\n\n\t}\n\n\tprivate record Converter(ArgumentConverter argumentConverter) implements Resolver {\n\n\t\tstatic final Converter DEFAULT = new Converter(DefaultArgumentConverter.INSTANCE);\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(ParameterContext parameterContext, int parameterIndex,\n\t\t\t\tExtensionContext extensionContext, EvaluatedArgumentSet arguments, int invocationIndex) {\n\t\t\tObject argument = arguments.getConsumedPayload(parameterIndex);\n\t\t\ttry {\n\t\t\t\treturn this.argumentConverter.convert(argument, parameterContext);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow parameterResolutionException(\"Error converting parameter\", ex, parameterContext.getIndex());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(FieldContext fieldContext, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex) {\n\n\t\t\tObject argument = arguments.getConsumedPayload(fieldContext.getParameterIndex());\n\t\t\ttry {\n\t\t\t\treturn this.argumentConverter.convert(argument, fieldContext);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow parameterResolutionException(\"Error converting parameter\", ex, fieldContext.getParameterIndex());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate record Aggregator(ArgumentsAggregator argumentsAggregator) implements Resolver {\n\n\t\tprivate static final Aggregator DEFAULT = new Aggregator(new SimpleArgumentsAggregator() {\n\t\t\t@Override\n\t\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\t\t\t\treturn accessor;\n\t\t\t}\n\t\t});\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(ParameterContext parameterContext, int parameterIndex,\n\t\t\t\tExtensionContext extensionContext, EvaluatedArgumentSet arguments, int invocationIndex) {\n\t\t\tArgumentsAccessor accessor = requireNonNull(ParameterInfo.get(extensionContext)).getArguments();\n\t\t\ttry {\n\t\t\t\treturn this.argumentsAggregator.aggregateArguments(accessor, parameterContext);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow parameterResolutionException(\"Error aggregating arguments for parameter\", ex,\n\t\t\t\t\tparameterContext.getIndex());\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(FieldContext fieldContext, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex) {\n\t\t\tArgumentsAccessor accessor = requireNonNull(ParameterInfo.get(extensionContext)).getArguments();\n\t\t\ttry {\n\t\t\t\treturn this.argumentsAggregator.aggregateArguments(accessor, fieldContext);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow parameterResolutionException(\"Error aggregating arguments for parameter\", ex,\n\t\t\t\t\tfieldContext.getParameterIndex());\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate record DefaultParameterDeclarations(AnnotatedElement sourceElement,\n\t\t\tNavigableMap<Integer, ? extends ResolvableParameterDeclaration> declarationsByIndex)\n\t\t\timplements ParameterDeclarations {\n\n\t\t@Override\n\t\tpublic AnnotatedElement getSourceElement() {\n\t\t\treturn this.sourceElement;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ParameterDeclaration> getFirst() {\n\t\t\treturn this.declarationsByIndex.isEmpty() //\n\t\t\t\t\t? Optional.empty() //\n\t\t\t\t\t: Optional.of(this.declarationsByIndex.firstEntry().getValue());\n\t\t}\n\n\t\t@Override\n\t\tpublic List<ParameterDeclaration> getAll() {\n\t\t\treturn List.copyOf(this.declarationsByIndex.values());\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ParameterDeclaration> get(int parameterIndex) {\n\t\t\treturn Optional.ofNullable(this.declarationsByIndex.get(parameterIndex));\n\t\t}\n\n\t\t@Override\n\t\tpublic String getSourceElementDescription() {\n\t\t\treturn describe(this.sourceElement);\n\t\t}\n\n\t\tstatic String describe(AnnotatedElement sourceElement) {\n\t\t\tif (sourceElement instanceof Method method) {\n\t\t\t\treturn \"method [%s]\".formatted(method.toGenericString());\n\t\t\t}\n\t\t\tif (sourceElement instanceof Constructor<?> constructor) {\n\t\t\t\treturn \"constructor [%s]\".formatted(constructor.toGenericString());\n\t\t\t}\n\t\t\tif (sourceElement instanceof Class<?> clazz) {\n\t\t\t\treturn \"class [%s]\".formatted(clazz.getName());\n\t\t\t}\n\t\t\treturn sourceElement.toString();\n\t\t}\n\t}\n\n\tprivate abstract static class ResolvableParameterDeclaration implements ParameterDeclaration {\n\n\t\t/**\n\t\t * Determine if the supplied {@link Parameter} is an aggregator (i.e., of\n\t\t * type {@link ArgumentsAccessor} or annotated with {@link AggregateWith}).\n\t\t *\n\t\t * @return {@code true} if the parameter is an aggregator\n\t\t */\n\t\tboolean isAggregator() {\n\t\t\treturn ArgumentsAccessor.class.isAssignableFrom(getParameterType())\n\t\t\t\t\t|| isAnnotated(getAnnotatedElement(), AggregateWith.class);\n\t\t}\n\n\t\tabstract @Nullable Object resolve(Resolver resolver, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex,\n\t\t\t\tOptional<ParameterContext> originalParameterContext);\n\t}\n\n\tprivate static class FieldParameterDeclaration extends ResolvableParameterDeclaration implements FieldContext {\n\n\t\tprivate final Field field;\n\t\tprivate final int index;\n\n\t\tFieldParameterDeclaration(Field field, int index) {\n\t\t\tthis.field = field;\n\t\t\tthis.index = index;\n\t\t}\n\n\t\t@Override\n\t\tpublic Field getField() {\n\t\t\treturn this.field;\n\t\t}\n\n\t\t@Override\n\t\tpublic Field getAnnotatedElement() {\n\t\t\treturn this.field;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getParameterType() {\n\t\t\treturn this.field.getType();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getParameterIndex() {\n\t\t\treturn index;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getParameterName() {\n\t\t\treturn Optional.of(this.field.getName());\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(Resolver resolver, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex,\n\t\t\t\tOptional<ParameterContext> originalParameterContext) {\n\t\t\treturn resolver.resolve(this, extensionContext, arguments, invocationIndex);\n\t\t}\n\t}\n\n\tprivate static class ExecutableParameterDeclaration extends ResolvableParameterDeclaration {\n\n\t\tprivate final java.lang.reflect.Parameter parameter;\n\t\tprivate final int index;\n\t\tprivate final int indexOffset;\n\n\t\tExecutableParameterDeclaration(java.lang.reflect.Parameter parameter, int index, int indexOffset) {\n\t\t\tthis.parameter = parameter;\n\t\t\tthis.index = index;\n\t\t\tthis.indexOffset = indexOffset;\n\t\t}\n\n\t\t@Override\n\t\tpublic java.lang.reflect.Parameter getAnnotatedElement() {\n\t\t\treturn this.parameter;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> getParameterType() {\n\t\t\treturn this.parameter.getType();\n\t\t}\n\n\t\t@Override\n\t\tpublic int getParameterIndex() {\n\t\t\treturn this.index - this.indexOffset;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getParameterName() {\n\t\t\treturn this.parameter.isNamePresent() ? Optional.of(this.parameter.getName()) : Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(Resolver resolver, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex,\n\t\t\t\tOptional<ParameterContext> originalParameterContext) {\n\t\t\tParameterContext parameterContext = originalParameterContext //\n\t\t\t\t\t.filter(it -> it.getParameter().equals(this.parameter)) //\n\t\t\t\t\t.orElseGet(() -> toParameterContext(extensionContext, originalParameterContext));\n\t\t\treturn resolver.resolve(parameterContext, getParameterIndex(), extensionContext, arguments,\n\t\t\t\tinvocationIndex);\n\t\t}\n\n\t\tprivate ParameterContext toParameterContext(ExtensionContext extensionContext,\n\t\t\t\tOptional<ParameterContext> originalParameterContext) {\n\t\t\tOptional<Object> target = originalParameterContext.flatMap(ParameterContext::getTarget);\n\t\t\tif (target.isEmpty()) {\n\t\t\t\ttarget = extensionContext.getTestInstance();\n\t\t\t}\n\t\t\treturn toParameterContext(target);\n\t\t}\n\n\t\tprivate ParameterContext toParameterContext(Optional<Object> target) {\n\t\t\treturn new ParameterContext() {\n\t\t\t\t@Override\n\t\t\t\tpublic java.lang.reflect.Parameter getParameter() {\n\t\t\t\t\treturn ExecutableParameterDeclaration.this.parameter;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int getIndex() {\n\t\t\t\t\treturn ExecutableParameterDeclaration.this.index;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Optional<Object> getTarget() {\n\t\t\t\t\treturn target;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate record DefaultArgumentSetLifecycleMethodParameterResolver(ResolverFacade originalResolverFacade,\n\t\t\tResolverFacade lifecycleMethodResolverFacade,\n\t\t\tMap<ParameterDeclaration, ResolvableParameterDeclaration> parameterDeclarationMapping)\n\t\t\timplements ArgumentSetLifecycleMethod.ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supports(ParameterContext parameterContext) {\n\t\t\treturn this.lifecycleMethodResolverFacade.findDeclaration(parameterContext.getIndex()) //\n\t\t\t\t\t.filter(it -> this.parameterDeclarationMapping.containsKey(it) || it.isAggregator()) //\n\t\t\t\t\t.isPresent();\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolve(ParameterContext parameterContext, ExtensionContext extensionContext,\n\t\t\t\tEvaluatedArgumentSet arguments, int invocationIndex, ResolutionCache resolutionCache) {\n\n\t\t\tResolvableParameterDeclaration actualDeclaration = this.lifecycleMethodResolverFacade //\n\t\t\t\t\t.findDeclaration(parameterContext.getIndex()) //\n\t\t\t\t\t.orElseThrow(() -> new ParameterResolutionException(\n\t\t\t\t\t\t\"Parameter index out of bounds: \" + parameterContext.getIndex()));\n\n\t\t\tResolvableParameterDeclaration originalDeclaration = this.parameterDeclarationMapping //\n\t\t\t\t\t.get(actualDeclaration);\n\t\t\tif (originalDeclaration == null) {\n\t\t\t\treturn this.lifecycleMethodResolverFacade.resolve(actualDeclaration, extensionContext, arguments,\n\t\t\t\t\tinvocationIndex, Optional.of(parameterContext));\n\t\t\t}\n\t\t\treturn resolutionCache.resolve(originalDeclaration,\n\t\t\t\t() -> this.originalResolverFacade.resolve(originalDeclaration, extensionContext, arguments,\n\t\t\t\t\tinvocationIndex, Optional.of(parameterContext)));\n\t\t}\n\t}\n\n\trecord RequiredParameterCount(int value, String reason) {\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/AggregateWith.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @AggregateWith} is an annotation that allows one to specify an\n * {@link ArgumentsAggregator}.\n *\n * <p>This annotation may be applied to parameters of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass}\n * constructor or its\n * {@link org.junit.jupiter.params.Parameter @Parameter}-annotated fields, or to\n * parameters of a\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest} method\n * in order for an aggregated value to be resolved for the annotated parameter\n * when the parameterized class or method is invoked.\n *\n * <p>{@code @AggregateWith} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @AggregateWith}.\n *\n * @since 5.2\n * @see ArgumentsAggregator\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER, ElementType.FIELD })\n@Documented\n@API(status = STABLE, since = \"5.7\")\npublic @interface AggregateWith {\n\n\tClass<? extends ArgumentsAggregator> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentAccessException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ArgumentAccessException} is an exception thrown by an\n * {@link ArgumentsAccessor} if an error occurs while accessing\n * or converting an argument.\n *\n * @since 5.2\n * @see ArgumentsAccessor\n */\n@API(status = STABLE, since = \"5.7\")\npublic class ArgumentAccessException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ArgumentAccessException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ArgumentAccessException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAccessor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * {@code ArgumentsAccessor} defines the public API for accessing arguments provided\n * by an {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider}\n * for a single invocation of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n *\n * <p>Specifically, an {@code ArgumentsAccessor} <em>aggregates</em> a set of\n * arguments for a given invocation of a parameterized class or parameterized\n * test and provides convenience methods for accessing those arguments in a\n * type-safe manner with support for automatic type conversion.\n *\n * <p>An instance of {@code ArgumentsAccessor} will be automatically supplied\n * for any parameter of type {@code ArgumentsAccessor} in a parameterized class\n * or parameterized test. In addition, {@link ArgumentsAggregator} implementations\n * are given access to an {@code ArgumentsAccessor}.\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * <p>Additional <a href=\"https://kotlinlang.org/\">Kotlin</a> arguments accessors can be\n * found as <em>extension functions</em> in the {@link org.junit.jupiter.params.aggregator}\n * package.\n *\n * @since 5.2\n * @see ArgumentsAggregator\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface ArgumentsAccessor {\n\n\t/**\n\t * Get the value of the argument at the given index as an {@link Object}.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t */\n\t@Nullable\n\tObject get(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as an instance of the\n\t * required type.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @param requiredType the required type of the value; never {@code null}\n\t * @return the value at the given index, potentially {@code null}\n\t */\n\t<T> @Nullable T get(int index, Class<T> requiredType) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Character},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tCharacter getCharacter(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Boolean},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tBoolean getBoolean(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Byte},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tByte getByte(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Short},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tShort getShort(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Integer},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tInteger getInteger(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Long},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tLong getLong(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Float},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tFloat getFloat(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link Double},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tDouble getDouble(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the value of the argument at the given index as a {@link String},\n\t * performing automatic type conversion as necessary.\n\t *\n\t * @param index the index of the argument to get; must be greater than or\n\t * equal to zero and less than {@link #size}\n\t * @return the value at the given index, potentially {@code null}\n\t * @throws ArgumentAccessException if the value cannot be accessed\n\t * or converted to the desired type\n\t */\n\t@Nullable\n\tString getString(int index) throws ArgumentAccessException;\n\n\t/**\n\t * Get the number of arguments in this accessor.\n\t */\n\tint size();\n\n\t/**\n\t * Get all arguments in this accessor as an array.\n\t */\n\t@Nullable\n\tObject[] toArray();\n\n\t/**\n\t * Get all arguments in this accessor as an immutable list.\n\t */\n\tList<@Nullable Object> toList();\n\n\t/**\n\t * Get the index of the current invocation.\n\t */\n\tint getInvocationIndex();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ArgumentsAggregationException} is an exception thrown by an\n * {@link ArgumentsAggregator} when an error occurs while aggregating\n * arguments.\n *\n * @since 5.2\n * @see ArgumentsAggregator\n */\n@API(status = STABLE, since = \"5.7\")\npublic class ArgumentsAggregationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ArgumentsAggregationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ArgumentsAggregationException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/ArgumentsAggregator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ArgumentsAggregator} is an abstraction for the aggregation of arguments\n * provided by an {@link org.junit.jupiter.params.provider.ArgumentsProvider\n * ArgumentsProvider} for a single invocation of a\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest} method\n * into a single object.\n *\n * <p>An {@code ArgumentsAggregator} is applied to a method parameter of a\n * {@code @ParameterizedTest} method via the {@link AggregateWith @AggregateWith}\n * annotation.\n *\n * <p>The result of the aggregation will be passed as an argument to the\n * {@code @ParameterizedTest} method for the annotated parameter.\n *\n * <p>A common use case is the aggregation of multiple columns from a single line\n * in a CSV file into a domain object such as a {@code Person}, {@code Address},\n * {@code Order}, etc.\n *\n * <p>Implementations must provide a no-args constructor or a single unambiguous\n * constructor to use {@linkplain ParameterResolver parameter resolution}. They\n * should not make any assumptions regarding when they are instantiated or how\n * often they are called. Since instances may potentially be cached and called\n * from different threads, they should be thread-safe.\n *\n * @since 5.2\n * @see AggregateWith\n * @see ArgumentsAccessor\n * @see SimpleArgumentsAggregator\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface ArgumentsAggregator {\n\n\t/**\n\t * Aggregate the arguments contained in the supplied {@code accessor} into a\n\t * single object.\n\t *\n\t * @param accessor an {@link ArgumentsAccessor} containing the arguments to be\n\t * aggregated; never {@code null}\n\t * @param context the parameter context where the aggregated result is to be\n\t * supplied; never {@code null}\n\t * @return the aggregated result; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentsAggregationException if an error occurs during the\n\t * aggregation\n\t */\n\t@Nullable\n\tObject aggregateArguments(ArgumentsAccessor accessor, ParameterContext context)\n\t\t\tthrows ArgumentsAggregationException;\n\n\t/**\n\t * Aggregate the arguments contained in the supplied {@code accessor} into a\n\t * single object.\n\t *\n\t * @param accessor an {@link ArgumentsAccessor} containing the arguments to be\n\t * aggregated; never {@code null}\n\t * @param context the field context where the aggregated result is to be\n\t * injected; never {@code null}\n\t * @return the aggregated result; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentsAggregationException if an error occurs during the\n\t * aggregation\n\t * @since 5.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tdefault @Nullable Object aggregateArguments(ArgumentsAccessor accessor, FieldContext context)\n\t\t\tthrows ArgumentsAggregationException {\n\t\tthrow new JUnitException(\"\"\"\n\t\t\t\tArgumentsAggregator does not override the convert(ArgumentsAccessor, FieldContext) method. \\\n\t\t\t\tPlease report this issue to the maintainers of %s.\"\"\".formatted(getClass().getName()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.BiFunction;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.params.converter.DefaultArgumentConverter;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Default implementation of the {@link ArgumentsAccessor} API.\n *\n * <p>Delegates conversion to {@link DefaultArgumentConverter}.\n *\n * @since 5.2\n * @see ArgumentsAccessor\n * @see DefaultArgumentConverter\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@API(status = INTERNAL, since = \"5.2\")\npublic class DefaultArgumentsAccessor implements ArgumentsAccessor {\n\n\tprivate final int invocationIndex;\n\tprivate final @Nullable Object[] arguments;\n\tprivate final BiFunction<@Nullable Object, Class<?>, @Nullable Object> converter;\n\n\tpublic static DefaultArgumentsAccessor create(int invocationIndex, ClassLoader classLoader,\n\t\t\t@Nullable Object[] arguments) {\n\t\tPreconditions.notNull(classLoader, \"ClassLoader must not be null\");\n\n\t\tBiFunction<@Nullable Object, Class<?>, @Nullable Object> converter = (source,\n\t\t\t\ttargetType) -> DefaultArgumentConverter.INSTANCE.convert(source, targetType, classLoader);\n\t\treturn new DefaultArgumentsAccessor(converter, invocationIndex, arguments);\n\t}\n\n\tprivate DefaultArgumentsAccessor(BiFunction<@Nullable Object, Class<?>, @Nullable Object> converter,\n\t\t\tint invocationIndex, @Nullable Object... arguments) {\n\t\tPreconditions.notNull(converter, \"Converter must not be null\");\n\t\tPreconditions.condition(invocationIndex >= 1, () -> \"Invocation index must be >= 1\");\n\t\tPreconditions.notNull(arguments, \"Arguments array must not be null\");\n\t\tthis.converter = converter;\n\t\tthis.invocationIndex = invocationIndex;\n\t\tthis.arguments = arguments;\n\t}\n\n\t@Override\n\tpublic @Nullable Object get(int index) {\n\t\tPreconditions.condition(index >= 0 && index < this.arguments.length,\n\t\t\t() -> \"index must be >= 0 and < %d\".formatted(this.arguments.length));\n\t\treturn this.arguments[index];\n\t}\n\n\t@Override\n\tpublic <T> @Nullable T get(int index, Class<T> requiredType) {\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tObject value = get(index);\n\t\ttry {\n\t\t\tObject convertedValue = converter.apply(value, requiredType);\n\t\t\treturn requiredType.cast(convertedValue);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tString message = \"Argument at index [%d] with value [%s] and type [%s] could not be converted or cast to type [%s].\".formatted(\n\t\t\t\tindex, value, ClassUtils.nullSafeToString(value == null ? null : value.getClass()),\n\t\t\t\trequiredType.getName());\n\t\t\tthrow new ArgumentAccessException(message, ex);\n\t\t}\n\t}\n\n\t@Override\n\tpublic @Nullable Character getCharacter(int index) {\n\t\treturn get(index, Character.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Boolean getBoolean(int index) {\n\t\treturn get(index, Boolean.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Byte getByte(int index) {\n\t\treturn get(index, Byte.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Short getShort(int index) {\n\t\treturn get(index, Short.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Integer getInteger(int index) {\n\t\treturn get(index, Integer.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Long getLong(int index) {\n\t\treturn get(index, Long.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Float getFloat(int index) {\n\t\treturn get(index, Float.class);\n\t}\n\n\t@Override\n\tpublic @Nullable Double getDouble(int index) {\n\t\treturn get(index, Double.class);\n\t}\n\n\t@Override\n\tpublic @Nullable String getString(int index) {\n\t\treturn get(index, String.class);\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn this.arguments.length;\n\t}\n\n\t@Override\n\tpublic @Nullable Object[] toArray() {\n\t\treturn Arrays.copyOf(this.arguments, this.arguments.length);\n\t}\n\n\t@Override\n\tpublic List<@Nullable Object> toList() {\n\t\treturn Collections.<@Nullable Object> unmodifiableList(Arrays.asList(this.arguments));\n\t}\n\n\t@Override\n\tpublic int getInvocationIndex() {\n\t\treturn this.invocationIndex;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/SimpleArgumentsAggregator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.support.FieldContext;\n\n/**\n * {@code SimpleArgumentsAggregator} is an abstract base class for\n * {@link ArgumentsAggregator} implementations that do not need to distinguish\n * between fields and method/constructor parameters.\n *\n * @since 5.0\n * @see ArgumentsAggregator\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic abstract class SimpleArgumentsAggregator implements ArgumentsAggregator {\n\n\tpublic SimpleArgumentsAggregator() {\n\t}\n\n\t@Override\n\tpublic @Nullable Object aggregateArguments(ArgumentsAccessor accessor, ParameterContext context)\n\t\t\tthrows ArgumentsAggregationException {\n\t\treturn aggregateArguments(accessor, context.getParameter().getType(), context, context.getIndex());\n\t}\n\n\t@Override\n\tpublic @Nullable Object aggregateArguments(ArgumentsAccessor accessor, FieldContext context)\n\t\t\tthrows ArgumentsAggregationException {\n\t\treturn aggregateArguments(accessor, context.getField().getType(), context, context.getParameterIndex());\n\t}\n\n\tprotected abstract @Nullable Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException;\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/aggregator/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * The {@link org.junit.jupiter.params.aggregator.ArgumentsAggregator} and\n * {@link org.junit.jupiter.params.aggregator.ArgumentsAccessor} interfaces and the\n * {@link org.junit.jupiter.params.aggregator.AggregateWith} annotation.\n */\n\n@NullMarked\npackage org.junit.jupiter.params.aggregator;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/AnnotationBasedArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Annotation;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.support.AnnotationConsumer;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code AnnotationBasedArgumentConverter} is an abstract base class for\n * {@link ArgumentConverter} implementations that also need to consume an\n * annotation in order to perform the conversion.\n *\n * @since 5.10\n * @see ArgumentConverter\n * @see AnnotationConsumer\n * @see SimpleArgumentConverter\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic abstract class AnnotationBasedArgumentConverter<A extends Annotation>\n\t\timplements ArgumentConverter, AnnotationConsumer<A> {\n\n\tprivate @Nullable A annotation;\n\n\tpublic AnnotationBasedArgumentConverter() {\n\t}\n\n\t@Override\n\tpublic final void accept(A annotation) {\n\t\tthis.annotation = Preconditions.notNull(annotation, \"annotation must not be null\");\n\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, ParameterContext context)\n\t\t\tthrows ArgumentConversionException {\n\t\treturn convert(source, context.getParameter().getType(), requireNonNull(this.annotation));\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\tthrows ArgumentConversionException {\n\t\treturn convert(source, context.getField().getType(), requireNonNull(this.annotation));\n\t}\n\n\t/**\n\t * Convert the supplied {@code source} object into the supplied {@code targetType},\n\t * based on metadata in the provided annotation.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @param targetType the target type the source object should be converted\n\t * into; never {@code null}\n\t * @param annotation the annotation to process; never {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException in case an error occurs during the\n\t * conversion\n\t */\n\tprotected abstract @Nullable Object convert(@Nullable Object source, Class<?> targetType, A annotation)\n\t\t\tthrows ArgumentConversionException;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConversionException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ArgumentConversionException} is an exception that can occur when an\n * object is converted to another object by an implementation of an\n * {@link ArgumentConverter}.\n *\n * @since 5.0\n * @see ArgumentConverter\n */\n@API(status = STABLE, since = \"5.7\")\npublic class ArgumentConversionException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ArgumentConversionException(@Nullable String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ArgumentConversionException(@Nullable String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ArgumentConverter} is an abstraction that allows an input object to\n * be converted to an instance of a different class.\n *\n * <p>Such an {@code ArgumentConverter} is applied to the method parameter\n * of a {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}\n * or a constructor parameter or\n * {@link org.junit.jupiter.params.Parameter @Parameter}-annotated field of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} with\n * the help of a\n * {@link org.junit.jupiter.params.converter.ConvertWith @ConvertWith}\n * annotation.\n *\n * <p>Implementations must provide a no-args constructor or a single unambiguous\n * constructor to use {@linkplain ParameterResolver parameter resolution}. They\n * should not make any assumptions regarding when they are instantiated or how\n * often they are called. Since instances may potentially be cached and called\n * from different threads, they should be thread-safe.\n *\n * <p>Extend {@link SimpleArgumentConverter} if your implementation only needs\n * to know the target type and does not need access to the {@link ParameterContext}\n * to perform the conversion.\n *\n * <p>Extend {@link TypedArgumentConverter} if your implementation always converts\n * from a given source type into a given target type and does not need access to\n * the {@link ParameterContext} to perform the conversion.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.params.converter.ConvertWith\n * @see org.junit.jupiter.params.support.AnnotationConsumer\n * @see SimpleArgumentConverter\n * @see TypedArgumentConverter\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface ArgumentConverter {\n\n\t/**\n\t * Convert the supplied {@code source} object according to the supplied\n\t * {@code context}.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @param context the parameter context where the converted object will be\n\t * supplied; never {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException if an error occurs during the\n\t * conversion\n\t */\n\t@Nullable\n\tObject convert(@Nullable Object source, ParameterContext context) throws ArgumentConversionException;\n\n\t/**\n\t * Convert the supplied {@code source} object according to the supplied\n\t * {@code context}.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @param context the field context where the converted object will be\n\t * injected; never {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException if an error occurs during the\n\t * conversion\n\t * @since 5.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tdefault @Nullable Object convert(@Nullable Object source, FieldContext context) throws ArgumentConversionException {\n\t\tthrow new JUnitException(\"\"\"\n\t\t\t\tArgumentConverter does not override the convert(Object, FieldContext) method. \\\n\t\t\t\tPlease report this issue to the maintainers of %s.\"\"\".formatted(getClass().getName()));\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/ConvertWith.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ConvertWith} is an annotation that allows one to specify an explicit\n * {@link ArgumentConverter}.\n *\n * <p>This annotation may be applied to parameters of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass}\n * constructor or its\n * {@link org.junit.jupiter.params.Parameter @Parameter}-annotated fields, or to\n * parameters of a\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest} method\n * which need to have their {@code Arguments} converted before consuming them.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER, ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.7\")\npublic @interface ConvertWith {\n\n\t/**\n\t * The type of {@link ArgumentConverter} to use.\n\t */\n\tClass<? extends ArgumentConverter> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/DefaultArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getClassLoader;\n\nimport java.io.File;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.Currency;\nimport java.util.Locale;\nimport java.util.UUID;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.platform.commons.support.conversion.ConversionException;\nimport org.junit.platform.commons.support.conversion.ConversionSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code DefaultArgumentConverter} is the default implementation of the\n * {@link ArgumentConverter} API.\n *\n * <p>The {@code DefaultArgumentConverter} is able to convert from strings to a\n * number of primitive types and their corresponding wrapper types (Byte, Short,\n * Integer, Long, Float, and Double), date and time types from the\n * {@code java.time} package, and some additional common Java types such as\n * {@link File}, {@link BigDecimal}, {@link BigInteger}, {@link Currency},\n * {@link Locale}, {@link URI}, {@link URL}, {@link UUID}, etc.\n *\n * <p>If the source and target types are identical the source object will not\n * be modified.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n * @see org.junit.platform.commons.support.conversion.ConversionSupport\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic class DefaultArgumentConverter implements ArgumentConverter {\n\n\tpublic static final DefaultArgumentConverter INSTANCE = new DefaultArgumentConverter();\n\n\tprivate DefaultArgumentConverter() {\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, ParameterContext context) {\n\t\tClass<?> targetType = context.getParameter().getType();\n\t\tClassLoader classLoader = getClassLoader(context.getDeclaringExecutable().getDeclaringClass());\n\t\treturn convert(source, targetType, classLoader);\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\tthrows ArgumentConversionException {\n\n\t\tClass<?> targetType = context.getField().getType();\n\t\tClassLoader classLoader = getClassLoader(context.getField().getDeclaringClass());\n\t\treturn convert(source, targetType, classLoader);\n\t}\n\n\tpublic final @Nullable Object convert(@Nullable Object source, Class<?> targetType, ClassLoader classLoader) {\n\t\tif (source == null) {\n\t\t\tif (targetType.isPrimitive()) {\n\t\t\t\tthrow new ArgumentConversionException(\n\t\t\t\t\t\"Cannot convert null to primitive value of type \" + targetType.getTypeName());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif (ReflectionUtils.isAssignableTo(source, targetType)) {\n\t\t\treturn source;\n\t\t}\n\n\t\tif (source instanceof String string) {\n\t\t\ttry {\n\t\t\t\treturn convert(string, targetType, classLoader);\n\t\t\t}\n\t\t\tcatch (ConversionException ex) {\n\t\t\t\tthrow new ArgumentConversionException(ex.getMessage(), ex);\n\t\t\t}\n\t\t}\n\n\t\tthrow new ArgumentConversionException(\"No built-in converter for source type %s and target type %s\".formatted(\n\t\t\tsource.getClass().getTypeName(), targetType.getTypeName()));\n\t}\n\n\t@Nullable\n\tObject convert(@Nullable String source, Class<?> targetType, ClassLoader classLoader) {\n\t\treturn ConversionSupport.convert(source, targetType, classLoader);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZonedDateTime;\nimport java.time.chrono.ChronoLocalDate;\nimport java.time.chrono.ChronoLocalDateTime;\nimport java.time.chrono.ChronoZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.time.temporal.TemporalQuery;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 5.0\n */\nclass JavaTimeArgumentConverter extends AnnotationBasedArgumentConverter<JavaTimeConversionPattern> {\n\n\tprivate static final Map<Class<?>, TemporalQuery<?>> TEMPORAL_QUERIES;\n\tstatic {\n\t\tMap<Class<?>, TemporalQuery<?>> queries = new LinkedHashMap<>();\n\t\tqueries.put(ChronoLocalDate.class, ChronoLocalDate::from);\n\t\tqueries.put(ChronoLocalDateTime.class, ChronoLocalDateTime::from);\n\t\tqueries.put(ChronoZonedDateTime.class, ChronoZonedDateTime::from);\n\t\tqueries.put(LocalDate.class, LocalDate::from);\n\t\tqueries.put(LocalDateTime.class, LocalDateTime::from);\n\t\tqueries.put(LocalTime.class, LocalTime::from);\n\t\tqueries.put(OffsetDateTime.class, OffsetDateTime::from);\n\t\tqueries.put(OffsetTime.class, OffsetTime::from);\n\t\tqueries.put(Year.class, Year::from);\n\t\tqueries.put(YearMonth.class, YearMonth::from);\n\t\tqueries.put(ZonedDateTime.class, ZonedDateTime::from);\n\t\tTEMPORAL_QUERIES = Collections.unmodifiableMap(queries);\n\t}\n\n\t@Override\n\tprotected @Nullable Object convert(@Nullable Object input, Class<?> targetClass,\n\t\t\tJavaTimeConversionPattern annotation) {\n\n\t\tif (input == null) {\n\t\t\tif (annotation.nullable()) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow new ArgumentConversionException(\n\t\t\t\t\"Cannot convert null to \" + targetClass.getName() + \"; consider setting 'nullable = true'\");\n\t\t}\n\t\tTemporalQuery<?> temporalQuery = TEMPORAL_QUERIES.get(targetClass);\n\t\tif (temporalQuery == null) {\n\t\t\tthrow new ArgumentConversionException(\"Cannot convert to \" + targetClass.getName() + \": \" + input);\n\t\t}\n\t\tString pattern = annotation.value();\n\t\tDateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);\n\t\treturn formatter.parse(input.toString(), temporalQuery);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/JavaTimeConversionPattern.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @JavaTimeConversionPattern} is an annotation that allows a date/time\n * conversion pattern to be specified on a parameter of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass}\n * or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n *\n * @since 5.0\n * @see ConvertWith\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see java.time.format.DateTimeFormatterBuilder#appendPattern(String)\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER, ElementType.FIELD })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = STABLE, since = \"5.7\")\n@ConvertWith(JavaTimeArgumentConverter.class)\n@SuppressWarnings(\"exports\")\npublic @interface JavaTimeConversionPattern {\n\n\t/**\n\t * The date/time conversion pattern.\n\t *\n\t * @see java.time.format.DateTimeFormatterBuilder#appendPattern(String)\n\t */\n\tString value();\n\n\t/**\n\t * Whether {@code null} argument values are allowed.\n\t *\n\t * <p>Defaults to {@code false}, in which case a {@code null} value will result in\n\t * an exception.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tboolean nullable() default false;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/SimpleArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.support.FieldContext;\n\n/**\n * {@code SimpleArgumentConverter} is an abstract base class for\n * {@link ArgumentConverter} implementations that only need to know the target\n * type and do not need access to the {@link ParameterContext} to perform the\n * conversion.\n *\n * @since 5.0\n * @see ArgumentConverter\n * @see TypedArgumentConverter\n */\n@API(status = STABLE, since = \"5.7\")\npublic abstract class SimpleArgumentConverter implements ArgumentConverter {\n\n\tpublic SimpleArgumentConverter() {\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, ParameterContext context)\n\t\t\tthrows ArgumentConversionException {\n\t\treturn convert(source, context.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\tthrows ArgumentConversionException {\n\t\treturn convert(source, context.getField().getType());\n\t}\n\n\t/**\n\t * Convert the supplied {@code source} object into the supplied\n\t * {@code targetType}.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @param targetType the target type the source object should be converted\n\t * into; never {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException in case an error occurs during the\n\t * conversion\n\t */\n\tprotected abstract @Nullable Object convert(@Nullable Object source, Class<?> targetType)\n\t\t\tthrows ArgumentConversionException;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/TypedArgumentConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code TypedArgumentConverter} is an abstract base class for\n * {@link ArgumentConverter} implementations that always convert objects of a\n * given source type into a given target type.\n *\n * @param <S> the type of the source argument to convert\n * @param <T> the type of the target object to create from the source\n * @since 5.7\n * @see ArgumentConverter\n * @see SimpleArgumentConverter\n */\n@API(status = STABLE, since = \"5.10\")\npublic abstract class TypedArgumentConverter<S, T extends @Nullable Object> implements ArgumentConverter {\n\n\tprivate final Class<S> sourceType;\n\tprivate final Class<T> targetType;\n\n\t/**\n\t * Create a new {@code TypedArgumentConverter}.\n\t *\n\t * @param sourceType the type of the argument to convert; never {@code null}\n\t * @param targetType the type of the target object to create from the source;\n\t * never {@code null}\n\t */\n\tprotected TypedArgumentConverter(Class<S> sourceType,\n\t\t\t@SuppressWarnings(\"NullableProblems\") Class<@NonNull T> targetType) {\n\t\tthis.sourceType = Preconditions.notNull(sourceType, \"sourceType must not be null\");\n\t\tthis.targetType = Preconditions.notNull(targetType, \"targetType must not be null\");\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, ParameterContext context)\n\t\t\tthrows ArgumentConversionException {\n\n\t\treturn convert(source, context.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic final @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\tthrows ArgumentConversionException {\n\n\t\treturn convert(source, context.getField().getType());\n\t}\n\n\tprivate T convert(@Nullable Object source, Class<?> actualTargetType) {\n\t\tif (source == null) {\n\t\t\treturn convert(null);\n\t\t}\n\t\tif (!this.sourceType.isInstance(source)) {\n\t\t\tString message = \"%s cannot convert objects of type [%s]. Only source objects of type [%s] are supported.\".formatted(\n\t\t\t\tgetClass().getSimpleName(), source.getClass().getTypeName(), this.sourceType.getTypeName());\n\t\t\tthrow new ArgumentConversionException(message);\n\t\t}\n\t\tif (!ReflectionUtils.isAssignableTo(this.targetType, actualTargetType)) {\n\t\t\tString message = \"%s cannot convert to type [%s]. Only target type [%s] is supported.\".formatted(\n\t\t\t\tgetClass().getSimpleName(), actualTargetType.getTypeName(), this.targetType.getTypeName());\n\t\t\tthrow new ArgumentConversionException(message);\n\t\t}\n\t\treturn convert(this.sourceType.cast(source));\n\t}\n\n\t/**\n\t * Convert the supplied {@code source} object of type {@code S} into an object\n\t * of type {@code T}.\n\t *\n\t * @param source the source object to convert; may be {@code null}\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t * @throws ArgumentConversionException if an error occurs during the\n\t * conversion\n\t */\n\tprotected abstract T convert(@Nullable S source) throws ArgumentConversionException;\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/converter/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * {@link org.junit.jupiter.params.converter.ArgumentConverter ArgumentConverter}\n * implementations and the corresponding\n * {@link org.junit.jupiter.params.converter.ConvertWith @ConvertWith} annotation.\n */\n\n@NullMarked\npackage org.junit.jupiter.params.converter;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Jupiter extension for parameterized tests.\n */\n\n@NullMarked\npackage org.junit.jupiter.params;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Annotation;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.AnnotationConsumer;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code AnnotationBasedArgumentsProvider} is an abstract base class for\n * {@link ArgumentsProvider} implementations that also need to consume an\n * annotation in order to provide the arguments.\n *\n * @since 5.10\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.provider.Arguments\n * @see org.junit.jupiter.params.provider.ArgumentsProvider\n * @see org.junit.jupiter.params.support.AnnotationConsumer\n */\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic abstract class AnnotationBasedArgumentsProvider<A extends Annotation>\n\t\timplements ArgumentsProvider, AnnotationConsumer<A> {\n\n\tpublic AnnotationBasedArgumentsProvider() {\n\t}\n\n\tprivate final List<A> annotations = new ArrayList<>();\n\n\t@Override\n\tpublic final void accept(A annotation) {\n\t\tPreconditions.notNull(annotation, \"annotation must not be null\");\n\t\tannotations.add(annotation);\n\t}\n\n\t@Override\n\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context) {\n\t\treturn annotations.stream().flatMap(annotation -> provideArguments(parameters, context, annotation));\n\t}\n\n\t/**\n\t * Provide a {@link Stream} of {@link Arguments} &mdash; based on metadata in the\n\t * provided annotation &mdash; to be passed to a {@code @ParameterizedTest} method.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @param annotation the annotation to process; never {@code null}\n\t * @return a stream of arguments; never {@code null}\n\t * @deprecated Please implement\n\t * {@link #provideArguments(ParameterDeclarations, ExtensionContext, Annotation)}\n\t * instead.\n\t */\n\t@Deprecated(since = \"5.13\")\n\t@API(status = DEPRECATED, since = \"5.13\")\n\tprotected Stream<? extends Arguments> provideArguments(ExtensionContext context, A annotation) {\n\t\tthrow new JUnitException(\"\"\"\n\t\t\t\tAnnotationBasedArgumentsProvider does not override the \\\n\t\t\t\tprovideArguments(ParameterDeclarations, ExtensionContext, Annotation) method. \\\n\t\t\t\tPlease report this issue to the maintainers of %s.\"\"\".formatted(getClass().getName()));\n\t}\n\n\t/**\n\t * The returned {@code Stream} will be {@link Stream#close() properly closed}\n\t * by the default implementation of\n\t * {@link #provideArguments(ParameterDeclarations, ExtensionContext)},\n\t * making it safe to use a resource such as\n\t * {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.\n\t */\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tA annotation) {\n\t\treturn provideArguments(context, annotation);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/Arguments.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code Arguments} is an abstraction that provides access to an array of\n * objects to be used for the invocation of a {@code @ParameterizedClass} or\n * {@code @ParameterizedTest} method.\n *\n * <p>A {@link java.util.stream.Stream} of such {@code Arguments} will\n * typically be provided by an {@link ArgumentsProvider}.\n *\n * @apiNote <p>This interface is specifically designed as a simple holder of\n * arguments for a parameterized test. Therefore, if you end up\n * {@linkplain java.util.stream.Stream#map(java.util.function.Function) transforming} or\n * {@linkplain java.util.stream.Stream#filter(java.util.function.Predicate) filtering}\n * the arguments, you should consider using one of the following in intermediate steps:\n *\n * <ul>\n *   <li>The standard Java collections</li>\n *   <li>Tuples from third-party libraries &mdash; for example,\n *   <a href=\"https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/tuple/package-summary.html\">Commons Lang</a>\n *   or <a href=\"https://www.javatuples.org\">javatuples</a></li>\n *   <li>Your own data class</li>\n * </ul>\n *\n * <p>Alternatively, you can use an\n * {@link org.junit.jupiter.params.converter.ArgumentConverter ArgumentConverter}\n * to convert some of the arguments from one type to another.\n *\n * @since 5.0\n * @see ArgumentSet\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.provider.ArgumentsProvider\n * @see org.junit.jupiter.params.converter.ArgumentConverter\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.7\")\npublic interface Arguments {\n\n\t/**\n\t * Get the arguments used for an invocation of the {@code @ParameterizedClass}\n\t * or {@code @ParameterizedTest} method.\n\t *\n\t * @apiNote If you need a type-safe way to access some or all of the\n\t * arguments, please read the {@linkplain Arguments class-level API note}.\n\t *\n\t * @return the arguments; never {@code null} but may contain {@code null}\n\t */\n\t@Nullable\n\tObject[] get();\n\n\t/**\n\t * Convert the arguments to a new mutable {@link List} containing the same\n\t * elements as {@link #get()}.\n\t *\n\t * <p>This is useful for test logic that benefits from {@code List}\n\t * operations such as filtering, transformation, or assertions.\n\t *\n\t * @return a mutable List of arguments; never {@code null} but may contain\n\t * {@code null}\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tdefault List<@Nullable Object> toList() {\n\t\t// We could return List<?> here but the unbounded wildcard is painful\n\t\t// to work with.\n\t\treturn new ArrayList<>(Arrays.asList(get()));\n\t}\n\n\t/**\n\t * Factory method for creating an instance of {@code Arguments} based on\n\t * the supplied {@code arguments}.\n\t *\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an instance of {@code Arguments}; never {@code null}\n\t * @see #from(Iterable)\n\t * @see #arguments(Object...)\n\t * @see #argumentSet(String, Object...)\n\t */\n\tstatic Arguments of(@Nullable Object... arguments) {\n\t\tPreconditions.notNull(arguments, \"arguments array must not be null\");\n\t\treturn () -> arguments;\n\t}\n\n\t/**\n\t * Factory method for creating an instance of {@code Arguments} based on\n\t * the supplied {@link Iterable} of {@code arguments}.\n\t *\n\t * <p>The iterable supplied to this method should be a finite collection\n\t * and have a reliable iteration order to provide arguments in a consistent\n\t * order to tests. It is therefore recommended that the iterable be a\n\t * {@link java.util.SequencedCollection} (on Java 21 or higher),\n\t * {@link java.util.List}, or similar.\n\t *\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an instance of {@code Arguments}; never {@code null}\n\t * @since 6.1\n\t * @see #argumentsFrom(Iterable)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tstatic Arguments from(Iterable<? extends @Nullable Object> arguments) {\n\t\tPreconditions.notNull(arguments, \"arguments must not be null\");\n\t\treturn of(toArray(arguments));\n\t}\n\n\t/**\n\t * Factory method for creating an instance of {@code Arguments} based on\n\t * the supplied {@code arguments}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link Arguments#of} and is\n\t * intended to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.jupiter.params.provider.Arguments.arguments;}\n\t *\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an instance of {@code Arguments}; never {@code null}\n\t * @since 5.3\n\t * @see #argumentSet(String, Object...)\n\t * @see #argumentsFrom(Iterable)\n\t */\n\tstatic Arguments arguments(@Nullable Object... arguments) {\n\t\treturn of(arguments);\n\t}\n\n\t/**\n\t * Factory method for creating an instance of {@code Arguments} based on\n\t * the supplied {@link Iterable} of {@code arguments}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link Arguments#from} and is\n\t * intended to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.jupiter.params.provider.Arguments.argumentsFrom;}\n\t *\n\t * <p>The iterable supplied to this method should be a finite collection\n\t * and have a reliable iteration order to provide arguments in a consistent\n\t * order to tests. It is therefore recommended that the iterable be a\n\t * {@link java.util.SequencedCollection} (on Java 21 or higher),\n\t * {@link java.util.List}, or similar.\n\t *\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an instance of {@code Arguments}; never {@code null}\n\t * @since 6.1\n\t * @see #arguments(Object...)\n\t * @see #argumentSetFrom(String, Iterable)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tstatic Arguments argumentsFrom(Iterable<? extends @Nullable Object> arguments) {\n\t\treturn from(arguments);\n\t}\n\n\t/**\n\t * Factory method for creating an {@link ArgumentSet} based on the supplied\n\t * {@code name} and {@code arguments}.\n\t *\n\t * <p>Favor this method over {@link Arguments#of Arguments.of(...)} and\n\t * {@link Arguments#arguments arguments(...)} when you wish to assign a name\n\t * to the entire set of arguments.\n\t *\n\t * <p>This method is well suited to be used as a static import &mdash; for\n\t * example, via:\n\t * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSet;}.\n\t *\n\t * @param name the name of the argument set; must not be {@code null} or blank\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an {@code ArgumentSet}; never {@code null}\n\t * @since 5.11\n\t * @see ArgumentSet\n\t * @see #argumentSetFrom(String, Iterable)\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tstatic ArgumentSet argumentSet(String name, @Nullable Object... arguments) {\n\t\treturn new ArgumentSet(name, arguments);\n\t}\n\n\t/**\n\t * Factory method for creating an {@link ArgumentSet} based on the supplied\n\t * {@code name} and {@link Iterable} of {@code arguments}.\n\t *\n\t * <p>Favor this method over {@link Arguments#from(Iterable) Arguments.from(...)}\n\t * and {@link Arguments#argumentsFrom(Iterable) argumentsFrom(...)} when you\n\t * wish to assign a name to the entire set of arguments.\n\t *\n\t * <p>This method is well suited to be used as a static import &mdash; for\n\t * example, via:\n\t * {@code import static org.junit.jupiter.params.provider.Arguments.argumentSetFrom;}.\n\t *\n\t * <p>The iterable supplied to this method should be a finite collection\n\t * and have a reliable iteration order to provide arguments in a consistent\n\t * order to tests. It is therefore recommended that the iterable be a\n\t * {@link java.util.SequencedCollection} (on Java 21 or higher),\n\t * {@link java.util.List}, or similar.\n\t *\n\t * @param name the name of the argument set; must not be {@code null} or blank\n\t * @param arguments the arguments to be used for an invocation of the test\n\t * method; must not be {@code null} but may contain {@code null}\n\t * @return an {@code ArgumentSet}; never {@code null}\n\t * @since 6.1\n\t * @see ArgumentSet\n\t * @see #argumentSet(String, Object...)\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tstatic ArgumentSet argumentSetFrom(String name, Iterable<? extends @Nullable Object> arguments) {\n\t\tPreconditions.notNull(arguments, \"arguments must not be null\");\n\t\treturn argumentSet(name, toArray(arguments));\n\t}\n\n\tprivate static Object[] toArray(Iterable<? extends @Nullable Object> arguments) {\n\t\tif (arguments instanceof Collection<?> collection) {\n\t\t\treturn collection.toArray();\n\t\t}\n\t\tvar list = new ArrayList<>();\n\t\targuments.forEach(list::add);\n\t\treturn list.toArray();\n\t}\n\n\t/**\n\t * Specialization of {@link Arguments} that associates a {@link #getName() name}\n\t * with a set of {@link #get() arguments}.\n\t *\n\t * @since 5.11\n\t * @see Arguments#argumentSet(String, Object...)\n\t * @see Arguments#argumentSetFrom(String, Iterable)\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_PLACEHOLDER\n\t * @see org.junit.jupiter.params.ParameterizedInvocationConstants#ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tfinal class ArgumentSet implements Arguments {\n\n\t\tprivate final String name;\n\n\t\tprivate final @Nullable Object[] arguments;\n\n\t\tprivate ArgumentSet(String name, @Nullable Object[] arguments) {\n\t\t\tPreconditions.notBlank(name, \"name must not be null or blank\");\n\t\t\tPreconditions.notNull(arguments, \"arguments array must not be null\");\n\t\t\tthis.name = name;\n\t\t\tthis.arguments = arguments;\n\t\t}\n\n\t\t/**\n\t\t * Get the name of this {@code ArgumentSet}.\n\t\t * @return the name of this {@code ArgumentSet}; never {@code null} or blank\n\t\t */\n\t\tpublic String getName() {\n\t\t\treturn this.name;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object[] get() {\n\t\t\treturn this.arguments;\n\t\t}\n\n\t\t/**\n\t\t * Return the {@link #getName() name} of this {@code ArgumentSet}.\n\t\t * @return the name of this {@code ArgumentSet}\n\t\t */\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getName();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * An {@code ArgumentsProvider} is responsible for\n * {@linkplain #provideArguments(ParameterDeclarations, ExtensionContext) providing}\n * a stream of arguments to be passed to a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n *\n * <p>An {@code ArgumentsProvider} can be registered via the\n * {@link ArgumentsSource @ArgumentsSource} annotation.\n *\n * <p>Implementations must provide a no-args constructor or a single unambiguous\n * constructor to use {@linkplain ParameterResolver parameter resolution}.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.provider.Arguments\n * @see org.junit.jupiter.params.support.AnnotationConsumer\n */\n@API(status = STABLE, since = \"5.7\")\npublic interface ArgumentsProvider {\n\n\t/**\n\t * Provide a {@link Stream} of {@link Arguments} to be passed to a\n\t * {@code @ParameterizedTest} method.\n\t *\n\t * @param context the current extension context; never {@code null}\n\t * @return a stream of arguments; never {@code null}\n\t * @deprecated Please implement\n\t * {@link #provideArguments(ParameterDeclarations, ExtensionContext)} instead.\n\t */\n\t@Deprecated(since = \"5.13\")\n\t@API(status = DEPRECATED, since = \"5.13\")\n\tdefault Stream<? extends Arguments> provideArguments(@SuppressWarnings(\"unused\") ExtensionContext context)\n\t\t\tthrows Exception {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\"Please implement provideArguments(ParameterDeclarations, ExtensionContext) instead.\");\n\t}\n\n\t/**\n\t * Provide a {@link Stream} of {@link Arguments} to be passed to a\n\t * {@code @ParameterizedClass} or {@code @ParameterizedTest}.\n\t *\n\t * @param parameters the parameter declarations for the parameterized\n\t * class or test; never {@code null}\n\t * @param context the current extension context; never {@code null}\n\t * @return a stream of arguments; never {@code null}\n\t * @since 5.13\n\t */\n\t@API(status = MAINTAINED, since = \"6.0.2\")\n\tdefault Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context)\n\t\t\tthrows Exception {\n\t\ttry {\n\t\t\treturn provideArguments(context);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tString message = \"\"\"\n\t\t\t\t\tArgumentsProvider does not override the provideArguments(ParameterDeclarations, ExtensionContext) method. \\\n\t\t\t\t\tPlease report this issue to the maintainers of %s.\"\"\".formatted(\n\t\t\t\tgetClass().getName());\n\t\t\tthrow new JUnitException(message, e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ArgumentsSource} is a {@linkplain Repeatable repeatable} annotation\n * that is used to register {@linkplain ArgumentsProvider arguments providers}\n * for the annotated class or method.\n *\n * <p>{@code @ArgumentsSource} may also be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @ArgumentsSource}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.provider.ArgumentsProvider\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(ArgumentsSources.class)\n@API(status = STABLE, since = \"5.7\")\npublic @interface ArgumentsSource {\n\n\t/**\n\t * The type of {@link ArgumentsProvider} to be used.\n\t */\n\tClass<? extends ArgumentsProvider> value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ArgumentsSources} is a simple container for one or more\n * {@link ArgumentsSource @ArgumentsSource} annotations.\n *\n * <p>Note, however, that use of the {@code @ArgumentsSources} container is completely\n * optional since {@code @ArgumentsSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\npublic @interface ArgumentsSources {\n\n\t/**\n\t * An array of one or more {@link ArgumentsSource @ArgumentsSource}\n\t * annotations.\n\t */\n\tArgumentsSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ArgumentsUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Collection of utilities for working with {@link Arguments}.\n *\n * @since 5.11, when it was extracted from {@link MethodArgumentsProvider}\n */\nfinal class ArgumentsUtils {\n\n\tprivate ArgumentsUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Convert the supplied object into an {@link Arguments} instance.\n\t */\n\tstatic Arguments toArguments(Object item) {\n\t\t// Nothing to do except cast.\n\t\tif (item instanceof Arguments arguments) {\n\t\t\treturn arguments;\n\t\t}\n\n\t\t// Pass all multidimensional arrays \"as is\", in contrast to Object[].\n\t\t// See https://github.com/junit-team/junit-framework/issues/1665\n\t\tif (ReflectionUtils.isMultidimensionalArray(item)) {\n\t\t\treturn arguments(item);\n\t\t}\n\n\t\t// Special treatment for one-dimensional reference arrays.\n\t\t// See https://github.com/junit-team/junit-framework/issues/1665\n\t\tif (item instanceof Object[] objects) {\n\t\t\treturn arguments(objects);\n\t\t}\n\n\t\t// Pass everything else \"as is\".\n\t\treturn arguments(item);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport java.lang.annotation.Annotation;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport de.siegmar.fastcsv.reader.CsvRecord;\nimport de.siegmar.fastcsv.reader.NamedCsvRecord;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.jupiter.params.support.ParameterNameAndArgument;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * @since 5.0\n */\nclass CsvArgumentsProvider extends AnnotationBasedArgumentsProvider<CsvSource> {\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tCsvSource csvSource) {\n\n\t\tCsvReaderFactory.validate(csvSource);\n\n\t\tList<Arguments> arguments = new ArrayList<>();\n\n\t\ttry (var reader = CsvReaderFactory.createReaderFor(csvSource, getData(csvSource))) {\n\t\t\tboolean useHeadersInDisplayName = csvSource.useHeadersInDisplayName();\n\t\t\tfor (CsvRecord record : reader) {\n\t\t\t\targuments.add(processCsvRecord(record, useHeadersInDisplayName));\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tthrow handleCsvException(throwable, csvSource);\n\t\t}\n\n\t\treturn arguments.stream();\n\t}\n\n\tprivate static String getData(CsvSource csvSource) {\n\t\tvar values = csvSource.value();\n\t\tPreconditions.condition(values.length > 0 ^ !csvSource.textBlock().isEmpty(),\n\t\t\t() -> \"@CsvSource must be declared with either `value` or `textBlock` but not both\");\n\n\t\tif (!csvSource.textBlock().isEmpty()) {\n\t\t\treturn csvSource.textBlock();\n\t\t}\n\t\telse {\n\t\t\tfor (int i = 0; i < values.length; i++) {\n\t\t\t\tint finalI = i;\n\t\t\t\tPreconditions.notBlank(values[i],\n\t\t\t\t\t() -> \"CSV record at index %d must not be blank\".formatted(finalI + 1));\n\t\t\t}\n\t\t\treturn String.join(\"\\n\", values);\n\t\t}\n\t}\n\n\t/**\n\t * Processes custom null values, supports wrapping of column values in\n\t * {@link Named} if necessary (for CSV header support), and returns the\n\t * CSV record wrapped in an {@link Arguments} instance.\n\t */\n\tstatic Arguments processCsvRecord(CsvRecord record, boolean useHeadersInDisplayName) {\n\t\tList<String> fields = record.getFields();\n\t\tList<String> headers = useHeadersInDisplayName ? getHeaders(record) : List.of();\n\n\t\tPreconditions.condition(!useHeadersInDisplayName || fields.size() <= headers.size(), //\n\t\t\t() -> \"The number of columns (%d) exceeds the number of supplied headers (%d) in CSV record: %s\".formatted( //\n\t\t\t\tfields.size(), headers.size(), fields)); //\n\n\t\t@Nullable\n\t\tObject[] arguments = new Object[fields.size()];\n\n\t\tfor (int i = 0; i < fields.size(); i++) {\n\t\t\tObject argument = resolveNullMarker(fields.get(i));\n\t\t\tif (useHeadersInDisplayName) {\n\t\t\t\tString header = resolveNullMarker(headers.get(i));\n\t\t\t\targument = new ParameterNameAndArgument(String.valueOf(header), argument);\n\t\t\t}\n\t\t\targuments[i] = argument;\n\t\t}\n\n\t\treturn Arguments.of(arguments);\n\t}\n\n\tprivate static List<String> getHeaders(CsvRecord record) {\n\t\treturn ((NamedCsvRecord) record).getHeader();\n\t}\n\n\t@SuppressWarnings({ \"ReferenceEquality\", \"StringEquality\" })\n\tprivate static @Nullable String resolveNullMarker(String record) {\n\t\treturn record == CsvReaderFactory.DefaultFieldModifier.NULL_MARKER ? null : record;\n\t}\n\n\t/**\n\t * @return this method always throws an exception and therefore never\n\t * returns anything; the return type is merely present to allow this\n\t * method to be supplied as the operand in a {@code throw} statement\n\t */\n\tstatic RuntimeException handleCsvException(Throwable throwable, Annotation annotation) {\n\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\tif (throwable instanceof PreconditionViolationException exception) {\n\t\t\tthrow exception;\n\t\t}\n\t\tthrow new CsvParsingException(\"Failed to parse CSV input configured via \" + annotation, throwable);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Spliterator;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\nimport java.util.stream.StreamSupport;\n\nimport de.siegmar.fastcsv.reader.CsvReader;\nimport de.siegmar.fastcsv.reader.CsvRecord;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.0\n */\nclass CsvFileArgumentsProvider extends AnnotationBasedArgumentsProvider<CsvFileSource> {\n\n\tprivate final InputStreamProvider inputStreamProvider;\n\n\tCsvFileArgumentsProvider() {\n\t\tthis(DefaultInputStreamProvider.INSTANCE);\n\t}\n\n\tCsvFileArgumentsProvider(InputStreamProvider inputStreamProvider) {\n\t\tthis.inputStreamProvider = inputStreamProvider;\n\t}\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tCsvFileSource csvFileSource) {\n\n\t\tCharset charset = getCharsetFrom(csvFileSource);\n\n\t\tCsvReaderFactory.validate(csvFileSource);\n\n\t\tStream<Source> resources = Arrays.stream(csvFileSource.resources()).map(inputStreamProvider::classpathResource);\n\t\tStream<Source> files = Arrays.stream(csvFileSource.files()).map(inputStreamProvider::file);\n\t\tList<Source> sources = Stream.concat(resources, files).toList();\n\n\t\t// @formatter:off\n\t\treturn Preconditions.notEmpty(sources, \"Resources or files must not be empty\")\n\t\t\t\t.stream()\n\t\t\t\t.map(source -> source.open(context))\n\t\t\t\t.map(inputStream -> CsvReaderFactory.createReaderFor(csvFileSource, inputStream, charset))\n\t\t\t\t.flatMap(reader -> toStream(reader, csvFileSource));\n\t\t// @formatter:on\n\t}\n\n\tprivate static Charset getCharsetFrom(CsvFileSource csvFileSource) {\n\t\ttry {\n\t\t\treturn Charset.forName(csvFileSource.encoding());\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new PreconditionViolationException(\"The charset supplied in \" + csvFileSource + \" is invalid\", ex);\n\t\t}\n\t}\n\n\tprivate static Stream<Arguments> toStream(CsvReader<? extends CsvRecord> reader, CsvFileSource csvFileSource) {\n\t\tvar spliterator = CsvExceptionHandlingSpliterator.delegatingTo(reader.spliterator(), csvFileSource);\n\t\tboolean useHeadersInDisplayName = csvFileSource.useHeadersInDisplayName();\n\t\t// @formatter:off\n\t\treturn StreamSupport.stream(spliterator, false)\n\t\t\t\t.skip(csvFileSource.numLinesToSkip())\n\t\t\t\t.map(record -> CsvArgumentsProvider.processCsvRecord(\n\t\t\t\t\t\trecord, useHeadersInDisplayName)\n\t\t\t\t)\n\t\t\t\t.onClose(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treader.close();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable throwable) {\n\t\t\t\t\t\tthrow CsvArgumentsProvider.handleCsvException(throwable, csvFileSource);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate record CsvExceptionHandlingSpliterator<T>(Spliterator<T> delegate, CsvFileSource csvFileSource)\n\t\t\timplements Spliterator<T> {\n\n\t\tstatic <T> CsvExceptionHandlingSpliterator<T> delegatingTo(Spliterator<T> delegate,\n\t\t\t\tCsvFileSource csvFileSource) {\n\t\t\treturn new CsvExceptionHandlingSpliterator<>(delegate, csvFileSource);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean tryAdvance(final Consumer<? super T> action) {\n\t\t\ttry {\n\t\t\t\treturn delegate.tryAdvance(action);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tthrow CsvArgumentsProvider.handleCsvException(throwable, csvFileSource);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Spliterator<T> trySplit() {\n\t\t\treturn delegate.trySplit();\n\t\t}\n\n\t\t@Override\n\t\tpublic long estimateSize() {\n\t\t\treturn delegate.estimateSize();\n\t\t}\n\n\t\t@Override\n\t\tpublic int characteristics() {\n\t\t\treturn delegate.characteristics();\n\t\t}\n\n\t}\n\n\t@FunctionalInterface\n\tinterface Source {\n\n\t\tInputStream open(ExtensionContext context);\n\n\t}\n\n\tinterface InputStreamProvider {\n\n\t\tInputStream openClasspathResource(Class<?> baseClass, String path);\n\n\t\tInputStream openFile(String path);\n\n\t\tdefault Source classpathResource(String path) {\n\t\t\treturn context -> openClasspathResource(context.getRequiredTestClass(), path);\n\t\t}\n\n\t\tdefault Source file(String path) {\n\t\t\treturn __ -> openFile(path);\n\t\t}\n\n\t}\n\n\tprivate static class DefaultInputStreamProvider implements InputStreamProvider {\n\n\t\tprivate static final DefaultInputStreamProvider INSTANCE = new DefaultInputStreamProvider();\n\n\t\t@Override\n\t\tpublic InputStream openClasspathResource(Class<?> baseClass, String path) {\n\t\t\tPreconditions.notBlank(path, () -> \"Classpath resource [\" + path + \"] must not be null or blank\");\n\t\t\t//noinspection resource (closed elsewhere)\n\t\t\tInputStream inputStream = baseClass.getResourceAsStream(path);\n\t\t\treturn Preconditions.notNull(inputStream, () -> \"Classpath resource [\" + path + \"] does not exist\");\n\t\t}\n\n\t\t@Override\n\t\tpublic InputStream openFile(String path) {\n\t\t\tPreconditions.notBlank(path, () -> \"File [\" + path + \"] must not be null or blank\");\n\t\t\ttry {\n\t\t\t\treturn Files.newInputStream(Path.of(path));\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new JUnitException(\"File [\" + path + \"] could not be read\", e);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.ParameterizedInvocationConstants;\n\n/**\n * {@code @CsvFileSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} which is used to load comma-separated value (CSV)\n * files from one or more classpath {@link #resources} or {@link #files}.\n *\n * <p>The CSV records parsed from these resources and files will be provided as\n * arguments to the annotated\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}. Note\n * that the first record may optionally be used to supply CSV headers (see\n * {@link #useHeadersInDisplayName}).\n *\n * <p>Any line beginning with a {@link #commentCharacter}\n * will be interpreted as a comment and will be ignored.\n *\n * <p>The column delimiter (which defaults to a comma ({@code ,})) can be customized\n * via either {@link #delimiter} or {@link #delimiterString}.\n *\n * <p>The line separator is detected automatically, meaning that any of\n * {@code \"\\r\"}, {@code \"\\n\"}, or {@code \"\\r\\n\"} is treated as a line separator.\n *\n * <p>In contrast to the default syntax used in {@code @CsvSource}, {@code @CsvFileSource}\n * uses a double quote ({@code \"}) as its quote character by default, but this can\n * be changed via {@link #quoteCharacter}. An empty, quoted value ({@code \"\"})\n * results in an empty {@link String} unless the {@link #emptyValue} attribute is\n * set; whereas, an entirely <em>empty</em> value is interpreted as a {@code null}\n * reference. By specifying one or more {@link #nullValues} a custom value can be\n * interpreted as a {@code null} reference (see the User Guide for an example). An\n * {@link org.junit.jupiter.params.converter.ArgumentConversionException\n * ArgumentConversionException} is thrown if the target type of a {@code null}\n * reference is a primitive type.\n *\n * <p>NOTE: An <em>unquoted</em> empty value will always be converted to a\n * {@code null} reference regardless of any custom values configured via the\n * {@link #nullValues} attribute.\n *\n * <p>Except within a quoted string, leading and trailing whitespace in a CSV\n * column is trimmed by default. This behavior can be changed by setting the\n * {@link #ignoreLeadingAndTrailingWhitespace} attribute to {@code true}.\n *\n * <p>Note that {@link #delimiter} (or {@link #delimiterString}),\n * {@link #quoteCharacter}, and {@link #commentCharacter} are treated as\n * <em>control characters</em> and must all be distinct.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see CsvSource\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(CsvFileSources.class)\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(CsvFileArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface CsvFileSource {\n\n\t/**\n\t * The CSV classpath resources to use as the sources of arguments; must not\n\t * be empty unless {@link #files} is non-empty.\n\t */\n\tString[] resources() default {};\n\n\t/**\n\t * The CSV files to use as the sources of arguments; must not be empty\n\t * unless {@link #resources} is non-empty.\n\t */\n\tString[] files() default {};\n\n\t/**\n\t * The encoding to use when reading the CSV files; must be a valid charset.\n\t *\n\t * <p>Defaults to {@code \"UTF-8\"}.\n\t *\n\t * @see java.nio.charset.StandardCharsets\n\t */\n\tString encoding() default \"UTF-8\";\n\n\t/**\n\t * Configures whether the first CSV record should be treated as header names\n\t * for columns.\n\t *\n\t * <p>When set to {@code true}, the header names will be used in the\n\t * generated display name for each {@code @ParameterizedClass} or\n\t * {@code @ParameterizedTest} invocation. When using this feature, you must\n\t * ensure that the display name pattern for {@code @ParameterizedClass} or\n\t * {@code @ParameterizedTest} includes\n\t * {@value ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER} instead of\n\t * {@value ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}\n\t * as demonstrated in the example below.\n\t *\n\t * <p>Defaults to {@code false}.\n\t *\n\t * <h4>Example</h4>\n\t * <pre class=\"code\">\n\t * {@literal @}ParameterizedTest(name = \"[{index}] {arguments}\")\n\t * {@literal @}CsvFileSource(resources = \"fruits.csv\", useHeadersInDisplayName = true)\n\t * void test(String fruit, int rank) {\n\t *     // ...\n\t * }</pre>\n\t *\n\t * @since 5.8.2\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tboolean useHeadersInDisplayName() default false;\n\n\t/**\n\t * The quote character to use for <em>quoted strings</em>.\n\t *\n\t * <p>Defaults to a double quote ({@code \"}).\n\t *\n\t * <p>You may change the quote character to anything that makes sense for\n\t * your use case.\n\t *\n\t * @since 5.8.2\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tchar quoteCharacter() default '\"';\n\n\t/**\n\t * The column delimiter character to use when reading the CSV files.\n\t *\n\t * <p>This is an alternative to {@link #delimiterString} and cannot be\n\t * used in conjunction with {@link #delimiterString}.\n\t *\n\t * <p>Defaults implicitly to {@code ','}, if neither delimiter attribute is\n\t * explicitly set.\n\t */\n\tchar delimiter() default '\\0';\n\n\t/**\n\t * The column delimiter string to use when reading the CSV files.\n\t *\n\t * <p>This is an alternative to {@link #delimiter} and cannot be used in\n\t * conjunction with {@link #delimiter}.\n\t *\n\t * <p>Defaults implicitly to {@code \",\"}, if neither delimiter attribute is\n\t * explicitly set.\n\t *\n\t * @since 5.6\n\t */\n\tString delimiterString() default \"\";\n\n\t/**\n\t * The number of lines to skip when reading the CSV files.\n\t *\n\t * <p>Typically used to skip header lines.\n\t *\n\t * <p>Defaults to {@code 0}.\n\t *\n\t * @since 5.1\n\t */\n\tint numLinesToSkip() default 0;\n\n\t/**\n\t * The empty value to use when reading the CSV files.\n\t *\n\t * <p>This value replaces quoted empty strings read from the input.\n\t *\n\t * <p>Defaults to {@code \"\"}.\n\t *\n\t * @since 5.5\n\t */\n\tString emptyValue() default \"\";\n\n\t/**\n\t * A list of strings that should be interpreted as {@code null} references.\n\t *\n\t * <p>For example, you may wish for certain values such as {@code \"N/A\"} or\n\t * {@code \"NIL\"} to be converted to {@code null} references.\n\t *\n\t * <p>Please note that <em>unquoted</em> empty values will always be converted\n\t * to {@code null} references regardless of the value of this {@code nullValues}\n\t * attribute; whereas, a <em>quoted</em> empty string will be treated as an\n\t * {@link #emptyValue}.\n\t *\n\t * <p>Defaults to {@code {}}.\n\t *\n\t * @since 5.6\n\t */\n\tString[] nullValues() default {};\n\n\t/**\n\t * The maximum number of characters allowed per CSV column.\n\t *\n\t * <p>Must be a positive number or {@code -1} to allow an unlimited number\n\t * of characters.\n\t *\n\t * <p>Defaults to {@code 4096}.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tint maxCharsPerColumn() default 4096;\n\n\t/**\n\t * Controls whether leading and trailing whitespace characters of unquoted\n\t * CSV columns should be ignored.\n\t *\n\t * <p>Whitespace refers to characters with Unicode code points less than\n\t * or equal to {@code U+0020}, as defined by {@link String#trim()}.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tboolean ignoreLeadingAndTrailingWhitespace() default true;\n\n\t/**\n\t * The character used to denote comments when reading the CSV files.\n\t *\n\t * <p>Any line that begins with this character will be treated as a comment\n\t * and ignored during parsing. Note that there is one exception to this rule:\n\t * if the comment character appears within a quoted field, it loses its\n\t * special meaning.\n\t *\n\t * <p>The comment character must be the first character on the line without\n\t * any leading whitespace.\n\t *\n\t * <p>Defaults to {@code '#'}.\n\t *\n\t * @since 6.0.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0.1\")\n\tchar commentCharacter() default '#';\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvFileSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @CsvFileSources} is a simple container for one or more\n * {@link CsvFileSource @CsvFileSource} annotations.\n *\n * <p>Note, however, that use of the {@code @CsvFileSources} container is completely\n * optional since {@code @CsvFileSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see CsvFileSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.11\")\npublic @interface CsvFileSources {\n\n\t/**\n\t * An array of one or more {@link CsvFileSource @CsvFileSource}\n\t * annotations.\n\t */\n\tCsvFileSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvParsingException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Thrown if an error is encountered while parsing CSV input.\n *\n * @since 5.3\n * @see CsvSource\n * @see CsvFileSource\n */\n@API(status = STABLE, since = \"5.7\")\npublic class CsvParsingException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic CsvParsingException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic CsvParsingException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvReaderFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static de.siegmar.fastcsv.reader.CommentStrategy.NONE;\nimport static de.siegmar.fastcsv.reader.CommentStrategy.SKIP;\n\nimport java.io.InputStream;\nimport java.lang.annotation.Annotation;\nimport java.nio.charset.Charset;\nimport java.util.Set;\nimport java.util.UUID;\nimport java.util.stream.Stream;\n\nimport de.siegmar.fastcsv.reader.CommentStrategy;\nimport de.siegmar.fastcsv.reader.CsvCallbackHandler;\nimport de.siegmar.fastcsv.reader.CsvReader;\nimport de.siegmar.fastcsv.reader.CsvRecord;\nimport de.siegmar.fastcsv.reader.CsvRecordHandler;\nimport de.siegmar.fastcsv.reader.FieldMismatchStrategy;\nimport de.siegmar.fastcsv.reader.FieldModifier;\nimport de.siegmar.fastcsv.reader.NamedCsvRecordHandler;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 6.0\n */\nclass CsvReaderFactory {\n\n\tprivate static final String DEFAULT_DELIMITER = \",\";\n\tprivate static final char EMPTY_CHAR = '\\0';\n\tprivate static final boolean SKIP_EMPTY_LINES = true;\n\tprivate static final boolean TRIM_WHITESPACES_AROUND_QUOTES = true;\n\tprivate static final FieldMismatchStrategy ALLOW_EXTRA_FIELDS = FieldMismatchStrategy.IGNORE;\n\tprivate static final FieldMismatchStrategy ALLOW_MISSING_FIELDS = FieldMismatchStrategy.IGNORE;\n\tprivate static final boolean ALLOW_DUPLICATE_HEADER_FIELDS = true;\n\tprivate static final int MAX_FIELDS = 512;\n\tprivate static final int MAX_RECORD_SIZE = Integer.MAX_VALUE;\n\n\tstatic void validate(CsvSource csvSource) {\n\t\tvalidateMaxCharsPerColumn(csvSource.maxCharsPerColumn());\n\t\tvalidateDelimiter(csvSource.delimiter(), csvSource.delimiterString(), csvSource);\n\t}\n\n\tstatic void validate(CsvFileSource csvFileSource) {\n\t\tvalidateMaxCharsPerColumn(csvFileSource.maxCharsPerColumn());\n\t\tvalidateDelimiter(csvFileSource.delimiter(), csvFileSource.delimiterString(), csvFileSource);\n\t}\n\n\tprivate static void validateMaxCharsPerColumn(int maxCharsPerColumn) {\n\t\tPreconditions.condition(maxCharsPerColumn > 0 || maxCharsPerColumn == -1,\n\t\t\t() -> \"maxCharsPerColumn must be a positive number or -1: \" + maxCharsPerColumn);\n\t}\n\n\tprivate static void validateDelimiter(char delimiter, String delimiterString, Annotation annotation) {\n\t\tPreconditions.condition(delimiter == EMPTY_CHAR || delimiterString.isEmpty(),\n\t\t\t() -> \"The delimiter and delimiterString attributes cannot be set simultaneously in \" + annotation);\n\t}\n\n\tstatic CsvReader<? extends CsvRecord> createReaderFor(CsvSource csvSource, String data) {\n\t\tString delimiter = selectDelimiter(csvSource.delimiter(), csvSource.delimiterString());\n\t\tvar commentStrategy = csvSource.textBlock().isEmpty() ? NONE : SKIP;\n\t\t// @formatter:off\n\t\tvalidateControlCharactersDiffer(\n\t\t\t\tdelimiter, csvSource.quoteCharacter(), csvSource.commentCharacter(), commentStrategy);\n\n\t\tvar builder = CsvReader.builder()\n\t\t\t\t.skipEmptyLines(SKIP_EMPTY_LINES)\n\t\t\t\t.trimWhitespacesAroundQuotes(TRIM_WHITESPACES_AROUND_QUOTES)\n\t\t\t\t.extraFieldStrategy(ALLOW_EXTRA_FIELDS)\n\t\t\t\t.missingFieldStrategy(ALLOW_MISSING_FIELDS)\n\t\t\t\t.fieldSeparator(delimiter)\n\t\t\t\t.quoteCharacter(csvSource.quoteCharacter())\n\t\t\t\t.commentStrategy(commentStrategy)\n\t\t\t\t.commentCharacter(csvSource.commentCharacter());\n\n\t\tvar callbackHandler = createCallbackHandler(\n\t\t\t\tcsvSource.emptyValue(),\n\t\t\t\tSet.of(csvSource.nullValues()),\n\t\t\t\tcsvSource.ignoreLeadingAndTrailingWhitespace(),\n\t\t\t\tcsvSource.maxCharsPerColumn(),\n\t\t\t\tcsvSource.useHeadersInDisplayName()\n\t\t);\n\t\t// @formatter:on\n\t\treturn builder.build(callbackHandler, data);\n\t}\n\n\tstatic CsvReader<? extends CsvRecord> createReaderFor(CsvFileSource csvFileSource, InputStream inputStream,\n\t\t\tCharset charset) {\n\n\t\tString delimiter = selectDelimiter(csvFileSource.delimiter(), csvFileSource.delimiterString());\n\t\tvar commentStrategy = SKIP;\n\t\t// @formatter:off\n\t\tvalidateControlCharactersDiffer(\n\t\t\t\tdelimiter, csvFileSource.quoteCharacter(), csvFileSource.commentCharacter(), commentStrategy);\n\n\t\tvar builder = CsvReader.builder()\n\t\t\t\t.skipEmptyLines(SKIP_EMPTY_LINES)\n\t\t\t\t.trimWhitespacesAroundQuotes(TRIM_WHITESPACES_AROUND_QUOTES)\n\t\t\t\t.extraFieldStrategy(ALLOW_EXTRA_FIELDS)\n\t\t\t\t.missingFieldStrategy(ALLOW_MISSING_FIELDS)\n\t\t\t\t.fieldSeparator(delimiter)\n\t\t\t\t.quoteCharacter(csvFileSource.quoteCharacter())\n\t\t\t\t.commentStrategy(commentStrategy)\n\t\t\t\t.commentCharacter(csvFileSource.commentCharacter());\n\n\t\tvar callbackHandler = createCallbackHandler(\n\t\t\t\tcsvFileSource.emptyValue(),\n\t\t\t\tSet.of(csvFileSource.nullValues()),\n\t\t\t\tcsvFileSource.ignoreLeadingAndTrailingWhitespace(),\n\t\t\t\tcsvFileSource.maxCharsPerColumn(),\n\t\t\t\tcsvFileSource.useHeadersInDisplayName()\n\t\t);\n\t\t// @formatter:on\n\t\treturn builder.build(callbackHandler, inputStream, charset);\n\t}\n\n\tprivate static String selectDelimiter(char delimiter, String delimiterString) {\n\t\tif (delimiter != EMPTY_CHAR) {\n\t\t\treturn String.valueOf(delimiter);\n\t\t}\n\t\tif (!delimiterString.isEmpty()) {\n\t\t\treturn delimiterString;\n\t\t}\n\t\treturn DEFAULT_DELIMITER;\n\t}\n\n\tprivate static void validateControlCharactersDiffer(String delimiter, char quoteCharacter, char commentCharacter,\n\t\t\tCommentStrategy commentStrategy) {\n\n\t\tif (commentStrategy == NONE) {\n\t\t\tPreconditions.condition(stringValuesUnique(delimiter, quoteCharacter),\n\t\t\t\t() -> (\"delimiter or delimiterString: '%s' and quoteCharacter: '%s' \" + //\n\t\t\t\t\t\t\"must differ\").formatted(delimiter, quoteCharacter));\n\t\t}\n\t\telse {\n\t\t\tPreconditions.condition(stringValuesUnique(delimiter, quoteCharacter, commentCharacter),\n\t\t\t\t() -> (\"delimiter or delimiterString: '%s', quoteCharacter: '%s', and commentCharacter: '%s' \" + //\n\t\t\t\t\t\t\"must all differ\").formatted(delimiter, quoteCharacter, commentCharacter));\n\t\t}\n\t}\n\n\tprivate static boolean stringValuesUnique(Object... values) {\n\t\tlong uniqueCount = Stream.of(values).map(String::valueOf).distinct().count();\n\t\treturn uniqueCount == values.length;\n\t}\n\n\tprivate static CsvCallbackHandler<? extends CsvRecord> createCallbackHandler(String emptyValue,\n\t\t\tSet<String> nullValues, boolean ignoreLeadingAndTrailingWhitespaces, int maxCharsPerColumn,\n\t\t\tboolean useHeadersInDisplayName) {\n\n\t\tint maxFieldSize = maxCharsPerColumn == -1 ? Integer.MAX_VALUE : maxCharsPerColumn;\n\t\tFieldModifier modifier = new DefaultFieldModifier(emptyValue, nullValues, ignoreLeadingAndTrailingWhitespaces);\n\n\t\t// @formatter:off\n\t\tif (useHeadersInDisplayName) {\n\t\t\treturn NamedCsvRecordHandler.builder()\n\t\t\t\t\t.allowDuplicateHeaderFields(ALLOW_DUPLICATE_HEADER_FIELDS)\n\t\t\t\t\t.maxFields(MAX_FIELDS)\n\t\t\t\t\t.maxRecordSize(MAX_RECORD_SIZE)\n\t\t\t\t\t.maxFieldSize(maxFieldSize)\n\t\t\t\t\t.fieldModifier(modifier)\n\t\t\t\t\t.build();\n\t\t}\n\t\treturn CsvRecordHandler.builder()\n\t\t\t\t.maxFields(MAX_FIELDS)\n\t\t\t\t.maxRecordSize(MAX_RECORD_SIZE)\n\t\t\t\t.maxFieldSize(maxFieldSize)\n\t\t\t\t.fieldModifier(modifier)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\trecord DefaultFieldModifier(String emptyValue, Set<String> nullValues, boolean ignoreLeadingAndTrailingWhitespaces)\n\t\t\timplements FieldModifier {\n\n\t\t/**\n\t\t * Represents a {@code null} value and serves as a workaround since FastCSV\n\t\t * does not allow the modified field value to be {@code null}.\n\t\t *\n\t\t * <p>The marker is generated with a unique ID to ensure it cannot conflict\n\t\t * with actual CSV content.\n\t\t */\n\t\tstatic final String NULL_MARKER = \"<null marker: %s>\".formatted(UUID.randomUUID());\n\n\t\t@Override\n\t\tpublic String modify(long unusedStartingLineNumber, int unusedFieldIdx, boolean quoted, String field) {\n\t\t\tif (quoted && field.isEmpty() && !emptyValue.isEmpty()) {\n\t\t\t\treturn emptyValue;\n\t\t\t}\n\t\t\tif (!quoted && field.isBlank()) {\n\t\t\t\treturn NULL_MARKER;\n\t\t\t}\n\t\t\tString modifiedField = (!quoted && ignoreLeadingAndTrailingWhitespaces) ? field.trim() : field;\n\t\t\tif (nullValues.contains(modifiedField)) {\n\t\t\t\treturn NULL_MARKER;\n\t\t\t}\n\t\t\treturn modifiedField;\n\t\t}\n\n\t}\n\n\tprivate CsvReaderFactory() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.ParameterizedInvocationConstants;\n\n/**\n * {@code @CsvSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} which reads comma-separated values (CSV) from one\n * or more CSV records supplied via the {@link #value} attribute or\n * {@link #textBlock} attribute.\n *\n * <p>The supplied values will be provided as arguments to the annotated\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n *\n * <p>The column delimiter (which defaults to a comma ({@code ,})) can be customized\n * via either {@link #delimiter} or {@link #delimiterString}.\n *\n * <p>By default, {@code @CsvSource} uses a single quote ({@code '}) as its quote\n * character, but this can be changed via {@link #quoteCharacter}. See the\n * {@code 'lemon, lime'} examples in the documentation for the {@link #value}\n * and {@link #textBlock} attributes. An empty, quoted value ({@code ''}) results\n * in an empty {@link String} unless the {@link #emptyValue} attribute is set;\n * whereas, an entirely <em>empty</em> value is interpreted as a {@code null} reference.\n * By specifying one or more {@link #nullValues} a custom value can be interpreted\n * as a {@code null} reference (see the User Guide for an example). An\n * {@link org.junit.jupiter.params.converter.ArgumentConversionException\n * ArgumentConversionException} is thrown if the target type of a {@code null}\n * reference is a primitive type.\n *\n * <p>NOTE: An <em>unquoted</em> empty value will always be converted to a\n * {@code null} reference regardless of any custom values configured via the\n * {@link #nullValues} attribute.\n *\n * <p>Except within a quoted string, leading and trailing whitespace in a CSV\n * column is trimmed by default. This behavior can be changed by setting the\n * {@link #ignoreLeadingAndTrailingWhitespace} attribute to {@code true}.\n *\n * <p>In general, CSV records should not contain explicit newlines ({@code \\n})\n * unless they are placed within quoted strings. Note that CSV records supplied\n * via {@link #textBlock} will implicitly contain newlines at the end of each\n * physical line within the text block. Thus, if a CSV column wraps across a\n * new line in a text block, the column must be a quoted string.\n *\n * <p>Note that {@link #delimiter} (or {@link #delimiterString}),\n * {@link #quoteCharacter}, and {@link #commentCharacter} (when\n * {@link #textBlock} is used) are treated as <em>control characters</em>.\n *\n * <ul>\n *   <li>{@link #delimiter} and {@link #quoteCharacter} must always be distinct.</li>\n *   <li>{@link #commentCharacter} must be distinct from the others only when\n *   {@link #textBlock} is used.</li>\n * </ul>\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see CsvFileSource\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Repeatable(CsvSources.class)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(CsvArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface CsvSource {\n\n\t/**\n\t * The CSV records to use as the source of arguments; must not be empty.\n\t *\n\t * <p>Defaults to an empty array. You therefore must supply CSV content\n\t * via this attribute or the {@link #textBlock} attribute.\n\t *\n\t * <p>Each value corresponds to a record in a CSV file and will be split using\n\t * the specified {@link #delimiter} or {@link #delimiterString}. Note that\n\t * the first value may optionally be used to supply CSV headers (see\n\t * {@link #useHeadersInDisplayName}). Moreover, each specified value must\n\t * not be blank.\n\t *\n\t * <p>If <em>text block</em> syntax is supported by your programming language,\n\t * you may find it more convenient to declare your CSV content via the\n\t * {@link #textBlock} attribute.\n\t *\n\t * <h4>Example</h4>\n\t * <pre class=\"code\">\n\t * {@literal @}ParameterizedTest\n\t * {@literal @}CsvSource({\n\t *     \"apple,         1\",\n\t *     \"banana,        2\",\n\t *     \"'lemon, lime', 0xF1\",\n\t *     \"strawberry,    700_000\"\n\t * })\n\t * void test(String fruit, int rank) {\n\t *     // ...\n\t * }</pre>\n\t *\n\t * @see #textBlock\n\t */\n\tString[] value() default {};\n\n\t/**\n\t * The CSV records to use as the source of arguments, supplied as a single\n\t * <em>text block</em>; must not be empty.\n\t *\n\t * <p>Defaults to an empty string. You therefore must supply CSV content\n\t * via this attribute or the {@link #value} attribute.\n\t *\n\t * <p>Text block syntax is supported by various languages on the JVM\n\t * including Java SE. If text blocks are not supported, you\n\t * should declare your CSV content via the {@link #value} attribute.\n\t *\n\t * <p>Each record in the text block corresponds to a record in a CSV file and will\n\t * be split using the specified {@link #delimiter} or {@link #delimiterString}.\n\t * Note that the first record may optionally be used to supply CSV headers (see\n\t * {@link #useHeadersInDisplayName}).\n\t *\n\t * <p>In contrast to CSV records supplied via {@link #value}, a text block\n\t * can contain comments. Any line beginning with a {@link #commentCharacter}\n\t * will be treated as a comment and ignored. Note that there is one exception\n\t * to this rule: if the comment character appears within a quoted field,\n\t * it loses its special meaning.\n\t *\n\t * <p>The comment character must be the first character on the line without\n\t * any leading whitespace. It is therefore recommended that the closing text block\n\t * delimiter {@code \"\"\"} be placed either at the end of the last line of\n\t * input or on the following line, vertically aligned with the rest of the\n\t * input (as can be seen in the example below).\n\t *\n\t * <p>Java's <a href=\"https://docs.oracle.com/en/java/javase/17/text-blocks/index.html\">text block</a>\n\t * feature automatically removes <em>incidental whitespace</em> when the code\n\t * is compiled. However, other JVM languages such as Groovy and Kotlin do not.\n\t * Thus, if you are using a programming language other than Java and your text\n\t * block contains comments or new lines within quoted strings, you will need\n\t * to ensure that there is no leading whitespace within your text block.\n\t *\n\t * <h4>Example</h4>\n\t * <pre class=\"code\">\n\t * {@literal @}ParameterizedTest\n\t * {@literal @}CsvSource(quoteCharacter = '\"', textBlock = \"\"\"\n\t *     # FRUIT,       RANK\n\t *     apple,         1\n\t *     banana,        2\n\t *     \"lemon, lime\", 0xF1\n\t *     strawberry,    700_000\n\t *     \"\"\")\n\t * void test(String fruit, int rank) {\n\t *     // ...\n\t * }</pre>\n\t *\n\t * @since 5.8.1\n\t * @see #value\n\t * @see #quoteCharacter\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tString textBlock() default \"\";\n\n\t/**\n\t * Configures whether the first CSV record should be treated as header names\n\t * for columns.\n\t *\n\t * <p>When set to {@code true}, the header names will be used in the\n\t * generated display name for each {@code @ParameterizedClass} or\n\t * {@code @ParameterizedTest} invocation. When using this feature, you must\n\t * ensure that the display name pattern for {@code @ParameterizedClass} or\n\t * {@code @ParameterizedTest} includes\n\t * {@value ParameterizedInvocationConstants#ARGUMENTS_PLACEHOLDER} instead of\n\t * {@value ParameterizedInvocationConstants#ARGUMENTS_WITH_NAMES_PLACEHOLDER}\n\t * as demonstrated in the example below.\n\t *\n\t * <p>Defaults to {@code false}.\n\t *\n\t * <h4>Example</h4>\n\t * <pre class=\"code\">\n\t * {@literal @}ParameterizedTest(name = \"[{index}] {arguments}\")\n\t * {@literal @}CsvSource(useHeadersInDisplayName = true, textBlock = \"\"\"\n\t *     FRUIT,         RANK\n\t *     apple,         1\n\t *     banana,        2\n\t *     'lemon, lime', 0xF1\n\t *     strawberry,    700_000\n\t *     \"\"\")\n\t * void test(String fruit, int rank) {\n\t *     // ...\n\t * }</pre>\n\t *\n\t * @since 5.8.2\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tboolean useHeadersInDisplayName() default false;\n\n\t/**\n\t * The quote character to use for <em>quoted strings</em>.\n\t *\n\t * <p>Defaults to a single quote ({@code '}).\n\t *\n\t * <p>You may change the quote character to anything that makes sense for\n\t * your use case; however, the primary use case is to allow you to use double\n\t * quotes in {@link #textBlock}.\n\t *\n\t * @since 5.8.2\n\t * @see #textBlock\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tchar quoteCharacter() default '\\'';\n\n\t/**\n\t * The column delimiter character to use when reading the {@linkplain #value records}.\n\t *\n\t * <p>This is an alternative to {@link #delimiterString} and cannot be\n\t * used in conjunction with {@link #delimiterString}.\n\t *\n\t * <p>Defaults implicitly to {@code ','}, if neither delimiter attribute is\n\t * explicitly set.\n\t */\n\tchar delimiter() default '\\0';\n\n\t/**\n\t * The column delimiter string to use when reading the {@linkplain #value records}.\n\t *\n\t * <p>This is an alternative to {@link #delimiter} and cannot be used in\n\t * conjunction with {@link #delimiter}.\n\t *\n\t * <p>Defaults implicitly to {@code \",\"}, if neither delimiter attribute is\n\t * explicitly set.\n\t *\n\t * @since 5.6\n\t */\n\tString delimiterString() default \"\";\n\n\t/**\n\t * The empty value to use when reading the {@linkplain #value records}.\n\t *\n\t * <p>This value replaces quoted empty strings read from the input.\n\t *\n\t * <p>Defaults to {@code \"\"}.\n\t *\n\t * @since 5.5\n\t */\n\tString emptyValue() default \"\";\n\n\t/**\n\t * A list of strings that should be interpreted as {@code null} references.\n\t *\n\t * <p>For example, you may wish for certain values such as {@code \"N/A\"} or\n\t * {@code \"NIL\"} to be converted to {@code null} references.\n\t *\n\t * <p>Please note that <em>unquoted</em> empty values will always be converted\n\t * to {@code null} references regardless of the value of this {@code nullValues}\n\t * attribute; whereas, a <em>quoted</em> empty string will be treated as an\n\t * {@link #emptyValue}.\n\t *\n\t * <p>Defaults to {@code {}}.\n\t *\n\t * @since 5.6\n\t */\n\tString[] nullValues() default {};\n\n\t/**\n\t * The maximum number of characters allowed per CSV column.\n\t *\n\t * <p>Must be a positive number or {@code -1} to allow an unlimited number\n\t * of characters.\n\t *\n\t * <p>Defaults to {@code 4096}.\n\t *\n\t * @since 5.7\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tint maxCharsPerColumn() default 4096;\n\n\t/**\n\t * Controls whether leading and trailing whitespace characters of unquoted\n\t * CSV columns should be ignored.\n\t *\n\t * <p>Whitespace refers to characters with Unicode code points less than\n\t * or equal to {@code U+0020}, as defined by {@link String#trim()}.\n\t *\n\t * <p>Defaults to {@code true}.\n\t *\n\t * @since 5.8\n\t */\n\t@API(status = STABLE, since = \"5.10\")\n\tboolean ignoreLeadingAndTrailingWhitespace() default true;\n\n\t/**\n\t * The character used to denote comments in a {@linkplain #textBlock text block}.\n\t *\n\t * <p>Any line that begins with this character will be treated as a comment\n\t * and ignored during parsing. Note that there is one exception to this rule:\n\t * if the comment character appears within a quoted field, it loses its\n\t * special meaning.\n\t *\n\t * <p>The comment character must be the first character on the line without\n\t * any leading whitespace.\n\t *\n\t * <p>Defaults to {@code '#'}.\n\t *\n\t * @since 6.0.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0.1\")\n\tchar commentCharacter() default '#';\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/CsvSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @CsvSources} is a simple container for one or more\n * {@link CsvSource @CsvSource} annotations.\n *\n * <p>Note, however, that use of the {@code @CsvSources} container is completely\n * optional since {@code @CsvSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see CsvSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.11\")\npublic @interface CsvSources {\n\n\t/**\n\t * An array of one or more {@link CsvSource @CsvSource}\n\t * annotations.\n\t */\n\tCsvSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptyArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.util.ReflectionUtils.newInstance;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Constructor;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Map;\nimport java.util.NavigableMap;\nimport java.util.NavigableSet;\nimport java.util.NoSuchElementException;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclaration;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.4\n * @see EmptySource\n */\nclass EmptyArgumentsProvider extends AnnotationBasedArgumentsProvider<EmptySource> {\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tEmptySource annotation) {\n\n\t\tvar explicitType = annotation.type();\n\n\t\tif (explicitType.equals(Derived.class)) {\n\t\t\tOptional<ParameterDeclaration> firstParameter = parameters.getFirst();\n\n\t\t\tPreconditions.condition(firstParameter.isPresent(),\n\t\t\t\t() -> \"@EmptySource cannot provide an empty argument to %s: no formal parameters declared.\".formatted(\n\t\t\t\t\tparameters.getSourceElementDescription()));\n\n\t\t\treturn provideEmptyArgument(firstParameter.get().getParameterType(),\n\t\t\t\t() -> \"to \" + parameters.getSourceElementDescription());\n\t\t}\n\n\t\treturn provideEmptyArgument(explicitType, () -> \"for 'type'\");\n\t}\n\n\tprivate static Stream<Arguments> provideEmptyArgument(Class<?> parameterType,\n\t\t\tSupplier<String> errorDetailsSupplier) {\n\n\t\tif (String.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(\"\"));\n\t\t}\n\t\tif (Iterable.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(EmptyIterable.INSTANCE));\n\t\t}\n\t\tif (Iterator.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(EmptyIterator.INSTANCE));\n\t\t}\n\t\tif (ListIterator.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(EmptyListIterator.INSTANCE));\n\t\t}\n\t\tif (Collection.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptySet()));\n\t\t}\n\t\tif (List.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptyList()));\n\t\t}\n\t\tif (Set.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptySet()));\n\t\t}\n\t\tif (SortedSet.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptySortedSet()));\n\t\t}\n\t\tif (NavigableSet.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptyNavigableSet()));\n\t\t}\n\t\tif (Map.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptyMap()));\n\t\t}\n\t\tif (SortedMap.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptySortedMap()));\n\t\t}\n\t\tif (NavigableMap.class.equals(parameterType)) {\n\t\t\treturn Stream.of(arguments(Collections.emptyNavigableMap()));\n\t\t}\n\t\tif (Collection.class.isAssignableFrom(parameterType) || Map.class.isAssignableFrom(parameterType)) {\n\t\t\tOptional<Constructor<?>> defaultConstructor = getDefaultConstructor(parameterType);\n\t\t\tif (defaultConstructor.isPresent()) {\n\t\t\t\treturn Stream.of(arguments(newInstance(defaultConstructor.get())));\n\t\t\t}\n\t\t}\n\t\tif (parameterType.isArray()) {\n\t\t\tObject array = Array.newInstance(parameterType.getComponentType(), 0);\n\t\t\treturn Stream.of(arguments(array));\n\t\t}\n\t\t// else\n\t\tthrow new PreconditionViolationException(\n\t\t\t\"@EmptySource cannot provide an empty argument %s: [%s] is not a supported type.\".formatted(\n\t\t\t\terrorDetailsSupplier.get(), parameterType.getName()));\n\t}\n\n\tprivate static Optional<Constructor<?>> getDefaultConstructor(Class<?> clazz) {\n\t\ttry {\n\t\t\treturn Optional.of(clazz.getConstructor());\n\t\t}\n\t\tcatch (NoSuchMethodException e) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\tprivate static class EmptyIterable<E> implements Iterable<E> {\n\n\t\tprivate static final EmptyIterable<Object> INSTANCE = new EmptyIterable<>();\n\n\t\t@Override\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tpublic Iterator<E> iterator() {\n\t\t\treturn (Iterator<E>) EmptyIterator.INSTANCE;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"[]\";\n\t\t}\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\tprivate static sealed class EmptyIterator<E> implements Iterator<E> permits EmptyListIterator {\n\n\t\tprivate static final EmptyIterator<Object> INSTANCE = new EmptyIterator<>();\n\n\t\t@Override\n\t\tpublic boolean hasNext() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic E next() {\n\t\t\tthrow new NoSuchElementException();\n\t\t}\n\n\t\t@Override\n\t\tpublic void remove() {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"[]\";\n\t\t}\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\tprivate static final class EmptyListIterator<E> extends EmptyIterator<E> implements ListIterator<E> {\n\n\t\tprivate static final EmptyListIterator<Object> INSTANCE = new EmptyListIterator<>();\n\n\t\t@Override\n\t\tpublic boolean hasPrevious() {\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic E previous() {\n\t\t\tthrow new NoSuchElementException();\n\t\t}\n\n\t\t@Override\n\t\tpublic int nextIndex() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Override\n\t\tpublic int previousIndex() {\n\t\t\treturn -1;\n\t\t}\n\n\t\t@Override\n\t\tpublic void set(E e) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t@Override\n\t\tpublic void add(E e) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\t}\n\n\tstatic final class Derived {\n\t\tprivate Derived() {\n\t\t\tthrow new JUnitException(\"Must not be instantiated\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EmptySource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\n\n/**\n * {@code @EmptySource} is an {@link ArgumentsSource} which provides a single\n * <em>empty</em> argument to the annotated {@code @ParameterizedClass}\n * or {@code @ParameterizedTest}.\n *\n * <h2 id=\"supported-parameter-types\">Supported Parameter Types</h2>\n *\n * <p>This argument source will only provide an empty argument for the following\n * parameter types.\n *\n * <ul>\n * <li>{@link java.lang.String}</li>\n * <li>{@link java.lang.Iterable}</li>\n * <li>{@link java.util.Iterator}</li>\n * <li>{@link java.util.ListIterator}</li>\n * <li>{@link java.util.Collection} and concrete subtypes with a public no-arg constructor</li>\n * <li>{@link java.util.List}</li>\n * <li>{@link java.util.Set}</li>\n * <li>{@link java.util.SortedSet}</li>\n * <li>{@link java.util.NavigableSet}</li>\n * <li>{@link java.util.Map} and concrete subtypes with a public no-arg constructor</li>\n * <li>{@link java.util.SortedMap}</li>\n * <li>{@link java.util.NavigableMap}</li>\n * <li>primitive arrays &mdash; for example {@code int[]}, {@code char[][]}, etc.</li>\n * <li>object arrays &mdash; for example {@code String[]}, {@code Integer[][]}, etc.</li>\n * </ul>\n *\n * <p>Unless the {@link #type()} is set, the parameter type is derived from\n * the first (and only) parameter of the annotated {@code @ParameterizedClass}\n * or {@code @ParameterizedTest}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.4\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see NullSource\n * @see NullAndEmptySource\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(EmptyArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface EmptySource {\n\n\t/**\n\t * The type of the empty argument to provide.\n\t *\n\t * <p>Must be one of the\n\t * <a href=\"#supported-parameter-types\">supported parameter types</a>.\n\t *\n\t * <p>Setting this attribute is usually not necessary because the type will\n\t * be derived from the first (and only) parameter. Setting it explicitly\n\t * allows using an {@link ArgumentConverter} to convert from the specified\n\t * type to the actual parameter type.\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tClass<?> type() default EmptyArgumentsProvider.Derived.class;\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.Arrays.stream;\nimport static java.util.stream.Collectors.toSet;\n\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclaration;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.0\n */\nclass EnumArgumentsProvider extends AnnotationBasedArgumentsProvider<EnumSource> {\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tEnumSource enumSource) {\n\t\tSet<? extends Enum<?>> constants = getEnumConstants(parameters, enumSource);\n\t\tEnumSource.Mode mode = enumSource.mode();\n\t\tString[] declaredConstantNames = enumSource.names();\n\t\tif (declaredConstantNames.length > 0) {\n\t\t\tSet<String> uniqueNames = stream(declaredConstantNames).collect(toSet());\n\t\t\tPreconditions.condition(uniqueNames.size() == declaredConstantNames.length,\n\t\t\t\t() -> \"Duplicate enum constant name(s) found in \" + enumSource);\n\t\t\tmode.validate(enumSource, constants, uniqueNames);\n\t\t\tconstants.removeIf(constant -> !mode.select(constant, uniqueNames));\n\t\t}\n\t\treturn constants.stream().map(Arguments::of);\n\t}\n\n\tprivate <E extends Enum<E>> Set<? extends E> getEnumConstants(ParameterDeclarations parameters,\n\t\t\tEnumSource enumSource) {\n\t\tClass<E> enumClass = determineEnumClass(parameters, enumSource);\n\t\tE[] constants = enumClass.getEnumConstants();\n\t\tif (constants.length == 0) {\n\t\t\tPreconditions.condition(enumSource.from().isEmpty() && enumSource.to().isEmpty(),\n\t\t\t\t\"No enum constant in \" + enumClass.getSimpleName() + \", but 'from' or 'to' is not empty.\");\n\t\t\treturn EnumSet.noneOf(enumClass);\n\t\t}\n\t\tE from = enumSource.from().isEmpty() ? constants[0] : Enum.valueOf(enumClass, enumSource.from());\n\t\tE to = enumSource.to().isEmpty() ? constants[constants.length - 1] : Enum.valueOf(enumClass, enumSource.to());\n\t\tPreconditions.condition(from.compareTo(to) <= 0,\n\t\t\t() -> \"Invalid enum range: 'from' (%s) must come before 'to' (%s) in the natural order of enum constants.\".formatted(\n\t\t\t\tfrom, to));\n\t\treturn EnumSet.range(from, to);\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tprivate <E extends Enum<E>> Class<E> determineEnumClass(ParameterDeclarations parameters, EnumSource enumSource) {\n\t\tClass enumClass = enumSource.value();\n\t\tif (enumClass.equals(NullEnum.class)) {\n\t\t\tenumClass = parameters.getFirst() //\n\t\t\t\t\t.map(ParameterDeclaration::getParameterType).map(parameterType -> {\n\t\t\t\t\t\tPreconditions.condition(Enum.class.isAssignableFrom(parameterType),\n\t\t\t\t\t\t\t() -> \"First parameter must reference an Enum type (alternatively, use the annotation's 'value' attribute to specify the type explicitly): \"\n\t\t\t\t\t\t\t\t\t+ parameters.getSourceElementDescription());\n\t\t\t\t\t\treturn (Class<E>) parameterType;\n\t\t\t\t\t}).orElseThrow(\n\t\t\t\t\t\t() -> new PreconditionViolationException(\"There must be at least one declared parameter for \"\n\t\t\t\t\t\t\t\t+ parameters.getSourceElementDescription()));\n\t\t}\n\t\treturn enumClass;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.stream.Collectors.toSet;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Set;\nimport java.util.function.BiPredicate;\nimport java.util.regex.Pattern;\nimport java.util.regex.PatternSyntaxException;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code @EnumSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} for constants of an {@link Enum}.\n *\n * <p>The enum constants will be provided as arguments to the annotated\n * {@code @ParameterizedClass} or {@code @ParameterizedTest}.\n *\n * <p>The enum type can be specified explicitly using the {@link #value}\n * attribute. Otherwise, the declared type of the first parameter of the\n * {@code @ParameterizedClass} or {@code @ParameterizedTest} is used.\n *\n * <p>The set of enum constants can be restricted via the {@link #names},\n * {@link #from}, {@link #to} and {@link #mode} attributes.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(EnumSources.class)\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(EnumArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface EnumSource {\n\n\t/**\n\t * The enum type that serves as the source of the enum constants.\n\t *\n\t * <p>If this attribute is not set explicitly, the declared type of the\n\t * first parameter of the {@code @ParameterizedTest} method is used.\n\t *\n\t * @see #names\n\t * @see #from\n\t * @see #to\n\t * @see #mode\n\t */\n\tClass<? extends Enum<?>> value() default NullEnum.class;\n\n\t/**\n\t * The names of enum constants to provide, or regular expressions to select\n\t * the names of enum constants to provide.\n\t *\n\t * <p>If no names or regular expressions are specified, and neither {@link #from}\n\t * nor {@link #to} are specified, all enum constants declared in the specified\n\t * {@linkplain #value enum type} will be provided.\n\t *\n\t * <p>If {@link #from} or {@link #to} are specified, the elements in names must\n\t * fall within the range defined by {@link #from} and {@link #to}.\n\t *\n\t * <p>The {@link #mode} determines how the names are interpreted.\n\t *\n\t * @see #value\n\t * @see #from\n\t * @see #to\n\t * @see #mode\n\t */\n\tString[] names() default {};\n\n\t/**\n\t * The starting enum constant of the range to be included.\n\t *\n\t * <p>Defaults to an empty string, where the range starts from the first enum\n\t * constant of the specified {@linkplain #value enum type}.\n\t *\n\t * @see #value\n\t * @see #names\n\t * @see #to\n\t * @see #mode\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString from() default \"\";\n\n\t/**\n\t * The ending enum constant of the range to be included.\n\t *\n\t * <p>Defaults to an empty string, where the range ends at the last enum\n\t * constant of the specified {@linkplain #value enum type}.\n\t *\n\t * @see #value\n\t * @see #names\n\t * @see #from\n\t * @see #mode\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tString to() default \"\";\n\n\t/**\n\t * The enum constant selection mode.\n\t *\n\t * <p>The mode only applies to the {@link #names} attribute and does not change\n\t * the behavior of {@link #from} and {@link #to}, which always define a range\n\t * based on the natural order of the enum constants.\n\t *\n\t * <p>Defaults to {@link Mode#INCLUDE INCLUDE}.\n\t *\n\t * @see Mode#INCLUDE\n\t * @see Mode#EXCLUDE\n\t * @see Mode#MATCH_ALL\n\t * @see Mode#MATCH_ANY\n\t * @see Mode#MATCH_NONE\n\t * @see #names\n\t * @see #from\n\t * @see #to\n\t */\n\tMode mode() default Mode.INCLUDE;\n\n\t/**\n\t * Enumeration of modes for selecting enum constants by name.\n\t */\n\tenum Mode {\n\n\t\t/**\n\t\t * Select only those enum constants whose names are supplied via the\n\t\t * {@link EnumSource#names} attribute.\n\t\t */\n\t\tINCLUDE(Mode::validateNames, (name, names) -> names.contains(name)),\n\n\t\t/**\n\t\t * Select all declared enum constants except those supplied via the\n\t\t * {@link EnumSource#names} attribute.\n\t\t */\n\t\tEXCLUDE(Mode::validateNames, (name, names) -> !names.contains(name)),\n\n\t\t/**\n\t\t * Select only those enum constants whose names match all patterns supplied\n\t\t * via the {@link EnumSource#names} attribute.\n\t\t *\n\t\t * @see java.util.stream.Stream#allMatch(java.util.function.Predicate)\n\t\t */\n\t\tMATCH_ALL(Mode::validatePatterns, (name, patterns) -> patterns.stream().allMatch(name::matches)),\n\n\t\t/**\n\t\t * Select only those enum constants whose names match any pattern supplied\n\t\t * via the {@link EnumSource#names} attribute.\n\t\t *\n\t\t * @see java.util.stream.Stream#anyMatch(java.util.function.Predicate)\n\t\t */\n\t\tMATCH_ANY(Mode::validatePatterns, (name, patterns) -> patterns.stream().anyMatch(name::matches)),\n\n\t\t/**\n\t\t * Select only those enum constants whose names match none of the patterns supplied\n\t\t * via the {@link EnumSource#names} attribute.\n\t\t *\n\t\t * @since 5.9\n\t\t * @see java.util.stream.Stream#noneMatch(java.util.function.Predicate)\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"5.13.3\")\n\t\tMATCH_NONE(Mode::validatePatterns, (name, patterns) -> patterns.stream().noneMatch(name::matches));\n\n\t\tprivate final Validator validator;\n\t\tprivate final BiPredicate<String, Set<String>> selector;\n\n\t\tMode(Validator validator, BiPredicate<String, Set<String>> selector) {\n\t\t\tthis.validator = validator;\n\t\t\tthis.selector = selector;\n\t\t}\n\n\t\tvoid validate(EnumSource enumSource, Set<? extends Enum<?>> constants, Set<String> names) {\n\t\t\tPreconditions.notNull(enumSource, \"EnumSource must not be null\");\n\t\t\tPreconditions.notNull(names, \"names must not be null\");\n\n\t\t\tvalidator.validate(enumSource, constants, names);\n\t\t}\n\n\t\tboolean select(Enum<?> constant, Set<String> names) {\n\t\t\tPreconditions.notNull(constant, \"Enum constant must not be null\");\n\t\t\tPreconditions.notNull(names, \"names must not be null\");\n\n\t\t\treturn selector.test(constant.name(), names);\n\t\t}\n\n\t\tprivate static void validateNames(EnumSource enumSource, Set<? extends Enum<?>> constants, Set<String> names) {\n\t\t\tSet<String> allNames = constants.stream().map(Enum::name).collect(toSet());\n\t\t\tPreconditions.condition(allNames.containsAll(names),\n\t\t\t\t() -> \"Invalid enum constant name(s) in \" + enumSource + \". Valid names include: \" + allNames);\n\t\t}\n\n\t\tprivate static void validatePatterns(EnumSource enumSource, Set<? extends Enum<?>> constants,\n\t\t\t\tSet<String> names) {\n\t\t\ttry {\n\t\t\t\tnames.forEach(Pattern::compile);\n\t\t\t}\n\t\t\tcatch (PatternSyntaxException e) {\n\t\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\t\"Pattern compilation failed for a regular expression supplied in \" + enumSource, e);\n\t\t\t}\n\t\t}\n\n\t\tprivate interface Validator {\n\t\t\tvoid validate(EnumSource enumSource, Set<? extends Enum<?>> constants, Set<String> names);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/EnumSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @EnumSources} is a simple container for one or more\n * {@link EnumSource @EnumSource} annotations.\n *\n * <p>Note, however, that use of the {@code @EnumSources} container is completely\n * optional since {@code @EnumSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see EnumSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.11\")\npublic @interface EnumSources {\n\n\t/**\n\t * An array of one or more {@link EnumSource @EnumSource}\n\t * annotations.\n\t */\n\tEnumSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.Arrays.stream;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.Iterator;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.BaseStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode;\n\n/**\n * {@link ArgumentsProvider} for {@link FieldSource @FieldSource}.\n *\n * @since 5.11\n */\nclass FieldArgumentsProvider extends AnnotationBasedArgumentsProvider<FieldSource> {\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tFieldSource fieldSource) {\n\t\tClass<?> testClass = context.getRequiredTestClass();\n\t\tObject testInstance = context.getTestInstance().orElse(null);\n\t\tString[] fieldNames = fieldSource.value();\n\t\tif (fieldNames.length == 0) {\n\t\t\tOptional<Method> testMethod = context.getTestMethod();\n\t\t\tPreconditions.condition(testMethod.isPresent(),\n\t\t\t\t\"You must specify a field name when using @FieldSource with @ParameterizedClass\");\n\t\t\tfieldNames = new String[] { testMethod.get().getName() };\n\t\t}\n\t\t// @formatter:off\n\t\treturn stream(fieldNames)\n\t\t\t\t.map(fieldName -> findField(testClass, fieldName))\n\t\t\t\t.map(field -> validateField(field, testInstance))\n\t\t\t\t.map(field -> readField(field, testInstance))\n\t\t\t\t.flatMap(fieldValue -> {\n\t\t\t\t\tif (fieldValue instanceof Supplier<?> supplier) {\n\t\t\t\t\t\tfieldValue = supplier.get();\n\t\t\t\t\t}\n\t\t\t\t\treturn CollectionUtils.toStream(fieldValue);\n\t\t\t\t})\n\t\t\t\t.map(ArgumentsUtils::toArguments);\n\t\t// @formatter:on\n\t}\n\n\t// package-private for testing\n\tstatic Field findField(Class<?> testClass, String fieldName) {\n\t\tPreconditions.notBlank(fieldName, \"Field name must not be blank\");\n\t\tfieldName = fieldName.strip();\n\n\t\tClass<?> clazz = testClass;\n\t\tif (fieldName.contains(\"#\") || fieldName.contains(\".\")) {\n\t\t\tString[] fieldParts = ReflectionUtils.parseFullyQualifiedFieldName(fieldName);\n\t\t\tString className = fieldParts[0];\n\t\t\tfieldName = fieldParts[1];\n\t\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass);\n\t\t\tclazz = ReflectionUtils.loadRequiredClass(className, classLoader);\n\t\t}\n\n\t\tClass<?> resolvedClass = clazz;\n\t\tString resolvedFieldName = fieldName;\n\t\tPredicate<Field> nameMatches = field -> field.getName().equals(resolvedFieldName);\n\t\tField field = ReflectionUtils.streamFields(resolvedClass, nameMatches, HierarchyTraversalMode.BOTTOM_UP)//\n\t\t\t\t.findFirst()//\n\t\t\t\t.orElse(null);\n\n\t\treturn Preconditions.notNull(field,\n\t\t\t() -> \"Could not find field named [%s] in class [%s]\".formatted(resolvedFieldName,\n\t\t\t\tresolvedClass.getName()));\n\t}\n\n\tprivate static Field validateField(Field field, @Nullable Object testInstance) {\n\t\tPreconditions.condition(field.getDeclaringClass().isInstance(testInstance) || ModifierSupport.isStatic(field),\n\t\t\t() -> \"\"\"\n\t\t\t\t\tField '%s' must be static: local @FieldSource fields must be static \\\n\t\t\t\t\tunless the PER_CLASS @TestInstance lifecycle mode is used; \\\n\t\t\t\t\texternal @FieldSource fields must always be static.\"\"\".formatted(field.toGenericString()));\n\t\treturn field;\n\t}\n\n\tprivate static Object readField(Field field, @Nullable Object testInstance) {\n\t\tObject value = ReflectionSupport.tryToReadFieldValue(field, testInstance).getOrThrow(\n\t\t\tcause -> new JUnitException(\"Could not read field [%s]\".formatted(field.getName()), cause));\n\n\t\tString fieldName = field.getName();\n\t\tString declaringClass = field.getDeclaringClass().getName();\n\n\t\tPreconditions.notNull(value,\n\t\t\t() -> \"The value of field [%s] in class [%s] must not be null\".formatted(fieldName, declaringClass));\n\n\t\tPreconditions.condition(!(value instanceof BaseStream),\n\t\t\t() -> \"The value of field [%s] in class [%s] must not be a stream\".formatted(fieldName, declaringClass));\n\n\t\tPreconditions.condition(!(value instanceof Iterator),\n\t\t\t() -> \"The value of field [%s] in class [%s] must not be an Iterator\".formatted(fieldName, declaringClass));\n\n\t\tPreconditions.condition(isConvertibleToStream(field, value),\n\t\t\t() -> \"The value of field [%s] in class [%s] must be convertible to a Stream\".formatted(fieldName,\n\t\t\t\tdeclaringClass));\n\n\t\treturn value;\n\t}\n\n\t/**\n\t * Determine if the supplied value can be converted into a {@code Stream} or\n\t * if the declared type of the supplied field is a {@link Supplier} of a type\n\t * that can be converted into a {@code Stream}.\n\t */\n\tprivate static boolean isConvertibleToStream(Field field, Object value) {\n\t\t// Check actual value type.\n\t\tif (CollectionUtils.isConvertibleToStream(value.getClass())) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check declared type T of Supplier<T>.\n\t\tif (Supplier.class.isAssignableFrom(field.getType())) {\n\t\t\tType genericType = field.getGenericType();\n\t\t\tif (genericType instanceof ParameterizedType parameterizedType) {\n\t\t\t\tType[] typeArguments = parameterizedType.getActualTypeArguments();\n\t\t\t\tif (typeArguments.length == 1) {\n\t\t\t\t\tType type = typeArguments[0];\n\t\t\t\t\t// Handle cases such as Supplier<IntStream>\n\t\t\t\t\tif (type instanceof Class<?> clazz) {\n\t\t\t\t\t\treturn CollectionUtils.isConvertibleToStream(clazz);\n\t\t\t\t\t}\n\t\t\t\t\t// Handle cases such as Supplier<Stream<String>>\n\t\t\t\t\tif (type instanceof ParameterizedType innerParameterizedType) {\n\t\t\t\t\t\tType rawType = innerParameterizedType.getRawType();\n\t\t\t\t\t\tif (rawType instanceof Class<?> clazz) {\n\t\t\t\t\t\t\treturn CollectionUtils.isConvertibleToStream(clazz);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @FieldSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} which provides access to values of\n * {@linkplain #value() fields} of the class in which this annotation is declared\n * or from static fields in external classes referenced by <em>fully qualified\n * field name</em>.\n *\n * <p>Each field must be able to supply a <em>stream</em> of <em>arguments</em>,\n * and each set of \"arguments\" within the \"stream\" will be provided as the physical\n * arguments for individual invocations of the annotated\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n *\n * <p>In this context, a \"stream\" is anything that JUnit can reliably convert to\n * a {@link java.util.stream.Stream Stream}; however, the actual concrete field\n * type can take on many forms. Generally speaking this translates to a\n * {@link java.util.Collection Collection}, an {@link Iterable}, a\n * {@link java.util.function.Supplier Supplier} of a stream\n * ({@link java.util.stream.Stream Stream},\n * {@link java.util.stream.DoubleStream DoubleStream},\n * {@link java.util.stream.LongStream LongStream}, or\n * {@link java.util.stream.IntStream IntStream}), a {@code Supplier} of an\n * {@link java.util.Iterator Iterator}, an array of objects or primitives, or\n * any type that provides an {@link java.util.Iterator Iterator}-returning\n * {@code iterator()} method (such as, for example, a\n * {@code kotlin.sequences.Sequence}). Each set of \"arguments\" within the\n * \"stream\" can be supplied as an instance of {@link Arguments}, an array of\n * objects (for example, {@code Object[]}, {@code String[]}, etc.), or a single\n * <em>value</em> if the parameterized class or test accepts a single argument.\n *\n * <p>In contrast to the supported return types for {@link MethodSource @MethodSource}\n * factory methods, the value of a {@code @FieldSource} field cannot be an instance of\n * {@link java.util.stream.Stream Stream},\n * {@link java.util.stream.DoubleStream DoubleStream},\n * {@link java.util.stream.LongStream LongStream},\n * {@link java.util.stream.IntStream IntStream}, or\n * {@link java.util.Iterator Iterator}, since the values of such types are\n * <em>consumed</em> the first time they are processed. However, if you wish to\n * use one of these types, you can wrap it in a {@code Supplier} &mdash; for\n * example, {@code Supplier<IntStream>}.\n *\n * <p>If the {@code Supplier} return type is {@code Stream} or\n * one of the primitive streams, JUnit will properly close it by calling\n * {@link java.util.stream.BaseStream#close() BaseStream.close()},\n * making it safe to use a resource such as\n * {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.\n *\n * <p>Please note that a one-dimensional array of objects supplied as a set of\n * \"arguments\" will be handled differently than other types of arguments.\n * Specifically, all of the elements of a one-dimensional array of objects will\n * be passed as individual physical arguments to the {@code @ParameterizedTest}\n * method. This behavior can be seen in the table below for the\n * {@code Supplier<Stream<Object[]>> objectArrayStreamSupplier} field: the\n * {@code @ParameterizedTest} method accepts individual {@code String} and\n * {@code int} arguments rather than a single {@code Object[]} array. In contrast,\n * any multidimensional array supplied as a set of \"arguments\" will be passed as\n * a single physical argument to the {@code @ParameterizedTest} method without\n * modification. This behavior can be seen in the table below for the\n * {@code Supplier<Stream<int[][]>> twoDimensionalIntArrayStreamSupplier} and\n * {@code Supplier<Stream<Object[][]>> twoDimensionalObjectArrayStreamSupplier}\n * fields: the {@code @ParameterizedTest} methods for those fields accept individual\n * {@code int[][]} and {@code Object[][]} arguments, respectively.\n *\n * <h2>Examples</h2>\n *\n * <p>The following table displays compatible method signatures for parameterized\n * test methods and their corresponding {@code @FieldSource} fields.\n *\n * <table class=\"plain\">\n * <caption>Compatible method signatures and field declarations</caption>\n * <tr><th>{@code @ParameterizedTest} method</th><th>{@code @FieldSource} field</th></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static List<String> listOfStrings}</td></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static String[] arrayOfStrings}</td></tr>\n * <tr><td>{@code void test(int)}</td><td>{@code static int[] intArray}</td></tr>\n * <tr><td>{@code void test(int[])}</td><td>{@code static int[][] twoDimensionalIntArray}</td></tr>\n * <tr><td>{@code void test(String, String)}</td><td>{@code static String[][] twoDimensionalStringArray}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Object[][] twoDimensionalObjectArray}</td></tr>\n * <tr><td>{@code void test(int)}</td><td>{@code static Supplier<IntStream> intStreamSupplier}</td></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static Supplier<Stream<String>> stringStreamSupplier}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Supplier<Stream<Object[]>> objectArrayStreamSupplier}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Supplier<Stream<Arguments>> argumentsStreamSupplier}</td></tr>\n * <tr><td>{@code void test(int[])}</td><td>{@code static Supplier<Stream<int[]>> intArrayStreamSupplier}</td></tr>\n * <tr><td>{@code void test(int[][])}</td><td>{@code static Supplier<Stream<int[][]>> twoDimensionalIntArrayStreamSupplier}</td></tr>\n * <tr><td>{@code void test(Object[][])}</td><td>{@code static Supplier<Stream<Object[][]>> twoDimensionalObjectArrayStreamSupplier}</td></tr>\n * </table>\n *\n * <p>Fields within the test class must be {@code static} unless the\n * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n * test instance lifecycle mode is used; whereas, fields in external classes must\n * always be {@code static}.\n *\n * <p>This behavior and the above examples also apply to parameters of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass},\n * regardless whether field or constructor injection is used.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see MethodSource\n * @see Arguments\n * @see ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.api.TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(FieldSources.class)\n@API(status = MAINTAINED, since = \"5.13.3\")\n@ArgumentsSource(FieldArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface FieldSource {\n\n\t/**\n\t * The names of fields within the test class or in external classes to use\n\t * as sources for arguments.\n\t *\n\t * <p>Fields in external classes must be referenced by <em>fully qualified\n\t * field name</em> &mdash; for example,\n\t * {@code \"com.example.WebUtils#httpMethodNames\"} or\n\t * {@code \"com.example.TopLevelClass$NestedClass#numbers\"} for a field in a\n\t * static nested class.\n\t *\n\t * <p>If no field names are declared, a field within the test class that has\n\t * the same name as the test method will be used as the field by default in\n\t * case this annotation is applied to a {@code @ParameterizedTest} method.\n\t * For a {@code @ParameterizedClass}, at least one field name must be\n\t * declared explicitly.\n\t *\n\t * <p>For further information, see the {@linkplain FieldSource class-level Javadoc}.\n\t */\n\tString[] value() default {};\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/FieldSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @FieldSources} is a simple container for one or more\n * {@link FieldSource @FieldSource} annotations.\n *\n * <p>Note, however, that use of the {@code @FieldSources} container is completely\n * optional since {@code @FieldSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see FieldSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = MAINTAINED, since = \"5.13.3\")\npublic @interface FieldSources {\n\n\t/**\n\t * An array of one or more {@link FieldSource @FieldSource}\n\t * annotations.\n\t */\n\tFieldSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.Arrays.stream;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.commons.util.CollectionUtils.isConvertibleToStream;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * @since 5.0\n */\nclass MethodArgumentsProvider extends AnnotationBasedArgumentsProvider<MethodSource> {\n\n\tprivate static final Predicate<Method> isFactoryMethod = //\n\t\tmethod -> isConvertibleToStream(method.getReturnType()) && !isTestMethod(method);\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tMethodSource methodSource) {\n\t\tClass<?> testClass = context.getRequiredTestClass();\n\t\tOptional<Method> testMethod = context.getTestMethod();\n\t\tObject testInstance = context.getTestInstance().orElse(null);\n\t\tString[] methodNames = methodSource.value();\n\t\t// @formatter:off\n\t\treturn stream(methodNames)\n\t\t\t\t.map(factoryMethodName -> findFactoryMethod(testClass, testMethod, factoryMethodName))\n\t\t\t\t.map(factoryMethod -> validateFactoryMethod(factoryMethod, testInstance))\n\t\t\t\t.map(factoryMethod -> Preconditions.notNull(context.getExecutableInvoker().invoke(factoryMethod, testInstance), () -> \"@MethodSource-referenced method [%s] must not return null\".formatted(factoryMethod.toGenericString())))\n\t\t\t\t.flatMap(CollectionUtils::toStream)\n\t\t\t\t.map(ArgumentsUtils::toArguments);\n\t\t// @formatter:on\n\t}\n\n\tprivate static Method findFactoryMethod(Class<?> testClass, Optional<Method> testMethod, String factoryMethodName) {\n\t\tString originalFactoryMethodName = factoryMethodName;\n\n\t\t// If the user did not provide a factory method name, find a \"default\" local\n\t\t// factory method with the same name as the parameterized test method.\n\t\tif (StringUtils.isBlank(factoryMethodName)) {\n\t\t\tPreconditions.condition(testMethod.isPresent(),\n\t\t\t\t\"You must specify a method name when using @MethodSource with @ParameterizedClass\");\n\t\t\tfactoryMethodName = testMethod.get().getName();\n\t\t\treturn findFactoryMethodBySimpleName(testClass, testMethod, factoryMethodName);\n\t\t}\n\n\t\t// Convert local factory method name to fully qualified method name.\n\t\tif (!looksLikeAFullyQualifiedMethodName(factoryMethodName)) {\n\t\t\tfactoryMethodName = testClass.getName() + \"#\" + factoryMethodName;\n\t\t}\n\n\t\t// Find factory method using fully qualified name.\n\t\tMethod factoryMethod = findFactoryMethodByFullyQualifiedName(testClass, testMethod, factoryMethodName);\n\n\t\t// Ensure factory method has a valid return type and is not a test method.\n\t\tPreconditions.condition(isFactoryMethod.test(factoryMethod),\n\t\t\t() -> \"Could not find valid factory method [%s] for test class [%s] but found the following invalid candidate: %s\".formatted(\n\t\t\t\toriginalFactoryMethodName, testClass.getName(), factoryMethod));\n\n\t\treturn factoryMethod;\n\t}\n\n\tprivate static boolean looksLikeAFullyQualifiedMethodName(String factoryMethodName) {\n\t\tif (factoryMethodName.contains(\"#\")) {\n\t\t\treturn true;\n\t\t}\n\t\tint indexOfFirstDot = factoryMethodName.indexOf('.');\n\t\tif (indexOfFirstDot == -1) {\n\t\t\treturn false;\n\t\t}\n\t\tint indexOfLastOpeningParenthesis = factoryMethodName.lastIndexOf('(');\n\t\tif (indexOfLastOpeningParenthesis > 0) {\n\t\t\t// Exclude simple/local method names with parameters\n\t\t\treturn indexOfFirstDot < indexOfLastOpeningParenthesis;\n\t\t}\n\t\t// If we get this far, we conclude the supplied factory method name \"looks\"\n\t\t// like it was intended to be a fully qualified method name, even if the\n\t\t// syntax is invalid. We do this in order to provide better diagnostics for\n\t\t// the user when a fully qualified method name is in fact invalid.\n\t\treturn true;\n\t}\n\n\t// package-private for testing\n\tstatic Method findFactoryMethodByFullyQualifiedName(Class<?> testClass, Optional<Method> testMethod,\n\t\t\tString fullyQualifiedMethodName) {\n\t\tString[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName);\n\t\tString className = methodParts[0];\n\t\tString methodName = methodParts[1];\n\t\tString methodParameters = methodParts[2];\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(testClass);\n\t\tClass<?> clazz = ReflectionUtils.loadRequiredClass(className, classLoader);\n\n\t\t// Attempt to find an exact match first.\n\t\tMethod factoryMethod = ReflectionUtils.findMethod(clazz, methodName, methodParameters).orElse(null);\n\t\tif (factoryMethod != null) {\n\t\t\treturn factoryMethod;\n\t\t}\n\n\t\tboolean explicitParameterListSpecified = //\n\t\t\tStringUtils.isNotBlank(methodParameters) || fullyQualifiedMethodName.endsWith(\"()\");\n\n\t\t// If we didn't find an exact match but an explicit parameter list was specified,\n\t\t// that's a user configuration error.\n\t\tPreconditions.condition(!explicitParameterListSpecified,\n\t\t\t() -> \"Could not find factory method [%s(%s)] in class [%s]\".formatted(methodName, methodParameters,\n\t\t\t\tclassName));\n\n\t\t// Otherwise, fall back to the same lenient search semantics that are used\n\t\t// to locate a \"default\" local factory method.\n\t\treturn findFactoryMethodBySimpleName(clazz, testMethod, methodName);\n\t}\n\n\t/**\n\t * Find the factory method by searching for all methods in the given {@code clazz}\n\t * with the desired {@code factoryMethodName} which have return types that can be\n\t * converted to a {@link Stream}, ignoring the {@code testMethod} itself as well\n\t * as any {@code @Test}, {@code @TestTemplate}, or {@code @TestFactory} methods\n\t * with the same name.\n\t * @return the single factory method matching the search criteria\n\t * @throws PreconditionViolationException if the factory method was not found or\n\t * multiple competing factory methods with the same name were found\n\t */\n\tprivate static Method findFactoryMethodBySimpleName(Class<?> clazz, Optional<Method> testMethod,\n\t\t\tString factoryMethodName) {\n\t\tPredicate<Method> isCandidate = candidate -> factoryMethodName.equals(candidate.getName())\n\t\t\t\t&& !candidate.equals(testMethod.orElse(null));\n\t\tList<Method> candidates = ReflectionUtils.findMethods(clazz, isCandidate);\n\n\t\tList<Method> factoryMethods = candidates.stream().filter(isFactoryMethod).toList();\n\n\t\tPreconditions.notEmpty(factoryMethods, () -> {\n\t\t\tif (candidates.isEmpty()) {\n\t\t\t\t// Report that we didn't find anything.\n\t\t\t\treturn \"Could not find factory method [%s] in class [%s]\".formatted(factoryMethodName, clazz.getName());\n\t\t\t}\n\t\t\t// If we didn't find the factory method using the isFactoryMethod Predicate, perhaps\n\t\t\t// the specified factory method has an invalid return type or is a test method.\n\t\t\t// In that case, we report the invalid candidates that were found.\n\t\t\treturn \"Could not find valid factory method [%s] in class [%s] but found the following invalid candidates: %s\".formatted(\n\t\t\t\tfactoryMethodName, clazz.getName(), candidates);\n\t\t});\n\t\tPreconditions.condition(factoryMethods.size() == 1,\n\t\t\t() -> \"%d factory methods named [%s] were found in class [%s]: %s\".formatted(factoryMethods.size(),\n\t\t\t\tfactoryMethodName, clazz.getName(), factoryMethods));\n\t\treturn factoryMethods.get(0);\n\t}\n\n\tprivate static boolean isTestMethod(Method candidate) {\n\t\treturn isAnnotated(candidate, Test.class) || isAnnotated(candidate, TestTemplate.class)\n\t\t\t\t|| isAnnotated(candidate, TestFactory.class);\n\t}\n\n\tprivate static Method validateFactoryMethod(Method factoryMethod, @Nullable Object testInstance) {\n\t\tPreconditions.condition(\n\t\t\tfactoryMethod.getDeclaringClass().isInstance(testInstance) || ReflectionUtils.isStatic(factoryMethod),\n\t\t\t() -> \"\"\"\n\t\t\t\t\tMethod '%s' must be static: local factory methods must be static \\\n\t\t\t\t\tunless the PER_CLASS @TestInstance lifecycle mode is used; \\\n\t\t\t\t\texternal factory methods must always be static.\"\"\".formatted(factoryMethod.toGenericString()));\n\t\treturn factoryMethod;\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @MethodSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} which provides access to values returned from\n * {@linkplain #value() factory methods} of the class in which this annotation\n * is declared or from static factory methods in external classes referenced\n * by <em>fully qualified method name</em>.\n *\n * <p>Each factory method must generate a <em>stream</em> of <em>arguments</em>,\n * and each set of \"arguments\" within the \"stream\" will be provided as the\n * physical arguments for individual invocations of the annotated\n * {@code org.junit.jupiter.params.ParameterizedClass @ParameterizedClass} or\n * {@link org.junit.jupiter.params.ParameterizedTest @ParameterizedTest}.\n * Generally speaking this translates to a {@link java.util.stream.Stream Stream}\n * of {@link Arguments} (i.e., {@code Stream<Arguments>}); however, the actual\n * concrete return type can take on many forms. In this context, a \"stream\" is\n * anything that JUnit can reliably convert into a {@code Stream}, such as\n * {@link java.util.stream.Stream Stream},\n * {@link java.util.stream.DoubleStream DoubleStream},\n * {@link java.util.stream.LongStream LongStream},\n * {@link java.util.stream.IntStream IntStream},\n * {@link java.util.Collection Collection},\n * {@link java.util.Iterator Iterator}, an array of objects or primitives, or\n * any type that provides an {@link java.util.Iterator Iterator}-returning\n * {@code iterator()} method (such as, for example, a\n * {@code kotlin.sequences.Sequence}). Each set of \"arguments\" within the\n * \"stream\" can be supplied as an instance of {@link Arguments}, an array of\n * objects (e.g., {@code Object[]}, {@code String[]}, etc.), or a single\n * <em>value</em> if the parameterized test method accepts a single argument.\n *\n * <p>If the return type is {@code Stream} or\n * one of the primitive streams, JUnit will properly close it by calling\n * {@link java.util.stream.BaseStream#close() BaseStream.close()},\n * making it safe to use a resource such as\n * {@link java.nio.file.Files#lines(java.nio.file.Path) Files.lines()}.\n *\n * <p>Please note that a one-dimensional array of objects supplied as a set of\n * \"arguments\" will be handled differently than other types of arguments.\n * Specifically, all of the elements of a one-dimensional array of objects will\n * be passed as individual physical arguments to the {@code @ParameterizedTest}\n * method. This behavior can be seen in the table below for the\n * {@code static Stream<Object[]> factory()} method: the {@code @ParameterizedTest}\n * method accepts individual {@code String} and {@code int} arguments rather than\n * a single {@code Object[]} array. In contrast, any multidimensional array\n * supplied as a set of \"arguments\" will be passed as a single physical argument\n * to the {@code @ParameterizedTest} method without modification. This behavior\n * can be seen in the table below for the {@code static Stream<int[][]> factory()}\n * and {@code static Stream<Object[][]> factory()} methods: the\n * {@code @ParameterizedTest} methods for those factories accept individual\n * {@code int[][]} and {@code Object[][]} arguments, respectively.\n *\n * <h2>Examples</h2>\n *\n * <p>The following table displays compatible method signatures for parameterized\n * test methods and their corresponding factory methods.\n *\n * <table class=\"plain\">\n * <caption>Compatible method signatures and factory methods</caption>\n * <tr><th>{@code @ParameterizedTest} method</th><th>Factory method</th></tr>\n * <tr><td>{@code void test(int)}</td><td>{@code static int[] factory()}</td></tr>\n * <tr><td>{@code void test(int)}</td><td>{@code static IntStream factory()}</td></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static String[] factory()}</td></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static List<String> factory()}</td></tr>\n * <tr><td>{@code void test(String)}</td><td>{@code static Stream<String> factory()}</td></tr>\n * <tr><td>{@code void test(String, String)}</td><td>{@code static String[][] factory()}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Object[][] factory()}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Stream<Object[]> factory()}</td></tr>\n * <tr><td>{@code void test(String, int)}</td><td>{@code static Stream<Arguments> factory()}</td></tr>\n * <tr><td>{@code void test(int[])}</td><td>{@code static int[][] factory()}</td></tr>\n * <tr><td>{@code void test(int[])}</td><td>{@code static Stream<int[]> factory()}</td></tr>\n * <tr><td>{@code void test(int[][])}</td><td>{@code static Stream<int[][]> factory()}</td></tr>\n * <tr><td>{@code void test(Object[][])}</td><td>{@code static Stream<Object[][]> factory()}</td></tr>\n * </table>\n *\n * <p>Factory methods within the test class must be {@code static} unless the\n * {@link org.junit.jupiter.api.TestInstance.Lifecycle#PER_CLASS PER_CLASS}\n * test instance lifecycle mode is used; whereas, factory methods in external\n * classes must always be {@code static}.\n *\n * <p>This behavior and the above examples also apply to parameters of a\n * {@link org.junit.jupiter.params.ParameterizedClass @ParameterizedClass},\n * regardless whether field or constructor injection is used.\n *\n * <p>Factory methods can declare parameters, which will be provided by registered\n * implementations of {@link org.junit.jupiter.api.extension.ParameterResolver}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see FieldSource\n * @see Arguments\n * @see ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see org.junit.jupiter.api.TestInstance\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(MethodSources.class)\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(MethodArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface MethodSource {\n\n\t/**\n\t * The names of factory methods within the test class or in external classes\n\t * to use as sources for arguments.\n\t *\n\t * <p>Factory methods in external classes must be referenced by\n\t * <em>fully qualified method name</em> &mdash; for example,\n\t * {@code \"com.example.StringsProviders#blankStrings\"} or\n\t * {@code \"com.example.TopLevelClass$NestedClass#classMethod\"} for a factory\n\t * method in a static nested class.\n\t *\n\t * <p>If a factory method accepts arguments that are provided by a\n\t * {@link org.junit.jupiter.api.extension.ParameterResolver ParameterResolver},\n\t * you can supply the formal parameter list in the qualified method name to\n\t * disambiguate between overloaded variants of the factory method. For example,\n\t * {@code \"blankStrings(int)\"} for a local qualified method name or\n\t * {@code \"com.example.StringsProviders#blankStrings(int)\"} for a fully qualified\n\t * method name.\n\t *\n\t * <p>If no factory method names are declared, a method within the test class\n\t * that has the same name as the test method will be used as the factory\n\t * method by default in case this annotation is applied to a\n\t * {@code @ParameterizedTest} method. For a {@code @ParameterizedClass}, at\n\t * least one method name must be declared explicitly.\n\t *\n\t * <p>For further information, see the {@linkplain MethodSource class-level Javadoc}.\n\t */\n\tString[] value() default \"\";\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @MethodSources} is a simple container for one or more\n * {@link MethodSource @MethodSource} annotations.\n *\n * <p>Note, however, that use of the {@code @MethodSources} container is completely\n * optional since {@code @MethodSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see MethodSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.11\")\npublic @interface MethodSources {\n\n\t/**\n\t * An array of one or more {@link MethodSource @MethodSource}\n\t * annotations.\n\t */\n\tMethodSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullAndEmptySource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @NullAndEmptySource} is a <em>composed annotation</em> that combines\n * the functionality of {@link NullSource @NullSource} and\n * {@link EmptySource @EmptySource}.\n *\n * <p>Annotating a {@code @ParameterizedClass} or {@code @ParameterizedTest}\n * with {@code @NullAndEmptySource} is equivalent to annotating the method with\n * both {@code @NullSource} and {@code @EmptySource}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.4\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see NullSource\n * @see EmptySource\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\n@NullSource\n@EmptySource\npublic @interface NullAndEmptySource {\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.4\n * @see NullSource\n */\nclass NullArgumentsProvider implements ArgumentsProvider {\n\n\tprivate static final Arguments nullArguments = arguments(new @Nullable Object[] { null });\n\n\t@Override\n\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context) {\n\t\tPreconditions.condition(parameters.getFirst().isPresent(),\n\t\t\t() -> \"@NullSource cannot provide a null argument to %s: no formal parameters declared.\".formatted(\n\t\t\t\tparameters.getSourceElementDescription()));\n\n\t\treturn Stream.of(nullArguments);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullEnum.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\n\n/**\n * Dummy enum class used as the default value for optional attributes of annotations.\n *\n * @since 5.6\n * @see EnumSource#value()\n */\n@API(status = INTERNAL, since = \"5.7\")\npublic enum NullEnum {\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/NullSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @NullSource} is an {@link ArgumentsSource} which provides a single\n * {@code null} argument to the annotated {@code @ParameterizedClass} or\n * {@code @ParameterizedTest}.\n *\n * <p>Note that {@code @NullSource} cannot be used for an argument that has\n * a primitive type, unless the argument is converted to a corresponding wrapper\n * type with an {@link org.junit.jupiter.params.converter.ArgumentConverter}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.4\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n * @see EmptySource\n * @see NullAndEmptySource\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(NullArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface NullSource {\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueArgumentsProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 5.0\n */\nclass ValueArgumentsProvider extends AnnotationBasedArgumentsProvider<ValueSource> {\n\n\t@Override\n\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context,\n\t\t\tValueSource valueSource) {\n\t\tObject[] arguments = getArgumentsFromSource(valueSource);\n\t\treturn Arrays.stream(arguments).map(Arguments::of);\n\t}\n\n\tprivate Object[] getArgumentsFromSource(ValueSource valueSource) {\n\t\t// @formatter:off\n\t\tList<?> arrays =\n\t\t\tStream.of(\n\t\t\t\tvalueSource.shorts(),\n\t\t\t\tvalueSource.bytes(),\n\t\t\t\tvalueSource.ints(),\n\t\t\t\tvalueSource.longs(),\n\t\t\t\tvalueSource.floats(),\n\t\t\t\tvalueSource.doubles(),\n\t\t\t\tvalueSource.chars(),\n\t\t\t\tvalueSource.booleans(),\n\t\t\t\tvalueSource.strings(),\n\t\t\t\tvalueSource.classes()\n\t\t\t)\n\t\t\t.filter(array -> Array.getLength(array) > 0)\n\t\t\t.toList();\n\t\t// @formatter:on\n\n\t\tPreconditions.condition(arrays.size() == 1, () -> \"Exactly one type of input must be provided in the @\"\n\t\t\t\t+ ValueSource.class.getSimpleName() + \" annotation, but there were \" + arrays.size());\n\n\t\tObject originalArray = arrays.get(0);\n\t\treturn IntStream.range(0, Array.getLength(originalArray)) //\n\t\t\t\t.mapToObj(index -> Array.get(originalArray, index)) //\n\t\t\t\t.toArray();\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ValueSource} is a {@linkplain Repeatable repeatable}\n * {@link ArgumentsSource} which provides access to an array of literal values.\n *\n * <p>Supported types include {@link #shorts}, {@link #bytes}, {@link #ints},\n * {@link #longs}, {@link #floats}, {@link #doubles}, {@link #chars},\n * {@link #booleans}, {@link #strings}, and {@link #classes}. Note, however,\n * that only one of the supported types may be specified per\n * {@code @ValueSource} declaration.\n *\n * <p>The supplied literal values will be provided as arguments to the\n * annotated {@code @ParameterizedClass} or {@code @ParameterizedTest}.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.0\n * @see org.junit.jupiter.params.provider.ArgumentsSource\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@Repeatable(ValueSources.class)\n@API(status = STABLE, since = \"5.7\")\n@ArgumentsSource(ValueArgumentsProvider.class)\n@SuppressWarnings(\"exports\")\npublic @interface ValueSource {\n\n\t/**\n\t * The {@code short} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.1\n\t */\n\tshort[] shorts() default {};\n\n\t/**\n\t * The {@code byte} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.1\n\t */\n\tbyte[] bytes() default {};\n\n\t/**\n\t * The {@code int} values to use as sources of arguments; must not be empty.\n\t */\n\tint[] ints() default {};\n\n\t/**\n\t * The {@code long} values to use as sources of arguments; must not be empty.\n\t */\n\tlong[] longs() default {};\n\n\t/**\n\t * The {@code float} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.1\n\t */\n\tfloat[] floats() default {};\n\n\t/**\n\t * The {@code double} values to use as sources of arguments; must not be empty.\n\t */\n\tdouble[] doubles() default {};\n\n\t/**\n\t * The {@code char} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.1\n\t */\n\tchar[] chars() default {};\n\n\t/**\n\t * The {@code boolean} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.5\n\t */\n\tboolean[] booleans() default {};\n\n\t/**\n\t * The {@link String} values to use as sources of arguments; must not be empty.\n\t */\n\tString[] strings() default {};\n\n\t/**\n\t * The {@link Class} values to use as sources of arguments; must not be empty.\n\t *\n\t * @since 5.1\n\t */\n\tClass<?>[] classes() default {};\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/ValueSources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ValueSources} is a simple container for one or more\n * {@link ValueSource @ValueSource} annotations.\n *\n * <p>Note, however, that use of the {@code @ValueSources} container is completely\n * optional since {@code @ValueSource} is a {@linkplain java.lang.annotation.Repeatable\n * repeatable} annotation.\n *\n * <h2>Inheritance</h2>\n *\n * <p>This annotation is {@linkplain Inherited inherited} within class hierarchies.\n *\n * @since 5.11\n * @see ValueSource\n * @see java.lang.annotation.Repeatable\n */\n@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.TYPE })\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Inherited\n@API(status = STABLE, since = \"5.11\")\npublic @interface ValueSources {\n\n\t/**\n\t * An array of one or more {@link ValueSource @ValueSource}\n\t * annotations.\n\t */\n\tValueSource[] value();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider}\n * implementations and their corresponding\n * {@link org.junit.jupiter.params.provider.ArgumentsSource ArgumentsSource}\n * annotations.\n */\n\n@NullMarked\npackage org.junit.jupiter.params.provider;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Annotation;\nimport java.util.function.Consumer;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code AnnotationConsumer} is a {@linkplain FunctionalInterface functional\n * interface} for consuming annotations.\n *\n * <p>It is typically implemented by implementations of\n * {@link org.junit.jupiter.params.provider.ArgumentsProvider ArgumentsProvider}\n * and {@link org.junit.jupiter.params.converter.ArgumentConverter ArgumentConverter}\n * in order to signal that they can {@link #accept accept} a certain annotation.\n *\n * @since 5.0\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"5.7\")\npublic interface AnnotationConsumer<A extends Annotation> extends Consumer<A> {\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/AnnotationConsumerInitializer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Repeatable;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code AnnotationConsumerInitializer} is an internal helper class for\n * initializing {@link AnnotationConsumer AnnotationConsumers}.\n *\n * @since 5.0\n */\n@API(status = INTERNAL, since = \"5.0\")\npublic final class AnnotationConsumerInitializer {\n\n\tprivate static final List<MethodSignature> methodSignatures = List.of( //\n\t\tnew MethodSignature(\"accept\", 1, 0), //\n\t\tnew MethodSignature(\"provideArguments\", 3, 2), //\n\t\tnew MethodSignature(\"provideArguments\", 2, 1), //\n\t\tnew MethodSignature(\"convert\", 3, 2));\n\n\tprivate static final Predicate<Method> consumesAnnotation = methodSignatures.stream() //\n\t\t\t.map(signature -> (Predicate<Method>) signature::matches) //\n\t\t\t.reduce(method -> false, Predicate::or);\n\n\tprivate AnnotationConsumerInitializer() {\n\t\t/* no-op */\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic static <T> T initialize(AnnotatedElement annotatedElement, T annotationConsumerInstance) {\n\t\tif (annotationConsumerInstance instanceof AnnotationConsumer consumer) {\n\t\t\tClass<? extends Annotation> annotationType = findConsumedAnnotationType(annotationConsumerInstance);\n\t\t\tList<? extends Annotation> annotations = findAnnotations(annotatedElement, annotationType);\n\n\t\t\tif (annotations.isEmpty()) {\n\t\t\t\tthrow new JUnitException(annotationConsumerInstance.getClass().getName()\n\t\t\t\t\t\t+ \" must be used with an annotation of type \" + annotationType.getName());\n\t\t\t}\n\n\t\t\tannotations.forEach(annotation -> initializeAnnotationConsumer(consumer, annotation));\n\t\t}\n\t\treturn annotationConsumerInstance;\n\t}\n\n\tprivate static <T extends Annotation> List<T> findAnnotations(AnnotatedElement annotatedElement,\n\t\t\tClass<T> annotationType) {\n\n\t\treturn annotationType.isAnnotationPresent(Repeatable.class)\n\t\t\t\t? findRepeatableAnnotations(annotatedElement, annotationType)\n\t\t\t\t: findAnnotation(annotatedElement, annotationType).map(Collections::singletonList).orElse(emptyList());\n\t}\n\n\tprivate static <T> Class<? extends Annotation> findConsumedAnnotationType(T annotationConsumerInstance) {\n\t\tMethod method = findMethods(annotationConsumerInstance.getClass(), consumesAnnotation, BOTTOM_UP).get(0);\n\t\treturn getAnnotationType(method);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Class<? extends Annotation> getAnnotationType(Method method) {\n\t\tint annotationIndex = methodSignatures.stream() //\n\t\t\t\t.filter(signature -> signature.matches(method)) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.map(MethodSignature::annotationParameterIndex) //\n\t\t\t\t.orElse(0);\n\n\t\treturn (Class<? extends Annotation>) method.getParameterTypes()[annotationIndex];\n\t}\n\n\tprivate static <A extends Annotation> void initializeAnnotationConsumer(AnnotationConsumer<A> instance,\n\t\t\tA annotation) {\n\t\ttry {\n\t\t\tinstance.accept(annotation);\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new JUnitException(\"Failed to initialize AnnotationConsumer: \" + instance, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Annotation-consuming method signature.\n\t */\n\tprivate record MethodSignature(String methodName, int parameterCount, int annotationParameterIndex) {\n\n\t\tboolean matches(Method method) {\n\t\t\treturn method.getName().equals(methodName) //\n\t\t\t\t\t&& method.getParameterCount() == parameterCount //\n\t\t\t\t\t&& method.getParameterTypes()[annotationParameterIndex].isAnnotation();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/FieldContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.lang.reflect.Field;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\n\n/**\n * {@code FieldContext} encapsulates the <em>context</em> in which an\n * {@link Parameter @Parameter}-annotated {@link Field} is declared in a\n * {@link ParameterizedClass @ParameterizedClass}.\n *\n * @since 5.13\n * @see ParameterizedClass\n * @see Parameter\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface FieldContext extends AnnotatedElementContext {\n\n\t/**\n\t * {@return the field for this context; never {@code null}}\n\t */\n\tField getField();\n\n\t/**\n\t * {@return the index of the parameter}\n\t *\n\t * <p>This method returns {@value Parameter#UNSET_INDEX} for aggregator\n\t * fields and a value greater than or equal to zero for <em>indexed</em>\n\t * parameters.\n\t *\n\t * @see Parameter#value()\n\t */\n\tint getParameterIndex();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/ParameterDeclaration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code ParameterDeclaration} encapsulates the <em>declaration</em> of an\n * indexed {@code @ParameterizedClass} or {@code @ParameterizedTest} parameter.\n *\n * @since 5.13\n * @see ParameterDeclarations\n */\n@API(status = MAINTAINED, since = \"6.0.2\")\npublic interface ParameterDeclaration {\n\n\t/**\n\t * {@return the {@link AnnotatedElement} that declares the parameter; never\n\t * {@code null}}\n\t *\n\t * <p>This is either a {@link java.lang.reflect.Parameter} or a\n\t * {@link java.lang.reflect.Field}.\n\t */\n\tAnnotatedElement getAnnotatedElement();\n\n\t/**\n\t * {@return the type of the parameter; never {@code null}}\n\t */\n\tClass<?> getParameterType();\n\n\t/**\n\t * {@return the index of the parameter}\n\t */\n\tint getParameterIndex();\n\n\t/**\n\t * {@return the name of the parameter, if available; never {@code null} but\n\t * potentially empty}\n\t */\n\tOptional<String> getParameterName();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/ParameterDeclarations.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\n\n/**\n * {@code ParameterDeclarations} encapsulates the combined <em>declarations</em>\n * of all <em>indexed</em> parameters for a {@code @ParameterizedClass} or\n * {@code @ParameterizedTest}.\n *\n * <p>For a {@code @ParameterizedTest}, the parameter declarations are derived\n * from the method signature. For a {@code @ParameterizedClass}, they may be\n * derived from the constructor or\n * {@link org.junit.jupiter.params.Parameter @Parameter}-annotated fields.\n *\n * <p>Aggregators &mdash; parameters of type {@link ArgumentsAccessor ArgumentsAccessor}\n * or parameters annotated with\n * {@link org.junit.jupiter.params.aggregator.AggregateWith @AggregateWith} &mdash;\n * are <em>not</em> indexed and thus not included in the list of parameter\n * declarations.\n *\n * @since 5.13\n * @see ParameterDeclaration\n * @see org.junit.jupiter.params.ParameterizedClass\n * @see org.junit.jupiter.params.ParameterizedTest\n */\n@API(status = MAINTAINED, since = \"6.0.2\")\npublic interface ParameterDeclarations {\n\n\t/**\n\t * {@return all <em>indexed</em> parameter declarations, sorted by index;\n\t * never {@code null}, but potentially empty}\n\t */\n\tList<ParameterDeclaration> getAll();\n\n\t/**\n\t * {@return the first <em>indexed</em> parameter declaration, if available;\n\t * never {@code null}, but potentially empty}\n\t */\n\tOptional<ParameterDeclaration> getFirst();\n\n\t/**\n\t * {@return the <em>indexed</em> parameter declaration for the supplied\n\t * index, if available; never {@code null}, but potentially empty}\n\t */\n\tOptional<ParameterDeclaration> get(int parameterIndex);\n\n\t/**\n\t * {@return the source element of all parameter declarations}\n\t *\n\t * <p>For {@code @ParameterizedTest}, this always corresponds to the\n\t * parameterized test method. For {@code @ParameterizedClass}, this\n\t * corresponds to the parameterized test class constructor, if constructor\n\t * injection is used; or the test class itself, if field injection is used.\n\t */\n\tAnnotatedElement getSourceElement();\n\n\t/**\n\t * {@return a human-readable description of the source element}\n\t *\n\t * <p>This may, for example, be used in error messages.\n\t *\n\t * @see #getSourceElement()\n\t */\n\tString getSourceElementDescription();\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/ParameterInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\n\n/**\n * {@code ParameterInfo} is used to provide information about the current\n * invocation of a parameterized class or test.\n *\n * <p>Registered {@link Extension} implementations may retrieve the current\n * {@code ParameterInfo} instance by calling\n * {@link ExtensionContext#getStore(Namespace)} with {@link #NAMESPACE} and\n * {@link ExtensionContext.Store#get(Object, Class) Store.get(...)} with\n * {@link #KEY}. Alternatively, the {@link #get(ExtensionContext)} method may\n * be used to retrieve the {@code ParameterInfo} instance for the supplied\n * {@code ExtensionContext}. Extensions must not modify any entries in the\n * {@link ExtensionContext.Store Store} for {@link #NAMESPACE}.\n *\n * <p>When a {@link ParameterizedTest @ParameterizedTest} method is declared\n * inside a {@link ParameterizedClass @ParameterizedClass} or a\n * {@link Nested @Nested} {@link ParameterizedClass @ParameterizedClass} is\n * declared inside an enclosing {@link ParameterizedClass @ParameterizedClass},\n * there will be multiple {@code ParameterInfo} instances available on different\n * levels of the {@link ExtensionContext} hierarchy. In such cases, please use\n * {@link ExtensionContext#getParent()} to navigate to the right level before\n * retrieving the {@code ParameterInfo} instance from the\n * {@link ExtensionContext.Store Store}.\n *\n * @since 5.13\n * @see ParameterizedClass\n * @see ParameterizedTest\n * @deprecated Please use {@link org.junit.jupiter.params.ParameterInfo} instead\n */\n@Deprecated(since = \"5.14\", forRemoval = true)\n@API(status = DEPRECATED, since = \"5.14\")\npublic interface ParameterInfo extends org.junit.jupiter.params.ParameterInfo {\n\n\t/**\n\t * The {@link Namespace} for accessing the\n\t * {@link ExtensionContext.Store Store} for {@code ParameterInfo}.\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.params.ParameterInfo#NAMESPACE} instead\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\tNamespace NAMESPACE = Namespace.create(ParameterInfo.class);\n\n\t/**\n\t * The key for retrieving the {@code ParameterInfo} instance from the\n\t * {@link ExtensionContext.Store Store}.\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.params.ParameterInfo#KEY} instead\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\tObject KEY = ParameterInfo.class;\n\n\t/**\n\t * {@return the closest {@code ParameterInfo} instance for the supplied\n\t * {@code ExtensionContext}; potentially {@code null}}\n\t * @deprecated Please use\n\t * {@link org.junit.jupiter.params.ParameterInfo#get(ExtensionContext)}\n\t * instead\n\t */\n\t@Deprecated(since = \"5.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"5.14\")\n\tstatic @Nullable ParameterInfo get(ExtensionContext context) {\n\t\treturn context.getStore(NAMESPACE).get(KEY, ParameterInfo.class);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/ParameterNameAndArgument.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Named;\n\n/**\n * Customized parameter name and its associated argument value.\n *\n * <p>Although this class implements {@link Named} for technical reasons, it\n * serves a different purpose than {@link Named#of(String, Object)} and is only\n * used for internal display name processing.\n *\n * @since 6.0\n */\n@API(status = INTERNAL, since = \"6.0\")\npublic class ParameterNameAndArgument implements Named<@Nullable Object> {\n\n\tprivate final String name;\n\n\tprivate final @Nullable Object argument;\n\n\tpublic ParameterNameAndArgument(String name, @Nullable Object argument) {\n\t\tthis.name = name;\n\t\tthis.argument = argument;\n\t}\n\n\t/**\n\t * Get the customized name of the parameter.\n\t */\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t/**\n\t * Get the argument for the parameter.\n\t */\n\t@Override\n\tpublic @Nullable Object getPayload() {\n\t\treturn this.argument;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"ParameterNameAndArgument[name = %s, argument = %s]\".formatted(this.name, this.argument);\n\t}\n\n}\n"
  },
  {
    "path": "junit-jupiter-params/src/main/java/org/junit/jupiter/params/support/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support classes for building\n * {@linkplain org.junit.jupiter.params.provider.ArgumentsProvider providers}\n * and\n * {@linkplain org.junit.jupiter.params.converter.ArgumentConverter converters}\n * for arguments.\n */\n\n@NullMarked\npackage org.junit.jupiter.params.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-jupiter-params/src/main/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessor.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n@file:API(status = API.Status.STABLE, since = \"5.7\")\n\npackage org.junit.jupiter.params.aggregator\n\nimport org.apiguardian.api.API\n\n/**\n * Get the value of the argument at the given index as an instance of the\n * reified type.\n *\n * @param index the index of the argument to get; must be greater than or\n * equal to zero and less than {@link #size}\n * @return the value at the given index, potentially {@code null}\n * @since 5.3\n * @receiver[ArgumentsAccessor]\n * @see ArgumentsAccessor.get(Int, Class<T!>!)\n */\n@Suppress(\"EXTENSION_SHADOWED_BY_MEMBER\") // method is in fact not shadowed due to reified type\ninline fun <reified T : Any> ArgumentsAccessor.get(index: Int): T =\n    this.get(index, T::class.java)!!\n"
  },
  {
    "path": "junit-jupiter-params/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `jupiter-tests` project.\n"
  },
  {
    "path": "junit-jupiter-params/src/testFixtures/java/org/junit/jupiter/params/provider/RecordArguments.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport java.util.Arrays;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\npublic interface RecordArguments extends Arguments {\n\n\t@Override\n\tdefault @Nullable Object[] get() {\n\t\treturn Arrays.stream(getClass().getRecordComponents()) //\n\t\t\t\t.map(component -> ReflectionSupport.invokeMethod(component.getAccessor(), this)) //\n\t\t\t\t.toArray();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/junit-platform-commons.gradle.kts",
    "content": "import junitbuild.extensions.javaModuleName\n\nplugins {\n\tid(\"junitbuild.kotlin-library-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Platform Commons\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tcompileOnly(kotlin(\"stdlib\"))\n\tcompileOnly(kotlin(\"reflect\"))\n\tcompileOnly(libs.kotlinx.coroutines.core)\n\n\ttestFixturesImplementation(libs.assertj)\n}\n\njavadocConventions {\n\taddExtraModuleReferences(projects.junitPlatformEngine)\n}\n\neclipseConventions {\n\thideModularity = false\n}\n\ntasks.compileJava {\n\toptions.compilerArgs.add(\"-Xlint:-module\") // due to qualified exports\n\tval moduleName = javaModuleName\n\tval mainOutput = files(sourceSets.main.get().output)\n\toptions.compilerArgumentProviders.add(CommandLineArgumentProvider {\n\t\tlistOf(\"--patch-module\", \"${moduleName}=${mainOutput.asPath}\")\n\t})\n}\n\ntasks.jar {\n\tbundle {\n\t\tval importAPIGuardian: String by extra\n\t\tval importJSpecify: String by extra\n\t\tbnd(\"\"\"\n\t\t\tImport-Package: \\\n\t\t\t\t$importAPIGuardian,\\\n\t\t\t\t$importJSpecify,\\\n\t\t\t\tkotlin.*;resolution:=\"optional\",\\\n\t\t\t\tkotlinx.*;resolution:=\"optional\",\\\n\t\t\t\t*\n\t\t\"\"\")\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common APIs and support utilities for the JUnit Platform.\n *\n * @since 1.0\n */\nmodule org.junit.platform.commons {\n\trequires java.logging;\n\trequires java.management; // needed by RuntimeUtils to determine input arguments\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires static kotlin.stdlib;\n\trequires static kotlin.reflect;\n\trequires static kotlinx.coroutines.core;\n\n\texports org.junit.platform.commons;\n\texports org.junit.platform.commons.annotation;\n\texports org.junit.platform.commons.function;\n\texports org.junit.platform.commons.io;\n\texports org.junit.platform.commons.logging to\n\t\t\torg.junit.jupiter.api,\n\t\t\torg.junit.jupiter.engine,\n\t\t\torg.junit.jupiter.migrationsupport,\n\t\t\torg.junit.jupiter.params,\n\t\t\torg.junit.platform.console,\n\t\t\torg.junit.platform.engine,\n\t\t\torg.junit.platform.launcher,\n\t\t\torg.junit.platform.reporting,\n\t\t\torg.junit.platform.suite.api,\n\t\t\torg.junit.platform.suite.engine,\n\t\t\torg.junit.platform.testkit,\n\t\t\torg.junit.vintage.engine;\n\texports org.junit.platform.commons.support;\n\texports org.junit.platform.commons.support.conversion;\n\texports org.junit.platform.commons.support.scanning;\n\texports org.junit.platform.commons.util to\n\t\t\torg.junit.jupiter.api,\n\t\t\torg.junit.jupiter.engine,\n\t\t\torg.junit.jupiter.migrationsupport,\n\t\t\torg.junit.jupiter.params,\n\t\t\torg.junit.platform.console,\n\t\t\torg.junit.platform.engine,\n\t\t\torg.junit.platform.launcher,\n\t\t\torg.junit.platform.reporting,\n\t\t\torg.junit.platform.suite.api,\n\t\t\torg.junit.platform.suite.engine,\n\t\t\torg.junit.platform.testkit,\n\t\t\torg.junit.vintage.engine;\n\tuses org.junit.platform.commons.support.scanning.ClasspathScanner;\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/JUnitException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Base class for all {@link RuntimeException RuntimeExceptions} thrown\n * by JUnit.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.5\")\npublic class JUnitException extends RuntimeException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic JUnitException(@Nullable String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic JUnitException(@Nullable String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n\t/**\n\t * @since 1.13\n\t */\n\t@API(status = MAINTAINED, since = \"1.13\")\n\tprotected JUnitException(@Nullable String message, @Nullable Throwable cause, boolean enableSuppression,\n\t\t\tboolean writableStackTrace) {\n\t\tsuper(message, cause, enableSuppression, writableStackTrace);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/PreconditionViolationException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Thrown if a <em>precondition</em> is violated.\n *\n * @since 1.5\n */\n@API(status = STABLE, since = \"1.5\")\npublic class PreconditionViolationException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic PreconditionViolationException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic PreconditionViolationException(String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Contract.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.annotation;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * Specifies some aspects of the annotated method's behavior to be used by tools\n * for data flow analysis.\n *\n * @since 6.0\n * @see <a href=\"https://github.com/JetBrains/java-annotations/blob/2a28eab73042023559d2ec4cd00d6779213b6425/src/jvmMain/java/org/jetbrains/annotations/Contract.java\">org.jetbrains.annotations.Contract</a>\n * @see <a href=\"https://github.com/uber/NullAway/wiki/Configuration#custom-contract-annotations\">NullAway custom contract annotations</a>\n */\n@Documented\n@Target(ElementType.METHOD)\n@API(status = INTERNAL, since = \"6.0\")\npublic @interface Contract {\n\n\t/**\n\t * Contains the contract clauses describing causal relations between call\n\t * arguments and the returned value.\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/Testable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.annotation;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Testable} is used to signal to IDEs and tooling vendors that the\n * annotated or meta-annotated element is <em>testable</em>.\n *\n * <p>In this context, the term \"testable\" means that the annotated element\n * (typically a method, field, or class) can be executed by a {@code TestEngine}\n * as a test or test container on the JUnit Platform.\n *\n * <h2>Motivation for {@code @Testable}</h2>\n * <p>Some clients of the JUnit Platform, notably IDEs such as IntelliJ IDEA,\n * operate only on sources for test discovery. Thus, they cannot use the full\n * runtime discovery mechanism of the JUnit Platform since it relies on compiled\n * classes. {@code @Testable} therefore serves as an alternative mechanism for\n * IDEs to discover potential tests by analyzing the source code only.\n *\n * <h2>Common Use Cases</h2>\n * <p>{@code @Testable} will typically be used as a meta-annotation in order to\n * create a custom <em>composed annotation</em> that inherits the semantics\n * of {@code @Testable}. For example, the {@code @Test} and {@code @TestFactory}\n * annotations in JUnit Jupiter are meta-annotated with {@code @Testable}.\n * <p>For test programming models that do not rely on annotations, test classes,\n * test methods, or test fields may be directly annotated with {@code @Testable}.\n * Alternatively, if concrete test classes extend from a base class, the base class\n * can be annotated with {@code @Testable}. Note that {@code @Testable} is an\n * {@link Inherited @Inherited} annotation.\n *\n * <h2>Requirements for IDEs and Tooling Vendors</h2>\n * <ul>\n * <li>If a top-level class, static nested class, or inner class is not\n * annotated or meta-annotated with {@code @Testable} but contains a method or field\n * that is annotated or meta-annotated with {@code @Testable}, the class must\n * be considered to be a <em>testable</em> class.</li>\n * <li>If annotation hierarchies containing {@code @Testable} are present on\n * classes, methods, or fields in compiled byte code (e.g., in JARs in the user's\n * classpath), IDEs and tooling vendors must also take such annotation\n * hierarchies into consideration when performing annotation processing for\n * source code.</li>\n * </ul>\n *\n * <h2>Restrictions for TestEngine Implementations</h2>\n * <p>A {@code TestEngine} must <strong>not</strong> in any way perform\n * <em>discovery</em> based on the presence of {@code @Testable}. In terms of\n * discovery, the presence of {@code @Testable} should only be meaningful to\n * clients such as IDEs and tooling vendors. A {@code TestEngine} implementation\n * is therefore required to discover tests based on information specific to\n * that test engine (e.g., annotations specific to that test engine).\n *\n * <h2>Supported Target Elements</h2>\n * <p>{@code @Testable} may target any declaration\n * {@linkplain java.lang.annotation.ElementType element type}. This includes the\n * aforementioned method, field, and class elements.\n *\n * @since 1.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.0\")\npublic @interface Testable {\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/annotation/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common annotations for the JUnit Platform.\n */\n\n@NullMarked\npackage org.junit.platform.commons.annotation;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/function/Try.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.function;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.Callable;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * A container object which may either contain a nullable value in case of\n * <em>success</em> or an exception in case of <em>failure</em>.\n *\n * <p>Instances of this class should be returned by methods instead of\n * {@link Optional} when callers might want to report the exception via logging\n * or by wrapping it in another exception at a later point in time, e.g. via\n * {@link #getOrThrow(Function)}.\n *\n * <p>Moreover, it makes it particularly convenient to attach follow-up actions\n * should the {@code Try} have been successful (cf. {@link #andThen} and\n * {@link #andThenTry}) or fallback actions should it not have been (cf.\n * {@link #orElse} and {@link #orElseTry}).\n *\n * @since 1.4\n */\n@API(status = MAINTAINED, since = \"1.4\")\npublic abstract class Try<V extends @Nullable Object> {\n\n\t/**\n\t * Call the supplied {@link Callable} and return a successful {@code Try}\n\t * that contains the returned value or, in case an exception was thrown, a\n\t * failed {@code Try} that contains the exception.\n\t *\n\t * @param action the action to try; must not be {@code null}\n\t * @return a succeeded or failed {@code Try} depending on the outcome of the\n\t * supplied action; never {@code null}\n\t * @see #success(Object)\n\t * @see #failure(Exception)\n\t */\n\tpublic static <V extends @Nullable Object> Try<V> call(Callable<V> action) {\n\t\tcheckNotNull(action, \"action\");\n\t\treturn Try.of(() -> success(action.call()));\n\t}\n\n\t/**\n\t * Convert the supplied value into a succeeded {@code Try}.\n\t *\n\t * @param value the value to wrap; potentially {@code null}\n\t * @return a succeeded {@code Try} that contains the supplied value; never\n\t * {@code null}\n\t */\n\tpublic static <V extends @Nullable Object> Try<V> success(V value) {\n\t\treturn new Success<>(value);\n\t}\n\n\t/**\n\t * Convert the supplied exception into a failed {@code Try}.\n\t *\n\t * @param cause the exception to wrap; must not be {@code null}\n\t * @return a failed {@code Try} that contains the supplied value; never\n\t * {@code null}\n\t */\n\tpublic static <V> Try<V> failure(Exception cause) {\n\t\treturn new Failure<>(checkNotNull(cause, \"cause\"));\n\t}\n\n\t// Cannot use Preconditions due to package cycle\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tprivate static <T> T checkNotNull(@Nullable T input, String title) {\n\t\tif (input == null) {\n\t\t\t// Cannot use PreconditionViolationException due to package cycle\n\t\t\tthrow new JUnitException(title + \" must not be null\");\n\t\t}\n\t\treturn input;\n\t}\n\n\tprivate static <V> Try<V> of(Callable<Try<V>> action) {\n\t\ttry {\n\t\t\treturn action.call();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\treturn failure(e);\n\t\t}\n\t}\n\n\tprivate Try() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * If this {@code Try} is a success, apply the supplied transformer to its\n\t * value and return a new successful or failed {@code Try} depending on the\n\t * transformer's outcome; if this {@code Try} is a failure, do nothing.\n\t *\n\t * @param transformer the transformer to try; must not be {@code null}\n\t * @return a succeeded or failed {@code Try}; never {@code null}\n\t */\n\tpublic abstract <U> Try<U> andThenTry(Transformer<V, U> transformer);\n\n\t/**\n\t * If this {@code Try} is a success, apply the supplied function to its\n\t * value and return the resulting {@code Try}; if this {@code Try} is a\n\t * failure, do nothing.\n\t *\n\t * @param function the function to apply; must not be {@code null}\n\t * @return a succeeded or failed {@code Try}; never {@code null}\n\t */\n\tpublic abstract <U> Try<U> andThen(Function<V, Try<U>> function);\n\n\t/**\n\t * If this {@code Try} is a failure, call the supplied action and return a\n\t * new successful or failed {@code Try} depending on the action's outcome;\n\t * if this {@code Try} is a success, do nothing.\n\t *\n\t * @param action the action to try; must not be {@code null}\n\t * @return a succeeded or failed {@code Try}; never {@code null}\n\t */\n\tpublic abstract Try<V> orElseTry(Callable<V> action);\n\n\t/**\n\t * If this {@code Try} is a failure, call the supplied supplier and return\n\t * the resulting {@code Try}; if this {@code Try} is a success, do nothing.\n\t *\n\t * @param supplier the supplier to call; must not be {@code null}\n\t * @return a succeeded or failed {@code Try}; never {@code null}\n\t */\n\tpublic abstract Try<V> orElse(Supplier<Try<V>> supplier);\n\n\t/**\n\t * If this {@code Try} is a success, get the contained value; if this\n\t * {@code Try} is a failure, throw the contained exception.\n\t *\n\t * @return the contained value, if available; potentially {@code null}\n\t * @throws Exception if this {@code Try} is a failure\n\t */\n\tpublic abstract V get() throws Exception;\n\n\t/**\n\t * If this {@code Try} is a success, get the contained value; if this\n\t * {@code Try} is a failure, throw the contained exception.\n\t *\n\t * @return the contained value, if available\n\t * @throws Exception if this {@code Try} is a failure or the contained value\n\t * is {@code null}\n\t *\n\t * @since 6.0\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic final @NonNull V getNonNull() throws Exception {\n\t\tvar value = get();\n\t\treturn checkNotNull(value, \"value\");\n\t}\n\n\t/**\n\t * If this {@code Try} is a success, get the contained value; if this\n\t * {@code Try} is a failure, call the supplied {@link Function} with the\n\t * contained exception and throw the resulting {@link Exception}.\n\t *\n\t * @param exceptionTransformer the transformer to be called with the\n\t * contained exception, if available; must not be {@code null}\n\t * @return the contained value, if available\n\t * @throws E if this {@code Try} is a failure\n\t */\n\tpublic abstract <E extends Exception> V getOrThrow(Function<? super Exception, E> exceptionTransformer) throws E;\n\n\t/**\n\t * If this {@code Try} is a success, get the contained value; if this\n\t * {@code Try} is a failure, call the supplied {@link Function} with the\n\t * contained exception and throw the resulting {@link Exception}.\n\t *\n\t * @param exceptionTransformer the transformer to be called with the\n\t * contained exception, if available; must not be {@code null}\n\t * @return the contained value, if available and not {@code null}\n\t * @throws E if this {@code Try} is a failure or the contained value\n\t * is {@code null}\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic final <E extends Exception> @NonNull V getNonNullOrThrow(\n\t\t\tFunction<@Nullable Exception, E> exceptionTransformer) throws E {\n\t\tvar value = getOrThrow(exceptionTransformer);\n\t\tif (value == null) {\n\t\t\tthrow exceptionTransformer.apply(null);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * If this {@code Try} is a success, call the supplied {@link Consumer} with\n\t * the contained value; otherwise, do nothing.\n\t *\n\t * @param valueConsumer the consumer to be called with the contained value,\n\t * if available; must not be {@code null}\n\t * @return the same {@code Try} for method chaining\n\t */\n\tpublic abstract Try<V> ifSuccess(Consumer<V> valueConsumer);\n\n\t/**\n\t * If this {@code Try} is a failure, call the supplied {@link Consumer} with\n\t * the contained exception; otherwise, do nothing.\n\t *\n\t * @param causeConsumer the consumer to be called with the contained\n\t * exception, if available; must not be {@code null}\n\t * @return the same {@code Try} for method chaining\n\t */\n\tpublic abstract Try<V> ifFailure(Consumer<Exception> causeConsumer);\n\n\t/**\n\t * If this {@code Try} is a failure, return an empty {@link Optional}; if\n\t * this {@code Try} is a success, wrap the contained value using\n\t * {@link Optional#ofNullable(Object)}.\n\t *\n\t * @return an {@link Optional}; never {@code null} but potentially\n\t * <em>empty</em>\n\t */\n\tpublic abstract Optional<V> toOptional();\n\n\t/**\n\t * A transformer for values of type {@code S} to type {@code T}.\n\t *\n\t * <p>The {@code Transformer} interface is similar to {@link Function},\n\t * except that a {@code Transformer} may throw an exception.\n\t */\n\t@FunctionalInterface\n\tpublic interface Transformer<S extends @Nullable Object, T extends @Nullable Object> {\n\n\t\t/**\n\t\t * Apply this transformer to the supplied value.\n\t\t *\n\t\t * @throws Exception if the transformation fails\n\t\t */\n\t\tT apply(S value) throws Exception;\n\n\t}\n\n\tprivate static final class Success<V extends @Nullable Object> extends Try<V> {\n\n\t\tprivate final V value;\n\n\t\tSuccess(V value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Override\n\t\tpublic <U> Try<U> andThenTry(Transformer<V, U> transformer) {\n\t\t\tcheckNotNull(transformer, \"transformer\");\n\t\t\treturn Try.call(() -> transformer.apply(this.value));\n\t\t}\n\n\t\t@Override\n\t\tpublic <U> Try<U> andThen(Function<V, Try<U>> function) {\n\t\t\tcheckNotNull(function, \"function\");\n\t\t\treturn Try.of(() -> function.apply(this.value));\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> orElseTry(Callable<V> action) {\n\t\t\t// don't call action because this Try is a success\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> orElse(Supplier<Try<V>> supplier) {\n\t\t\t// don't call supplier because this Try is a success\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic V get() {\n\t\t\treturn this.value;\n\t\t}\n\n\t\t@Override\n\t\tpublic <E extends Exception> V getOrThrow(Function<? super Exception, E> exceptionTransformer) {\n\t\t\t// don't call exceptionTransformer because this Try is a success\n\t\t\treturn this.value;\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> ifSuccess(Consumer<V> valueConsumer) {\n\t\t\tcheckNotNull(valueConsumer, \"valueConsumer\");\n\t\t\tvalueConsumer.accept(this.value);\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> ifFailure(Consumer<Exception> causeConsumer) {\n\t\t\t// don't call causeConsumer because this Try was a success\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<V> toOptional() {\n\t\t\treturn Optional.ofNullable(this.value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object that) {\n\t\t\tif (this == that) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (that == null || this.getClass() != that.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Objects.equals(this.value, ((Success<?>) that).value);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(value);\n\t\t}\n\t}\n\n\tprivate static final class Failure<V extends @Nullable Object> extends Try<V> {\n\n\t\tprivate final Exception cause;\n\n\t\tFailure(Exception cause) {\n\t\t\tthis.cause = cause;\n\t\t}\n\n\t\t@Override\n\t\tpublic <U> Try<U> andThenTry(Transformer<V, U> transformer) {\n\t\t\t// don't call transformer because this Try is a failure\n\t\t\treturn uncheckedCast();\n\t\t}\n\n\t\t@Override\n\t\tpublic <U> Try<U> andThen(Function<V, Try<U>> function) {\n\t\t\t// don't call function because this Try is a failure\n\t\t\treturn uncheckedCast();\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <U> Try<U> uncheckedCast() {\n\t\t\treturn (Try<U>) this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> orElseTry(Callable<V> action) {\n\t\t\tcheckNotNull(action, \"action\");\n\t\t\treturn Try.call(action);\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> orElse(Supplier<Try<V>> supplier) {\n\t\t\tcheckNotNull(supplier, \"supplier\");\n\t\t\treturn Try.of(supplier::get);\n\t\t}\n\n\t\t@Contract(\" -> fail\")\n\t\t@Override\n\t\tpublic V get() throws Exception {\n\t\t\tthrow this.cause;\n\t\t}\n\n\t\t@Contract(\"_ -> fail\")\n\t\t@Override\n\t\tpublic <E extends Exception> V getOrThrow(Function<? super Exception, E> exceptionTransformer) throws E {\n\t\t\tcheckNotNull(exceptionTransformer, \"exceptionTransformer\");\n\t\t\tthrow exceptionTransformer.apply(this.cause);\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> ifSuccess(Consumer<V> valueConsumer) {\n\t\t\t// don't call valueConsumer because this Try is a failure\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Try<V> ifFailure(Consumer<Exception> causeConsumer) {\n\t\t\tcheckNotNull(causeConsumer, \"causeConsumer\");\n\t\t\tcauseConsumer.accept(this.cause);\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<V> toOptional() {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object that) {\n\t\t\tif (this == that) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (that == null || this.getClass() != that.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn Objects.equals(this.cause, ((Failure<?>) that).cause);\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(cause);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/function/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Functional interfaces and support classes.\n */\n\n@NullMarked\npackage org.junit.platform.commons.function;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/io/DefaultResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.io;\n\nimport java.net.URI;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Default implementation of {@link Resource}.\n *\n * @since 1.14\n */\nrecord DefaultResource(String name, URI uri) implements Resource {\n\n\tDefaultResource {\n\t\tcheckNotNull(name, \"name\");\n\t\tcheckNotNull(uri, \"uri\");\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t@Override\n\tpublic URI getUri() {\n\t\treturn this.uri;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == this) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof org.junit.platform.commons.io.Resource that) {\n\t\t\treturn this.name.equals(that.getName()) //\n\t\t\t\t\t&& this.uri.equals(that.getUri());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(name, uri);\n\t}\n\n\t// Cannot use Preconditions due to package cycle\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tprivate static <T> void checkNotNull(@Nullable T input, String title) {\n\t\tif (input == null) {\n\t\t\tthrow new PreconditionViolationException(title + \" must not be null\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/io/Resource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.io;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code Resource} represents a resource on the classpath.\n *\n * <p><strong>WARNING</strong>: a {@code Resource} must provide correct\n * {@link Object#equals(Object) equals} and {@link Object#hashCode() hashCode}\n * implementations since a {@code Resource} may potentially be stored in a\n * collection or map.\n *\n * @since 1.14\n * @see org.junit.platform.commons.support.ResourceSupport\n */\n@API(status = MAINTAINED, since = \"1.14\")\npublic interface Resource {\n\n\t/**\n\t * Create a new {@link Resource} with the given name and URI.\n\t *\n\t * @param name the name of the resource; never {@code null}\n\t * @param uri the URI of the resource; never {@code null}\n\t * @return a new {@code Resource}\n\t * @since 1.14\n\t */\n\tstatic Resource of(String name, URI uri) {\n\t\treturn new DefaultResource(name, uri);\n\t}\n\n\t/**\n\t * Get the name of this resource.\n\t *\n\t * <p>The resource name is a {@code /}-separated path. The path is relative\n\t * to the classpath root in which the resource is located.\n\t *\n\t * @return the resource name; never {@code null}\n\t */\n\tString getName();\n\n\t/**\n\t * Get the URI of this resource.\n\t *\n\t * @return the URI of the resource; never {@code null}\n\t */\n\tURI getUri();\n\n\t/**\n\t * Get an {@link InputStream} for reading this resource.\n\t *\n\t * <p>The default implementation delegates to {@link java.net.URL#openStream()}\n\t * for this resource's {@link #getUri() URI}.\n\t *\n\t * @return an input stream for this resource; never {@code null}\n\t * @throws IOException if an I/O exception occurs\n\t */\n\tdefault InputStream getInputStream() throws IOException {\n\t\treturn getUri().toURL().openStream();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/io/ResourceFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.io;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Resource filter used by reflection and classpath scanning support.\n *\n * @since 1.14\n * @see Resource\n */\n@API(status = MAINTAINED, since = \"1.14\")\npublic class ResourceFilter {\n\n\t/**\n\t * Create a {@link ResourceFilter} instance from a predicate.\n\t *\n\t * @param resourcePredicate the resource predicate; never {@code null}\n\t * @return an instance of {@code ResourceFilter}; never {@code null}\n\t */\n\tpublic static ResourceFilter of(Predicate<? super Resource> resourcePredicate) {\n\t\treturn new ResourceFilter(checkNotNull(resourcePredicate, \"resourcePredicate\"));\n\t}\n\n\tprivate final Predicate<? super Resource> predicate;\n\n\tprivate ResourceFilter(Predicate<? super Resource> predicate) {\n\t\tthis.predicate = predicate;\n\t}\n\n\t/**\n\t * Test whether the given resource matches this filter.\n\t *\n\t * @param resource the resource to test; never {@code null}\n\t * @return {@code true} if the resource matches this filter, otherwise\n\t * {@code false}\n\t */\n\tpublic boolean match(Resource resource) {\n\t\treturn predicate.test(checkNotNull(resource, \"resource\"));\n\t}\n\n\t// Cannot use Preconditions due to package cycle\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tprivate static <T> T checkNotNull(@Nullable T input, String title) {\n\t\tif (input == null) {\n\t\t\tthrow new PreconditionViolationException(title + \" must not be null\");\n\t\t}\n\t\treturn input;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/io/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * IO-related interfaces and support classes\n */\n\n@NullMarked\npackage org.junit.platform.commons.io;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LogRecordListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.logging;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code LogRecordListener} is only intended for testing purposes within\n * JUnit's own test suite.\n *\n * @since 1.1\n */\n@API(status = INTERNAL, since = \"1.1\")\npublic class LogRecordListener {\n\n\t// capture log records by thread to support parallel test execution\n\tprivate final ThreadLocal<List<LogRecord>> logRecords = ThreadLocal.withInitial(ArrayList::new);\n\n\t/**\n\t * Inform the listener of a {@link LogRecord} that was submitted to JUL for\n\t * processing.\n\t */\n\tpublic void logRecordSubmitted(LogRecord logRecord) {\n\t\tthis.logRecords.get().add(logRecord);\n\t}\n\n\t/**\n\t * Get a stream of {@link LogRecord log records} that have been\n\t * {@linkplain #logRecordSubmitted submitted} to this listener by the\n\t * current thread.\n\t *\n\t * <p>As stated in the Javadoc for {@code LogRecord}, a submitted\n\t * {@code LogRecord} should not be updated by the client application. Thus,\n\t * the {@code LogRecords} in the returned stream should only be inspected for\n\t * testing purposes and not modified in any way.\n\t *\n\t * @see #stream(Level)\n\t * @see #stream(Class)\n\t * @see #stream(Class, Level)\n\t */\n\tpublic Stream<LogRecord> stream() {\n\t\treturn this.logRecords.get().stream();\n\t}\n\n\t/**\n\t * Get a stream of {@link LogRecord log records} that have been\n\t * {@linkplain #logRecordSubmitted submitted} to this listener by the current\n\t * thread at the given log level.\n\t *\n\t * <p>As stated in the Javadoc for {@code LogRecord}, a submitted\n\t * {@code LogRecord} should not be updated by the client application. Thus,\n\t * the {@code LogRecords} in the returned stream should only be inspected for\n\t * testing purposes and not modified in any way.\n\t *\n\t * @param level the log level for which to get the log records; never {@code null}\n\t * @since 1.4\n\t * @see #stream()\n\t * @see #stream(Class)\n\t * @see #stream(Class, Level)\n\t */\n\t@SuppressWarnings({ \"ConstantValue\", \"ReferenceEquality\" })\n\tpublic Stream<LogRecord> stream(Level level) {\n\t\t// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here\n\t\t// since that would introduce a package cycle.\n\t\tif (level == null) {\n\t\t\tthrow new JUnitException(\"Level must not be null\");\n\t\t}\n\n\t\treturn stream().filter(logRecord -> logRecord.getLevel() == level);\n\t}\n\n\t/**\n\t * Get a stream of {@link LogRecord log records} that have been\n\t * {@linkplain #logRecordSubmitted submitted} to this listener by the current\n\t * thread for the logger name equal to the name of the given class.\n\t *\n\t * <p>As stated in the Javadoc for {@code LogRecord}, a submitted\n\t * {@code LogRecord} should not be updated by the client application. Thus,\n\t * the {@code LogRecords} in the returned stream should only be inspected for\n\t * testing purposes and not modified in any way.\n\t *\n\t * @param clazz the class for which to get the log records; never {@code null}\n\t * @see #stream()\n\t * @see #stream(Level)\n\t * @see #stream(Class, Level)\n\t */\n\t@SuppressWarnings(\"ConstantValue\")\n\tpublic Stream<LogRecord> stream(Class<?> clazz) {\n\t\t// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here\n\t\t// since that would introduce a package cycle.\n\t\tif (clazz == null) {\n\t\t\tthrow new JUnitException(\"Class must not be null\");\n\t\t}\n\n\t\treturn stream().filter(logRecord -> logRecord.getLoggerName().equals(clazz.getName()));\n\t}\n\n\t/**\n\t * Get a stream of {@link LogRecord log records} that have been\n\t * {@linkplain #logRecordSubmitted submitted} to this listener by the current\n\t * thread for the logger name equal to the name of the given class at the given\n\t * log level.\n\t *\n\t * <p>As stated in the Javadoc for {@code LogRecord}, a submitted\n\t * {@code LogRecord} should not be updated by the client application. Thus,\n\t * the {@code LogRecords} in the returned stream should only be inspected for\n\t * testing purposes and not modified in any way.\n\t *\n\t * @param clazz the class for which to get the log records; never {@code null}\n\t * @param level the log level for which to get the log records; never {@code null}\n\t * @see #stream()\n\t * @see #stream(Level)\n\t * @see #stream(Class)\n\t */\n\t@SuppressWarnings({ \"ConstantValue\", \"ReferenceEquality\" })\n\tpublic Stream<LogRecord> stream(Class<?> clazz, Level level) {\n\t\t// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here\n\t\t// since that would introduce a package cycle.\n\t\tif (level == null) {\n\t\t\tthrow new JUnitException(\"Level must not be null\");\n\t\t}\n\n\t\treturn stream(clazz).filter(logRecord -> logRecord.getLevel() == level);\n\t}\n\n\t/**\n\t * Clear all existing {@link LogRecord log records} that have been\n\t * {@linkplain #logRecordSubmitted submitted} to this listener by the\n\t * current thread.\n\t */\n\tpublic void clear() {\n\t\tthis.logRecords.get().clear();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/logging/Logger.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.logging;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * The {@code Logger} API serves as a simple logging facade for\n * {@code java.util.logging} (JUL).\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic interface Logger {\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at error level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#SEVERE} in JUL.\n\t */\n\tvoid error(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at error level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#SEVERE} in JUL.\n\t */\n\tvoid error(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at warning level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#WARNING} in JUL.\n\t */\n\tvoid warn(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at warning level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#WARNING} in JUL.\n\t */\n\tvoid warn(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at info level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#INFO} in JUL.\n\t */\n\tvoid info(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at info level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#INFO} in JUL.\n\t */\n\tvoid info(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at config level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#CONFIG} in JUL.\n\t */\n\tvoid config(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at config level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#CONFIG} in JUL.\n\t */\n\tvoid config(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at debug level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#FINE} in JUL.\n\t */\n\tvoid debug(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at debug level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#FINE} in JUL.\n\t */\n\tvoid debug(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the message from the provided {@code messageSupplier} at trace level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#FINER} in JUL.\n\t */\n\tvoid trace(Supplier<String> messageSupplier);\n\n\t/**\n\t * Log the provided {@code Throwable} and message from the provided\n\t * {@code messageSupplier} at trace level.\n\t *\n\t * <p>Maps to {@link java.util.logging.Level#FINER} in JUL.\n\t */\n\tvoid trace(@Nullable Throwable throwable, Supplier<String> messageSupplier);\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/logging/LoggerFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.logging;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Factory for the {@link Logger} facade for JUL.\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class LoggerFactory {\n\n\tprivate LoggerFactory() {\n\t\t/* no-op */\n\t}\n\n\tprivate static final Set<LogRecordListener> listeners = ConcurrentHashMap.newKeySet();\n\n\t/**\n\t * Get a {@link Logger} for the specified class.\n\t *\n\t * @param clazz the class for which to get the logger; never {@code null}\n\t * @return the logger\n\t */\n\t@SuppressWarnings(\"ConstantValue\")\n\tpublic static Logger getLogger(Class<?> clazz) {\n\t\t// NOTE: we cannot use org.junit.platform.commons.util.Preconditions here\n\t\t// since that would introduce a package cycle.\n\t\tif (clazz == null) {\n\t\t\tthrow new JUnitException(\"Class must not be null\");\n\t\t}\n\n\t\treturn new DelegatingLogger(clazz.getName());\n\t}\n\n\t/**\n\t * Add the supplied {@link LogRecordListener} to the set of registered\n\t * listeners.\n\t */\n\tpublic static void addListener(LogRecordListener listener) {\n\t\tlisteners.add(listener);\n\t}\n\n\t/**\n\t * Remove the supplied {@link LogRecordListener} from the set of registered\n\t * listeners.\n\t */\n\tpublic static void removeListener(LogRecordListener listener) {\n\t\tlisteners.remove(listener);\n\t}\n\n\tprivate static final class DelegatingLogger implements Logger {\n\n\t\tprivate static final String FQCN = DelegatingLogger.class.getName();\n\n\t\tprivate final String name;\n\n\t\tprivate final java.util.logging.Logger julLogger;\n\n\t\tDelegatingLogger(String name) {\n\t\t\tthis.name = name;\n\t\t\tthis.julLogger = java.util.logging.Logger.getLogger(this.name);\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.SEVERE, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void error(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.SEVERE, throwable, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void warn(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.WARNING, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void warn(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.WARNING, throwable, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void info(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.INFO, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void info(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.INFO, throwable, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void config(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.CONFIG, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void config(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.CONFIG, throwable, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void debug(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.FINE, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void debug(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.FINE, throwable, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void trace(Supplier<String> messageSupplier) {\n\t\t\tlog(Level.FINER, null, messageSupplier);\n\t\t}\n\n\t\t@Override\n\t\tpublic void trace(@Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tlog(Level.FINER, throwable, messageSupplier);\n\t\t}\n\n\t\tprivate void log(Level level, @Nullable Throwable throwable, Supplier<String> messageSupplier) {\n\t\t\tboolean loggable = this.julLogger.isLoggable(level);\n\t\t\tif (loggable || !listeners.isEmpty()) {\n\t\t\t\tLogRecord logRecord = createLogRecord(level, throwable, messageSupplier.get());\n\t\t\t\tif (loggable) {\n\t\t\t\t\tthis.julLogger.log(logRecord);\n\t\t\t\t}\n\t\t\t\tlisteners.forEach(listener -> listener.logRecordSubmitted(logRecord));\n\t\t\t}\n\t\t}\n\n\t\tprivate LogRecord createLogRecord(Level level, @Nullable Throwable throwable, String message) {\n\t\t\tString sourceClassName = null;\n\t\t\tString sourceMethodName = null;\n\t\t\tboolean found = false;\n\t\t\tfor (StackTraceElement element : new Throwable().getStackTrace()) {\n\t\t\t\tString className = element.getClassName();\n\t\t\t\tif (FQCN.equals(className)) {\n\t\t\t\t\tfound = true;\n\t\t\t\t}\n\t\t\t\telse if (found) {\n\t\t\t\t\tsourceClassName = className;\n\t\t\t\t\tsourceMethodName = element.getMethodName();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tLogRecord logRecord = new LogRecord(level, message);\n\t\t\tlogRecord.setLoggerName(this.name);\n\t\t\tlogRecord.setThrown(throwable);\n\t\t\tlogRecord.setSourceClassName(sourceClassName);\n\t\t\tlogRecord.setSourceMethodName(sourceMethodName);\n\t\t\tlogRecord.setResourceBundleName(this.julLogger.getResourceBundleName());\n\t\t\tlogRecord.setResourceBundle(this.julLogger.getResourceBundle());\n\n\t\t\treturn logRecord;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/logging/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal <em>logging</em> package.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These classes are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n */\n\n@NullMarked\npackage org.junit.platform.commons.logging;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common APIs and support utilities for the JUnit Platform.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>Any API annotated with {@code @API(status = INTERNAL)} is intended solely\n * for usage within the JUnit framework itself. <strong>Any usage of internal\n * APIs by external parties is not supported!</strong>\n */\n\n@NullMarked\npackage org.junit.platform.commons;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/AnnotationSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\nimport org.junit.platform.commons.util.AnnotationUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code AnnotationSupport} provides static utility methods for common tasks\n * regarding annotations &mdash; for example, checking if a class, method, or\n * field is annotated with a particular annotation; finding annotations on a\n * given class, method, or field; finding fields or methods annotated with\n * a particular annotation, etc.\n *\n * <p>{@link org.junit.platform.engine.TestEngine TestEngine} and extension\n * authors are encouraged to use these supported methods in order to align with\n * the behavior of the JUnit Platform.\n *\n * @since 1.0\n * @see ClassSupport\n * @see ModifierSupport\n * @see ReflectionSupport\n * @see ResourceSupport\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic final class AnnotationSupport {\n\n\tprivate AnnotationSupport() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the supplied optional\n\t * {@code element}.\n\t *\n\t * <p><strong>Note:</strong> This method does not find repeatable annotations.\n\t * To check for repeatable annotations, use {@link #findRepeatableAnnotations(Optional, Class)}\n\t * and verify that the returned list is not empty.\n\t *\n\t * @param element an {@link Optional} containing the element on which to\n\t * search for the annotation; may be {@code null} or <em>empty</em>\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @since 1.3\n\t * @see #isAnnotated(AnnotatedElement, Class)\n\t * @see #findAnnotation(Optional, Class)\n\t * @see #findRepeatableAnnotations(Optional, Class)\n\t */\n\t@API(status = MAINTAINED, since = \"1.3\")\n\t@Contract(\"null, _ -> false\")\n\t@SuppressWarnings(\"NullableOptional\")\n\tpublic static boolean isAnnotated(@Nullable Optional<? extends AnnotatedElement> element,\n\t\t\tClass<? extends Annotation> annotationType) {\n\n\t\treturn AnnotationUtils.isAnnotated(element, annotationType);\n\t}\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the supplied\n\t * {@code element}.\n\t *\n\t * <p><strong>Note:</strong> This method does not find repeatable annotations.\n\t * To check for repeatable annotations, use {@link #findRepeatableAnnotations(AnnotatedElement, Class)}\n\t * and verify that the returned list is not empty.\n\t *\n\t * @param element the element on which to search for the annotation; may be\n\t * {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @see #isAnnotated(Optional, Class)\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t * @see #findRepeatableAnnotations(AnnotatedElement, Class)\n\t */\n\t@Contract(\"null, _ -> false\")\n\tpublic static boolean isAnnotated(@Nullable AnnotatedElement element, Class<? extends Annotation> annotationType) {\n\t\treturn AnnotationUtils.isAnnotated(element, annotationType);\n\t}\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>present</em> or <em>meta-present</em> on the supplied optional\n\t * {@code element}.\n\t *\n\t * @param <A> the annotation type\n\t * @param element an {@link Optional} containing the element on which to\n\t * search for the annotation; may be {@code null} or <em>empty</em>\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @since 1.1\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t */\n\t@API(status = MAINTAINED, since = \"1.1\")\n\t@SuppressWarnings(\"NullableOptional\")\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(\n\t\t\t@Nullable Optional<? extends AnnotatedElement> element, Class<A> annotationType) {\n\n\t\treturn AnnotationUtils.findAnnotation(element, annotationType);\n\t}\n\n\t/**\n\t * Find the first annotation of {@code annotationType} that is either\n\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly\n\t * present</em> on the supplied {@code element}.\n\t *\n\t * <p>If the element is a class and the annotation is neither <em>directly\n\t * present</em> nor <em>meta-present</em> on the class, this method will\n\t * additionally search on interfaces implemented by the class before\n\t * finding an annotation that is <em>indirectly present</em> on the class.\n\t *\n\t * @param <A> the annotation type\n\t * @param element the element on which to search for the annotation; may be\n\t * {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t */\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(@Nullable AnnotatedElement element,\n\t\t\tClass<A> annotationType) {\n\t\treturn AnnotationUtils.findAnnotation(element, annotationType);\n\t}\n\n\t/**\n\t * Find the first annotation of the specified type that is either\n\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly\n\t * present</em> on the supplied class.\n\t *\n\t * <p>If the annotation is neither <em>directly present</em> nor <em>meta-present</em>\n\t * on the class, this method will additionally search on interfaces implemented\n\t * by the class before searching for an annotation that is <em>indirectly present</em>\n\t * on the class (i.e., within the class inheritance hierarchy).\n\t *\n\t * <p>If the annotation still has not been found, this method will optionally\n\t * search recursively through the enclosing class hierarchy if\n\t * {@link SearchOption#INCLUDE_ENCLOSING_CLASSES} is specified.\n\t *\n\t * <p>If {@link SearchOption#DEFAULT} is specified, this method has the same\n\t * semantics as {@link #findAnnotation(AnnotatedElement, Class)}.\n\t *\n\t * @param <A> the annotation type\n\t * @param clazz the class on which to search for the annotation; may be {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param searchOption the {@code SearchOption} to use; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @since 1.8\n\t * @see SearchOption\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t * @deprecated Use {@link #findAnnotation(AnnotatedElement, Class)}\n\t * (for {@code SearchOption.DEFAULT}) or\n\t * {@link #findAnnotation(Class, Class, List)} (for\n\t * {@code SearchOption.INCLUDE_ENCLOSING_CLASSES}) instead\n\t */\n\t@Deprecated(since = \"1.12\")\n\t@API(status = DEPRECATED, since = \"1.12\")\n\t@SuppressWarnings(\"deprecation\")\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(@Nullable Class<?> clazz, Class<A> annotationType,\n\t\t\tSearchOption searchOption) {\n\n\t\tPreconditions.notNull(searchOption, \"SearchOption must not be null\");\n\n\t\treturn AnnotationUtils.findAnnotation(clazz, annotationType,\n\t\t\tsearchOption == SearchOption.INCLUDE_ENCLOSING_CLASSES);\n\t}\n\n\t/**\n\t * Find the first annotation of the specified type that is either\n\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly\n\t * present</em> on the supplied class.\n\t *\n\t * <p>If the annotation is neither <em>directly present</em> nor <em>meta-present</em>\n\t * on the class, this method will additionally search on interfaces implemented\n\t * by the class before searching for an annotation that is <em>indirectly present</em>\n\t * on the class (i.e., within the class inheritance hierarchy).\n\t *\n\t * <p>If the annotation still has not been found, this method will optionally\n\t * search recursively through the supplied enclosing instance types, starting\n\t * at the innermost enclosing class (the last one in the supplied list of\n\t * {@code enclosingInstanceTypes}).\n\t *\n\t * @implNote The classes supplied as {@code enclosingInstanceTypes} may\n\t * differ from the classes returned from invocations of\n\t * {@link Class#getEnclosingClass()} &mdash; for example, when a nested test\n\t * class is inherited from a superclass.\n\t *\n\t * @param <A> the annotation type\n\t * @param clazz the class on which to search for the annotation; may be {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param enclosingInstanceTypes the runtime types of the enclosing\n\t * instances for the class, ordered from outermost to innermost,\n\t * excluding {@code clazz}; never {@code null}\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty if {@code clazz} is not an inner class\n\t * @since 1.12\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(@Nullable Class<?> clazz, Class<A> annotationType,\n\t\t\tList<Class<?>> enclosingInstanceTypes) {\n\n\t\tPreconditions.notNull(enclosingInstanceTypes, \"enclosingInstanceTypes must not be null\");\n\n\t\tOptional<A> annotation = findAnnotation(clazz, annotationType);\n\t\tif (annotation.isEmpty()) {\n\t\t\tListIterator<Class<?>> iterator = enclosingInstanceTypes.listIterator(enclosingInstanceTypes.size());\n\t\t\twhile (iterator.hasPrevious()) {\n\t\t\t\tannotation = findAnnotation(iterator.previous(), annotationType);\n\t\t\t\tif (annotation.isPresent()) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn annotation;\n\t}\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of the\n\t * supplied {@code annotationType} that are either <em>present</em>,\n\t * <em>indirectly present</em>, or <em>meta-present</em> on the supplied\n\t * optional {@code element}.\n\t *\n\t * <p>See {@link #findRepeatableAnnotations(AnnotatedElement, Class)} for\n\t * details of the algorithm used.\n\t *\n\t * @param <A> the annotation type\n\t * @param element an {@link Optional} containing the element on which to\n\t * search for the annotation; may be {@code null} or <em>empty</em>\n\t * @param annotationType the repeatable annotation type to search for; never {@code null}\n\t * @return an immutable list of all such annotations found; never {@code null}\n\t * @since 1.5\n\t * @see java.lang.annotation.Repeatable\n\t * @see java.lang.annotation.Inherited\n\t * @see #findRepeatableAnnotations(AnnotatedElement, Class)\n\t */\n\t@API(status = MAINTAINED, since = \"1.5\")\n\t@SuppressWarnings(\"NullableOptional\")\n\tpublic static <A extends Annotation> List<A> findRepeatableAnnotations(\n\t\t\t@Nullable Optional<? extends AnnotatedElement> element, Class<A> annotationType) {\n\n\t\treturn AnnotationUtils.findRepeatableAnnotations(element, annotationType);\n\t}\n\n\t/**\n\t * Find all <em>repeatable</em> {@linkplain Annotation annotations} of the\n\t * supplied {@code annotationType} that are either <em>present</em>,\n\t * <em>indirectly present</em>, or <em>meta-present</em> on the supplied\n\t * {@link AnnotatedElement}.\n\t *\n\t * <p>This method extends the functionality of\n\t * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)}\n\t * with additional support for meta-annotations.\n\t *\n\t * <p>In addition, if the element is a class and the repeatable annotation\n\t * is {@link java.lang.annotation.Inherited @Inherited}, this method will\n\t * search on superclasses first in order to support top-down semantics.\n\t * The result is that this algorithm finds repeatable annotations that\n\t * would be <em>shadowed</em> and therefore not visible according to Java's\n\t * standard semantics for inherited, repeatable annotations, but most\n\t * developers will naturally assume that all repeatable annotations in JUnit\n\t * are discovered regardless of whether they are declared stand-alone, in a\n\t * container, or as a meta-annotation (e.g., multiple declarations of\n\t * {@code @ExtendWith} within a test class hierarchy).\n\t *\n\t * <p>If the element is a class and the repeatable annotation is not\n\t * discovered within the class hierarchy, this method will additionally\n\t * search on interfaces implemented by each class in the hierarchy.\n\t *\n\t * <p>If the supplied {@code element} is {@code null}, this method returns\n\t * an empty list.\n\t *\n\t * <p>The search algorithm will also find repeatable annotations used as\n\t * meta-annotations on other repeatable annotations.\n\t *\n\t * @param <A> the annotation type\n\t * @param element the element to search on; may be {@code null}\n\t * @param annotationType the repeatable annotation type to search for; never {@code null}\n\t * @return an immutable list of all such annotations found; never {@code null}\n\t * @see java.lang.annotation.Repeatable\n\t * @see java.lang.annotation.Inherited\n\t */\n\tpublic static <A extends Annotation> List<A> findRepeatableAnnotations(@Nullable AnnotatedElement element,\n\t\t\tClass<A> annotationType) {\n\n\t\treturn AnnotationUtils.findRepeatableAnnotations(element, annotationType);\n\t}\n\n\t/**\n\t * Find all {@code public} {@linkplain Field fields} of the supplied class\n\t * or interface that are declared to be of the specified {@code fieldType}\n\t * and are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType}.\n\t *\n\t * <p>Consult the Javadoc for {@link Class#getFields()} for details on\n\t * inheritance and ordering.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param fieldType the declared type of fields to find; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return the list of all such fields found; neither {@code null} nor mutable\n\t * @see Class#getFields()\n\t * @see Field#getType()\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\tpublic static List<Field> findPublicAnnotatedFields(Class<?> clazz, Class<?> fieldType,\n\t\t\tClass<? extends Annotation> annotationType) {\n\n\t\treturn AnnotationUtils.findPublicAnnotatedFields(clazz, fieldType, annotationType);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Field fields} of the supplied class or\n\t * interface that are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType}, using top-down search semantics within the type\n\t * hierarchy.\n\t *\n\t * <p>Fields declared in the same class or interface will be ordered using\n\t * an algorithm that is deterministic but intentionally nonobvious.\n\t *\n\t * <p>The results will not contain fields that are <em>hidden</em> or\n\t * {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return the list of all such fields found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see Class#getDeclaredFields()\n\t * @see #findPublicAnnotatedFields(Class, Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType) {\n\t\treturn findAnnotatedFields(clazz, annotationType, field -> true);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Field fields} of the supplied class or\n\t * interface that are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType} and match the specified {@code predicate}, using\n\t * top-down search semantics within the type hierarchy.\n\t *\n\t * <p>Fields declared in the same class or interface will be ordered using\n\t * an algorithm that is deterministic but intentionally nonobvious.\n\t *\n\t * <p>The results will not contain fields that are <em>hidden</em> or\n\t * {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param predicate the field filter; never {@code null}\n\t * @return the list of all such fields found; neither {@code null} nor mutable\n\t * @since 1.10\n\t * @see Class#getDeclaredFields()\n\t * @see #findPublicAnnotatedFields(Class, Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tPredicate<Field> predicate) {\n\t\treturn AnnotationUtils.findAnnotatedFields(clazz, annotationType, predicate);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Field fields} of the supplied class or\n\t * interface that are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType} and match the specified {@code predicate}, using\n\t * the supplied hierarchy traversal mode.\n\t *\n\t * <p>Fields declared in the same class or interface will be ordered using\n\t * an algorithm that is deterministic but intentionally nonobvious.\n\t *\n\t * <p>The results will not contain fields that are <em>hidden</em> or\n\t * {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param predicate the field filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return the list of all such fields found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see Class#getDeclaredFields()\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tPredicate<Field> predicate, HierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn AnnotationUtils.findAnnotatedFields(clazz, annotationType, predicate,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n\t/**\n\t * Find the values of all non-static {@linkplain Field fields} of the supplied\n\t * {@code instance} that are annotated or <em>meta-annotated</em> with the\n\t * specified {@code annotationType}, using top-down search semantics within\n\t * the type hierarchy.\n\t *\n\t * <p>Values from fields declared in the same class or interface will be\n\t * ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>The results will not contain values from fields that are <em>hidden</em>\n\t * or {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param instance the instance in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return the list of all such field values found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static List<@Nullable Object> findAnnotatedFieldValues(Object instance,\n\t\t\tClass<? extends Annotation> annotationType) {\n\t\tPreconditions.notNull(instance, \"instance must not be null\");\n\n\t\tList<Field> fields = findAnnotatedFields(instance.getClass(), annotationType, ModifierSupport::isNotStatic,\n\t\t\tHierarchyTraversalMode.TOP_DOWN);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tList<@Nullable Object> result = (List<@Nullable Object>) ReflectionUtils.readFieldValues(fields, instance);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Find the values of all static {@linkplain Field fields} of the supplied\n\t * class or interface that are annotated or <em>meta-annotated</em> with the\n\t * specified {@code annotationType}, using top-down search semantics within\n\t * the type hierarchy.\n\t *\n\t * <p>Values from fields declared in the same class or interface will be\n\t * ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>The results will not contain values from fields that are <em>hidden</em>\n\t * or {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return the list of all such field values found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static List<@Nullable Object> findAnnotatedFieldValues(Class<?> clazz,\n\t\t\tClass<? extends Annotation> annotationType) {\n\n\t\tList<Field> fields = findAnnotatedFields(clazz, annotationType, ModifierSupport::isStatic,\n\t\t\tHierarchyTraversalMode.TOP_DOWN);\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tList<@Nullable Object> result = (List<@Nullable Object>) ReflectionUtils.readFieldValues(fields, null);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Find the values of all non-static {@linkplain Field fields} of the supplied\n\t * {@code instance} that are declared to be of the specified {@code fieldType}\n\t * and are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType}, using top-down search semantics within the type\n\t * hierarchy.\n\t *\n\t * <p>Values from fields declared in the same class or interface will be\n\t * ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>The results will not contain values from fields that are <em>hidden</em>\n\t * or {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param instance the instance in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param fieldType the declared type of fields to find; never {@code null}\n\t * @return the list of all such field values found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see Field#getType()\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static <T extends @Nullable Object> List<T> findAnnotatedFieldValues(Object instance,\n\t\t\tClass<? extends Annotation> annotationType, Class<T> fieldType) {\n\n\t\tPreconditions.notNull(instance, \"instance must not be null\");\n\t\tPreconditions.notNull(fieldType, \"fieldType must not be null\");\n\n\t\tPredicate<Field> predicate = //\n\t\t\tfield -> ModifierSupport.isNotStatic(field) && fieldType.isAssignableFrom(field.getType());\n\n\t\tList<Field> fields = findAnnotatedFields(instance.getClass(), annotationType, predicate,\n\t\t\tHierarchyTraversalMode.TOP_DOWN);\n\n\t\treturn (List<T>) ReflectionUtils.readFieldValues(fields, instance);\n\t}\n\n\t/**\n\t * Find the values of all static {@linkplain Field fields} of the supplied\n\t * class or interface that are declared to be of the specified\n\t * {@code fieldType} and are annotated or <em>meta-annotated</em> with the\n\t * specified {@code annotationType}, using top-down search semantics within\n\t * the type hierarchy.\n\t *\n\t * <p>Values from fields declared in the same class or interface will be\n\t * ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>The results will not contain values from fields that are <em>hidden</em>\n\t * or {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param fieldType the declared type of fields to find; never {@code null}\n\t * @return the list of all such field values found; neither {@code null} nor mutable\n\t * @since 1.4\n\t * @see Field#getType()\n\t * @see #findAnnotatedFields(Class, Class)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#findFields(Class, Predicate, HierarchyTraversalMode)\n\t * @see ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static <T extends @Nullable Object> List<T> findAnnotatedFieldValues(Class<?> clazz,\n\t\t\tClass<? extends Annotation> annotationType, Class<T> fieldType) {\n\n\t\tPreconditions.notNull(fieldType, \"fieldType must not be null\");\n\n\t\tPredicate<Field> predicate = //\n\t\t\tfield -> ModifierSupport.isStatic(field) && fieldType.isAssignableFrom(field.getType());\n\n\t\tList<Field> fields = findAnnotatedFields(clazz, annotationType, predicate, HierarchyTraversalMode.TOP_DOWN);\n\n\t\treturn (List<T>) ReflectionUtils.readFieldValues(fields, null);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Method methods} of the supplied class or\n\t * interface that are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType}.\n\t *\n\t * @param clazz the class or interface in which to find the methods; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return the list of all such methods found; neither {@code null} nor mutable\n\t */\n\tpublic static List<Method> findAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn AnnotationUtils.findAnnotatedMethods(clazz, annotationType,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/ClassSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ClassUtils;\n\n/**\n * {@code ClassSupport} provides static utility methods for common tasks\n * regarding {@linkplain Class classes} &mdash; for example, generating a\n * comma-separated list of fully qualified class names for a set of supplied\n * classes.\n *\n * <p>{@link org.junit.platform.engine.TestEngine TestEngine} and extension\n * authors are encouraged to use these supported methods in order to align with\n * the behavior of the JUnit Platform.\n *\n * @since 1.1\n * @see AnnotationSupport\n * @see ModifierSupport\n * @see ReflectionSupport\n * @see ResourceSupport\n */\n@API(status = MAINTAINED, since = \"1.1\")\npublic final class ClassSupport {\n\n\tprivate ClassSupport() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Generate a comma-separated list of fully qualified class names for the\n\t * supplied classes.\n\t *\n\t * @param classes the classes whose names should be included in the\n\t * generated string\n\t * @return a comma-separated list of fully qualified class names, or an empty\n\t * string if the supplied class array is {@code null} or empty\n\t * @see #nullSafeToString(Function, Class...)\n\t */\n\tpublic static String nullSafeToString(Class<?>... classes) {\n\t\treturn ClassUtils.nullSafeToString(classes);\n\t}\n\n\t/**\n\t * Generate a comma-separated list of mapped values for the supplied classes.\n\t *\n\t * <p>The values are generated by the supplied {@code mapper}\n\t * (e.g., {@code Class::getName}, {@code Class::getSimpleName}, etc.), unless\n\t * a class reference is {@code null} in which case it will be mapped to\n\t * {@code \"null\"}.\n\t *\n\t * @param mapper the mapper to use; never {@code null}\n\t * @param classes the classes to map\n\t * @return a comma-separated list of mapped values, or an empty string if\n\t * the supplied class array is {@code null} or empty\n\t * @see #nullSafeToString(Class...)\n\t */\n\tpublic static String nullSafeToString(Function<? super Class<?>, ? extends String> mapper, Class<?>... classes) {\n\t\treturn ClassUtils.nullSafeToString(mapper, classes);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/DefaultResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport java.net.URI;\nimport java.util.Objects;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link Resource}.\n *\n * @since 1.11\n */\n@SuppressWarnings(\"removal\")\nrecord DefaultResource(String name, URI uri) implements Resource {\n\n\tpublic DefaultResource {\n\t\tPreconditions.notNull(name, \"name must not be null\");\n\t\tPreconditions.notNull(uri, \"uri must not be null\");\n\t}\n\n\t@Override\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\t@Override\n\tpublic URI getUri() {\n\t\treturn uri;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (obj == this) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj instanceof org.junit.platform.commons.io.Resource that) {\n\t\t\treturn this.name.equals(that.getName()) //\n\t\t\t\t\t&& this.uri.equals(that.getUri());\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(name, uri);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"name\", name) //\n\t\t\t\t.append(\"uri\", uri) //\n\t\t\t\t.toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/HierarchyTraversalMode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * Modes in which a hierarchy can be traversed &mdash; for example, when\n * searching for methods or fields within a class hierarchy.\n *\n * @since 1.0\n * @see #TOP_DOWN\n * @see #BOTTOM_UP\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic enum HierarchyTraversalMode {\n\n\t/**\n\t * Traverse the hierarchy using top-down semantics.\n\t */\n\tTOP_DOWN,\n\n\t/**\n\t * Traverse the hierarchy using bottom-up semantics.\n\t */\n\tBOTTOM_UP\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/ModifierSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.Member;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code ModifierSupport} provides static utility methods for working with\n * class and member {@linkplain java.lang.reflect.Modifier modifiers} &mdash;\n * for example, to determine if a class or member is declared as\n * {@code public}, {@code private}, {@code abstract}, {@code static}, etc.\n *\n * <p>{@link org.junit.platform.engine.TestEngine TestEngine} and extension\n * authors are encouraged to use these supported methods in order to align with\n * the behavior of the JUnit Platform.\n *\n * @since 1.4\n * @see java.lang.reflect.Modifier\n * @see AnnotationSupport\n * @see ClassSupport\n * @see ReflectionSupport\n * @see ResourceSupport\n */\n@API(status = MAINTAINED, since = \"1.4\")\npublic final class ModifierSupport {\n\n\tprivate ModifierSupport() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Determine if the supplied class is {@code public}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is {@code public}\n\t * @see java.lang.reflect.Modifier#isPublic(int)\n\t */\n\tpublic static boolean isPublic(Class<?> clazz) {\n\t\treturn ReflectionUtils.isPublic(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is {@code public}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is {@code public}\n\t * @see java.lang.reflect.Modifier#isPublic(int)\n\t */\n\tpublic static boolean isPublic(Member member) {\n\t\treturn ReflectionUtils.isPublic(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is {@code private}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is {@code private}\n\t * @see java.lang.reflect.Modifier#isPrivate(int)\n\t */\n\tpublic static boolean isPrivate(Class<?> clazz) {\n\t\treturn ReflectionUtils.isPrivate(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is {@code private}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is {@code private}\n\t * @see java.lang.reflect.Modifier#isPrivate(int)\n\t */\n\tpublic static boolean isPrivate(Member member) {\n\t\treturn ReflectionUtils.isPrivate(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is not {@code private}.\n\t *\n\t * <p>In other words this method will return {@code true} for classes\n\t * declared as {@code public}, {@code protected}, or\n\t * <em>package private</em> and {@code false} for classes declared as\n\t * {@code private}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is not {@code private}\n\t * @see java.lang.reflect.Modifier#isPublic(int)\n\t * @see java.lang.reflect.Modifier#isProtected(int)\n\t * @see java.lang.reflect.Modifier#isPrivate(int)\n\t */\n\tpublic static boolean isNotPrivate(Class<?> clazz) {\n\t\treturn ReflectionUtils.isNotPrivate(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is not {@code private}.\n\t *\n\t * <p>In other words this method will return {@code true} for members\n\t * declared as {@code public}, {@code protected}, or\n\t * <em>package private</em> and {@code false} for members declared as\n\t * {@code private}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is not {@code private}\n\t * @see java.lang.reflect.Modifier#isPublic(int)\n\t * @see java.lang.reflect.Modifier#isProtected(int)\n\t * @see java.lang.reflect.Modifier#isPrivate(int)\n\t */\n\tpublic static boolean isNotPrivate(Member member) {\n\t\treturn ReflectionUtils.isNotPrivate(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is {@code abstract}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is {@code abstract}\n\t * @see java.lang.reflect.Modifier#isAbstract(int)\n\t */\n\tpublic static boolean isAbstract(Class<?> clazz) {\n\t\treturn ReflectionUtils.isAbstract(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is {@code abstract}.\n\t *\n\t * @param member the class to check; never {@code null}\n\t * @return {@code true} if the member is {@code abstract}\n\t * @see java.lang.reflect.Modifier#isAbstract(int)\n\t */\n\tpublic static boolean isAbstract(Member member) {\n\t\treturn ReflectionUtils.isAbstract(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is not {@code abstract}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is not {@code abstract}\n\t * @since 1.13\n\t * @see java.lang.reflect.Modifier#isAbstract(int)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static boolean isNotAbstract(Class<?> clazz) {\n\t\treturn ReflectionUtils.isNotAbstract(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is not {@code abstract}.\n\t *\n\t * @param member the class to check; never {@code null}\n\t * @return {@code true} if the member is not {@code abstract}\n\t * @since 1.13\n\t * @see java.lang.reflect.Modifier#isAbstract(int)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static boolean isNotAbstract(Member member) {\n\t\treturn ReflectionUtils.isNotAbstract(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is {@code static}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is {@code static}\n\t * @see java.lang.reflect.Modifier#isStatic(int)\n\t */\n\tpublic static boolean isStatic(Class<?> clazz) {\n\t\treturn ReflectionUtils.isStatic(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is {@code static}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is {@code static}\n\t * @see java.lang.reflect.Modifier#isStatic(int)\n\t */\n\tpublic static boolean isStatic(Member member) {\n\t\treturn ReflectionUtils.isStatic(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is not {@code static}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is not {@code static}\n\t * @see java.lang.reflect.Modifier#isStatic(int)\n\t */\n\tpublic static boolean isNotStatic(Class<?> clazz) {\n\t\treturn ReflectionUtils.isNotStatic(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is not {@code static}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is not {@code static}\n\t * @see java.lang.reflect.Modifier#isStatic(int)\n\t */\n\tpublic static boolean isNotStatic(Member member) {\n\t\treturn ReflectionUtils.isNotStatic(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is {@code final}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is {@code final}\n\t * @since 1.5\n\t * @see java.lang.reflect.Modifier#isFinal(int)\n\t */\n\t@API(status = MAINTAINED, since = \"1.5\")\n\tpublic static boolean isFinal(Class<?> clazz) {\n\t\treturn ReflectionUtils.isFinal(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied class is not {@code final}.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is not {@code final}\n\t * @since 1.5\n\t * @see java.lang.reflect.Modifier#isFinal(int)\n\t */\n\t@API(status = MAINTAINED, since = \"1.5\")\n\tpublic static boolean isNotFinal(Class<?> clazz) {\n\t\treturn ReflectionUtils.isNotFinal(clazz);\n\t}\n\n\t/**\n\t * Determine if the supplied member is {@code final}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is {@code final}\n\t * @since 1.5\n\t * @see java.lang.reflect.Modifier#isFinal(int)\n\t */\n\t@API(status = MAINTAINED, since = \"1.5\")\n\tpublic static boolean isFinal(Member member) {\n\t\treturn ReflectionUtils.isFinal(member);\n\t}\n\n\t/**\n\t * Determine if the supplied member is not {@code final}.\n\t *\n\t * @param member the member to check; never {@code null}\n\t * @return {@code true} if the member is not {@code final}\n\t * @since 1.5\n\t * @see java.lang.reflect.Modifier#isFinal(int)\n\t */\n\t@API(status = MAINTAINED, since = \"1.5\")\n\tpublic static boolean isNotFinal(Member member) {\n\t\treturn ReflectionUtils.isNotFinal(member);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/ReflectionSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code ReflectionSupport} provides static utility methods for common\n * reflection tasks &mdash; for example, scanning for classes in the class-path\n * or module-path, loading classes, finding methods, invoking methods, etc.\n *\n * <p>{@link org.junit.platform.engine.TestEngine TestEngine} and extension\n * authors are encouraged to use these supported methods in order to align with\n * the behavior of the JUnit Platform.\n *\n * @since 1.0\n * @see AnnotationSupport\n * @see ClassSupport\n * @see ModifierSupport\n * @see ResourceSupport\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic final class ReflectionSupport {\n\n\tprivate ReflectionSupport() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Try to load a class by its <em>primitive name</em> or <em>fully qualified\n\t * name</em>, using the default {@link ClassLoader}.\n\t *\n\t * <p>Class names for arrays may be specified using either the JVM's internal\n\t * String representation (e.g., {@code [[I} for {@code int[][]},\n\t * {@code [Lava.lang.String;} for {@code java.lang.String[]}, etc.) or\n\t * <em>source code syntax</em> (e.g., {@code int[][]}, {@code java.lang.String[]},\n\t * etc.).\n\t *\n\t * @param name the name of the class to load; never {@code null} or blank\n\t * @return a successful {@code Try} containing the loaded class or a failed\n\t * {@code Try} containing the exception if no such class could be loaded;\n\t * never {@code null}\n\t * @since 1.4\n\t * @see #tryToLoadClass(String, ClassLoader)\n\t * @see ResourceSupport#tryToGetResources(String)\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static Try<Class<?>> tryToLoadClass(String name) {\n\t\treturn ReflectionUtils.tryToLoadClass(name);\n\t}\n\n\t/**\n\t * Try to load a class by its <em>primitive name</em> or <em>fully qualified\n\t * name</em>, using the supplied {@link ClassLoader}.\n\t *\n\t * <p>See {@link ReflectionSupport#tryToLoadClass(String) tryToLoadClass(String)}\n\t * for details on support for class names for arrays.\n\t *\n\t * @param name the name of the class to load; never {@code null} or blank\n\t * @param classLoader the {@code ClassLoader} to use; never {@code null}\n\t * @return a successful {@code Try} containing the loaded class or a failed\n\t * {@code Try} containing the exception if no such class could be loaded;\n\t * never {@code null}\n\t * @since 1.10\n\t * @see #tryToLoadClass(String)\n\t * @see ResourceSupport#tryToGetResources(String, ClassLoader)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Try<Class<?>> tryToLoadClass(String name, ClassLoader classLoader) {\n\t\treturn ReflectionUtils.tryToLoadClass(name, classLoader);\n\t}\n\n\t/**\n\t * Try to get the {@linkplain Resource resources} for the supplied classpath\n\t * resource name.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the resource to load; never\n\t * {@code null} or blank\n\t * @return a successful {@code Try} containing the set of loaded resources\n\t * (potentially empty) or a failed {@code Try} containing the exception in\n\t * case a failure occurred while trying to list resources; never\n\t * {@code null}\n\t * @since 1.12\n\t * @see #tryToGetResources(String, ClassLoader)\n\t * @deprecated Please use {@link ResourceSupport#tryToGetResources(String)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName) {\n\t\treturn ResourceSupport.tryToGetResources(classpathResourceName) //\n\t\t\t\t.andThenTry(ReflectionSupport::toSupportResourcesSet);\n\t}\n\n\t/**\n\t * Try to load the {@linkplain Resource resources} for the supplied classpath\n\t * resource name, using the supplied {@link ClassLoader}.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the resource to load; never\n\t * {@code null} or blank\n\t * @param classLoader the {@code ClassLoader} to use; never {@code null}\n\t * @return a successful {@code Try} containing the set of loaded resources\n\t * (potentially empty) or a failed {@code Try} containing the exception in\n\t * case a failure occurred while trying to list resources; never\n\t * {@code null}\n\t * @since 1.12\n\t * @see #tryToGetResources(String)\n\t * @deprecated Please use {@link ResourceSupport#tryToGetResources(String, ClassLoader)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName, ClassLoader classLoader) {\n\t\treturn ResourceSupport.tryToGetResources(classpathResourceName, classLoader) //\n\t\t\t\t.andThenTry(ReflectionSupport::toSupportResourcesSet);\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied classpath {@code root}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @see #findAllClassesInPackage(String, Predicate, Predicate)\n\t * @see #findAllClassesInModule(String, Predicate, Predicate)\n\t * @see ResourceSupport#findAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t */\n\tpublic static List<Class<?>> findAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.findAllClassesInClasspathRoot(root, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #findAllResourcesInPackage(String, Predicate)\n\t * @see #findAllResourcesInModule(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#findAllResourcesInClasspathRoot(URI, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static List<Resource> findAllResourcesInClasspathRoot(URI root, Predicate<Resource> resourceFilter) {\n\t\treturn toSupportResourcesList(\n\t\t\tResourceSupport.findAllResourcesInClasspathRoot(root, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied classpath {@code root}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.10\n\t * @see #streamAllClassesInPackage(String, Predicate, Predicate)\n\t * @see #streamAllClassesInModule(String, Predicate, Predicate)\n\t * @see ResourceSupport#streamAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Class<?>> streamAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.streamAllClassesInClasspathRoot(root, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #streamAllResourcesInPackage(String, Predicate)\n\t * @see #streamAllResourcesInModule(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#streamAllResourcesInClasspathRoot(URI, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static Stream<Resource> streamAllResourcesInClasspathRoot(URI root, Predicate<Resource> resourceFilter) {\n\t\treturn toSupportResourcesStream(\n\t\t\tResourceSupport.streamAllResourcesInClasspathRoot(root, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied {@code basePackageName}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t * @see #findAllClassesInModule(String, Predicate, Predicate)\n\t * @see ResourceSupport#findAllResourcesInPackage(String, ResourceFilter)\n\t */\n\tpublic static List<Class<?>> findAllClassesInPackage(String basePackageName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.findAllClassesInPackage(basePackageName, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code basePackageName}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package. The resulting list may include\n\t * identically named resources from different classpath roots.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #findAllResourcesInClasspathRoot(URI, Predicate)\n\t * @see #findAllResourcesInModule(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#findAllResourcesInPackage(String, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static List<Resource> findAllResourcesInPackage(String basePackageName, Predicate<Resource> resourceFilter) {\n\t\treturn toSupportResourcesList(\n\t\t\tResourceSupport.findAllResourcesInPackage(basePackageName, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied {@code basePackageName}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package. The resulting stream may\n\t * include identically named resources from different classpath roots.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.10\n\t * @see #streamAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t * @see #streamAllClassesInModule(String, Predicate, Predicate)\n\t * @see ResourceSupport#streamAllResourcesInPackage(String, ResourceFilter)\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Class<?>> streamAllClassesInPackage(String basePackageName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.streamAllClassesInPackage(basePackageName, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code basePackageName}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package. The resulting stream may\n\t * include identically named resources from different classpath roots.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #streamAllResourcesInClasspathRoot(URI, Predicate)\n\t * @see #streamAllResourcesInModule(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#streamAllResourcesInPackage(String, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static Stream<Resource> streamAllResourcesInPackage(String basePackageName,\n\t\t\tPredicate<Resource> resourceFilter) {\n\n\t\treturn toSupportResourcesStream(\n\t\t\tResourceSupport.streamAllResourcesInPackage(basePackageName, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied {@code moduleName}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.1.1\n\t * @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t * @see #findAllClassesInPackage(String, Predicate, Predicate)\n\t * @see ResourceSupport#findAllResourcesInModule(String, ResourceFilter)\n\t */\n\t@API(status = MAINTAINED, since = \"1.1.1\")\n\tpublic static List<Class<?>> findAllClassesInModule(String moduleName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.findAllClassesInModule(moduleName, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied {@code module}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param module the module to scan; never {@code null} or <em>unnamed</em>\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 6.1\n\t * @see #findAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t * @see #findAllClassesInPackage(String, Predicate, Predicate)\n\t * @see ResourceSupport#findAllResourcesInModule(String, ResourceFilter)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static List<Class<?>> findAllClassesInModule(Module module, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.findAllClassesInModule(module, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code moduleName}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #findAllResourcesInClasspathRoot(URI, Predicate)\n\t * @see #findAllResourcesInPackage(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#findAllResourcesInModule(String, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static List<Resource> findAllResourcesInModule(String moduleName, Predicate<Resource> resourceFilter) {\n\t\treturn toSupportResourcesList(\n\t\t\tResourceSupport.findAllResourcesInModule(moduleName, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied {@code moduleName}\n\t * that match the specified {@code classFilter} and {@code classNameFilter}\n\t * predicates.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param classFilter the class type filter; never {@code null}\n\t * @param classNameFilter the class name filter; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 1.10\n\t * @see #streamAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t * @see #streamAllClassesInPackage(String, Predicate, Predicate)\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Class<?>> streamAllClassesInModule(String moduleName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\n\t\treturn ReflectionUtils.streamAllClassesInModule(moduleName, classFilter, classNameFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code moduleName}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t * @see #streamAllResourcesInClasspathRoot(URI, Predicate)\n\t * @see #streamAllResourcesInPackage(String, Predicate)\n\t * @deprecated Please use {@link ResourceSupport#streamAllResourcesInModule(String, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static Stream<Resource> streamAllResourcesInModule(String moduleName, Predicate<Resource> resourceFilter) {\n\t\treturn toSupportResourcesStream(\n\t\t\tResourceSupport.streamAllResourcesInModule(moduleName, toResourceFilter(resourceFilter)));\n\t}\n\n\t/**\n\t * Create a new instance of the specified {@link Class} by invoking\n\t * the constructor whose argument list matches the types of the supplied\n\t * arguments.\n\t *\n\t * <p>The constructor will be made accessible if necessary, and any checked\n\t * exception will be {@linkplain ExceptionUtils#throwAsUncheckedException masked}\n\t * as an unchecked exception.\n\t *\n\t * @param clazz the class to instantiate; never {@code null}\n\t * @param args the arguments to pass to the constructor, none of which may\n\t * be {@code null}\n\t * @return the new instance; never {@code null}\n\t * @see ExceptionUtils#throwAsUncheckedException(Throwable)\n\t */\n\tpublic static <T> T newInstance(Class<T> clazz, Object... args) {\n\t\treturn ReflectionUtils.newInstance(clazz, args);\n\t}\n\n\t/**\n\t * Invoke the supplied method, making it accessible if necessary and\n\t * {@linkplain ExceptionUtils#throwAsUncheckedException masking} any\n\t * checked exception as an unchecked exception.\n\t *\n\t * @param method the method to invoke; never {@code null}\n\t * @param target the object on which to invoke the method; may be\n\t * {@code null} if the method is {@code static}\n\t * @param args the arguments to pass to the method; never {@code null}\n\t * @return the value returned by the method invocation or {@code null}\n\t * if the return type is {@code void}\n\t * @see ExceptionUtils#throwAsUncheckedException(Throwable)\n\t */\n\tpublic static @Nullable Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) {\n\t\treturn ReflectionUtils.invokeMethod(method, target, args);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Field fields} of the supplied class or\n\t * interface that match the specified {@code predicate}.\n\t *\n\t * <p>Fields declared in the same class or interface will be ordered using\n\t * an algorithm that is deterministic but intentionally nonobvious.\n\t *\n\t * <p>The results will not contain fields that are\n\t * {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param predicate the field filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return an immutable list of all such fields found; never {@code null}\n\t * but potentially empty\n\t * @since 1.4\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static List<Field> findFields(Class<?> clazz, Predicate<Field> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn ReflectionUtils.findFields(clazz, predicate,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Field fields} of the supplied class or\n\t * interface that match the specified {@code predicate}.\n\t *\n\t * <p>Fields declared in the same class or interface will be ordered using\n\t * an algorithm that is deterministic but intentionally nonobvious.\n\t *\n\t * <p>The results will not contain fields that are\n\t * {@linkplain Field#isSynthetic() synthetic}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param predicate the field filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return a stream of all such fields found; never {@code null}\n\t * but potentially empty\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Field> streamFields(Class<?> clazz, Predicate<Field> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn ReflectionUtils.streamFields(clazz, predicate,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n\t/**\n\t * Try to read the value of a potentially inaccessible field.\n\t *\n\t * <p>If an exception occurs while reading the field, a failed {@link Try}\n\t * is returned that contains the corresponding exception.\n\t *\n\t * @param field the field to read; never {@code null}\n\t * @param instance the instance from which the value is to be read; may\n\t * be {@code null} for a static field\n\t * @since 1.4\n\t */\n\t@API(status = MAINTAINED, since = \"1.4\")\n\tpublic static Try<@Nullable Object> tryToReadFieldValue(Field field, @Nullable Object instance) {\n\t\treturn ReflectionUtils.tryToReadFieldValue(field, instance);\n\t}\n\n\t/**\n\t * Find the first {@link Method} of the supplied class or interface that\n\t * meets the specified criteria, beginning with the specified class or\n\t * interface and traversing up the type hierarchy until such a method is\n\t * found or the type hierarchy is exhausted.\n\t *\n\t * <p>This method uses the {@link ClassLoader} of the supplied {@code clazz}\n\t * to load parameter types instead of using the <em>default</em>\n\t * {@code ClassLoader}, which allows parameter types to be resolved in different\n\t * {@code ClassLoader} arrangements.\n\t *\n\t * <p>The algorithm does not search for methods in {@link java.lang.Object}.\n\t *\n\t * @param clazz the class or interface in which to find the method; never {@code null}\n\t * @param methodName the name of the method to find; never {@code null} or empty\n\t * @param parameterTypeNames the fully qualified names of the types of parameters\n\t * accepted by the method, if any, provided as a comma-separated list\n\t * @return an {@code Optional} containing the method found; never {@code null}\n\t * but potentially empty if no such method could be found\n\t * @see #findMethod(Class, String, Class...)\n\t */\n\tpublic static Optional<Method> findMethod(Class<?> clazz, String methodName, @Nullable String parameterTypeNames) {\n\t\treturn ReflectionUtils.findMethod(clazz, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * Find the first {@link Method} of the supplied class or interface that\n\t * meets the specified criteria, beginning with the specified class or\n\t * interface and traversing up the type hierarchy until such a method is\n\t * found or the type hierarchy is exhausted.\n\t *\n\t * <p>The algorithm does not search for methods in {@link java.lang.Object}.\n\t *\n\t * @param clazz the class or interface in which to find the method; never {@code null}\n\t * @param methodName the name of the method to find; never {@code null} or empty\n\t * @param parameterTypes the types of parameters accepted by the method, if any;\n\t * never {@code null}\n\t * @return an {@code Optional} containing the method found; never {@code null}\n\t * but potentially empty if no such method could be found\n\t * @see #findMethod(Class, String, String)\n\t */\n\tpublic static Optional<Method> findMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {\n\t\treturn ReflectionUtils.findMethod(clazz, methodName, parameterTypes);\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Method methods} of the supplied class or\n\t * interface that match the specified {@code predicate}.\n\t *\n\t * <p>The results will not contain methods that are <em>overridden</em>.\n\t *\n\t * <p>If you are looking for methods annotated with a certain annotation\n\t * type, consider using\n\t * {@link AnnotationSupport#findAnnotatedMethods(Class, Class, HierarchyTraversalMode)}.\n\t *\n\t * @param clazz the class or interface in which to find the methods; never {@code null}\n\t * @param predicate the method filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return an immutable list of all such methods found; never {@code null}\n\t * but potentially empty\n\t */\n\tpublic static List<Method> findMethods(Class<?> clazz, Predicate<Method> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn ReflectionUtils.findMethods(clazz, predicate,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n\t/**\n\t * Find all distinct {@linkplain Method methods} of the supplied class or\n\t * interface that match the specified {@code predicate}.\n\t *\n\t * <p>The results will not contain methods that are <em>overridden</em>.\n\t *\n\t * <p>If you are looking for methods annotated with a certain annotation\n\t * type, consider using\n\t * {@link AnnotationSupport#findAnnotatedMethods(Class, Class, HierarchyTraversalMode)}.\n\t *\n\t * @param clazz the class or interface in which to find the methods; never {@code null}\n\t * @param predicate the method filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return a stream of all such methods found; never {@code null}\n\t * but potentially empty\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Method> streamMethods(Class<?> clazz, Predicate<Method> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\treturn ReflectionUtils.streamMethods(clazz, predicate,\n\t\t\tReflectionUtils.HierarchyTraversalMode.valueOf(traversalMode.name()));\n\t}\n\n\t/**\n\t * Find all nested classes within the supplied class, or inherited by the\n\t * supplied class, that conform to the supplied predicate.\n\t *\n\t * <p>This method does <strong>not</strong> search for nested classes\n\t * recursively.\n\t *\n\t * <p>Nested classes declared in the same enclosing class or interface will\n\t * be ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>This method detects cycles in <em>inner</em> class hierarchies &mdash;\n\t * from the supplied class up to the outermost enclosing class &mdash; and\n\t * throws a {@link JUnitException} if such a cycle is detected. Cycles within\n\t * inner class hierarchies <em>below</em> the supplied class are not detected\n\t * by this method.\n\t *\n\t * @param clazz the class to be searched; never {@code null}\n\t * @param predicate the predicate against which the list of nested classes is\n\t * checked; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @throws JUnitException if a cycle is detected within an inner class hierarchy\n\t */\n\tpublic static List<Class<?>> findNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate)\n\t\t\tthrows JUnitException {\n\n\t\treturn ReflectionUtils.findNestedClasses(clazz, predicate);\n\t}\n\n\t/**\n\t * Find all nested classes within the supplied class, or inherited by the\n\t * supplied class, that conform to the supplied predicate.\n\t *\n\t * <p>This method does <strong>not</strong> search for nested classes\n\t * recursively.\n\t *\n\t * <p>Nested classes declared in the same enclosing class or interface will\n\t * be ordered using an algorithm that is deterministic but intentionally\n\t * nonobvious.\n\t *\n\t * <p>This method detects cycles in <em>inner</em> class hierarchies &mdash;\n\t * from the supplied class up to the outermost enclosing class &mdash; and\n\t * throws a {@link JUnitException} if such a cycle is detected. Cycles within\n\t * inner class hierarchies <em>below</em> the supplied class are not detected\n\t * by this method.\n\t *\n\t * @param clazz the class to be searched; never {@code null}\n\t * @param predicate the predicate against which the list of nested classes is\n\t * checked; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @throws JUnitException if a cycle is detected within an inner class hierarchy\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic static Stream<Class<?>> streamNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate)\n\t\t\tthrows JUnitException {\n\n\t\treturn ReflectionUtils.streamNestedClasses(clazz, predicate);\n\t}\n\n\t/**\n\t * Make the supplied field accessible via reflection.\n\t *\n\t * <p>If you're looking for similar functionality for constructors or\n\t * methods, consider using {@link #newInstance(Class, Object...)} or\n\t * {@link #invokeMethod(Method, Object, Object...)}.\n\t *\n\t * @param field the field to make accessible; never {@code null}\n\t * @return the supplied field\n\t * @since 1.12\n\t * @see Field#setAccessible(boolean)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Field makeAccessible(Field field) {\n\t\treturn ReflectionUtils.makeAccessible(Preconditions.notNull(field, \"field must not be null\"));\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tprivate static ResourceFilter toResourceFilter(Predicate<Resource> resourceFilter) {\n\t\tPreconditions.notNull(resourceFilter, \"resourceFilter must not be null\");\n\t\treturn ResourceFilter.of(r -> resourceFilter.test(Resource.of(r)));\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic List<Resource> toSupportResourcesList(List<org.junit.platform.commons.io.Resource> resources) {\n\t\treturn toSupportResourcesStream(resources.stream()).toList();\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic Set<Resource> toSupportResourcesSet(Set<org.junit.platform.commons.io.Resource> resources) {\n\t\treturn toSupportResourcesStream(resources.stream()).collect(Collectors.toCollection(LinkedHashSet::new));\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\tstatic Stream<Resource> toSupportResourcesStream(Stream<org.junit.platform.commons.io.Resource> resources) {\n\t\treturn resources.map(Resource::of);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/Resource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport java.net.URI;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code Resource} represents a resource on the classpath.\n *\n * <p><strong>WARNING</strong>: a {@code Resource} must provide correct\n * {@link Object#equals(Object) equals} and {@link Object#hashCode() hashCode}\n * implementations since a {@code Resource} may potentially be stored in a\n * collection or map.\n *\n * @since 1.11\n * @see ReflectionSupport#findAllResourcesInClasspathRoot(URI, Predicate)\n * @see ReflectionSupport#findAllResourcesInPackage(String, Predicate)\n * @see ReflectionSupport#findAllResourcesInModule(String, Predicate)\n * @see ReflectionSupport#streamAllResourcesInClasspathRoot(URI, Predicate)\n * @see ReflectionSupport#streamAllResourcesInPackage(String, Predicate)\n * @see ReflectionSupport#streamAllResourcesInModule(String, Predicate)\n * @deprecated Please use {@link org.junit.platform.commons.io.Resource} instead.\n */\n@SuppressWarnings(\"removal\")\n@API(status = DEPRECATED, since = \"1.14\")\n@Deprecated(since = \"1.14\", forRemoval = true)\npublic interface Resource extends org.junit.platform.commons.io.Resource {\n\n\t/**\n\t * Create a new {@link Resource} from the supplied\n\t * {@link org.junit.platform.commons.io.Resource}.\n\t *\n\t * @param resource the resource to copy attributes from; never {@code null}\n\t * @return a new {@code Resource}\n\t * @since 1.14\n\t */\n\tstatic Resource of(org.junit.platform.commons.io.Resource resource) {\n\t\tPreconditions.notNull(resource, \"resource must not be null\");\n\t\treturn new DefaultResource(resource.getName(), resource.getUri());\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/ResourceSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.net.URI;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * {@code ResourceSupport} provides static utility methods for common tasks\n * dealing with resources; for example, scanning for resources on the class path\n * or module path.\n *\n * <p>{@link org.junit.platform.engine.TestEngine TestEngine} and extension\n * authors are encouraged to use these supported methods in order to align with\n * the behavior of the JUnit Platform.\n *\n * @since 1.14\n * @see AnnotationSupport\n * @see ClassSupport\n * @see ModifierSupport\n * @see ReflectionSupport\n */\n@API(status = MAINTAINED, since = \"1.14\")\npublic class ResourceSupport {\n\n\t/**\n\t * Try to get the {@linkplain Resource resources} for the supplied classpath\n\t * resource name.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the resource to load; never\n\t * {@code null} or blank\n\t * @return a successful {@code Try} containing the set of loaded resources\n\t * (potentially empty) or a failed {@code Try} containing the exception in\n\t * case a failure occurred while trying to list resources; never\n\t * {@code null}\n\t * @see #tryToGetResources(String, ClassLoader)\n\t * @see ReflectionSupport#tryToLoadClass(String)\n\t */\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName) {\n\t\treturn ReflectionUtils.tryToGetResources(classpathResourceName);\n\t}\n\n\t/**\n\t * Try to load the {@linkplain Resource resources} for the supplied classpath\n\t * resource name, using the supplied {@link ClassLoader}.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the resource to load; never\n\t * {@code null} or blank\n\t * @param classLoader the {@code ClassLoader} to use; never {@code null}\n\t * @return a successful {@code Try} containing the set of loaded resources\n\t * (potentially empty) or a failed {@code Try} containing the exception in\n\t * case a failure occurred while trying to list resources; never\n\t * {@code null}\n\t * @see #tryToGetResources(String)\n\t * @see ReflectionSupport#tryToLoadClass(String, ClassLoader)\n\t */\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName, ClassLoader classLoader) {\n\t\treturn ReflectionUtils.tryToGetResources(classpathResourceName, classLoader);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @see #findAllResourcesInPackage(String, ResourceFilter)\n\t * @see #findAllResourcesInModule(String, ResourceFilter)\n\t * @see ReflectionSupport#findAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t */\n\tpublic static List<Resource> findAllResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.findAllResourcesInClasspathRoot(root, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @see #streamAllResourcesInPackage(String, ResourceFilter)\n\t * @see #streamAllResourcesInModule(String, ResourceFilter)\n\t * @see ReflectionSupport#streamAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.streamAllResourcesInClasspathRoot(root, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code basePackageName}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package. The resulting list may include\n\t * identically named resources from different classpath roots.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @see #findAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t * @see #findAllResourcesInModule(String, ResourceFilter)\n\t * @see ReflectionSupport#findAllClassesInPackage(String, Predicate, Predicate)\n\t */\n\tpublic static List<Resource> findAllResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.findAllResourcesInPackage(basePackageName, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code basePackageName}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning within the supplied base package. The resulting stream may\n\t * include identically named resources from different classpath roots.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @see #streamAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t * @see #streamAllResourcesInModule(String, ResourceFilter)\n\t * @see ReflectionSupport#streamAllClassesInPackage(String, Predicate, Predicate)\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.streamAllResourcesInPackage(basePackageName, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code moduleName}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @see #findAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t * @see #findAllResourcesInPackage(String, ResourceFilter)\n\t * @see ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate)\n\t */\n\tpublic static List<Resource> findAllResourcesInModule(String moduleName, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.findAllResourcesInModule(moduleName, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code module}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param module the module to scan; never {@code null} or <em>unnamed</em>\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 6.1\n\t * @see #findAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t * @see #findAllResourcesInPackage(String, ResourceFilter)\n\t * @see ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static List<Resource> findAllResourcesInModule(Module module, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.findAllResourcesInModule(module, resourceFilter);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied {@code moduleName}\n\t * that match the specified {@code resourceFilter}.\n\t *\n\t * <p>The module-path scanning algorithm searches recursively in all\n\t * packages contained in the module.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a stream of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @see #streamAllResourcesInClasspathRoot(URI, ResourceFilter)\n\t * @see #streamAllResourcesInPackage(String, ResourceFilter)\n\t * @see ReflectionSupport#streamAllClassesInModule(String, Predicate, Predicate)\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInModule(String moduleName, ResourceFilter resourceFilter) {\n\t\treturn ReflectionUtils.streamAllResourcesInModule(moduleName, resourceFilter);\n\t}\n\n\tprivate ResourceSupport() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/SearchOption.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\n\nimport org.apiguardian.api.API;\n\n/**\n * Search options for finding an annotation within a class hierarchy.\n *\n * @since 1.8\n * @see #DEFAULT\n * @see #INCLUDE_ENCLOSING_CLASSES\n * @deprecated because there is only a single non-deprecated search option left\n */\n@Deprecated(since = \"1.12\")\n@API(status = DEPRECATED, since = \"1.12\")\npublic enum SearchOption {\n\n\t/**\n\t * Search the inheritance hierarchy (i.e., the current class, implemented\n\t * interfaces, and superclasses), but do not search on enclosing classes.\n\t *\n\t * @see Class#getSuperclass()\n\t * @see Class#getInterfaces()\n\t */\n\tDEFAULT,\n\n\t/**\n\t * Search the inheritance hierarchy as with the {@link #DEFAULT} search option\n\t * but also search the {@linkplain Class#getEnclosingClass() enclosing class}\n\t * hierarchy for <em>inner classes</em> (i.e., a non-static member classes).\n\t *\n\t * @deprecated because it is preferable to inspect the runtime enclosing\n\t * types of a class rather than where they are declared.\n\t */\n\t@Deprecated(since = \"1.12\") //\n\t@API(status = DEPRECATED, since = \"1.12\")\n\tINCLUDE_ENCLOSING_CLASSES\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code ConversionException} is an exception that can occur when an\n * object is converted to another object.\n *\n * @since 1.11\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic class ConversionException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic ConversionException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ConversionException(String message, @Nullable Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/ConversionSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.ReflectionUtils.getWrapperType;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\n\n/**\n * {@code ConversionSupport} provides static utility methods for converting a\n * given object into an instance of a specified type.\n *\n * @since 1.11\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic final class ConversionSupport {\n\n\tprivate static final List<StringToObjectConverter> stringToObjectConverters = List.of( //\n\t\tnew StringToBooleanConverter(), //\n\t\tnew StringToCharacterConverter(), //\n\t\tnew StringToNumberConverter(), //\n\t\tnew StringToClassConverter(), //\n\t\tnew StringToEnumConverter(), //\n\t\tnew StringToJavaTimeConverter(), //\n\t\tnew StringToCommonJavaTypesConverter(), //\n\t\tnew FallbackStringToObjectConverter() //\n\t);\n\n\tprivate ConversionSupport() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Convert the supplied source {@code String} into an instance of the specified\n\t * target type.\n\t *\n\t * <p>If the target type is {@code String}, the source {@code String} will not\n\t * be modified.\n\t *\n\t * <p>Some forms of conversion require a {@link ClassLoader}. If none is\n\t * provided, the {@linkplain ClassLoaderUtils#getDefaultClassLoader() default\n\t * ClassLoader} will be used.\n\t *\n\t * <p>This method is able to convert strings into primitive types and their\n\t * corresponding wrapper types ({@link Boolean}, {@link Character}, {@link Byte},\n\t * {@link Short}, {@link Integer}, {@link Long}, {@link Float}, and\n\t * {@link Double}), enum constants, date and time types from the\n\t * {@code java.time} package, as well as common Java types such as {@link Class},\n\t * {@link java.io.File}, {@link java.nio.file.Path}, {@link java.nio.charset.Charset},\n\t * {@link java.math.BigDecimal}, {@link java.math.BigInteger},\n\t * {@link java.util.Currency}, {@link java.util.Locale}, {@link java.util.UUID},\n\t * {@link java.net.URI}, and {@link java.net.URL}.\n\t *\n\t * <p>If the target type is not covered by any of the above, a convention-based\n\t * conversion strategy will be used to convert the source {@code String} into the\n\t * given target type by invoking a static factory method or factory constructor\n\t * defined in the target type. The search algorithm used in this strategy is\n\t * outlined below.\n\t *\n\t * <h4>Search Algorithm</h4>\n\t *\n\t * <ol>\n\t * <li>Search for a single, non-private static factory method in the target\n\t * type that converts from a {@link String} to the target type. Use the\n\t * factory method if present.</li>\n\t * <li>Search for a single, non-private constructor in the target type that\n\t * accepts a {@link String}. Use the constructor if present.</li>\n\t * <li>Search for a single, non-private static factory method in the target\n\t * type that converts from a {@link CharSequence} to the target type. Use the\n\t * factory method if present.</li>\n\t * <li>Search for a single, non-private constructor in the target type that\n\t * accepts a {@link CharSequence}. Use the constructor if present.</li>\n\t * </ol>\n\t *\n\t * <p>If multiple suitable factory methods or constructors are discovered they\n\t * will be ignored. If neither a single factory method nor a single constructor\n\t * is found, the convention-based conversion strategy will not apply.\n\t *\n\t * @param source the source {@code String} to convert; may be {@code null}\n\t * but only if the target type is a reference type\n\t * @param targetType the target type the source should be converted into;\n\t * never {@code null}\n\t * @param classLoader the {@code ClassLoader} to use; may be {@code null} to\n\t * use the default {@code ClassLoader}\n\t * @param <T> the type of the target\n\t * @return the converted object; may be {@code null} but only if the target\n\t * type is a reference type\n\t *\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> @Nullable T convert(@Nullable String source, Class<T> targetType,\n\t\t\t@Nullable ClassLoader classLoader) {\n\t\tif (source == null) {\n\t\t\tif (targetType.isPrimitive()) {\n\t\t\t\tthrow new ConversionException(\n\t\t\t\t\t\"Cannot convert null to primitive value of type \" + targetType.getTypeName());\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tif (String.class.equals(targetType)) {\n\t\t\treturn (T) source;\n\t\t}\n\n\t\tClass<?> targetTypeToUse = toWrapperType(targetType);\n\t\tOptional<StringToObjectConverter> converter = stringToObjectConverters.stream().filter(\n\t\t\tcandidate -> candidate.canConvertTo(targetTypeToUse)).findFirst();\n\t\tif (converter.isPresent()) {\n\t\t\ttry {\n\t\t\t\tClassLoader classLoaderToUse = classLoader != null ? classLoader\n\t\t\t\t\t\t: ClassLoaderUtils.getDefaultClassLoader();\n\t\t\t\treturn (T) converter.get().convert(source, targetTypeToUse, classLoaderToUse);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tif (ex instanceof ConversionException conversionException) {\n\t\t\t\t\t// simply rethrow it\n\t\t\t\t\tthrow conversionException;\n\t\t\t\t}\n\t\t\t\t// else\n\t\t\t\tthrow new ConversionException(\n\t\t\t\t\t\"Failed to convert String \\\"%s\\\" to type %s\".formatted(source, targetType.getTypeName()), ex);\n\t\t\t}\n\t\t}\n\n\t\tthrow new ConversionException(\n\t\t\t\"No built-in converter for source type java.lang.String and target type \" + targetType.getTypeName());\n\t}\n\n\tprivate static Class<?> toWrapperType(Class<?> targetType) {\n\t\tClass<?> wrapperType = getWrapperType(targetType);\n\t\treturn wrapperType != null ? wrapperType : targetType;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotPrivate;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotStatic;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\nimport static org.junit.platform.commons.support.ReflectionSupport.invokeMethod;\nimport static org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.DeprecationStatus.EXCLUDE_DEPRECATED;\nimport static org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.DeprecationStatus.INCLUDE_DEPRECATED;\nimport static org.junit.platform.commons.util.ReflectionUtils.findConstructors;\nimport static org.junit.platform.commons.util.ReflectionUtils.newInstance;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code FallbackStringToObjectConverter} is a {@link StringToObjectConverter}\n * that provides a fallback conversion strategy for converting from a\n * {@link String} or {@link CharSequence} to a given target type by invoking a\n * static factory method or factory constructor defined in the target type.\n *\n * <h2>Search Algorithm</h2>\n *\n * <ol>\n * <li>Search for a single, non-private static factory method in the target type\n * that converts from a {@link String} to the target type. Use the factory method\n * if present.</li>\n * <li>Search for a single, non-private constructor in the target type that accepts\n * a {@link String}. Use the constructor if present.</li>\n * <li>Search for a single, non-private static factory method in the target type\n * that converts from a {@link CharSequence} to the target type. Use the factory\n * method if present.</li>\n * <li>Search for a single, non-private constructor in the target type that accepts\n * a {@link CharSequence}. Use the constructor if present.</li>\n * <li>Search for a single, non-private static factory method in the target type\n * that converts from a {@link String} to the target type, excluding candidates\n * annotated with {@link Deprecated @Deprecated}. Use the factory method if\n * present.</li>\n * <li>Search for a single, non-private static factory method in the target type\n * that converts from a {@link CharSequence} to the target type, excluding\n * candidates annotated with {@link Deprecated @Deprecated}. Use the factory\n * method if present.</li>\n * </ol>\n *\n * <p>If a suitable factory method or constructor is not found, this converter\n * acts as a no-op.\n *\n * @since 1.11\n * @see ConversionSupport\n */\nclass FallbackStringToObjectConverter implements StringToObjectConverter {\n\n\t/**\n\t * Implementation of the NULL Object Pattern.\n\t */\n\tprivate static final Function<String, Object> NULL_EXECUTABLE = source -> source;\n\n\t/**\n\t * Cache for factory methods and factory constructors.\n\t *\n\t * <p>Searches that do not find a factory method or constructor are tracked\n\t * by the presence of a {@link #NULL_EXECUTABLE} object stored in the map.\n\t * This prevents the framework from repeatedly searching for things which\n\t * are already known not to exist.\n\t */\n\tprivate static final ConcurrentHashMap<Class<?>, Function<String, @Nullable Object>> factoryExecutableCache //\n\t\t= new ConcurrentHashMap<>(64);\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn findFactoryExecutable(targetType) != NULL_EXECUTABLE;\n\t}\n\n\t@Override\n\tpublic @Nullable Object convert(String source, Class<?> targetType) throws Exception {\n\t\tFunction<String, @Nullable Object> executable = findFactoryExecutable(targetType);\n\t\tPreconditions.condition(executable != NULL_EXECUTABLE,\n\t\t\t\"Illegal state: convert() must not be called if canConvert() returned false\");\n\n\t\treturn executable.apply(source);\n\t}\n\n\tprivate static Function<String, @Nullable Object> findFactoryExecutable(Class<?> targetType) {\n\t\treturn factoryExecutableCache.computeIfAbsent(targetType, type -> {\n\t\t\t// First, search for exact String argument matches.\n\t\t\tvar factory = findFactoryMethodExecutable(type, String.class, INCLUDE_DEPRECATED);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\tfactory = findFactoryConstructorExecutable(type, String.class);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\t// Second, fall back to CharSequence argument matches.\n\t\t\tfactory = findFactoryMethodExecutable(type, CharSequence.class, INCLUDE_DEPRECATED);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\tfactory = findFactoryConstructorExecutable(type, CharSequence.class);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\t// Third, try factory methods again, but exclude deprecated methods\n\t\t\tfactory = findFactoryMethodExecutable(type, String.class, EXCLUDE_DEPRECATED);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\tfactory = findFactoryMethodExecutable(type, CharSequence.class, EXCLUDE_DEPRECATED);\n\t\t\tif (factory != null) {\n\t\t\t\treturn factory;\n\t\t\t}\n\t\t\t// Else, nothing found.\n\t\t\treturn NULL_EXECUTABLE;\n\t\t});\n\t}\n\n\tprivate static @Nullable Function<String, @Nullable Object> findFactoryMethodExecutable(Class<?> targetType,\n\t\t\tClass<?> parameterType, DeprecationStatus deprecationStatus) {\n\t\tMethod factoryMethod = findFactoryMethod(targetType, parameterType, deprecationStatus);\n\t\tif (factoryMethod != null) {\n\t\t\treturn source -> invokeMethod(factoryMethod, null, source);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable Function<String, @Nullable Object> findFactoryConstructorExecutable(Class<?> targetType,\n\t\t\tClass<?> parameterType) {\n\t\tConstructor<?> constructor = findFactoryConstructor(targetType, parameterType);\n\t\tif (constructor != null) {\n\t\t\treturn source -> newInstance(constructor, source);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable Method findFactoryMethod(Class<?> targetType, Class<?> parameterType,\n\t\t\tDeprecationStatus deprecationStatus) {\n\t\tvar isFactoryMethod = new IsFactoryMethod(targetType, parameterType, deprecationStatus);\n\t\tList<Method> factoryMethods = findMethods(targetType, isFactoryMethod, BOTTOM_UP);\n\t\tif (factoryMethods.size() == 1) {\n\t\t\treturn factoryMethods.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static @Nullable Constructor<?> findFactoryConstructor(Class<?> targetType, Class<?> parameterType) {\n\t\tList<Constructor<?>> constructors = findConstructors(targetType,\n\t\t\tnew IsFactoryConstructor(targetType, parameterType));\n\t\tif (constructors.size() == 1) {\n\t\t\treturn constructors.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n\tenum DeprecationStatus {\n\t\tINCLUDE_DEPRECATED, EXCLUDE_DEPRECATED\n\t}\n\n\t/**\n\t * {@link Predicate} that determines if the {@link Method} supplied to\n\t * {@link #test(Method)} is a non-private static factory method for the\n\t * supplied {@link #targetType} and {@link #parameterType}.\n\t */\n\trecord IsFactoryMethod(Class<?> targetType, Class<?> parameterType, DeprecationStatus deprecationStatus)\n\t\t\timplements Predicate<Method> {\n\n\t\t@Override\n\t\tpublic boolean test(Method method) {\n\t\t\t// Please do not collapse the following into a single statement.\n\t\t\tif (!method.getReturnType().equals(this.targetType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (isNotStatic(method)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (deprecationStatus == DeprecationStatus.EXCLUDE_DEPRECATED\n\t\t\t\t\t&& method.isAnnotationPresent(Deprecated.class)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn isFactoryCandidate(method, this.parameterType);\n\t\t}\n\t}\n\n\t/**\n\t * {@link Predicate} that determines if the {@link Constructor} supplied to\n\t * {@link #test(Constructor)} is a non-private factory constructor for the\n\t * supplied {@link #targetType} and {@link #parameterType}.\n\t */\n\trecord IsFactoryConstructor(Class<?> targetType, Class<?> parameterType) implements Predicate<Constructor<?>> {\n\n\t\t@Override\n\t\tpublic boolean test(Constructor<?> constructor) {\n\t\t\t// Please do not collapse the following into a single statement.\n\t\t\tif (!constructor.getDeclaringClass().equals(this.targetType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn isFactoryCandidate(constructor, this.parameterType);\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the supplied {@link Executable} is not private and accepts a\n\t * single argument of the supplied parameter type.\n\t */\n\tprivate static boolean isFactoryCandidate(Executable executable, Class<?> parameterType) {\n\t\treturn isNotPrivate(executable) //\n\t\t\t\t&& (executable.getParameterCount() == 1) //\n\t\t\t\t&& (executable.getParameterTypes()[0] == parameterType);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToBooleanConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport org.junit.platform.commons.util.Preconditions;\n\nclass StringToBooleanConverter implements StringToObjectConverter {\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn targetType == Boolean.class;\n\t}\n\n\t@Override\n\tpublic Boolean convert(String source, Class<?> targetType) {\n\t\tboolean isTrue = \"true\".equalsIgnoreCase(source);\n\t\tPreconditions.condition(isTrue || \"false\".equalsIgnoreCase(source),\n\t\t\t() -> \"String must be 'true' or 'false' (ignoring case): \" + source);\n\t\treturn isTrue;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCharacterConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport org.junit.platform.commons.util.Preconditions;\n\nclass StringToCharacterConverter implements StringToObjectConverter {\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn targetType == Character.class;\n\t}\n\n\t@Override\n\tpublic Character convert(String source, Class<?> targetType) {\n\t\tPreconditions.condition(source.length() == 1, () -> \"String must have length of 1: \" + source);\n\t\treturn source.charAt(0);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToClassConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport org.junit.platform.commons.support.ReflectionSupport;\n\nclass StringToClassConverter implements StringToObjectConverter {\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn targetType == Class.class;\n\t}\n\n\t@Override\n\tpublic Object convert(String source, Class<?> targetType) throws Exception {\n\t\tthrow new UnsupportedOperationException(\"Invoke convert(String, Class<?>, ClassLoader) instead\");\n\t}\n\n\t@Override\n\tpublic Class<?> convert(String className, Class<?> targetType, ClassLoader classLoader) throws Exception {\n\t\t// @formatter:off\n\t\treturn ReflectionSupport.tryToLoadClass(className, classLoader)\n\t\t\t\t.getNonNullOrThrow(cause -> new ConversionException(\n\t\t\t\t\t\t\"Failed to convert String \\\"\" + className + \"\\\" to type java.lang.Class\", cause));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToCommonJavaTypesConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport java.io.File;\nimport java.net.MalformedURLException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.nio.file.Path;\nimport java.util.Currency;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.UUID;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.util.Preconditions;\n\nclass StringToCommonJavaTypesConverter implements StringToObjectConverter {\n\n\tprivate static final Map<Class<?>, Function<String, ?>> CONVERTERS = Map.of( //\n\t\t// java.io and java.nio\n\t\tFile.class, File::new, //\n\t\tCharset.class, Charset::forName, //\n\t\tPath.class, Path::of,\n\t\t// java.net\n\t\tURI.class, URI::create, //\n\t\tURL.class, StringToCommonJavaTypesConverter::toURL,\n\t\t// java.util\n\t\tCurrency.class, Currency::getInstance, //\n\t\tLocale.class, Locale::forLanguageTag, //\n\t\tUUID.class, UUID::fromString //\n\t);\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn CONVERTERS.containsKey(targetType);\n\t}\n\n\t@Override\n\tpublic Object convert(String source, Class<?> targetType) throws Exception {\n\t\tFunction<String, ?> converter = Preconditions.notNull(CONVERTERS.get(targetType),\n\t\t\t() -> \"No registered converter for %s\".formatted(targetType.getName()));\n\t\treturn converter.apply(source);\n\t}\n\n\tprivate static URL toURL(String url) {\n\t\ttry {\n\t\t\treturn URI.create(url).toURL();\n\t\t}\n\t\tcatch (MalformedURLException ex) {\n\t\t\tthrow new ConversionException(\"Failed to convert String \\\"\" + url + \"\\\" to type java.net.URL\", ex);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToEnumConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nclass StringToEnumConverter implements StringToObjectConverter {\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn targetType.isEnum();\n\t}\n\n\t@Override\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tpublic Enum convert(String source, Class targetType) throws Exception {\n\t\treturn Enum.valueOf(targetType, source);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToJavaTimeConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static java.util.Map.entry;\n\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.MonthDay;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.Period;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.util.Preconditions;\n\nclass StringToJavaTimeConverter implements StringToObjectConverter {\n\n\tprivate static final Map<Class<?>, Function<String, ?>> CONVERTERS = Map.ofEntries( //\n\t\tentry(Duration.class, Duration::parse), //\n\t\tentry(Instant.class, Instant::parse), //\n\t\tentry(LocalDate.class, LocalDate::parse), //\n\t\tentry(LocalDateTime.class, LocalDateTime::parse), //\n\t\tentry(LocalTime.class, LocalTime::parse), //\n\t\tentry(MonthDay.class, MonthDay::parse), //\n\t\tentry(OffsetDateTime.class, OffsetDateTime::parse), //\n\t\tentry(OffsetTime.class, OffsetTime::parse), //\n\t\tentry(Period.class, Period::parse), //\n\t\tentry(Year.class, Year::parse), //\n\t\tentry(YearMonth.class, YearMonth::parse), //\n\t\tentry(ZonedDateTime.class, ZonedDateTime::parse), //\n\t\tentry(ZoneId.class, ZoneId::of), //\n\t\tentry(ZoneOffset.class, ZoneOffset::of) //\n\t);\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn CONVERTERS.containsKey(targetType);\n\t}\n\n\t@Override\n\tpublic Object convert(String source, Class<?> targetType) throws Exception {\n\t\tFunction<String, ?> converter = Preconditions.notNull(CONVERTERS.get(targetType),\n\t\t\t() -> \"No registered converter for %s\".formatted(targetType.getName()));\n\t\treturn converter.apply(source);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToNumberConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.Map;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.util.Preconditions;\n\nclass StringToNumberConverter implements StringToObjectConverter {\n\n\tprivate static final Map<Class<? extends Number>, Function<String, ? extends Number>> CONVERTERS = Map.of( //\n\t\tByte.class, Byte::decode, //\n\t\tShort.class, Short::decode, //\n\t\tInteger.class, Integer::decode, //\n\t\tLong.class, Long::decode, //\n\t\tFloat.class, Float::valueOf, //\n\t\tDouble.class, Double::valueOf, //\n\t\t// Technically, BigInteger and BigDecimal constructors are covered by\n\t\t// FallbackStringToObjectConverter, but we have explicit conversion\n\t\t// configured for them anyway.\n\t\tBigInteger.class, BigInteger::new, //\n\t\tBigDecimal.class, BigDecimal::new //\n\t);\n\n\t@Override\n\tpublic boolean canConvertTo(Class<?> targetType) {\n\t\treturn CONVERTERS.containsKey(targetType);\n\t}\n\n\t@Override\n\tpublic Number convert(String source, Class<?> targetType) {\n\t\tFunction<String, ? extends Number> converter = Preconditions.notNull(CONVERTERS.get(targetType),\n\t\t\t() -> \"No registered converter for %s\".formatted(targetType.getName()));\n\t\treturn converter.apply(source.replace(\"_\", \"\"));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/StringToObjectConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Internal API for converting arguments of type {@link String} to a specified\n * target type.\n */\ninterface StringToObjectConverter {\n\n\t/**\n\t * Determine if this converter can convert from a {@link String} to the\n\t * supplied target type (which is guaranteed to be a wrapper type for\n\t * primitives &mdash; for example, {@link Integer} instead of {@code int}).\n\t */\n\tboolean canConvertTo(Class<?> targetType);\n\n\t/**\n\t * Convert the supplied {@link String} to the supplied target type (which is\n\t * guaranteed to be a wrapper type for primitives &mdash; for example,\n\t * {@link Integer} instead of {@code int}).\n\t *\n\t * <p>This method will only be invoked if {@link #canConvertTo(Class)}\n\t * returns {@code true} for the same target type.\n\t */\n\t@Nullable\n\tObject convert(String source, Class<?> targetType) throws Exception;\n\n\t/**\n\t * Convert the supplied {@link String} to the supplied target type (which is\n\t * guaranteed to be a wrapper type for primitives &mdash; for example,\n\t * {@link Integer} instead of {@code int}).\n\t *\n\t * <p>This method will only be invoked if {@link #canConvertTo(Class)}\n\t * returns {@code true} for the same target type.\n\t *\n\t * <p>The default implementation simply delegates to {@link #convert(String, Class)}.\n\t * Can be overridden by concrete implementations of this interface that need\n\t * access to the supplied {@link ClassLoader}.\n\t */\n\tdefault @Nullable Object convert(String source, Class<?> targetType, ClassLoader classLoader) throws Exception {\n\t\treturn convert(source, targetType);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/conversion/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Conversion APIs provided by the JUnit Platform.\n */\n\n@NullMarked\npackage org.junit.platform.commons.support.conversion;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common support APIs provided by the JUnit Platform.\n *\n * <p>The purpose of this package is to provide {@code TestEngine} and\n * {@code Extension} authors convenient access to a subset of internal utility\n * methods to assist with their implementation. This prevents re-inventing the\n * wheel and ensures that common tasks are handled in third-party engines and\n * extensions with the same semantics as within the JUnit Platform itself.\n */\n\n@NullMarked\npackage org.junit.platform.commons.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/scanning/ClassFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.scanning;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Class-related predicate used by reflection utilities.\n *\n * @since 1.1\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic class ClassFilter {\n\n\t/**\n\t * Create a {@link ClassFilter} instance that accepts all names but filters classes.\n\t *\n\t * @param classPredicate the class type predicate; never {@code null}\n\t * @return an instance of {@code ClassFilter}; never {@code null}\n\t */\n\tpublic static ClassFilter of(Predicate<Class<?>> classPredicate) {\n\t\treturn of(name -> true, classPredicate);\n\t}\n\n\t/**\n\t * Create a {@link ClassFilter} instance that filters by names and classes.\n\t *\n\t * @param namePredicate the class name predicate; never {@code null}\n\t * @param classPredicate the class type predicate; never {@code null}\n\t * @return an instance of {@code ClassFilter}; never {@code null}\n\t */\n\tpublic static ClassFilter of(Predicate<String> namePredicate, Predicate<Class<?>> classPredicate) {\n\t\treturn new ClassFilter(namePredicate, classPredicate);\n\t}\n\n\tprivate final Predicate<String> namePredicate;\n\tprivate final Predicate<Class<?>> classPredicate;\n\n\tprivate ClassFilter(Predicate<String> namePredicate, Predicate<Class<?>> classPredicate) {\n\t\tthis.namePredicate = checkNotNull(namePredicate, \"name predicate\");\n\t\tthis.classPredicate = checkNotNull(classPredicate, \"class predicate\");\n\t}\n\n\t// Cannot use Preconditions due to package cycle\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tprivate static <T> T checkNotNull(@Nullable T input, String title) {\n\t\tif (input == null) {\n\t\t\tthrow new PreconditionViolationException(title + \" must not be null\");\n\t\t}\n\t\treturn input;\n\t}\n\n\t/**\n\t * Test the given name using the stored name predicate.\n\t *\n\t * @param name the name to test; never {@code null}\n\t * @return {@code true} if the input name matches the predicate, otherwise\n\t * {@code false}\n\t */\n\tpublic boolean match(String name) {\n\t\treturn namePredicate.test(name);\n\t}\n\n\t/**\n\t * Test the given class using the stored class predicate.\n\t *\n\t * @param type the type to test; never {@code null}\n\t * @return {@code true} if the input type matches the predicate, otherwise\n\t * {@code false}\n\t */\n\tpublic boolean match(Class<?> type) {\n\t\treturn classPredicate.test(type);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/scanning/ClasspathScanner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.scanning;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.net.URI;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\n\n/**\n * {@code ClasspathScanner} allows to scan the classpath for classes and\n * resources.\n *\n * <p>An implementation of this interface can be registered via the\n * {@link java.util.ServiceLoader ServiceLoader} mechanism.\n *\n * @since 1.12\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic interface ClasspathScanner {\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied classpath {@code root}\n\t * that match the specified {@code classFilter} filter.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param classFilter the class type filter; never {@code null}\n\t * @return a list of all such classes found; never {@code null}\n\t * but potentially empty\n\t */\n\tList<Class<?>> scanForClassesInPackage(String basePackageName, ClassFilter classFilter);\n\n\t/**\n\t * Find all {@linkplain Class classes} in the supplied classpath {@code root}\n\t * that match the specified {@code classFilter} filter.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param classFilter the class type filter; never {@code null}\n\t * @return a list of all such classes found; never {@code null}\n\t * but potentially empty\n\t */\n\tList<Class<?>> scanForClassesInClasspathRoot(URI root, ClassFilter classFilter);\n\n\t/**\n\t * Find all {@linkplain org.junit.platform.commons.support.Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @deprecated Please implement\n\t * {@link #scanForResourcesInPackage(String, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings({ \"removal\", \"unused\" })\n\tdefault List<org.junit.platform.commons.support.Resource> scanForResourcesInPackage(String basePackageName,\n\t\t\tPredicate<org.junit.platform.commons.support.Resource> resourceFilter) {\n\t\tthrow new UnsupportedOperationException(\"Implement scanForResourcesInPackage(String, ResourceFilter) instead\");\n\t}\n\n\t/**\n\t * Find all {@linkplain org.junit.platform.commons.support.Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @deprecated Please implement\n\t * {@link #scanForResourcesInClasspathRoot(URI, ResourceFilter)} instead\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tdefault List<org.junit.platform.commons.support.Resource> scanForResourcesInClasspathRoot(URI root,\n\t\t\tPredicate<org.junit.platform.commons.support.Resource> resourceFilter) {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\"Implement scanForResourcesInClasspathRoot(URI, ResourceFilter) instead\");\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param basePackageName the name of the base package in which to start\n\t * scanning; must not be {@code null} and must be valid in terms of Java\n\t * syntax\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\t@SuppressWarnings(\"removal\")\n\tdefault List<? extends Resource> scanForResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\treturn scanForResourcesInPackage(basePackageName, resourceFilter::match);\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} in the supplied classpath {@code root}\n\t * that match the specified {@code resourceFilter} predicate.\n\t *\n\t * <p>The classpath scanning algorithm searches recursively in subpackages\n\t * beginning with the root of the classpath.\n\t *\n\t * @param root the URI for the classpath root in which to scan; never\n\t * {@code null}\n\t * @param resourceFilter the resource type filter; never {@code null}\n\t * @return a list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\t@SuppressWarnings(\"removal\")\n\tdefault List<? extends Resource> scanForResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\treturn scanForResourcesInClasspathRoot(root, resourceFilter::match);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/support/scanning/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Classpath scanning APIs provided by the JUnit Platform.\n */\n\n@NullMarked\npackage org.junit.platform.commons.support.scanning;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/AnnotationUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.annotation.Contract;\nimport org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode;\n\n/**\n * Collection of utilities for working with {@linkplain Annotation annotations}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * <p>Some utilities are published via the maintained {@code AnnotationSupport}\n * class.\n *\n * @since 1.0\n * @see Annotation\n * @see AnnotatedElement\n * @see org.junit.platform.commons.support.AnnotationSupport\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class AnnotationUtils {\n\n\tprivate AnnotationUtils() {\n\t\t/* no-op */\n\t}\n\n\tprivate static final ConcurrentHashMap<Class<? extends Annotation>, Boolean> repeatableAnnotationContainerCache = //\n\t\tnew ConcurrentHashMap<>(16);\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the supplied optional\n\t * {@code element}.\n\t *\n\t * @see #findAnnotation(Optional, Class)\n\t * @see org.junit.platform.commons.support.AnnotationSupport#isAnnotated(Optional, Class)\n\t */\n\t@SuppressWarnings(\"NullableOptional\")\n\t@Contract(\"null, _ -> false\")\n\tpublic static boolean isAnnotated(@Nullable Optional<? extends AnnotatedElement> element,\n\t\t\tClass<? extends Annotation> annotationType) {\n\n\t\treturn findAnnotation(element, annotationType).isPresent();\n\t}\n\n\t/**\n\t * @since 1.8\n\t * @see #findAnnotation(Parameter, int, Class)\n\t */\n\tpublic static boolean isAnnotated(Parameter parameter, int index, Class<? extends Annotation> annotationType) {\n\t\treturn findAnnotation(parameter, index, annotationType).isPresent();\n\t}\n\n\t/**\n\t * Determine if an annotation of {@code annotationType} is either\n\t * <em>present</em> or <em>meta-present</em> on the supplied\n\t * {@code element}.\n\t *\n\t * @param element the element on which to search for the annotation; may be\n\t * {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @return {@code true} if the annotation is present or meta-present\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t * @see org.junit.platform.commons.support.AnnotationSupport#isAnnotated(AnnotatedElement, Class)\n\t */\n\t@Contract(\"null, _ -> false\")\n\tpublic static boolean isAnnotated(@Nullable AnnotatedElement element, Class<? extends Annotation> annotationType) {\n\t\treturn findAnnotation(element, annotationType).isPresent();\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findAnnotation(Optional, Class)\n\t */\n\t@SuppressWarnings({ \"OptionalAssignedToNull\", \"NullableOptional\" })\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(\n\t\t\t@Nullable Optional<? extends AnnotatedElement> element, Class<A> annotationType) {\n\n\t\tif (element == null || element.isEmpty()) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\treturn findAnnotation(element.get(), annotationType);\n\t}\n\n\t/**\n\t * @since 1.8\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t */\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(Parameter parameter, int index,\n\t\t\tClass<A> annotationType) {\n\n\t\treturn findAnnotation(getEffectiveAnnotatedParameter(parameter, index), annotationType);\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findAnnotation(AnnotatedElement, Class)\n\t */\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(@Nullable AnnotatedElement element,\n\t\t\tClass<A> annotationType) {\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\tboolean inherited = annotationType.isAnnotationPresent(Inherited.class);\n\t\treturn findAnnotation(element, annotationType, inherited, new HashSet<>());\n\t}\n\n\tprivate static <A extends Annotation> Optional<A> findAnnotation(@Nullable AnnotatedElement element,\n\t\t\tClass<A> annotationType, boolean inherited, Set<Annotation> visited) {\n\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\n\t\tif (element == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t// Directly present?\n\t\tA annotation = element.getDeclaredAnnotation(annotationType);\n\t\tif (annotation != null) {\n\t\t\treturn Optional.of(annotation);\n\t\t}\n\n\t\t// Meta-present on directly present annotations?\n\t\tOptional<A> directMetaAnnotation = findMetaAnnotation(annotationType, element.getDeclaredAnnotations(),\n\t\t\tinherited, visited);\n\t\tif (directMetaAnnotation.isPresent()) {\n\t\t\treturn directMetaAnnotation;\n\t\t}\n\n\t\tif (element instanceof Class<?> clazz) {\n\n\t\t\t// Search on interfaces\n\t\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\t\t\t\tif (ifc != Annotation.class) {\n\t\t\t\t\tOptional<A> annotationOnInterface = findAnnotation(ifc, annotationType, inherited, visited);\n\t\t\t\t\tif (annotationOnInterface.isPresent()) {\n\t\t\t\t\t\treturn annotationOnInterface;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Indirectly present?\n\t\t\t// Search in class hierarchy\n\t\t\tif (inherited) {\n\t\t\t\tClass<?> superclass = clazz.getSuperclass();\n\t\t\t\tif (superclass != null && superclass != Object.class) {\n\t\t\t\t\tOptional<A> annotationOnSuperclass = findAnnotation(superclass, annotationType, inherited, visited);\n\t\t\t\t\tif (annotationOnSuperclass.isPresent()) {\n\t\t\t\t\t\treturn annotationOnSuperclass;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Meta-present on indirectly present annotations?\n\t\treturn findMetaAnnotation(annotationType, element.getAnnotations(), inherited, visited);\n\t}\n\n\tprivate static <A extends Annotation> Optional<A> findMetaAnnotation(Class<A> annotationType,\n\t\t\tAnnotation[] candidates, boolean inherited, Set<Annotation> visited) {\n\n\t\tfor (Annotation candidateAnnotation : candidates) {\n\t\t\tClass<? extends Annotation> candidateAnnotationType = candidateAnnotation.annotationType();\n\t\t\tif (!isInJavaLangAnnotationPackage(candidateAnnotationType) && visited.add(candidateAnnotation)) {\n\t\t\t\tOptional<A> metaAnnotation = findAnnotation(candidateAnnotationType, annotationType, inherited,\n\t\t\t\t\tvisited);\n\t\t\t\tif (metaAnnotation.isPresent()) {\n\t\t\t\t\treturn metaAnnotation;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * Find the first annotation of the specified type that is either\n\t * <em>directly present</em>, <em>meta-present</em>, or <em>indirectly\n\t * present</em> on the supplied class, optionally searching recursively\n\t * through the enclosing class hierarchy if not found on the supplied class.\n\t *\n\t * <p>The enclosing class hierarchy will only be searched above an <em>inner\n\t * class</em> (i.e., a non-static member class).\n\t *\n\t * @param <A> the annotation type\n\t * @param clazz the class on which to search for the annotation; may be {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param searchEnclosingClasses whether the enclosing class hierarchy should\n\t * be searched\n\t * @return an {@code Optional} containing the annotation; never {@code null} but\n\t * potentially empty\n\t * @since 1.8\n\t * @see #findAnnotation(AnnotatedElement, Class)\n\t */\n\tpublic static <A extends Annotation> Optional<A> findAnnotation(@Nullable Class<?> clazz, Class<A> annotationType,\n\t\t\tboolean searchEnclosingClasses) {\n\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\n\t\tif (!searchEnclosingClasses) {\n\t\t\treturn findAnnotation(clazz, annotationType);\n\t\t}\n\n\t\tClass<?> candidate = clazz;\n\t\twhile (candidate != null) {\n\t\t\tOptional<A> annotation = findAnnotation(candidate, annotationType);\n\t\t\tif (annotation.isPresent()) {\n\t\t\t\treturn annotation;\n\t\t\t}\n\t\t\tcandidate = (isInnerClass(candidate) ? candidate.getEnclosingClass() : null);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * @since 1.5\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findRepeatableAnnotations(Optional, Class)\n\t */\n\t@SuppressWarnings({ \"OptionalAssignedToNull\", \"NullableOptional\" })\n\tpublic static <A extends Annotation> List<A> findRepeatableAnnotations(\n\t\t\t@Nullable Optional<? extends AnnotatedElement> element, Class<A> annotationType) {\n\n\t\tif (element == null || element.isEmpty()) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\treturn findRepeatableAnnotations(element.get(), annotationType);\n\t}\n\n\t/**\n\t * @since 1.8\n\t * @see #findRepeatableAnnotations(AnnotatedElement, Class)\n\t */\n\tpublic static <A extends Annotation> List<A> findRepeatableAnnotations(Parameter parameter, int index,\n\t\t\tClass<A> annotationType) {\n\n\t\treturn findRepeatableAnnotations(getEffectiveAnnotatedParameter(parameter, index), annotationType);\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findRepeatableAnnotations(AnnotatedElement, Class)\n\t */\n\tpublic static <A extends Annotation> List<A> findRepeatableAnnotations(@Nullable AnnotatedElement element,\n\t\t\tClass<A> annotationType) {\n\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\tRepeatable repeatable = annotationType.getAnnotation(Repeatable.class);\n\t\tPreconditions.notNull(repeatable, () -> annotationType.getName() + \" must be @Repeatable\");\n\t\tClass<? extends Annotation> containerType = repeatable.value();\n\t\tboolean inherited = containerType.isAnnotationPresent(Inherited.class);\n\n\t\t// Short circuit the search algorithm.\n\t\tif (element == null) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\n\t\t// We use a LinkedHashSet because the search algorithm may discover\n\t\t// duplicates, but we need to maintain the original order.\n\t\tSet<A> found = new LinkedHashSet<>(16);\n\t\tfindRepeatableAnnotations(element, annotationType, containerType, inherited, found, new HashSet<>(16));\n\t\t// unmodifiable since returned from public, non-internal method(s)\n\t\treturn List.copyOf(found);\n\t}\n\n\tprivate static <A extends Annotation> void findRepeatableAnnotations(AnnotatedElement element,\n\t\t\tClass<A> annotationType, Class<? extends Annotation> containerType, boolean inherited, Set<A> found,\n\t\t\tSet<Annotation> visited) {\n\n\t\tif (element instanceof Class<?> clazz) {\n\n\t\t\t// Recurse first in order to support top-down semantics for inherited, repeatable annotations.\n\t\t\tif (inherited) {\n\t\t\t\tClass<?> superclass = clazz.getSuperclass();\n\t\t\t\tif (superclass != null && superclass != Object.class) {\n\t\t\t\t\tfindRepeatableAnnotations(superclass, annotationType, containerType, inherited, found, visited);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Search on interfaces\n\t\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\t\t\t\tif (ifc != Annotation.class) {\n\t\t\t\t\tfindRepeatableAnnotations(ifc, annotationType, containerType, inherited, found, visited);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Find annotations that are directly present or meta-present on directly present annotations.\n\t\tfindRepeatableAnnotations(element.getDeclaredAnnotations(), annotationType, containerType, inherited, found,\n\t\t\tvisited);\n\n\t\t// Find annotations that are indirectly present or meta-present on indirectly present annotations.\n\t\tfindRepeatableAnnotations(element.getAnnotations(), annotationType, containerType, inherited, found, visited);\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"GetClassOnAnnotation\" })\n\tprivate static <A extends Annotation> void findRepeatableAnnotations(Annotation[] candidates,\n\t\t\tClass<A> annotationType, Class<? extends Annotation> containerType, boolean inherited, Set<A> found,\n\t\t\tSet<Annotation> visited) {\n\n\t\tfor (Annotation candidate : candidates) {\n\t\t\tClass<? extends Annotation> candidateAnnotationType = candidate.annotationType();\n\t\t\tif (!isInJavaLangAnnotationPackage(candidateAnnotationType) && visited.add(candidate)) {\n\t\t\t\t// Exact match?\n\t\t\t\tif (candidateAnnotationType.equals(annotationType)) {\n\t\t\t\t\tfound.add(annotationType.cast(candidate));\n\t\t\t\t}\n\t\t\t\t// Container?\n\t\t\t\telse if (candidateAnnotationType.equals(containerType)) {\n\t\t\t\t\t// Note: it's not a legitimate containing annotation type if it doesn't declare\n\t\t\t\t\t// a 'value' attribute that returns an array of the contained annotation type.\n\t\t\t\t\t// See https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.6.3\n\t\t\t\t\tMethod method = ReflectionUtils.tryToGetMethod(containerType, \"value\").getOrThrow(\n\t\t\t\t\t\tcause -> new JUnitException(\n\t\t\t\t\t\t\t\"Container annotation type '%s' must declare a 'value' attribute of type %s[].\".formatted(\n\t\t\t\t\t\t\t\tcontainerType, annotationType),\n\t\t\t\t\t\t\tcause));\n\n\t\t\t\t\tAnnotation[] containedAnnotations = (Annotation[]) ReflectionUtils.invokeMethod(method, candidate);\n\t\t\t\t\tCollections.addAll(found, (A[]) requireNonNull(containedAnnotations));\n\t\t\t\t}\n\t\t\t\t// Nested container annotation?\n\t\t\t\telse if (isRepeatableAnnotationContainer(candidateAnnotationType)) {\n\t\t\t\t\tMethod method = ReflectionUtils.tryToGetMethod(candidateAnnotationType, \"value\").toOptional().get();\n\t\t\t\t\tAnnotation[] containedAnnotations = (Annotation[]) ReflectionUtils.invokeMethod(method, candidate);\n\n\t\t\t\t\tfor (Annotation containedAnnotation : requireNonNull(containedAnnotations)) {\n\t\t\t\t\t\tfindRepeatableAnnotations(containedAnnotation.getClass(), annotationType, containerType,\n\t\t\t\t\t\t\tinherited, found, visited);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Otherwise search recursively through the meta-annotation hierarchy...\n\t\t\t\telse {\n\t\t\t\t\tfindRepeatableAnnotations(candidateAnnotationType, annotationType, containerType, inherited, found,\n\t\t\t\t\t\tvisited);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the supplied annotation type is a container for a repeatable\n\t * annotation.\n\t *\n\t * @since 1.5\n\t */\n\tprivate static boolean isRepeatableAnnotationContainer(Class<? extends Annotation> candidateContainerType) {\n\t\treturn repeatableAnnotationContainerCache.computeIfAbsent(candidateContainerType, candidate -> {\n\t\t\t// @formatter:off\n\t\t\tRepeatable repeatable = Arrays.stream(candidate.getMethods())\n\t\t\t\t\t.filter(attribute -> \"value\".equals(attribute.getName()) && attribute.getReturnType().isArray())\n\t\t\t\t\t.findFirst()\n\t\t\t\t\t.map(attribute -> attribute.getReturnType().getComponentType().getAnnotation(Repeatable.class))\n\t\t\t\t\t.orElse(null);\n\t\t\t// @formatter:on\n\n\t\t\treturn repeatable != null && candidate.equals(repeatable.value());\n\t\t});\n\t}\n\n\t/**\n\t * Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up\n\t * annotations directly on a {@link Parameter} will fail for inner class\n\t * constructors.\n\t *\n\t * <h3>Bug in {@code javac} on JDK versions prior to JDK 9</h3>\n\t *\n\t * <p>The parameter annotations array in the compiled byte code for the user's\n\t * class excludes an entry for the implicit <em>enclosing instance</em>\n\t * parameter for an inner class constructor.\n\t *\n\t * <h3>Workaround</h3>\n\t *\n\t * <p>This method provides a workaround for this off-by-one error by helping\n\t * JUnit maintainers and extension authors to access annotations on the preceding\n\t * {@link Parameter} object (i.e., {@code index - 1}). If the supplied\n\t * {@code index} is zero in such situations this method will return {@code null}\n\t * since the parameter for the implicit <em>enclosing instance</em> will never\n\t * be annotated.\n\t *\n\t * <h4>WARNING</h4>\n\t *\n\t * <p>The {@code AnnotatedElement} returned by this method should never be cast and\n\t * treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},\n\t * {@link Parameter#getType()}, etc.) will not match those for the declared parameter\n\t * at the given index in an inner class constructor for code compiled with JDK 8.\n\t *\n\t * @return the supplied {@code Parameter}, or the <em>effective</em> {@code Parameter}\n\t * if the aforementioned bug is detected, or {@code null} if the bug is detected and\n\t * the supplied {@code index} is {@code 0}\n\t * @since 1.8\n\t */\n\tprivate static @Nullable AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) {\n\t\tPreconditions.notNull(parameter, \"Parameter must not be null\");\n\t\tExecutable executable = parameter.getDeclaringExecutable();\n\n\t\tif (executable instanceof Constructor && isInnerClass(executable.getDeclaringClass())\n\t\t\t\t&& executable.getParameterAnnotations().length == executable.getParameterCount() - 1) {\n\n\t\t\tif (index == 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn executable.getParameters()[index - 1];\n\t\t}\n\n\t\treturn parameter;\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findPublicAnnotatedFields(Class, Class, Class)\n\t */\n\tpublic static List<Field> findPublicAnnotatedFields(Class<?> clazz, Class<?> fieldType,\n\t\t\tClass<? extends Annotation> annotationType) {\n\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(fieldType, \"fieldType must not be null\");\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\n\t\t// @formatter:off\n\t\treturn Arrays.stream(clazz.getFields())\n\t\t\t\t.filter(field -> fieldType.isAssignableFrom(field.getType()) && isAnnotated(field, annotationType))\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findAnnotatedFields(Class, Class, Predicate)\n\t * @see #findAnnotatedFields(Class, Class, Predicate, HierarchyTraversalMode)\n\t */\n\tpublic static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tPredicate<Field> predicate) {\n\n\t\treturn findAnnotatedFields(clazz, annotationType, predicate, HierarchyTraversalMode.TOP_DOWN);\n\t}\n\n\t/**\n\t * Find all {@linkplain Field fields} of the supplied class or interface\n\t * that are annotated or <em>meta-annotated</em> with the specified\n\t * {@code annotationType} and match the specified {@code predicate}.\n\t *\n\t * @param clazz the class or interface in which to find the fields; never {@code null}\n\t * @param annotationType the annotation type to search for; never {@code null}\n\t * @param predicate the field filter; never {@code null}\n\t * @param traversalMode the hierarchy traversal mode; never {@code null}\n\t * @return the list of all such fields found; neither {@code null} nor mutable\n\t */\n\tpublic static List<Field> findAnnotatedFields(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tPredicate<Field> predicate, HierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\tPredicate<Field> annotated = field -> isAnnotated(field, annotationType);\n\n\t\treturn ReflectionUtils.findFields(clazz, annotated.and(predicate), traversalMode);\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.AnnotationSupport#findAnnotatedMethods(Class, Class, org.junit.platform.commons.support.HierarchyTraversalMode)\n\t */\n\tpublic static List<Method> findAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationType,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(annotationType, \"annotationType must not be null\");\n\n\t\treturn ReflectionUtils.findMethods(clazz, method -> isAnnotated(method, annotationType), traversalMode);\n\t}\n\n\tprivate static boolean isInJavaLangAnnotationPackage(Class<? extends Annotation> annotationType) {\n\t\treturn (annotationType != null && annotationType.getName().startsWith(\"java.lang.annotation\"));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassLoaderUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.net.URL;\nimport java.security.CodeSource;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of utilities for working with {@linkplain ClassLoader} and associated tasks.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class ClassLoaderUtils {\n\n\tprivate ClassLoaderUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Get the {@link ClassLoader} for the supplied {@link Class}, falling back\n\t * to the {@link #getDefaultClassLoader() default class loader} if the class\n\t * loader for the supplied class is {@code null}.\n\t * @param clazz the class for which to retrieve the class loader; never {@code null}\n\t * @since 1.10\n\t */\n\tpublic static ClassLoader getClassLoader(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tClassLoader classLoader = clazz.getClassLoader();\n\t\treturn (classLoader != null) ? classLoader : getDefaultClassLoader();\n\t}\n\n\tpublic static ClassLoader getDefaultClassLoader() {\n\t\ttry {\n\t\t\tClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n\t\t\tif (contextClassLoader != null) {\n\t\t\t\treturn contextClassLoader;\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t/* otherwise ignore */\n\t\t}\n\t\treturn ClassLoader.getSystemClassLoader();\n\t}\n\n\t/**\n\t * Get the location from which the supplied object's class was loaded.\n\t *\n\t * @param object the object for whose class the location should be retrieved\n\t * @return an {@code Optional} containing the URL of the class' location; never\n\t * {@code null} but potentially empty\n\t */\n\tpublic static Optional<URL> getLocation(Object object) {\n\t\tPreconditions.notNull(object, \"object must not be null\");\n\t\t// determine class loader\n\t\tClassLoader loader = object.getClass().getClassLoader();\n\t\tif (loader == null) {\n\t\t\tloader = ClassLoader.getSystemClassLoader();\n\t\t\twhile (loader != null && loader.getParent() != null) {\n\t\t\t\tloader = loader.getParent();\n\t\t\t}\n\t\t}\n\t\t// try finding resource by name\n\t\tif (loader != null) {\n\t\t\tString name = object.getClass().getName();\n\t\t\tname = name.replace(\".\", \"/\") + \".class\";\n\t\t\ttry {\n\t\t\t\treturn Optional.ofNullable(loader.getResource(name));\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\t/* otherwise ignore */\n\t\t\t}\n\t\t}\n\t\t// try protection domain\n\t\ttry {\n\t\t\tCodeSource codeSource = object.getClass().getProtectionDomain().getCodeSource();\n\t\t\tif (codeSource != null) {\n\t\t\t\treturn Optional.ofNullable(codeSource.getLocation());\n\t\t\t}\n\t\t}\n\t\tcatch (SecurityException ignore) {\n\t\t\t/* ignore */\n\t\t}\n\t\treturn Optional.empty();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassNamePatternFilterUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Collection of utilities for creating filters based on class names.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.7\n */\n@API(status = INTERNAL, since = \"1.7\")\npublic class ClassNamePatternFilterUtils {\n\n\tprivate ClassNamePatternFilterUtils() {\n\t\t/* no-op */\n\t}\n\n\tpublic static final String ALL_PATTERN = \"*\";\n\n\tpublic static final String BLANK = \"\";\n\n\t/**\n\t * Create a {@link Predicate} that can be used to exclude (i.e., filter out)\n\t * objects of type {@code T} whose fully qualified class names match any of\n\t * the supplied patterns.\n\t *\n\t * @param patterns a comma-separated list of patterns\n\t */\n\tpublic static <T> Predicate<T> excludeMatchingClasses(@Nullable String patterns) {\n\t\treturn matchingClasses(patterns, object -> object.getClass().getName(), FilterType.EXCLUDE);\n\t}\n\n\t/**\n\t * Create a {@link Predicate} that can be used to exclude (i.e., filter out)\n\t * fully qualified class names matching any of the supplied patterns.\n\t *\n\t * @param patterns a comma-separated list of patterns\n\t */\n\tpublic static Predicate<String> excludeMatchingClassNames(@Nullable String patterns) {\n\t\treturn matchingClasses(patterns, Function.identity(), FilterType.EXCLUDE);\n\t}\n\n\t/**\n\t * Create a {@link Predicate} that can be used to include (i.e., filter in)\n\t * objects of type {@code T} whose fully qualified class names match any of\n\t * the supplied patterns.\n\t *\n\t * @param patterns a comma-separated list of patterns\n\t */\n\tpublic static <T> Predicate<T> includeMatchingClasses(@Nullable String patterns) {\n\t\treturn matchingClasses(patterns, object -> object.getClass().getName(), FilterType.INCLUDE);\n\t}\n\n\t/**\n\t * Create a {@link Predicate} that can be used to include (i.e., filter in)\n\t * fully qualified class names matching any of the supplied patterns.\n\t *\n\t * @param patterns a comma-separated list of patterns\n\t */\n\tpublic static Predicate<String> includeMatchingClassNames(@Nullable String patterns) {\n\t\treturn matchingClasses(patterns, Function.identity(), FilterType.INCLUDE);\n\t}\n\n\tprivate enum FilterType {\n\t\tINCLUDE, EXCLUDE\n\t}\n\n\tprivate static <T> Predicate<T> matchingClasses(@Nullable String patterns, Function<T, String> classNameProvider,\n\t\t\tFilterType type) {\n\t\t// @formatter:off\n\t\treturn Optional.ofNullable(patterns)\n\t\t\t\t.filter(StringUtils::isNotBlank)\n\t\t\t\t.map(String::strip)\n\t\t\t\t.map(trimmedPatterns -> createPredicateFromPatterns(trimmedPatterns, classNameProvider, type))\n\t\t\t\t.orElse(type == FilterType.EXCLUDE ? __ -> true : __ -> false);\n\t\t// @formatter:on\n\t}\n\n\tprivate static <T> Predicate<T> createPredicateFromPatterns(String patterns, Function<T, String> classNameProvider,\n\t\t\tFilterType type) {\n\t\tif (ALL_PATTERN.equals(patterns)) {\n\t\t\treturn type == FilterType.INCLUDE ? __ -> true : __ -> false;\n\t\t}\n\n\t\tList<Pattern> patternList = convertToRegularExpressions(patterns);\n\t\treturn object -> {\n\t\t\tboolean isMatchingAnyPattern = patternList.stream().anyMatch(\n\t\t\t\tpattern -> pattern.matcher(classNameProvider.apply(object)).matches());\n\t\t\treturn (type == FilterType.INCLUDE) == isMatchingAnyPattern;\n\t\t};\n\t}\n\n\tprivate static List<Pattern> convertToRegularExpressions(String patterns) {\n\t\t// @formatter:off\n\t\treturn Arrays.stream(patterns.split(\",\"))\n\t\t\t\t.filter(StringUtils::isNotBlank)\n\t\t\t\t.map(String::strip)\n\t\t\t\t.map(ClassNamePatternFilterUtils::replaceRegExElements)\n\t\t\t\t.map(Pattern::compile)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate static String replaceRegExElements(String pattern) {\n\t\treturn Matcher.quoteReplacement(pattern)\n\t\t\t\t// Match \".\" against \".\" and \"$\" since users may declare a \".\" instead of a\n\t\t\t\t// \"$\" as the separator between classes and nested classes.\n\t\t\t\t.replace(\".\", \"[.$]\")\n\t\t\t\t// Convert our \"*\" wildcard into a proper RegEx pattern.\n\t\t\t\t.replace(\"*\", \".+\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClassUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Arrays.stream;\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Collection of utilities for working with {@link Class classes}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class ClassUtils {\n\n\tprivate ClassUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Get the fully qualified name of the supplied class.\n\t *\n\t * <p>This is a null-safe variant of {@link Class#getName()}.\n\t *\n\t * @param clazz the class whose name should be retrieved, potentially\n\t * {@code null}\n\t * @return the fully qualified class name or {@code \"null\"} if the supplied\n\t * class reference is {@code null}\n\t * @since 1.3\n\t * @see #nullSafeToString(Class...)\n\t * @see StringUtils#nullSafeToString(Object)\n\t */\n\tpublic static String nullSafeToString(@Nullable Class<?> clazz) {\n\t\treturn clazz == null ? \"null\" : clazz.getName();\n\t}\n\n\t/**\n\t * Generate a comma-separated list of fully qualified class names for the\n\t * supplied classes.\n\t *\n\t * @param classes the classes whose names should be included in the\n\t * generated string\n\t * @return a comma-separated list of fully qualified class names, or an empty\n\t * string if the supplied class array is {@code null} or empty\n\t * @see #nullSafeToString(Function, Class...)\n\t * @see StringUtils#nullSafeToString(Object)\n\t */\n\tpublic static String nullSafeToString(@Nullable Class<?> @Nullable... classes) {\n\t\treturn nullSafeToString(Class::getName, classes);\n\t}\n\n\t/**\n\t * Generate a comma-separated list of mapped values for the supplied classes.\n\t *\n\t * <p>The values are generated by the supplied {@code mapper}\n\t * (e.g., {@code Class::getName}, {@code Class::getSimpleName}, etc.), unless\n\t * a class reference is {@code null} in which case it will be mapped to\n\t * {@code \"null\"}.\n\t *\n\t * @param mapper the mapper to use; never {@code null}\n\t * @param classes the classes to map\n\t * @return a comma-separated list of mapped values, or an empty string if\n\t * the supplied class array is {@code null} or empty\n\t * @see #nullSafeToString(Class...)\n\t * @see StringUtils#nullSafeToString(Object)\n\t */\n\tpublic static String nullSafeToString(Function<? super Class<?>, ? extends String> mapper,\n\t\t\t@Nullable Class<?> @Nullable... classes) {\n\t\tPreconditions.notNull(mapper, \"Mapping function must not be null\");\n\n\t\tif (classes == null || classes.length == 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn stream(classes) //\n\t\t\t\t.map(clazz -> clazz == null ? \"null\" : mapper.apply(clazz)) //\n\t\t\t\t.collect(joining(\", \"));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathFileVisitor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.nio.file.FileVisitResult.CONTINUE;\n\nimport java.io.IOException;\nimport java.nio.file.FileVisitResult;\nimport java.nio.file.Path;\nimport java.nio.file.SimpleFileVisitor;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.util.function.BiConsumer;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\n\n/**\n * @since 1.11\n */\nclass ClasspathFileVisitor extends SimpleFileVisitor<Path> {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ClasspathFileVisitor.class);\n\n\tprivate final Path basePath;\n\tprivate final BiConsumer<Path, Path> consumer;\n\tprivate final Predicate<Path> filter;\n\n\tClasspathFileVisitor(Path basePath, Predicate<Path> filter, BiConsumer<Path, Path> consumer) {\n\t\tthis.basePath = basePath;\n\t\tthis.filter = filter;\n\t\tthis.consumer = consumer;\n\t}\n\n\t@Override\n\tpublic FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {\n\t\tif (filter.test(file)) {\n\t\t\tconsumer.accept(basePath, file);\n\t\t}\n\t\treturn CONTINUE;\n\t}\n\n\t@Override\n\tpublic FileVisitResult visitFileFailed(Path file, IOException ex) {\n\t\tlogger.warn(ex, () -> \"I/O error visiting file: \" + file);\n\t\treturn CONTINUE;\n\t}\n\n\t@Override\n\tpublic FileVisitResult postVisitDirectory(Path dir, @Nullable IOException ex) {\n\t\tif (ex != null) {\n\t\t\tlogger.warn(ex, () -> \"I/O error visiting directory: \" + dir);\n\t\t}\n\t\treturn CONTINUE;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ClasspathScannerLoader.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.ServiceLoader.Provider;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.scanning.ClasspathScanner;\n\n/**\n * @since 1.12\n */\nclass ClasspathScannerLoader {\n\n\tstatic ClasspathScanner getInstance() {\n\t\tServiceLoader<ClasspathScanner> serviceLoader = ServiceLoader.load(ClasspathScanner.class,\n\t\t\tClassLoaderUtils.getDefaultClassLoader());\n\n\t\tList<Provider<ClasspathScanner>> classpathScanners = serviceLoader.stream().toList();\n\n\t\tif (classpathScanners.size() == 1) {\n\t\t\treturn classpathScanners.get(0).get();\n\t\t}\n\n\t\tif (classpathScanners.size() > 1) {\n\t\t\tthrow new JUnitException(\n\t\t\t\t\"There should not be more than one ClasspathScanner implementation present on the classpath but there were %d: %s\".formatted(\n\t\t\t\t\tclasspathScanners.size(),\n\t\t\t\t\tclasspathScanners.stream().map(Provider::type).map(Class::getName).toList()));\n\t\t}\n\n\t\treturn new DefaultClasspathScanner(ClassLoaderUtils::getDefaultClassLoader, ReflectionUtils::tryToLoadClass);\n\t}\n\n\tprivate ClasspathScannerLoader() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/CloseablePath.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Collections.emptyMap;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.0\n */\nfinal class CloseablePath implements Closeable {\n\n\tprivate static final String FILE_URI_SCHEME = \"file\";\n\tstatic final String JAR_URI_SCHEME = \"jar\";\n\tprivate static final String JAR_FILE_EXTENSION = \".jar\";\n\tprivate static final String JAR_URI_SEPARATOR = \"!/\";\n\n\tprivate static final Closeable NULL_CLOSEABLE = () -> {\n\t};\n\n\tprivate static final ConcurrentMap<URI, ManagedFileSystem> MANAGED_FILE_SYSTEMS = new ConcurrentHashMap<>();\n\n\tprivate final AtomicBoolean closed = new AtomicBoolean();\n\n\tprivate final Path path;\n\tprivate final Closeable delegate;\n\n\tstatic CloseablePath create(URI uri) throws URISyntaxException {\n\t\treturn create(uri, it -> FileSystems.newFileSystem(it, emptyMap()));\n\t}\n\n\tstatic CloseablePath create(URI uri, FileSystemProvider fileSystemProvider) throws URISyntaxException {\n\t\tif (JAR_URI_SCHEME.equals(uri.getScheme())) {\n\t\t\t// Parsing: jar:<url>!/[<entry>], see java.net.JarURLConnection\n\t\t\tString uriString = uri.toString();\n\t\t\tint lastJarUriSeparator = uriString.lastIndexOf(JAR_URI_SEPARATOR);\n\t\t\tString jarUri = uriString.substring(0, lastJarUriSeparator);\n\t\t\tString jarEntry = uriString.substring(lastJarUriSeparator + 1);\n\t\t\treturn createForJarFileSystem(new URI(jarUri), fileSystem -> fileSystem.getPath(jarEntry),\n\t\t\t\tfileSystemProvider);\n\t\t}\n\t\tif (FILE_URI_SCHEME.equals(uri.getScheme()) && uri.getPath().endsWith(JAR_FILE_EXTENSION)) {\n\t\t\treturn createForJarFileSystem(new URI(JAR_URI_SCHEME + ':' + uri),\n\t\t\t\tfileSystem -> fileSystem.getRootDirectories().iterator().next(), fileSystemProvider);\n\t\t}\n\t\treturn new CloseablePath(Path.of(uri), NULL_CLOSEABLE);\n\t}\n\n\tprivate static CloseablePath createForJarFileSystem(URI jarUri, Function<FileSystem, Path> pathProvider,\n\t\t\tFileSystemProvider fileSystemProvider) {\n\t\tManagedFileSystem managedFileSystem = MANAGED_FILE_SYSTEMS.compute(jarUri,\n\t\t\t(__, oldValue) -> oldValue == null ? new ManagedFileSystem(jarUri, fileSystemProvider) : oldValue.retain());\n\t\tPath path = pathProvider.apply(managedFileSystem.fileSystem);\n\t\treturn new CloseablePath(path,\n\t\t\t() -> MANAGED_FILE_SYSTEMS.compute(jarUri, (__, ___) -> managedFileSystem.release()));\n\t}\n\n\tprivate CloseablePath(Path path, Closeable delegate) {\n\t\tthis.path = path;\n\t\tthis.delegate = delegate;\n\t}\n\n\tpublic Path getPath() {\n\t\treturn path;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\tif (closed.compareAndSet(false, true)) {\n\t\t\tdelegate.close();\n\t\t}\n\t}\n\n\tprivate static class ManagedFileSystem {\n\n\t\tprivate final AtomicInteger referenceCount = new AtomicInteger(1);\n\t\tprivate final FileSystem fileSystem;\n\t\tprivate final URI jarUri;\n\n\t\tManagedFileSystem(URI jarUri, FileSystemProvider fileSystemProvider) {\n\t\t\tthis.jarUri = jarUri;\n\t\t\ttry {\n\t\t\t\tfileSystem = fileSystemProvider.newFileSystem(jarUri);\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to create file system for \" + jarUri, e);\n\t\t\t}\n\t\t}\n\n\t\tprivate ManagedFileSystem retain() {\n\t\t\treferenceCount.incrementAndGet();\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate @Nullable ManagedFileSystem release() {\n\t\t\tif (referenceCount.decrementAndGet() == 0) {\n\t\t\t\tclose();\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tprivate void close() {\n\t\t\ttry {\n\t\t\t\tfileSystem.close();\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to close file system for \" + jarUri, e);\n\t\t\t}\n\t\t}\n\t}\n\n\tinterface FileSystemProvider {\n\t\tFileSystem newFileSystem(URI uri) throws IOException;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/CollectionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Spliterator.ORDERED;\nimport static java.util.Spliterators.spliteratorUnknownSize;\nimport static java.util.stream.StreamSupport.stream;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ReflectionUtils.invokeMethod;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.stream.DoubleStream;\nimport java.util.stream.IntStream;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\n\n/**\n * Collection of utilities for working with {@link Collection Collections}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class CollectionUtils {\n\n\tprivate CollectionUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Get the only element of a collection of size 1.\n\t *\n\t * @param collection the collection to get the element from\n\t * @return the only element of the collection\n\t * @throws PreconditionViolationException if the collection is {@code null}\n\t * or does not contain exactly one element\n\t */\n\tpublic static <T> T getOnlyElement(Collection<T> collection) {\n\t\tPreconditions.notNull(collection, \"collection must not be null\");\n\t\tPreconditions.condition(collection.size() == 1,\n\t\t\t() -> \"collection must contain exactly one element: \" + collection);\n\t\treturn firstElement(collection);\n\t}\n\n\t/**\n\t * Get the first element of the supplied collection unless it's empty.\n\t *\n\t * @param collection the collection to get the element from\n\t * @return the first element of the collection; empty if the collection is empty\n\t * @throws PreconditionViolationException if the collection is {@code null}\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static <T extends @Nullable Object> Optional<T> getFirstElement(Collection<T> collection) {\n\t\tPreconditions.notNull(collection, \"collection must not be null\");\n\t\treturn collection.isEmpty() //\n\t\t\t\t? Optional.empty() //\n\t\t\t\t: Optional.ofNullable(firstElement(collection));\n\t}\n\n\tprivate static <T extends @Nullable Object> T firstElement(Collection<T> collection) {\n\t\treturn collection instanceof List<T> list //\n\t\t\t\t? list.get(0) //\n\t\t\t\t: collection.iterator().next();\n\t}\n\n\t/**\n\t * Determine if an instance of the supplied type can be converted into a\n\t * {@code Stream}.\n\t *\n\t * <p>If this method returns {@code true}, {@link #toStream(Object)} can\n\t * successfully convert an object of the specified type into a stream. See\n\t * {@link #toStream(Object)} for supported types.\n\t *\n\t * @param type the type to check; may be {@code null}\n\t * @return {@code true} if an instance of the type can be converted into a stream\n\t * @since 1.9.1\n\t * @see #toStream(Object)\n\t */\n\t@API(status = INTERNAL, since = \"1.9.1\")\n\tpublic static boolean isConvertibleToStream(@Nullable Class<?> type) {\n\t\tif (type == null || type == void.class) {\n\t\t\treturn false;\n\t\t}\n\t\treturn (Stream.class.isAssignableFrom(type)//\n\t\t\t\t|| DoubleStream.class.isAssignableFrom(type)//\n\t\t\t\t|| IntStream.class.isAssignableFrom(type)//\n\t\t\t\t|| LongStream.class.isAssignableFrom(type)//\n\t\t\t\t|| Iterable.class.isAssignableFrom(type)//\n\t\t\t\t|| Iterator.class.isAssignableFrom(type)//\n\t\t\t\t|| Object[].class.isAssignableFrom(type)//\n\t\t\t\t|| (type.isArray() && type.getComponentType().isPrimitive())//\n\t\t\t\t|| findIteratorMethod(type).isPresent());\n\t}\n\n\t/**\n\t * Convert an object of one of the following supported types into a {@code Stream}.\n\t *\n\t * <ul>\n\t * <li>{@link Stream}</li>\n\t * <li>{@link DoubleStream}</li>\n\t * <li>{@link IntStream}</li>\n\t * <li>{@link LongStream}</li>\n\t * <li>{@link Collection}</li>\n\t * <li>{@link Iterable}</li>\n\t * <li>{@link Iterator}</li>\n\t * <li>{@link Object} array</li>\n\t * <li>primitive array</li>\n\t * <li>any type that provides an\n\t * {@link java.util.Iterator Iterator}-returning {@code iterator()} method\n\t * (such as, for example, a {@code kotlin.sequences.Sequence})</li>\n\t * </ul>\n\t *\n\t * @param object the object to convert into a stream; never {@code null}\n\t * @return the resulting stream\n\t * @throws PreconditionViolationException if the supplied object is {@code null}\n\t * or not one of the supported types\n\t * @see #isConvertibleToStream(Class)\n\t */\n\tpublic static Stream<?> toStream(Object object) {\n\t\tPreconditions.notNull(object, \"Object must not be null\");\n\t\tif (object instanceof Stream<?> stream) {\n\t\t\treturn stream;\n\t\t}\n\t\tif (object instanceof DoubleStream stream) {\n\t\t\treturn stream.boxed();\n\t\t}\n\t\tif (object instanceof IntStream stream) {\n\t\t\treturn stream.boxed();\n\t\t}\n\t\tif (object instanceof LongStream stream) {\n\t\t\treturn stream.boxed();\n\t\t}\n\t\tif (object instanceof Collection<?> collection) {\n\t\t\treturn collection.stream();\n\t\t}\n\t\tif (object instanceof Iterable<?> iterable) {\n\t\t\treturn stream(iterable.spliterator(), false);\n\t\t}\n\t\tif (object instanceof Iterator<?> iterator) {\n\t\t\treturn stream(spliteratorUnknownSize(iterator, ORDERED), false);\n\t\t}\n\t\tif (object instanceof Object[] array) {\n\t\t\treturn Arrays.stream(array);\n\t\t}\n\t\tif (object instanceof double[] array) {\n\t\t\treturn DoubleStream.of(array).boxed();\n\t\t}\n\t\tif (object instanceof int[] array) {\n\t\t\treturn IntStream.of(array).boxed();\n\t\t}\n\t\tif (object instanceof long[] array) {\n\t\t\treturn LongStream.of(array).boxed();\n\t\t}\n\t\tif (object.getClass().isArray() && object.getClass().getComponentType().isPrimitive()) {\n\t\t\treturn IntStream.range(0, Array.getLength(object)).mapToObj(i -> Array.get(object, i));\n\t\t}\n\t\treturn tryConvertToStreamByReflection(object);\n\t}\n\n\tprivate static Stream<?> tryConvertToStreamByReflection(Object object) {\n\t\treturn findIteratorMethod(object.getClass()) //\n\t\t\t\t.map(method -> (Iterator<?>) invokeMethod(method, object)) //\n\t\t\t\t.map(iterator -> spliteratorUnknownSize(iterator, ORDERED)) //\n\t\t\t\t.map(spliterator -> stream(spliterator, false)) //\n\t\t\t\t.orElseThrow(() -> new PreconditionViolationException(\n\t\t\t\t\t\"Cannot convert instance of %s into a Stream: %s\".formatted(object.getClass().getName(), object)));\n\t}\n\n\tprivate static Optional<Method> findIteratorMethod(Class<?> type) {\n\t\treturn ReflectionUtils.findMethod(type, \"iterator\") //\n\t\t\t\t.filter(method -> method.getReturnType() == Iterator.class);\n\t}\n\n\t/**\n\t * Call the supplied action on each element of the supplied {@link List} from last to first element.\n\t */\n\t@API(status = INTERNAL, since = \"1.9.2\")\n\tpublic static <T extends @Nullable Object> void forEachInReverseOrder(List<T> list, Consumer<? super T> action) {\n\t\tif (list.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (list.size() == 1) {\n\t\t\taction.accept(list.get(0));\n\t\t\treturn;\n\t\t}\n\t\tfor (ListIterator<T> iterator = list.listIterator(list.size()); iterator.hasPrevious();) {\n\t\t\taction.accept(iterator.previous());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/DefaultClasspathScanner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.commons.util.SearchPathUtils.PACKAGE_SEPARATOR_CHAR;\nimport static org.junit.platform.commons.util.SearchPathUtils.PACKAGE_SEPARATOR_STRING;\nimport static org.junit.platform.commons.util.SearchPathUtils.determineSimpleClassName;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\nimport org.junit.platform.commons.support.scanning.ClasspathScanner;\n\n/**\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\nclass DefaultClasspathScanner implements ClasspathScanner {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DefaultClasspathScanner.class);\n\n\tprivate static final char CLASSPATH_RESOURCE_PATH_SEPARATOR = '/';\n\tprivate static final String CLASSPATH_RESOURCE_PATH_SEPARATOR_STRING = String.valueOf(\n\t\tCLASSPATH_RESOURCE_PATH_SEPARATOR);\n\n\t/**\n\t * Malformed class name InternalError like reported in #401.\n\t */\n\tprivate static final String MALFORMED_CLASS_NAME_ERROR_MESSAGE = \"Malformed class name\";\n\n\tprivate final Supplier<ClassLoader> classLoaderSupplier;\n\n\tprivate final BiFunction<String, ClassLoader, Try<Class<?>>> loadClass;\n\n\tDefaultClasspathScanner(Supplier<ClassLoader> classLoaderSupplier,\n\t\t\tBiFunction<String, ClassLoader, Try<Class<?>>> loadClass) {\n\n\t\tthis.classLoaderSupplier = classLoaderSupplier;\n\t\tthis.loadClass = loadClass;\n\t}\n\n\t@Override\n\tpublic List<Class<?>> scanForClassesInPackage(String basePackageName, ClassFilter classFilter) {\n\t\tPreconditions.condition(\n\t\t\tPackageUtils.DEFAULT_PACKAGE_NAME.equals(basePackageName) || isNotBlank(basePackageName),\n\t\t\t\"basePackageName must not be null or blank\");\n\t\tPreconditions.notNull(classFilter, \"classFilter must not be null\");\n\t\tbasePackageName = basePackageName.strip();\n\n\t\tList<URI> roots = getRootUrisForPackageNameOnClassPathAndModulePath(basePackageName);\n\t\treturn findClassesForUris(roots, basePackageName, classFilter);\n\t}\n\n\t@Override\n\tpublic List<Class<?>> scanForClassesInClasspathRoot(URI root, ClassFilter classFilter) {\n\t\tPreconditions.notNull(root, \"root must not be null\");\n\t\tPreconditions.notNull(classFilter, \"classFilter must not be null\");\n\n\t\treturn findClassesForUri(root, PackageUtils.DEFAULT_PACKAGE_NAME, classFilter);\n\t}\n\n\t@Override\n\tpublic List<Resource> scanForResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\tPreconditions.condition(\n\t\t\tPackageUtils.DEFAULT_PACKAGE_NAME.equals(basePackageName) || isNotBlank(basePackageName),\n\t\t\t\"basePackageName must not be null or blank\");\n\t\tPreconditions.notNull(resourceFilter, \"resourceFilter must not be null\");\n\t\tbasePackageName = basePackageName.strip();\n\n\t\tList<URI> roots = getRootUrisForPackageNameOnClassPathAndModulePath(basePackageName);\n\t\treturn findResourcesForUris(roots, basePackageName, resourceFilter);\n\t}\n\n\t@Override\n\tpublic List<Resource> scanForResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\tPreconditions.notNull(root, \"root must not be null\");\n\t\tPreconditions.notNull(resourceFilter, \"resourceFilter must not be null\");\n\n\t\treturn findResourcesForUri(root, PackageUtils.DEFAULT_PACKAGE_NAME, resourceFilter);\n\t}\n\n\t/**\n\t * Recursively scan for classes in all the supplied source directories.\n\t */\n\tprivate List<Class<?>> findClassesForUris(List<URI> baseUris, String basePackageName, ClassFilter classFilter) {\n\t\t// @formatter:off\n\t\treturn baseUris.stream()\n\t\t\t\t.map(baseUri -> findClassesForUri(baseUri, basePackageName, classFilter))\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.distinct()\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate List<Class<?>> findClassesForUri(URI baseUri, String basePackageName, ClassFilter classFilter) {\n\t\tList<Class<?>> classes = new ArrayList<>();\n\t\t// @formatter:off\n\t\twalkFilesForUri(baseUri, SearchPathUtils::isClassOrSourceFile,\n\t\t\t\t(baseDir, file) ->\n\t\t\t\t\t\tprocessClassFileSafely(baseDir, basePackageName, classFilter, file, classes::add));\n\t\t// @formatter:on\n\t\treturn classes;\n\t}\n\n\t/**\n\t * Recursively scan for resources in all the supplied source directories.\n\t */\n\tprivate List<Resource> findResourcesForUris(List<URI> baseUris, String basePackageName,\n\t\t\tResourceFilter resourceFilter) {\n\t\t// @formatter:off\n\t\treturn baseUris.stream()\n\t\t\t\t.map(baseUri -> findResourcesForUri(baseUri, basePackageName, resourceFilter))\n\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t.distinct()\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate List<Resource> findResourcesForUri(URI baseUri, String basePackageName, ResourceFilter resourceFilter) {\n\t\tList<Resource> resources = new ArrayList<>();\n\t\t// @formatter:off\n\t\twalkFilesForUri(baseUri, SearchPathUtils::isResourceFile,\n\t\t\t\t(baseDir, file) ->\n\t\t\t\t\t\tprocessResourceFileSafely(baseDir, basePackageName, resourceFilter, file, resources::add));\n\t\t// @formatter:on\n\t\treturn resources;\n\t}\n\n\tprivate static void walkFilesForUri(URI baseUri, Predicate<Path> filter, BiConsumer<Path, Path> consumer) {\n\t\ttry (CloseablePath closeablePath = CloseablePath.create(baseUri)) {\n\t\t\tPath baseDir = closeablePath.getPath();\n\t\t\tPreconditions.condition(Files.exists(baseDir), () -> \"baseDir must exist: \" + baseDir);\n\t\t\ttry {\n\t\t\t\tFiles.walkFileTree(baseDir, new ClasspathFileVisitor(baseDir, filter, consumer));\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tlogger.warn(ex, () -> \"I/O error scanning files in \" + baseDir);\n\t\t\t}\n\t\t}\n\t\tcatch (PreconditionViolationException ex) {\n\t\t\tthrow ex;\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.warn(ex, () -> \"Error scanning files for URI \" + baseUri);\n\t\t}\n\t}\n\n\tprivate void processClassFileSafely(Path baseDir, String basePackageName, ClassFilter classFilter, Path file,\n\t\t\tConsumer<Class<?>> classConsumer) {\n\t\ttry {\n\t\t\tString fullyQualifiedClassName = determineFullyQualifiedClassName(baseDir, basePackageName, file);\n\t\t\tif (classFilter.match(fullyQualifiedClassName)) {\n\t\t\t\ttry {\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\tloadClass.apply(fullyQualifiedClassName, getClassLoader())\n\t\t\t\t\t\t\t.toOptional()\n\t\t\t\t\t\t\t.filter(classFilter::match)\n\t\t\t\t\t\t\t.ifPresent(classConsumer);\n\t\t\t\t\t// @formatter:on\n\t\t\t\t}\n\t\t\t\tcatch (InternalError internalError) {\n\t\t\t\t\thandleInternalError(file, fullyQualifiedClassName, internalError);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\thandleThrowable(file, throwable);\n\t\t}\n\t}\n\n\tprivate void processResourceFileSafely(Path baseDir, String basePackageName, ResourceFilter resourceFilter,\n\t\t\tPath resourceFile, Consumer<Resource> resourceConsumer) {\n\t\ttry {\n\t\t\tString fullyQualifiedResourceName = determineFullyQualifiedResourceName(baseDir, basePackageName,\n\t\t\t\tresourceFile);\n\t\t\tResource resource = Resource.of(fullyQualifiedResourceName, resourceFile.toUri());\n\t\t\tif (resourceFilter.match(resource)) {\n\t\t\t\tresourceConsumer.accept(resource);\n\t\t\t}\n\t\t\t// @formatter:on\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\thandleThrowable(resourceFile, throwable);\n\t\t}\n\t}\n\n\tprivate String determineFullyQualifiedClassName(Path baseDir, String basePackageName, Path file) {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\t\t\tbasePackageName,\n\t\t\t\t\tdetermineSubpackageName(baseDir, file),\n\t\t\t\t\tdetermineSimpleClassName(file)\n\t\t\t\t)\n\t\t\t\t.filter(value -> !value.isEmpty()) // Handle default package appropriately.\n\t\t\t\t.collect(joining(PACKAGE_SEPARATOR_STRING));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * The fully qualified resource name is a {@code /}-separated path.\n\t *\n\t * <p>The path is relative to the classpath root in which the resource is\n\t * located.\n\t *\n\t * @return the resource name; never {@code null}\n\t */\n\tprivate String determineFullyQualifiedResourceName(Path baseDir, String basePackageName, Path resourceFile) {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\t\t\tpackagePath(basePackageName),\n\t\t\t\t\tpackagePath(determineSubpackageName(baseDir, resourceFile)),\n\t\t\t\t\tdetermineSimpleResourceName(resourceFile)\n\t\t\t\t)\n\t\t\t\t.filter(value -> !value.isEmpty()) // Handle default package appropriately.\n\t\t\t\t.collect(joining(CLASSPATH_RESOURCE_PATH_SEPARATOR_STRING));\n\t\t// @formatter:on\n\t}\n\n\tprivate String determineSimpleResourceName(Path resourceFile) {\n\t\treturn resourceFile.getFileName().toString();\n\t}\n\n\tprivate String determineSubpackageName(Path baseDir, Path file) {\n\t\tPath relativePath = baseDir.relativize(file.getParent());\n\t\tString pathSeparator = baseDir.getFileSystem().getSeparator();\n\t\treturn relativePath.toString().replace(pathSeparator, PACKAGE_SEPARATOR_STRING);\n\t}\n\n\tprivate void handleInternalError(Path classFile, String fullyQualifiedClassName, InternalError ex) {\n\t\tif (MALFORMED_CLASS_NAME_ERROR_MESSAGE.equals(ex.getMessage())) {\n\t\t\tlogMalformedClassName(classFile, fullyQualifiedClassName, ex);\n\t\t}\n\t\telse {\n\t\t\tlogGenericFileProcessingException(classFile, ex);\n\t\t}\n\t}\n\n\tprivate void handleThrowable(Path classFile, Throwable throwable) {\n\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\tlogGenericFileProcessingException(classFile, throwable);\n\t}\n\n\tprivate void logMalformedClassName(Path classFile, String fullyQualifiedClassName, InternalError ex) {\n\t\ttry {\n\t\t\tlogger.debug(ex,\n\t\t\t\t() -> \"The java.lang.Class loaded from path [%s] has a malformed class name [%s].\".formatted(\n\t\t\t\t\tclassFile.toAbsolutePath(), fullyQualifiedClassName));\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tex.addSuppressed(t);\n\t\t\tlogGenericFileProcessingException(classFile, ex);\n\t\t}\n\t}\n\n\tprivate void logGenericFileProcessingException(Path classpathFile, Throwable throwable) {\n\t\tlogger.debug(throwable,\n\t\t\t() -> \"Failed to load [%s] during classpath scanning.\".formatted(classpathFile.toAbsolutePath()));\n\t}\n\n\tprivate ClassLoader getClassLoader() {\n\t\treturn this.classLoaderSupplier.get();\n\t}\n\n\tprivate List<URI> getRootUrisForPackageNameOnClassPathAndModulePath(String basePackageName) {\n\t\tSet<URI> uriSet = new LinkedHashSet<>(getRootUrisForPackage(basePackageName));\n\t\tif (!basePackageName.isEmpty() && !basePackageName.endsWith(PACKAGE_SEPARATOR_STRING)) {\n\t\t\tgetRootUrisForPackage(basePackageName + PACKAGE_SEPARATOR_STRING).stream() //\n\t\t\t\t\t.map(DefaultClasspathScanner::removeTrailingClasspathResourcePathSeparator) //\n\t\t\t\t\t.forEach(uriSet::add);\n\t\t}\n\t\treturn new ArrayList<>(uriSet);\n\t}\n\n\tprivate static URI removeTrailingClasspathResourcePathSeparator(URI uri) {\n\t\tString string = uri.toString();\n\t\tif (string.endsWith(CLASSPATH_RESOURCE_PATH_SEPARATOR_STRING)) {\n\t\t\treturn URI.create(string.substring(0, string.length() - 1));\n\t\t}\n\t\treturn uri;\n\t}\n\n\tprivate static String packagePath(String packageName) {\n\t\tif (packageName.isEmpty()) {\n\t\t\treturn \"\";\n\t\t}\n\t\treturn packageName.replace(PACKAGE_SEPARATOR_CHAR, CLASSPATH_RESOURCE_PATH_SEPARATOR);\n\t}\n\n\tprivate List<URI> getRootUrisForPackage(String basePackageName) {\n\t\tList<URI> uris = new ArrayList<>();\n\t\ttry {\n\t\t\tEnumeration<URL> resources = getClassLoader().getResources(packagePath(basePackageName));\n\t\t\twhile (resources.hasMoreElements()) {\n\t\t\t\tURL resource = resources.nextElement();\n\t\t\t\turis.add(resource.toURI());\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.warn(ex, () -> \"Error reading URIs from class loader for base package \" + basePackageName);\n\t\t}\n\t\treturn uris;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ExceptionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Collection of utilities for working with exceptions.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class ExceptionUtils {\n\n\tprivate static final String JUNIT_START_PACKAGE_PREFIX = \"org.junit.start.\";\n\n\tprivate static final String JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX = \"org.junit.platform.launcher.\";\n\n\tprivate static final Predicate<String> STACK_TRACE_ELEMENT_FILTER = ClassNamePatternFilterUtils //\n\t\t\t.excludeMatchingClassNames(\"org.junit.*,jdk.internal.reflect.*,sun.reflect.*\");\n\n\tprivate ExceptionUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Throw the supplied {@link Throwable}, <em>masked</em> as an\n\t * unchecked exception.\n\t *\n\t * <p>The supplied {@code Throwable} will not be wrapped. Rather, it\n\t * will be thrown <em>as is</em> using an exploit of the Java language\n\t * that relies on a combination of generics and type erasure to trick\n\t * the Java compiler into believing that the thrown exception is an\n\t * unchecked exception even if it is a checked exception.\n\t *\n\t * <h4>Warning</h4>\n\t *\n\t * <p>This method should be used sparingly.\n\t *\n\t * @param t the {@code Throwable} to throw as an unchecked exception;\n\t * never {@code null}\n\t * @return this method always throws an exception and therefore never\n\t * returns anything; the return type is merely present to allow this\n\t * method to be supplied as the operand in a {@code throw} statement\n\t */\n\t@Contract(\"_ -> fail\")\n\tpublic static RuntimeException throwAsUncheckedException(Throwable t) {\n\t\tPreconditions.notNull(t, \"Throwable must not be null\");\n\t\t// The following line will never actually return an exception but rather\n\t\t// throw t masked as a RuntimeException.\n\t\treturn ExceptionUtils.throwAs(t);\n\t}\n\n\t@Contract(\"_ -> fail\")\n\t@SuppressWarnings({ \"unchecked\", \"TypeParameterUnusedInFormals\" })\n\tprivate static <T extends Throwable> T throwAs(Throwable t) throws T {\n\t\tthrow (T) t;\n\t}\n\n\t/**\n\t * Read the stacktrace of the supplied {@link Throwable} into a String.\n\t */\n\tpublic static String readStackTrace(Throwable throwable) {\n\t\tPreconditions.notNull(throwable, \"Throwable must not be null\");\n\t\tStringWriter stringWriter = new StringWriter();\n\t\ttry (PrintWriter printWriter = new PrintWriter(stringWriter)) {\n\t\t\tthrowable.printStackTrace(printWriter);\n\t\t}\n\t\treturn stringWriter.toString();\n\t}\n\n\t/**\n\t * Prune the stack trace of the supplied {@link Throwable}.\n\t *\n\t * <p>Prune all {@linkplain StackTraceElement stack trace elements} up to one\n\t * of the supplied {@code classNames}. All subsequent elements in the stack\n\t * trace will be retained.\n\t *\n\t * <p>If the {@code classNames} do not match any of the stacktrace elements\n\t * then the {@code org.junit}, {@code jdk.internal.reflect}, and\n\t * {@code sun.reflect} packages are pruned.\n\t *\n\t * <p>Additionally:\n\t * <ul>\n\t *     <li>all elements prior to and including the first JUnit Platform Launcher call will be removed.\n\t *     <li>all elements prior to and including {@code org.junit.start} are kept.\n\t * </ul>\n\t *\n\t * @param throwable the {@code Throwable} whose stack trace should be pruned;\n\t * never {@code null}\n\t * @param classNames the class names that should stop the pruning if encountered;\n\t * never {@code null}\n\t *\n\t * @since 1.10\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static void pruneStackTrace(Throwable throwable, List<String> classNames) {\n\t\tPreconditions.notNull(throwable, \"Throwable must not be null\");\n\t\tPreconditions.notNull(classNames, \"List of class names must not be null\");\n\n\t\tList<StackTraceElement> stackTrace = Arrays.asList(throwable.getStackTrace());\n\t\tList<StackTraceElement> prunedStackTrace = new ArrayList<>();\n\t\tList<StackTraceElement> junitStartStackTrace = new ArrayList<>(0);\n\n\t\tCollections.reverse(stackTrace);\n\n\t\tfor (int i = 0; i < stackTrace.size(); i++) {\n\t\t\tStackTraceElement element = stackTrace.get(i);\n\t\t\tString className = element.getClassName();\n\n\t\t\tif (classNames.contains(className) && !includesJunitStart(stackTrace, i + 1)) {\n\t\t\t\t// We found the test\n\t\t\t\t// everything before that is not informative.\n\t\t\t\tprunedStackTrace.clear();\n\t\t\t\t// Include all elements called by the test\n\t\t\t\tprunedStackTrace.addAll(stackTrace.subList(i, stackTrace.size()));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (className.startsWith(JUNIT_START_PACKAGE_PREFIX)) {\n\t\t\t\tjunitStartStackTrace.addAll(prunedStackTrace);\n\t\t\t\tprunedStackTrace.clear();\n\t\t\t\tjunitStartStackTrace.add(element);\n\t\t\t}\n\t\t\telse if (className.startsWith(JUNIT_PLATFORM_LAUNCHER_PACKAGE_PREFIX)) {\n\t\t\t\tprunedStackTrace.clear();\n\t\t\t}\n\t\t\telse if (STACK_TRACE_ELEMENT_FILTER.test(className)) {\n\t\t\t\tprunedStackTrace.add(element);\n\t\t\t}\n\t\t}\n\n\t\tif (!junitStartStackTrace.isEmpty()) {\n\t\t\tjunitStartStackTrace.addAll(prunedStackTrace);\n\t\t\tprunedStackTrace = junitStartStackTrace;\n\t\t}\n\n\t\tCollections.reverse(prunedStackTrace);\n\t\tthrowable.setStackTrace(prunedStackTrace.toArray(new StackTraceElement[0]));\n\t}\n\n\tprivate static boolean includesJunitStart(List<StackTraceElement> stackTrace, int fromIndex) {\n\t\treturn stackTrace.stream() //\n\t\t\t\t.skip(fromIndex) //\n\t\t\t\t.map(StackTraceElement::getClassName) //\n\t\t\t\t.anyMatch(className -> className.startsWith(JUNIT_START_PACKAGE_PREFIX));\n\t}\n\n\t/**\n\t * Find all causes and suppressed exceptions in the stack trace of the\n\t * supplied {@link Throwable}.\n\t *\n\t * @param rootThrowable the {@code Throwable} to explore; never {@code null}\n\t * @return an immutable list of all throwables found, including the supplied\n\t * one; never {@code null}\n\t *\n\t * @since 1.10\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static List<Throwable> findNestedThrowables(Throwable rootThrowable) {\n\t\tPreconditions.notNull(rootThrowable, \"Throwable must not be null\");\n\n\t\tSet<Throwable> visited = new LinkedHashSet<>();\n\t\tDeque<Throwable> toVisit = new ArrayDeque<>();\n\t\ttoVisit.add(rootThrowable);\n\n\t\twhile (!toVisit.isEmpty()) {\n\t\t\tThrowable current = toVisit.remove();\n\t\t\tboolean isFirstVisit = visited.add(current);\n\t\t\tif (isFirstVisit) {\n\t\t\t\tThrowable cause = current.getCause();\n\t\t\t\tif (cause != null) {\n\t\t\t\t\ttoVisit.add(cause);\n\t\t\t\t}\n\t\t\t\tCollections.addAll(toVisit, current.getSuppressed());\n\t\t\t}\n\t\t}\n\n\t\treturn List.copyOf(visited);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/FunctionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Collection of utilities for working with {@link Function Functions},\n * {@link Predicate Predicates}, etc.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class FunctionUtils {\n\n\tprivate FunctionUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Return a predicate that first applies the specified function and then\n\t * tests the specified predicate against the result of the function.\n\t *\n\t * @param function the function to apply\n\t * @param predicate the predicate to test against the result of the function\n\t */\n\tpublic static <T extends @Nullable Object, V extends @Nullable Object> Predicate<T> where(Function<T, V> function,\n\t\t\tPredicate<? super V> predicate) {\n\t\tPreconditions.notNull(function, \"function must not be null\");\n\t\tPreconditions.notNull(predicate, \"predicate must not be null\");\n\t\treturn input -> predicate.test(function.apply(input));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/KotlinFunctionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static kotlin.jvm.JvmClassMappingKt.getJavaClass;\nimport static kotlin.reflect.full.KCallables.callSuspendBy;\nimport static kotlin.reflect.jvm.KCallablesJvm.isAccessible;\nimport static kotlin.reflect.jvm.KCallablesJvm.setAccessible;\nimport static kotlin.reflect.jvm.KTypesJvm.getJvmErasure;\nimport static kotlin.reflect.jvm.ReflectJvmMapping.getJavaType;\nimport static kotlinx.coroutines.BuildersKt.runBlocking;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\nimport static org.junit.platform.commons.util.ReflectionUtils.EMPTY_CLASS_ARRAY;\nimport static org.junit.platform.commons.util.ReflectionUtils.getUnderlyingCause;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\nimport kotlin.Unit;\nimport kotlin.coroutines.EmptyCoroutineContext;\nimport kotlin.reflect.KFunction;\nimport kotlin.reflect.KParameter;\nimport kotlin.reflect.jvm.ReflectJvmMapping;\n\nclass KotlinFunctionUtils {\n\n\tstatic Class<?> getReturnType(Method method) {\n\t\tvar returnType = getJavaClass(getJvmErasure(getKotlinFunction(method).getReturnType()));\n\t\tif (Unit.class.equals(returnType)) {\n\t\t\treturn void.class;\n\t\t}\n\t\treturn returnType;\n\t}\n\n\tstatic Type getGenericReturnType(Method method) {\n\t\treturn getJavaType(getKotlinFunction(method).getReturnType());\n\t}\n\n\tstatic Parameter[] getParameters(Method method) {\n\t\tvar parameterCount = method.getParameterCount();\n\t\tif (parameterCount == 1) {\n\t\t\treturn new Parameter[0];\n\t\t}\n\t\treturn Arrays.copyOf(method.getParameters(), parameterCount - 1);\n\t}\n\n\tstatic Class<?>[] getParameterTypes(Method method) {\n\t\tvar parameterCount = method.getParameterCount();\n\t\tif (parameterCount == 1) {\n\t\t\treturn EMPTY_CLASS_ARRAY;\n\t\t}\n\t\treturn Arrays.stream(method.getParameterTypes()).limit(parameterCount - 1).toArray(Class<?>[]::new);\n\t}\n\n\tstatic @Nullable Object invokeKotlinFunction(Method method, @Nullable Object target, @Nullable Object[] args) {\n\t\ttry {\n\t\t\treturn invokeKotlinFunction(getKotlinFunction(method), target, args);\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tthrow throwAsUncheckedException(e);\n\t\t}\n\t}\n\n\tprivate static <T extends @Nullable Object> T invokeKotlinFunction(KFunction<T> function, @Nullable Object target,\n\t\t\t@Nullable Object[] args) throws InterruptedException {\n\t\tif (!isAccessible(function)) {\n\t\t\tsetAccessible(function, true);\n\t\t}\n\t\treturn function.callBy(toArgumentMap(target, args, function));\n\t}\n\n\tstatic @Nullable Object invokeKotlinSuspendingFunction(Method method, @Nullable Object target,\n\t\t\t@Nullable Object[] args) {\n\t\ttry {\n\t\t\treturn invokeKotlinSuspendingFunction(getKotlinFunction(method), target, args);\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tthrow throwAsUncheckedException(e);\n\t\t}\n\t}\n\n\tprivate static <T extends @Nullable Object> T invokeKotlinSuspendingFunction(KFunction<T> function,\n\t\t\t@Nullable Object target, @Nullable Object[] args) throws InterruptedException {\n\t\tif (!isAccessible(function)) {\n\t\t\tsetAccessible(function, true);\n\t\t}\n\t\treturn runBlocking(EmptyCoroutineContext.INSTANCE, (__, continuation) -> {\n\t\t\ttry {\n\t\t\t\treturn callSuspendBy(function, toArgumentMap(target, args, function), continuation);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow throwAsUncheckedException(getUnderlyingCause(e));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static Map<KParameter, @Nullable Object> toArgumentMap(@Nullable Object target, @Nullable Object[] args,\n\t\t\tKFunction<?> function) {\n\t\tMap<KParameter, @Nullable Object> arguments = new HashMap<>(args.length + 1);\n\t\tint index = 0;\n\t\tfor (KParameter parameter : function.getParameters()) {\n\t\t\tswitch (parameter.getKind()) {\n\t\t\t\tcase INSTANCE -> arguments.put(parameter, target);\n\t\t\t\tcase VALUE, EXTENSION_RECEIVER -> {\n\t\t\t\t\targuments.put(parameter, args[index]);\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t\tdefault -> throw new JUnitException(\"Unsupported parameter kind: \" + parameter.getKind());\n\t\t\t}\n\t\t}\n\t\treturn arguments;\n\t}\n\n\tprivate static KFunction<?> getKotlinFunction(Method method) {\n\t\treturn Preconditions.notNull(ReflectJvmMapping.getKotlinFunction(method),\n\t\t\t() -> \"Failed to get Kotlin function for method: \" + method);\n\t}\n\n\tprivate KotlinFunctionUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/KotlinReflectionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ReflectionUtils.EMPTY_CLASS_ARRAY;\nimport static org.junit.platform.commons.util.ReflectionUtils.findMethod;\nimport static org.junit.platform.commons.util.ReflectionUtils.isStatic;\nimport static org.junit.platform.commons.util.ReflectionUtils.tryToLoadClass;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.function.Try;\n\n/**\n * Internal Kotlin-specific reflection utilities\n *\n * @since 1.13.3\n */\n@API(status = INTERNAL, since = \"1.13.3\")\npublic class KotlinReflectionUtils {\n\n\tprivate static final String DEFAULT_IMPLS_CLASS_NAME = \"DefaultImpls\";\n\n\tprivate static final @Nullable Class<? extends Annotation> kotlinMetadata;\n\tprivate static final @Nullable Class<? extends Annotation> jvmInline;\n\tprivate static final @Nullable Class<?> kotlinCoroutineContinuation;\n\tprivate static final boolean kotlinReflectPresent;\n\tprivate static final boolean kotlinxCoroutinesPresent;\n\n\tstatic {\n\t\tvar metadata = tryToLoadKotlinMetadataClass();\n\t\tkotlinMetadata = metadata.toOptional().orElse(null);\n\t\tjvmInline = tryToLoadJvmInlineClass().toOptional().orElse(null);\n\t\tkotlinCoroutineContinuation = metadata //\n\t\t\t\t.andThen(__ -> tryToLoadClass(\"kotlin.coroutines.Continuation\")) //\n\t\t\t\t.toOptional() //\n\t\t\t\t.orElse(null);\n\t\tkotlinReflectPresent = metadata.andThen(__ -> tryToLoadClass(\"kotlin.reflect.jvm.ReflectJvmMapping\")) //\n\t\t\t\t.toOptional() //\n\t\t\t\t.isPresent();\n\t\tkotlinxCoroutinesPresent = metadata.andThen(__ -> tryToLoadClass(\"kotlinx.coroutines.BuildersKt\")) //\n\t\t\t\t.toOptional() //\n\t\t\t\t.isPresent();\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Try<Class<? extends Annotation>> tryToLoadKotlinMetadataClass() {\n\t\treturn tryToLoadClass(\"kotlin.Metadata\") //\n\t\t\t\t.andThenTry(it -> (Class<? extends Annotation>) it);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Try<Class<? extends Annotation>> tryToLoadJvmInlineClass() {\n\t\treturn tryToLoadClass(\"kotlin.jvm.JvmInline\") //\n\t\t\t\t.andThenTry(it -> (Class<? extends Annotation>) it);\n\t}\n\n\t/**\n\t * @since 6.0\n\t */\n\t@API(status = INTERNAL, since = \"6.0\")\n\tpublic static boolean isKotlinSuspendingFunction(Method method) {\n\t\tif (!method.isSynthetic() && kotlinCoroutineContinuation != null && isKotlinType(method.getDeclaringClass())) {\n\t\t\tint parameterCount = method.getParameterCount();\n\t\t\treturn parameterCount > 0 //\n\t\t\t\t\t&& method.getParameterTypes()[parameterCount - 1] == kotlinCoroutineContinuation;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Determines whether the supplied class is a {@code DefaultImpls} class\n\t * generated by the Kotlin compiler.\n\t *\n\t * <p>See\n\t * <a href=\"https://kotlinlang.org/docs/interfaces.html#jvm-default-method-generation-for-interface-functions\">Kotlin documentation</a>\n\t * for details.\n\t */\n\tpublic static boolean isKotlinInterfaceDefaultImplsClass(Class<?> clazz) {\n\t\tif (!isKotlinType(clazz) || !DEFAULT_IMPLS_CLASS_NAME.equals(clazz.getSimpleName()) || !isStatic(clazz)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tClass<?> enclosingClass = clazz.getEnclosingClass();\n\t\tif (enclosingClass != null && enclosingClass.isInterface()) {\n\t\t\treturn Arrays.stream(clazz.getDeclaredMethods()) //\n\t\t\t\t\t.anyMatch(method -> isCompilerGeneratedDefaultMethod(method, enclosingClass));\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tprivate static boolean isCompilerGeneratedDefaultMethod(Method method, Class<?> enclosingClass) {\n\t\tif (isStatic(method) && method.getParameterCount() > 0) {\n\t\t\tvar parameterTypes = method.getParameterTypes();\n\t\t\tif (parameterTypes[0] == enclosingClass) {\n\t\t\t\tvar originalParameterTypes = copyWithoutFirst(parameterTypes);\n\t\t\t\treturn findMethod(enclosingClass, method.getName(), originalParameterTypes).isPresent();\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static Class<?>[] copyWithoutFirst(Class<?>[] values) {\n\t\tif (values.length == 1) {\n\t\t\treturn EMPTY_CLASS_ARRAY;\n\t\t}\n\t\tvar result = new Class<?>[values.length - 1];\n\t\tSystem.arraycopy(values, 1, result, 0, result.length);\n\t\treturn result;\n\t}\n\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static boolean isKotlinType(Class<?> clazz) {\n\t\treturn kotlinMetadata != null //\n\t\t\t\t&& clazz.getDeclaredAnnotation(kotlinMetadata) != null;\n\t}\n\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static boolean isKotlinReflectPresent() {\n\t\treturn kotlinReflectPresent;\n\t}\n\n\tpublic static Class<?> getKotlinSuspendingFunctionReturnType(Method method) {\n\t\trequireKotlinReflect(method);\n\t\treturn KotlinFunctionUtils.getReturnType(method);\n\t}\n\n\tpublic static Type getKotlinSuspendingFunctionGenericReturnType(Method method) {\n\t\trequireKotlinReflect(method);\n\t\treturn KotlinFunctionUtils.getGenericReturnType(method);\n\t}\n\n\tpublic static Parameter[] getKotlinSuspendingFunctionParameters(Method method) {\n\t\trequireKotlinReflect(method);\n\t\treturn KotlinFunctionUtils.getParameters(method);\n\t}\n\n\tpublic static Class<?>[] getKotlinSuspendingFunctionParameterTypes(Method method) {\n\t\trequireKotlinReflect(method);\n\t\treturn KotlinFunctionUtils.getParameterTypes(method);\n\t}\n\n\tpublic static @Nullable Object invokeKotlinSuspendingFunction(Method method, @Nullable Object target,\n\t\t\t@Nullable Object[] args) {\n\t\trequireKotlinReflect(method);\n\t\trequireKotlinxCoroutines(method);\n\t\treturn KotlinFunctionUtils.invokeKotlinSuspendingFunction(method, target, args);\n\t}\n\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static boolean isInstanceOfInlineType(@Nullable Object value) {\n\t\treturn jvmInline != null && value != null && value.getClass().getDeclaredAnnotation(jvmInline) != null;\n\t}\n\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static @Nullable Object invokeKotlinFunction(Method method, @Nullable Object target,\n\t\t\t@Nullable Object... args) {\n\t\trequireKotlinReflect(method);\n\t\treturn KotlinFunctionUtils.invokeKotlinFunction(method, target, args);\n\t}\n\n\tprivate static void requireKotlinReflect(Method method) {\n\t\trequireDependency(method, kotlinReflectPresent, \"org.jetbrains.kotlin:kotlin-reflect\");\n\t}\n\n\tprivate static void requireKotlinxCoroutines(Method method) {\n\t\trequireDependency(method, kotlinxCoroutinesPresent, \"org.jetbrains.kotlinx:kotlinx-coroutines-core\");\n\t}\n\n\tprivate static void requireDependency(Method method, boolean condition, String dependencyNotation) {\n\t\tPreconditions.condition(condition,\n\t\t\t() -> (\"Kotlin suspending function [%s] requires %s to be on the classpath or module path. \"\n\t\t\t\t\t+ \"Please add a corresponding dependency.\").formatted(method.toGenericString(),\n\t\t\t\t\t\tdependencyNotation));\n\t}\n\n\tprivate KotlinReflectionUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/LruCache.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.Serial;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.apiguardian.api.API;\n\n/**\n * A simple LRU cache with a maximum size.\n *\n * <p>This class is not thread-safe.\n *\n * @param <K> the type of keys maintained by this cache\n * @param <V> the type of values maintained by this cache\n * @since 1.6\n */\n@API(status = INTERNAL, since = \"1.6\")\npublic class LruCache<K, V> extends LinkedHashMap<K, V> {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final int maxSize;\n\n\t/**\n\t * Create a new LRU cache that maintains at most the supplied number of\n\t * entries.\n\t *\n\t * <p>For optimal use of the internal data structures, you should pick a\n\t * number that's one below a power of two since this is based on a\n\t * {@link java.util.HashMap} and the eldest entry will be evicted after\n\t * adding the entry that increases the {@linkplain #size() size} to be above\n\t * {@code maxSize}.\n\t */\n\tpublic LruCache(int maxSize) {\n\t\tsuper(maxSize + 1, 1, true);\n\t\tthis.maxSize = maxSize;\n\t}\n\n\t@Override\n\tprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {\n\t\treturn size() > maxSize;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ModuleUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.function.Predicate.isEqual;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toSet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.IOException;\nimport java.lang.module.Configuration;\nimport java.lang.module.ModuleFinder;\nimport java.lang.module.ModuleReader;\nimport java.lang.module.ModuleReference;\nimport java.lang.module.ResolvedModule;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\n\n/**\n * Collection of utilities for working with {@code java.lang.Module}\n * and friends.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.1\n */\n@API(status = INTERNAL, since = \"1.1\")\npublic class ModuleUtils {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ModuleUtils.class);\n\n\t/**\n\t * Find all non-system boot modules names.\n\t *\n\t * @return a set of all such module names; never {@code null} but\n\t * potentially empty\n\t */\n\tpublic static Set<String> findAllNonSystemBootModuleNames() {\n\t\t// @formatter:off\n\t\tSet<String> systemModules = ModuleFinder.ofSystem().findAll().stream()\n\t\t\t\t.map(reference -> reference.descriptor().name())\n\t\t\t\t.collect(toSet());\n\t\treturn streamResolvedModules(name -> !systemModules.contains(name))\n\t\t\t\t.map(ResolvedModule::name)\n\t\t\t\t.collect(toCollection(LinkedHashSet::new));\n\t\t// @formatter:on\n\t}\n\n\tpublic static Optional<String> getModuleName(Class<?> type) {\n\t\tPreconditions.notNull(type, \"Class type must not be null\");\n\n\t\treturn Optional.ofNullable(type.getModule().getName());\n\t}\n\n\tpublic static Optional<String> getModuleVersion(Class<?> type) {\n\t\tPreconditions.notNull(type, \"Class type must not be null\");\n\n\t\tModule module = type.getModule();\n\t\treturn module.isNamed() ? module.getDescriptor().rawVersion() : Optional.empty();\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} for the given module name.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param filter the class filter to apply; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t */\n\tpublic static List<Class<?>> findAllClassesInModule(String moduleName, ClassFilter filter) {\n\t\tPreconditions.notBlank(moduleName, \"Module name must not be null or empty\");\n\t\tPreconditions.notNull(filter, \"Class filter must not be null\");\n\n\t\tlogger.debug(() -> \"Looking for classes in module: \" + moduleName);\n\t\t// @formatter:off\n\t\tSet<ModuleReference> moduleReferences = streamResolvedModules(isEqual(moduleName))\n\t\t\t\t.map(ResolvedModule::reference)\n\t\t\t\t.collect(toSet());\n\t\t// @formatter:on\n\t\treturn scan(moduleReferences, filter, ModuleUtils.class.getClassLoader());\n\t}\n\n\t/**\n\t * Find all {@linkplain Class classes} for the given module.\n\t *\n\t * @param module the module to scan; never {@code null} or <em>unnamed</em>\n\t * @param filter the class filter to apply; never {@code null}\n\t * @return an immutable list of all such classes found; never {@code null}\n\t * but potentially empty\n\t * @since 6.1\n\t */\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static List<Class<?>> findAllClassesInModule(Module module, ClassFilter filter) {\n\t\tPreconditions.notNull(module, \"Module must not be null\");\n\t\tPreconditions.condition(module.isNamed(), \"Module must not be unnamed\");\n\t\tPreconditions.notNull(filter, \"Class filter must not be null\");\n\n\t\tString name = module.getName();\n\t\tlogger.debug(() -> \"Looking for classes in module: \" + name);\n\t\tvar reference = module.getLayer().configuration().findModule(name).orElseThrow().reference();\n\t\treturn scan(Set.of(reference), filter, module.getClassLoader());\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} for the given module name.\n\t *\n\t * @param moduleName the name of the module to scan; never {@code null} or\n\t * <em>empty</em>\n\t * @param filter the class filter to apply; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static List<Resource> findAllResourcesInModule(String moduleName, ResourceFilter filter) {\n\t\tPreconditions.notBlank(moduleName, \"Module name must not be null or empty\");\n\t\tPreconditions.notNull(filter, \"Resource filter must not be null\");\n\n\t\tlogger.debug(() -> \"Looking for resources in module: \" + moduleName);\n\t\t// @formatter:off\n\t\tSet<ModuleReference> moduleReferences = streamResolvedModules(isEqual(moduleName))\n\t\t\t\t.map(ResolvedModule::reference)\n\t\t\t\t.collect(toSet());\n\t\t// @formatter:on\n\t\treturn scan(moduleReferences, filter, ModuleUtils.class.getClassLoader());\n\t}\n\n\t/**\n\t * Find all {@linkplain Resource resources} for the given module.\n\t *\n\t * @param module the module to scan; never {@code null} or <em>empty</em>\n\t * @param filter the class filter to apply; never {@code null}\n\t * @return an immutable list of all such resources found; never {@code null}\n\t * but potentially empty\n\t * @since 6.1\n\t */\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic static List<Resource> findAllResourcesInModule(Module module, ResourceFilter filter) {\n\t\tPreconditions.notNull(module, \"Module must not be null\");\n\t\tPreconditions.condition(module.isNamed(), \"Module must not be unnamed\");\n\t\tPreconditions.notNull(filter, \"Resource filter must not be null\");\n\n\t\tString name = module.getName();\n\t\tlogger.debug(() -> \"Looking for resources in module: \" + name);\n\t\tvar reference = module.getLayer().configuration().findModule(name).orElseThrow().reference();\n\t\treturn scan(Set.of(reference), filter, module.getClassLoader());\n\t}\n\n\t/**\n\t * Stream resolved modules from current (or boot) module layer.\n\t */\n\tprivate static Stream<ResolvedModule> streamResolvedModules(Predicate<String> moduleNamePredicate) {\n\t\tModule module = ModuleUtils.class.getModule();\n\t\tModuleLayer layer = module.getLayer();\n\t\tif (layer == null) {\n\t\t\tlogger.config(() -> ModuleUtils.class + \" is a member of \" + module\n\t\t\t\t\t+ \" - using boot layer returned by ModuleLayer.boot() as fall-back.\");\n\t\t\tlayer = ModuleLayer.boot();\n\t\t}\n\t\treturn streamResolvedModules(moduleNamePredicate, layer);\n\t}\n\n\t/**\n\t * Stream resolved modules from the supplied layer.\n\t */\n\tprivate static Stream<ResolvedModule> streamResolvedModules(Predicate<String> moduleNamePredicate,\n\t\t\tModuleLayer layer) {\n\t\tlogger.debug(() -> \"Streaming modules for layer @\" + System.identityHashCode(layer) + \": \" + layer);\n\t\tConfiguration configuration = layer.configuration();\n\t\tlogger.debug(() -> \"Module layer configuration: \" + configuration);\n\t\tStream<ResolvedModule> stream = configuration.modules().stream();\n\t\treturn stream.filter(module -> moduleNamePredicate.test(module.name()));\n\t}\n\n\t/**\n\t * Scan for classes using the supplied set of module references, class\n\t * filter, and loader.\n\t */\n\tprivate static List<Class<?>> scan(Set<ModuleReference> references, ClassFilter filter, ClassLoader loader) {\n\t\tlogger.debug(() -> \"Scanning \" + references.size() + \" module references: \" + references);\n\t\tModuleReferenceClassScanner scanner = new ModuleReferenceClassScanner(filter, loader);\n\t\tList<Class<?>> classes = new ArrayList<>();\n\t\tfor (ModuleReference reference : references) {\n\t\t\tclasses.addAll(scanner.scan(reference));\n\t\t}\n\t\tlogger.debug(() -> \"Found \" + classes.size() + \" classes: \" + classes);\n\t\treturn List.copyOf(classes);\n\t}\n\n\t/**\n\t * Scan for resources using the supplied set of module references, class\n\t * filter, and loader.\n\t */\n\tprivate static List<Resource> scan(Set<ModuleReference> references, ResourceFilter filter, ClassLoader loader) {\n\t\tlogger.debug(() -> \"Scanning \" + references.size() + \" module references: \" + references);\n\t\tModuleReferenceResourceScanner scanner = new ModuleReferenceResourceScanner(filter, loader);\n\t\tList<Resource> resources = new ArrayList<>();\n\t\tfor (ModuleReference reference : references) {\n\t\t\tresources.addAll(scanner.scan(reference));\n\t\t}\n\t\tlogger.debug(() -> \"Found \" + resources.size() + \" resources: \" + resources);\n\t\treturn List.copyOf(resources);\n\t}\n\n\t/**\n\t * {@link ModuleReference} class scanner.\n\t *\n\t * @since 1.1\n\t */\n\tstatic class ModuleReferenceClassScanner {\n\n\t\tprivate final ClassFilter classFilter;\n\t\tprivate final ClassLoader classLoader;\n\n\t\tModuleReferenceClassScanner(ClassFilter classFilter, ClassLoader classLoader) {\n\t\t\tthis.classFilter = classFilter;\n\t\t\tthis.classLoader = classLoader;\n\t\t}\n\n\t\t/**\n\t\t * Scan module reference for classes that potentially contain testable methods.\n\t\t */\n\t\tList<Class<?>> scan(ModuleReference reference) {\n\t\t\ttry (ModuleReader reader = reference.open()) {\n\t\t\t\ttry (Stream<String> names = reader.list()) {\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\treturn names.filter(name -> !name.endsWith(\"/\")) // remove directories\n\t\t\t\t\t\t\t.map(Path::of)\n\t\t\t\t\t\t\t.filter(SearchPathUtils::isClassOrSourceFile)\n\t\t\t\t\t\t\t.map(SearchPathUtils::determineFullyQualifiedClassName)\n\t\t\t\t\t\t\t.filter(classFilter::match)\n\t\t\t\t\t\t\t.<Class<?>> map(this::loadClassUnchecked)\n\t\t\t\t\t\t\t.filter(classFilter::match)\n\t\t\t\t\t\t\t.toList();\n\t\t\t\t\t// @formatter:on\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new JUnitException(\"Failed to read contents of \" + reference + \".\", e);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Load class by its binary name.\n\t\t *\n\t\t * @see ClassLoader#loadClass(String)\n\t\t */\n\t\tprivate Class<?> loadClassUnchecked(String binaryName) {\n\t\t\ttry {\n\t\t\t\treturn classLoader.loadClass(binaryName);\n\t\t\t}\n\t\t\tcatch (ClassNotFoundException e) {\n\t\t\t\tthrow new JUnitException(\"Failed to load class with name '\" + binaryName + \"'.\", e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link ModuleReference} resource class scanner.\n\t *\n\t * @since 1.11\n\t */\n\tstatic class ModuleReferenceResourceScanner {\n\n\t\tprivate final ResourceFilter resourceFilter;\n\t\tprivate final ClassLoader classLoader;\n\n\t\tModuleReferenceResourceScanner(ResourceFilter resourceFilter, ClassLoader classLoader) {\n\t\t\tthis.resourceFilter = resourceFilter;\n\t\t\tthis.classLoader = classLoader;\n\t\t}\n\n\t\t/**\n\t\t * Scan module reference for resources that potentially contain testable resources.\n\t\t */\n\t\tList<Resource> scan(ModuleReference reference) {\n\t\t\ttry (ModuleReader reader = reference.open()) {\n\t\t\t\ttry (Stream<String> names = reader.list()) {\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\treturn names.filter(name -> !name.endsWith(\".class\"))\n\t\t\t\t\t\t\t.map(this::loadResourceUnchecked)\n\t\t\t\t\t\t\t.filter(resourceFilter::match)\n\t\t\t\t\t\t\t.toList();\n\t\t\t\t\t// @formatter:on\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new JUnitException(\"Failed to read contents of \" + reference + \".\", e);\n\t\t\t}\n\t\t}\n\n\t\tprivate Resource loadResourceUnchecked(String binaryName) {\n\t\t\ttry {\n\t\t\t\tURI uri = requireNonNull(classLoader.getResource(binaryName)).toURI();\n\t\t\t\treturn Resource.of(binaryName, uri);\n\t\t\t}\n\t\t\tcatch (NullPointerException | URISyntaxException e) {\n\t\t\t\tthrow new JUnitException(\"Failed to load resource with name '\" + binaryName + \"'.\", e);\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate ModuleUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/PackageUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.File;\nimport java.net.URL;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.jar.Attributes;\nimport java.util.jar.JarFile;\nimport java.util.jar.Manifest;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of utilities for working with {@linkplain Package packages}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class PackageUtils {\n\n\tprivate PackageUtils() {\n\t\t/* no-op */\n\t}\n\n\tpublic static final String DEFAULT_PACKAGE_NAME = \"\";\n\n\t/**\n\t * Get the package attribute for the supplied {@code type} using the\n\t * supplied {@code function}.\n\t *\n\t * <p>This method only returns a non-empty {@link Optional} value holder\n\t * if the class loader for the supplied type created a {@link Package}\n\t * object and the supplied function does not return {@code null} when\n\t * applied.\n\t *\n\t * @param type the type to get the package attribute for\n\t * @param function a function that computes the package attribute value\n\t * (e.g., {@code Package::getImplementationTitle}); never {@code null}\n\t * @return an {@code Optional} containing the attribute value; never\n\t * {@code null} but potentially empty\n\t * @throws org.junit.platform.commons.PreconditionViolationException if the\n\t * supplied type or function is {@code null}\n\t * @see Class#getPackage()\n\t * @see Package#getImplementationTitle()\n\t * @see Package#getImplementationVersion()\n\t */\n\tpublic static Optional<String> getAttribute(Class<?> type, Function<Package, String> function) {\n\t\tPreconditions.notNull(type, \"type must not be null\");\n\t\tPreconditions.notNull(function, \"function must not be null\");\n\t\treturn Optional.ofNullable(type.getPackage()).map(function);\n\t}\n\n\t/**\n\t * Get the value of the specified attribute name, specified as a string,\n\t * or an empty {@link Optional} if the attribute was not found. The attribute\n\t * name is case-insensitive.\n\t *\n\t * <p>This method also returns an empty {@link Optional} value holder\n\t * if any exception is caught while loading the manifest file via the\n\t * JAR file of the specified type.\n\t *\n\t * @param type the type to get the attribute for\n\t * @param name the attribute name as a string\n\t * @return an {@code Optional} containing the attribute value; never\n\t * {@code null} but potentially empty\n\t * @throws org.junit.platform.commons.PreconditionViolationException if the\n\t * supplied type is {@code null} or the specified name is blank\n\t * @see Manifest#getMainAttributes()\n\t */\n\tpublic static Optional<String> getAttribute(Class<?> type, String name) {\n\t\tPreconditions.notNull(type, \"type must not be null\");\n\t\tPreconditions.notBlank(name, \"name must not be blank\");\n\t\ttry {\n\t\t\tURL jarUrl = type.getProtectionDomain().getCodeSource().getLocation();\n\t\t\ttry (JarFile jarFile = new JarFile(new File(jarUrl.toURI()))) {\n\t\t\t\tAttributes mainAttributes = jarFile.getManifest().getMainAttributes();\n\t\t\t\treturn Optional.ofNullable(mainAttributes.getValue(name));\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n\t/**\n\t * Get the module or implementation version for the supplied {@code type}.\n\t *\n\t * <p>The former is only available if the type is part of a versioned module\n\t * on the module path; the latter only if the type is part of a JAR file with\n\t * a manifest that contains an {@code Implementation-Version} attribute.\n\t *\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static Optional<String> getModuleOrImplementationVersion(Class<?> type) {\n\t\tOptional<String> moduleVersion = ModuleUtils.getModuleVersion(type);\n\t\tif (moduleVersion.isPresent()) {\n\t\t\treturn moduleVersion;\n\t\t}\n\t\treturn getAttribute(type, Package::getImplementationVersion);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/Preconditions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Collection of utilities for asserting preconditions for method and\n * constructor arguments.\n *\n * <p>Each method in this class throws a {@link PreconditionViolationException}\n * if the precondition is violated.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class Preconditions {\n\n\tprivate Preconditions() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Assert that the supplied {@link Object} is not {@code null}.\n\t *\n\t * @param object the object to check\n\t * @param message precondition violation message\n\t * @return the supplied object as a convenience\n\t * @throws PreconditionViolationException if the supplied object is {@code null}\n\t * @see #notNull(Object, Supplier)\n\t */\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tpublic static <T> T notNull(@Nullable T object, String message) throws PreconditionViolationException {\n\t\tcondition(object != null, message);\n\t\treturn object;\n\t}\n\n\t/**\n\t * Assert that the supplied {@link Object} is not {@code null}.\n\t *\n\t * @param object the object to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied object as a convenience\n\t * @throws PreconditionViolationException if the supplied object is {@code null}\n\t * @see #condition(boolean, Supplier)\n\t */\n\t@Contract(\"null, _ -> fail; !null, _ -> param1\")\n\tpublic static <T> T notNull(@Nullable T object, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tcondition(object != null, messageSupplier);\n\t\treturn object;\n\t}\n\n\t/**\n\t * Assert that the supplied array is neither {@code null} nor <em>empty</em>.\n\t *\n\t * @param array the array to check\n\t * @param message precondition violation message\n\t * @return the supplied array as a convenience\n\t * @throws PreconditionViolationException if the supplied array is\n\t * {@code null} or <em>empty</em>\n\t * @since 1.9\n\t * @see #condition(boolean, String)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static int[] notEmpty(int @Nullable [] array, String message) throws PreconditionViolationException {\n\t\tcondition(array != null && array.length > 0, message);\n\t\treturn array;\n\t}\n\n\t/**\n\t * Assert that the supplied array is neither {@code null} nor <em>empty</em>.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * array contains any {@code null} elements.\n\t *\n\t * @param array the array to check\n\t * @param message precondition violation message\n\t * @return the supplied array as a convenience\n\t * @throws PreconditionViolationException if the supplied array is\n\t * {@code null} or <em>empty</em>\n\t * @see #containsNoNullElements(Object[], String)\n\t * @see #condition(boolean, String)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static <T> T[] notEmpty(T @Nullable [] array, String message) throws PreconditionViolationException {\n\t\tcondition(array != null && array.length > 0, message);\n\t\treturn array;\n\t}\n\n\t/**\n\t * Assert that the supplied array is neither {@code null} nor <em>empty</em>.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * array contains any {@code null} elements.\n\t *\n\t * @param array the array to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied array as a convenience\n\t * @throws PreconditionViolationException if the supplied array is\n\t * {@code null} or <em>empty</em>\n\t * @see #containsNoNullElements(Object[], String)\n\t * @see #condition(boolean, String)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static <T> T[] notEmpty(T @Nullable [] array, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tcondition(array != null && array.length > 0, messageSupplier);\n\t\treturn array;\n\t}\n\n\t/**\n\t * Assert that the supplied {@link Collection} is neither {@code null} nor empty.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection contains any {@code null} elements.\n\t *\n\t * @param collection the collection to check\n\t * @param message precondition violation message\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection is {@code null} or empty\n\t * @see #containsNoNullElements(Collection, String)\n\t * @see #condition(boolean, String)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static <T extends Collection<?>> T notEmpty(@Nullable T collection, String message)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tcondition(collection != null && !collection.isEmpty(), message);\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied {@link Collection} is neither {@code null} nor empty.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection contains any {@code null} elements.\n\t *\n\t * @param collection the collection to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection is {@code null} or empty\n\t * @see #containsNoNullElements(Collection, String)\n\t * @see #condition(boolean, String)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static <T extends Collection<?>> T notEmpty(@Nullable T collection, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tcondition(collection != null && !collection.isEmpty(), messageSupplier);\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied array contains no {@code null} elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * array is {@code null} or <em>empty</em>.\n\t *\n\t * @param array the array to check\n\t * @param message precondition violation message\n\t * @return the supplied array as a convenience\n\t * @throws PreconditionViolationException if the supplied array contains\n\t * any {@code null} elements\n\t * @see #notNull(Object, String)\n\t */\n\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T> T @Nullable [] containsNoNullElements(T @Nullable [] array, String message)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tif (array != null) {\n\t\t\tArrays.stream(array).forEach(object -> notNull(object, message));\n\t\t}\n\t\treturn array;\n\t}\n\n\t/**\n\t * Assert that the supplied array contains no {@code null} elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * array is {@code null} or <em>empty</em>.\n\t *\n\t * @param array the array to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied array as a convenience\n\t * @throws PreconditionViolationException if the supplied array contains\n\t * any {@code null} elements\n\t * @see #notNull(Object, String)\n\t */\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T> T @Nullable [] containsNoNullElements(T @Nullable [] array, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tif (array != null) {\n\t\t\tArrays.stream(array).forEach(object -> notNull(object, messageSupplier));\n\t\t}\n\t\treturn array;\n\t}\n\n\t/**\n\t * Assert that the supplied collection contains no {@code null} elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection is {@code null} or <em>empty</em>.\n\t *\n\t * @param collection the collection to check\n\t * @param message precondition violation message\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection contains\n\t * any {@code null} elements\n\t * @see #notNull(Object, String)\n\t */\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T extends Collection<?>> @Nullable T containsNoNullElements(@Nullable T collection, String message)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tif (collection != null) {\n\t\t\tcollection.forEach(object -> notNull(object, message));\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied collection contains no {@code null} elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection is {@code null} or <em>empty</em>.\n\t *\n\t * @param collection the collection to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection contains\n\t * any {@code null} elements\n\t * @see #notNull(Object, String)\n\t */\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T extends Collection<?>> @Nullable T containsNoNullElements(@Nullable T collection,\n\t\t\tSupplier<String> messageSupplier) throws PreconditionViolationException {\n\n\t\tif (collection != null) {\n\t\t\tcollection.forEach(object -> notNull(object, messageSupplier));\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied collection contains no blank elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection is {@code null} or <em>empty</em>.\n\t *\n\t * @param collection the collection to check\n\t * @param message precondition violation message\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection contains\n\t * any blank elements\n\t * @since 6.1\n\t * @see #notBlank(String, String)\n\t */\n\t@API(status = INTERNAL, since = \"6.1\")\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T extends Collection<String>> @Nullable T containsNoBlankElements(@Nullable T collection,\n\t\t\tString message) throws PreconditionViolationException {\n\n\t\tif (collection != null) {\n\t\t\tcollection.forEach(object -> notBlank(object, message));\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied collection contains no blank elements.\n\t *\n\t * <p><strong>WARNING</strong>: this method does NOT check if the supplied\n\t * collection is {@code null} or <em>empty</em>.\n\t *\n\t * @param collection the collection to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied collection as a convenience\n\t * @throws PreconditionViolationException if the supplied collection contains\n\t * any blank elements\n\t * @since 6.1\n\t * @see #notBlank(String, String)\n\t */\n\t@API(status = INTERNAL, since = \"6.1\")\n\t@Contract(\"null, _ -> null\")\n\tpublic static <T extends Collection<String>> @Nullable T containsNoBlankElements(@Nullable T collection,\n\t\t\tSupplier<String> messageSupplier) throws PreconditionViolationException {\n\n\t\tif (collection != null) {\n\t\t\tcollection.forEach(object -> notBlank(object, messageSupplier));\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * Assert that the supplied {@link String} is not blank.\n\t *\n\t * <p>A {@code String} is <em>blank</em> if it is {@code null} or consists\n\t * only of whitespace characters.\n\t *\n\t * @param str the string to check\n\t * @param message precondition violation message\n\t * @return the supplied string as a convenience\n\t * @throws PreconditionViolationException if the supplied string is blank\n\t * @see #notBlank(String, Supplier)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static String notBlank(@Nullable String str, String message) throws PreconditionViolationException {\n\t\tcondition(StringUtils.isNotBlank(str), message);\n\t\treturn str;\n\t}\n\n\t/**\n\t * Assert that the supplied {@link String} is not blank.\n\t *\n\t * <p>A {@code String} is <em>blank</em> if it is {@code null} or consists\n\t * only of whitespace characters.\n\t *\n\t * @param str the string to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @return the supplied string as a convenience\n\t * @throws PreconditionViolationException if the supplied string is blank\n\t * @see StringUtils#isNotBlank(String)\n\t * @see #condition(boolean, Supplier)\n\t */\n\t@Contract(\"null, _ -> fail\")\n\tpublic static String notBlank(@Nullable String str, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tcondition(StringUtils.isNotBlank(str), messageSupplier);\n\t\treturn str;\n\t}\n\n\t/**\n\t * Assert that the supplied {@code predicate} is {@code true}.\n\t *\n\t * @param predicate the predicate to check\n\t * @param message precondition violation message\n\t * @throws PreconditionViolationException if the predicate is {@code false}\n\t * @see #condition(boolean, Supplier)\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void condition(boolean predicate, String message) throws PreconditionViolationException {\n\t\tif (!predicate) {\n\t\t\tthrow new PreconditionViolationException(message);\n\t\t}\n\t}\n\n\t/**\n\t * Assert that the supplied {@code predicate} is {@code true}.\n\t *\n\t * @param predicate the predicate to check\n\t * @param messageSupplier precondition violation message supplier\n\t * @throws PreconditionViolationException if the predicate is {@code false}\n\t */\n\t@Contract(\"false, _ -> fail\")\n\tpublic static void condition(boolean predicate, Supplier<String> messageSupplier)\n\t\t\tthrows PreconditionViolationException {\n\n\t\tif (!predicate) {\n\t\t\tthrow new PreconditionViolationException(messageSupplier.get());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Collections.synchronizedMap;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toSet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN;\n\nimport java.io.File;\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.GenericArrayType;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.IdentityHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\nimport org.junit.platform.commons.support.scanning.ClasspathScanner;\n\n/**\n * Collection of utilities for working with the Java reflection APIs.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * <p>Some utilities are published via the maintained {@code ReflectionSupport}\n * class.\n *\n * @since 1.0\n * @see org.junit.platform.commons.support.ReflectionSupport\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class ReflectionUtils {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(ReflectionUtils.class);\n\n\tprivate ReflectionUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Modes in which a hierarchy can be traversed &mdash; for example, when\n\t * searching for methods or fields within a class hierarchy.\n\t */\n\tpublic enum HierarchyTraversalMode {\n\n\t\t/**\n\t\t * Traverse the hierarchy using top-down semantics.\n\t\t */\n\t\tTOP_DOWN,\n\n\t\t/**\n\t\t * Traverse the hierarchy using bottom-up semantics.\n\t\t */\n\t\tBOTTOM_UP\n\t}\n\n\t// Pattern: \"java.lang.String[]\", \"int[]\", \"int[][][][]\", etc.\n\t// ?> => non-capturing atomic group\n\t// ++ => possessive quantifier\n\tprivate static final Pattern SOURCE_CODE_SYNTAX_ARRAY_PATTERN = Pattern.compile(\"^([^\\\\[\\\\]]+)((?>\\\\[\\\\])++)$\");\n\n\tstatic final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];\n\n\tprivate static final ClasspathScanner classpathScanner = ClasspathScannerLoader.getInstance();\n\n\t/**\n\t * Cache for equivalent methods on an interface implemented by the declaring class.\n\t * @since 1.11\n\t * @see #getInterfaceMethodIfPossible(Method, Class)\n\t */\n\tprivate static final Map<Method, Method> interfaceMethodCache = synchronizedMap(new LruCache<>(255));\n\n\t/**\n\t * Set of fully qualified class names for which no cycles have been detected\n\t * in inner class hierarchies.\n\t * <p>This serves as a cache to avoid repeated cycle detection for classes\n\t * that have already been checked.\n\t * @since 1.6\n\t * @see #detectInnerClassCycle(Class, CycleErrorHandling)\n\t */\n\tprivate static final Set<String> noCyclesDetectedCache = ConcurrentHashMap.newKeySet();\n\n\t/**\n\t * Internal cache of common class names mapped to their types.\n\t */\n\tprivate static final Map<String, Class<?>> classNameToTypeMap;\n\n\t/**\n\t * Internal cache of primitive types mapped to their wrapper types.\n\t */\n\tprivate static final Map<Class<?>, Class<?>> primitiveToWrapperMap;\n\n\tstatic {\n\t\t// @formatter:off\n\t\tList<Class<?>> commonTypes = Arrays.asList(\n\t\t\tboolean.class,\n\t\t\tbyte.class,\n\t\t\tchar.class,\n\t\t\tshort.class,\n\t\t\tint.class,\n\t\t\tlong.class,\n\t\t\tfloat.class,\n\t\t\tdouble.class,\n\t\t\tvoid.class,\n\n\t\t\tboolean[].class,\n\t\t\tbyte[].class,\n\t\t\tchar[].class,\n\t\t\tshort[].class,\n\t\t\tint[].class,\n\t\t\tlong[].class,\n\t\t\tfloat[].class,\n\t\t\tdouble[].class,\n\n\t\t\tboolean[][].class,\n\t\t\tbyte[][].class,\n\t\t\tchar[][].class,\n\t\t\tshort[][].class,\n\t\t\tint[][].class,\n\t\t\tlong[][].class,\n\t\t\tfloat[][].class,\n\t\t\tdouble[][].class,\n\n\t\t\tBoolean.class,\n\t\t\tByte.class,\n\t\t\tCharacter.class,\n\t\t\tShort.class,\n\t\t\tInteger.class,\n\t\t\tLong.class,\n\t\t\tFloat.class,\n\t\t\tDouble.class,\n\t\t\tVoid.class,\n\t\t\tString.class,\n\n\t\t\tBoolean[].class,\n\t\t\tByte[].class,\n\t\t\tCharacter[].class,\n\t\t\tShort[].class,\n\t\t\tInteger[].class,\n\t\t\tLong[].class,\n\t\t\tFloat[].class,\n\t\t\tDouble[].class,\n\t\t\tString[].class,\n\n\t\t\tBoolean[][].class,\n\t\t\tByte[][].class,\n\t\t\tCharacter[][].class,\n\t\t\tShort[][].class,\n\t\t\tInteger[][].class,\n\t\t\tLong[][].class,\n\t\t\tFloat[][].class,\n\t\t\tDouble[][].class,\n\t\t\tString[][].class\n\t\t);\n\t\t// @formatter:on\n\n\t\tMap<String, Class<?>> classNamesToTypes = new HashMap<>(64);\n\n\t\tcommonTypes.forEach(type -> {\n\t\t\tclassNamesToTypes.put(type.getName(), type);\n\t\t\tclassNamesToTypes.put(type.getCanonicalName(), type);\n\t\t});\n\n\t\tclassNameToTypeMap = Collections.unmodifiableMap(classNamesToTypes);\n\n\t\tprimitiveToWrapperMap = createPrimitivesToWrapperMap();\n\t}\n\n\t@SuppressWarnings(\"IdentityHashMapUsage\")\n\tprivate static Map<Class<?>, Class<?>> createPrimitivesToWrapperMap() {\n\t\tMap<Class<?>, Class<?>> primitivesToWrappers = new IdentityHashMap<>(8);\n\n\t\tprimitivesToWrappers.put(boolean.class, Boolean.class);\n\t\tprimitivesToWrappers.put(byte.class, Byte.class);\n\t\tprimitivesToWrappers.put(char.class, Character.class);\n\t\tprimitivesToWrappers.put(short.class, Short.class);\n\t\tprimitivesToWrappers.put(int.class, Integer.class);\n\t\tprimitivesToWrappers.put(long.class, Long.class);\n\t\tprimitivesToWrappers.put(float.class, Float.class);\n\t\tprimitivesToWrappers.put(double.class, Double.class);\n\n\t\treturn Collections.unmodifiableMap(primitivesToWrappers);\n\t}\n\n\tpublic static boolean isPublic(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn Modifier.isPublic(clazz.getModifiers());\n\t}\n\n\tpublic static boolean isPublic(Member member) {\n\t\tPreconditions.notNull(member, \"Member must not be null\");\n\t\treturn Modifier.isPublic(member.getModifiers());\n\t}\n\n\tpublic static boolean isPrivate(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn Modifier.isPrivate(clazz.getModifiers());\n\t}\n\n\tpublic static boolean isPrivate(Member member) {\n\t\tPreconditions.notNull(member, \"Member must not be null\");\n\t\treturn Modifier.isPrivate(member.getModifiers());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static boolean isNotPrivate(Class<?> clazz) {\n\t\treturn !isPrivate(clazz);\n\t}\n\n\t@API(status = INTERNAL, since = \"1.1\")\n\tpublic static boolean isNotPrivate(Member member) {\n\t\treturn !isPrivate(member);\n\t}\n\n\tpublic static boolean isAbstract(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn Modifier.isAbstract(clazz.getModifiers());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic static boolean isNotAbstract(Class<?> clazz) {\n\t\treturn !isAbstract(clazz);\n\t}\n\n\tpublic static boolean isAbstract(Member member) {\n\t\tPreconditions.notNull(member, \"Member must not be null\");\n\t\treturn Modifier.isAbstract(member.getModifiers());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic static boolean isNotAbstract(Member member) {\n\t\treturn !isAbstract(member);\n\t}\n\n\tpublic static boolean isStatic(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn Modifier.isStatic(clazz.getModifiers());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static boolean isNotStatic(Class<?> clazz) {\n\t\treturn !isStatic(clazz);\n\t}\n\n\tpublic static boolean isStatic(Member member) {\n\t\tPreconditions.notNull(member, \"Member must not be null\");\n\t\treturn Modifier.isStatic(member.getModifiers());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.1\")\n\tpublic static boolean isNotStatic(Member member) {\n\t\treturn !isStatic(member);\n\t}\n\n\t/**\n\t * @since 1.5\n\t */\n\t@API(status = INTERNAL, since = \"1.5\")\n\tpublic static boolean isFinal(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn Modifier.isFinal(clazz.getModifiers());\n\t}\n\n\t/**\n\t * @since 1.5\n\t */\n\t@API(status = INTERNAL, since = \"1.5\")\n\tpublic static boolean isNotFinal(Class<?> clazz) {\n\t\treturn !isFinal(clazz);\n\t}\n\n\t/**\n\t * @since 1.5\n\t */\n\t@API(status = INTERNAL, since = \"1.5\")\n\tpublic static boolean isFinal(Member member) {\n\t\tPreconditions.notNull(member, \"Member must not be null\");\n\t\treturn Modifier.isFinal(member.getModifiers());\n\t}\n\n\t/**\n\t * @since 1.5\n\t */\n\t@API(status = INTERNAL, since = \"1.5\")\n\tpublic static boolean isNotFinal(Member member) {\n\t\treturn !isFinal(member);\n\t}\n\n\t/**\n\t * Determine if the supplied class is an <em>inner class</em> (i.e., a\n\t * non-static member class).\n\t *\n\t * <p>Technically speaking (i.e., according to the Java Language\n\t * Specification), \"an inner class may be a non-static member class, a\n\t * local class, or an anonymous class.\" However, this method does not\n\t * return {@code true} for a local or anonymous class.\n\t *\n\t * @param clazz the class to check; never {@code null}\n\t * @return {@code true} if the class is an <em>inner class</em>\n\t */\n\tpublic static boolean isInnerClass(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn !isStatic(clazz) && clazz.isMemberClass();\n\t}\n\n\t/**\n\t * {@return whether the supplied {@code object} is an instance of a record class}\n\t * @since 1.12\n\t */\n\t@API(status = INTERNAL, since = \"1.12\")\n\tpublic static boolean isRecordObject(@Nullable Object object) {\n\t\treturn object != null && isRecordClass(object.getClass());\n\t}\n\n\t/**\n\t * {@return whether the supplied {@code clazz} is a record class}\n\t * @since 1.12\n\t */\n\t@API(status = INTERNAL, since = \"1.12\")\n\tpublic static boolean isRecordClass(Class<?> clazz) {\n\t\treturn clazz.isRecord();\n\t}\n\n\t/**\n\t * Determine if the return type of the supplied method is primitive {@code void}.\n\t *\n\t * @param method the method to test; never {@code null}\n\t * @return {@code true} if the method's return type is {@code void}\n\t */\n\tpublic static boolean returnsPrimitiveVoid(Method method) {\n\t\treturn method.getReturnType() == void.class;\n\t}\n\n\t/**\n\t * Determine if the supplied object is an array.\n\t *\n\t * @param obj the object to test; potentially {@code null}\n\t * @return {@code true} if the object is an array\n\t */\n\tpublic static boolean isArray(@Nullable Object obj) {\n\t\treturn (obj != null && obj.getClass().isArray());\n\t}\n\n\t/**\n\t * Determine if the supplied object is a multidimensional array.\n\t *\n\t * @param obj the object to test; potentially {@code null}\n\t * @return {@code true} if the object is a multidimensional array\n\t * @since 1.3.2\n\t */\n\t@API(status = INTERNAL, since = \"1.3.2\")\n\tpublic static boolean isMultidimensionalArray(@Nullable Object obj) {\n\t\treturn (obj != null && obj.getClass().isArray() && obj.getClass().getComponentType().isArray());\n\t}\n\n\t/**\n\t * Determine if an object of the supplied source type can be assigned to the\n\t * supplied target type for the purpose of reflective method invocations.\n\t *\n\t * <p>In contrast to {@link Class#isAssignableFrom(Class)}, this method\n\t * returns {@code true} if the target type represents a primitive type whose\n\t * wrapper matches the supplied source type. In addition, this method also supports\n\t * <a href=\"https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2\">\n\t * widening conversions</a> for primitive target types.\n\t *\n\t * @param sourceType the non-primitive target type; never {@code null}\n\t * @param targetType the target type; never {@code null}\n\t * @return {@code true} if an object of the source type is assignment compatible\n\t * with the target type\n\t * @since 1.8\n\t * @see Class#isInstance(Object)\n\t * @see Class#isAssignableFrom(Class)\n\t * @see #isAssignableTo(Object, Class)\n\t */\n\tpublic static boolean isAssignableTo(Class<?> sourceType, Class<?> targetType) {\n\t\tPreconditions.notNull(sourceType, \"source type must not be null\");\n\t\tPreconditions.condition(!sourceType.isPrimitive(), \"source type must not be a primitive type\");\n\t\tPreconditions.notNull(targetType, \"target type must not be null\");\n\n\t\tif (targetType.isAssignableFrom(sourceType)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (targetType.isPrimitive()) {\n\t\t\treturn sourceType == primitiveToWrapperMap.get(targetType) || isWideningConversion(sourceType, targetType);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Determine if the supplied object can be assigned to the supplied target\n\t * type for the purpose of reflective method invocations.\n\t *\n\t * <p>In contrast to {@link Class#isInstance(Object)}, this method returns\n\t * {@code true} if the target type represents a primitive type whose wrapper\n\t * matches the supplied object's type. In addition, this method also supports\n\t * <a href=\"https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2\">\n\t * widening conversions</a> for primitive types and their corresponding\n\t * wrapper types.\n\t *\n\t * <p>If the supplied object is {@code null} and the supplied type does not\n\t * represent a primitive type, this method returns {@code true}.\n\t *\n\t * @param obj the object to test for assignment compatibility; potentially {@code null}\n\t * @param targetType the type to check against; never {@code null}\n\t * @return {@code true} if the object is assignment compatible\n\t * @see Class#isInstance(Object)\n\t * @see Class#isAssignableFrom(Class)\n\t * @see #isAssignableTo(Class, Class)\n\t */\n\tpublic static boolean isAssignableTo(@Nullable Object obj, Class<?> targetType) {\n\t\tPreconditions.notNull(targetType, \"target type must not be null\");\n\n\t\tif (obj == null) {\n\t\t\treturn !targetType.isPrimitive();\n\t\t}\n\n\t\tif (targetType.isInstance(obj)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (targetType.isPrimitive()) {\n\t\t\tClass<?> sourceType = obj.getClass();\n\t\t\treturn sourceType == primitiveToWrapperMap.get(targetType) || isWideningConversion(sourceType, targetType);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Determine if Java supports a <em>widening primitive conversion</em> from the\n\t * supplied source type to the supplied <strong>primitive</strong> target type.\n\t */\n\tstatic boolean isWideningConversion(Class<?> sourceType, Class<?> targetType) {\n\t\tPreconditions.condition(targetType.isPrimitive(), \"targetType must be primitive\");\n\n\t\tboolean isPrimitive = sourceType.isPrimitive();\n\t\tboolean isWrapper = primitiveToWrapperMap.containsValue(sourceType);\n\n\t\t// Neither a primitive nor a wrapper?\n\t\tif (!isPrimitive && !isWrapper) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (isPrimitive) {\n\t\t\tsourceType = primitiveToWrapperMap.get(sourceType);\n\t\t}\n\n\t\t// @formatter:off\n\t\tif (sourceType == Byte.class) {\n\t\t\treturn\n\t\t\t\t\ttargetType == short.class ||\n\t\t\t\t\ttargetType == int.class ||\n\t\t\t\t\ttargetType == long.class ||\n\t\t\t\t\ttargetType == float.class ||\n\t\t\t\t\ttargetType == double.class;\n\t\t}\n\n\t\tif (sourceType == Short.class || sourceType == Character.class) {\n\t\t\treturn\n\t\t\t\t\ttargetType == int.class ||\n\t\t\t\t\ttargetType == long.class ||\n\t\t\t\t\ttargetType == float.class ||\n\t\t\t\t\ttargetType == double.class;\n\t\t}\n\n\t\tif (sourceType == Integer.class) {\n\t\t\treturn\n\t\t\t\t\ttargetType == long.class ||\n\t\t\t\t\ttargetType == float.class ||\n\t\t\t\t\ttargetType == double.class;\n\t\t}\n\n\t\tif (sourceType == Long.class) {\n\t\t\treturn\n\t\t\t\t\ttargetType == float.class ||\n\t\t\t\t\ttargetType == double.class;\n\t\t}\n\n\t\tif (sourceType == Float.class) {\n\t\t\treturn\n\t\t\t\t\ttargetType == double.class;\n\t\t}\n\t\t// @formatter:on\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get the wrapper type for the supplied primitive type.\n\t *\n\t * @param type the primitive type for which to retrieve the wrapper type\n\t * @return the corresponding wrapper type or {@code null} if the\n\t * supplied type is not a primitive type\n\t */\n\tpublic static @Nullable Class<?> getWrapperType(Class<?> type) {\n\t\treturn primitiveToWrapperMap.get(type);\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#newInstance(Class, Object...)\n\t * @see #newInstance(Constructor, Object...)\n\t */\n\tpublic static <T> T newInstance(Class<T> clazz, Object... args) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(args, \"Argument array must not be null\");\n\t\tPreconditions.containsNoNullElements(args, \"Individual arguments must not be null\");\n\n\t\ttry {\n\t\t\tClass<?>[] parameterTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);\n\t\t\treturn newInstance(clazz.getDeclaredConstructor(parameterTypes), args);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));\n\t\t}\n\t}\n\n\t/**\n\t * Create a new instance of type {@code T} by invoking the supplied constructor\n\t * with the supplied arguments.\n\t *\n\t * <p>The constructor will be made accessible if necessary, and any checked\n\t * exception will be {@linkplain ExceptionUtils#throwAsUncheckedException masked}\n\t * as an unchecked exception.\n\t *\n\t * @param constructor the constructor to invoke; never {@code null}\n\t * @param args the arguments to pass to the constructor\n\t * @return the new instance; never {@code null}\n\t * @see #newInstance(Class, Object...)\n\t * @see ExceptionUtils#throwAsUncheckedException(Throwable)\n\t */\n\tpublic static <T> T newInstance(Constructor<T> constructor, @Nullable Object... args) {\n\t\tPreconditions.notNull(constructor, \"Constructor must not be null\");\n\n\t\ttry {\n\t\t\treturn makeAccessible(constructor).newInstance(args);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));\n\t\t}\n\t}\n\n\t/**\n\t * Try to read the value of a potentially inaccessible or nonexistent field.\n\t *\n\t * <p>If the field does not exist or an exception occurs while reading it, a\n\t * failed {@link Try} is returned that contains the corresponding exception.\n\t *\n\t * @param clazz the class where the field is declared; never {@code null}\n\t * @param fieldName the name of the field; never {@code null} or empty\n\t * @param instance the instance from where the value is to be read; may\n\t * be {@code null} for a static field\n\t * @since 1.4\n\t * @see #tryToReadFieldValue(Field)\n\t * @see #tryToReadFieldValue(Field, Object)\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static <T> Try<Object> tryToReadFieldValue(Class<T> clazz, String fieldName, @Nullable T instance) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notBlank(fieldName, \"Field name must not be null or blank\");\n\n\t\t// @formatter:off\n\t\treturn Try.call(() -> clazz.getDeclaredField(fieldName))\n\t\t\t\t.andThen(field -> tryToReadFieldValue(field, instance));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Try to read the value of a potentially inaccessible static field.\n\t *\n\t * <p>If an exception occurs while reading the field, a failed {@link Try}\n\t * is returned that contains the corresponding exception.\n\t *\n\t * @param field the field to read; never {@code null}\n\t * @since 1.4\n\t * @see #tryToReadFieldValue(Field, Object)\n\t * @see #tryToReadFieldValue(Class, String, Object)\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static Try<@Nullable Object> tryToReadFieldValue(Field field) {\n\t\treturn tryToReadFieldValue(field, null);\n\t}\n\n\t/**\n\t * @since 1.4\n\t * @see org.junit.platform.commons.support.ReflectionSupport#tryToReadFieldValue(Field, Object)\n\t * @see #tryToReadFieldValue(Class, String, Object)\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static Try<@Nullable Object> tryToReadFieldValue(Field field, @Nullable Object instance) {\n\t\tPreconditions.notNull(field, \"Field must not be null\");\n\t\tPreconditions.condition((instance != null || isStatic(field)),\n\t\t\t() -> \"Cannot read non-static field [%s] on a null instance.\".formatted(field));\n\n\t\treturn Try.<@Nullable Object> call(() -> makeAccessible(field).get(instance));\n\t}\n\n\t/**\n\t * Read the values of the supplied fields, making each field accessible if\n\t * necessary and {@linkplain ExceptionUtils#throwAsUncheckedException masking}\n\t * any checked exception as an unchecked exception.\n\t *\n\t * @param fields the list of fields to read; never {@code null}\n\t * @param instance the instance from which the values are to be read; may\n\t * be {@code null} for static fields\n\t * @return an immutable list of the values of the specified fields; never\n\t * {@code null} but may be empty or contain {@code null} entries\n\t */\n\tpublic static List<?> readFieldValues(List<Field> fields, @Nullable Object instance) {\n\t\treturn readFieldValues(fields, instance, field -> true);\n\t}\n\n\t/**\n\t * Read the values of the supplied fields, making each field accessible if\n\t * necessary, {@linkplain ExceptionUtils#throwAsUncheckedException masking}\n\t * any checked exception as an unchecked exception, and filtering out fields\n\t * that do not pass the supplied {@code predicate}.\n\t *\n\t * @param fields the list of fields to read; never {@code null}\n\t * @param instance the instance from which the values are to be read; may\n\t * be {@code null} for static fields\n\t * @param predicate the field filter; never {@code null}\n\t * @return an immutable list of the values of the specified fields; never\n\t * {@code null} but may be empty or contain {@code null} entries\n\t */\n\tpublic static List<?> readFieldValues(List<Field> fields, @Nullable Object instance, Predicate<Field> predicate) {\n\t\tPreconditions.notNull(fields, \"fields list must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\t// @formatter:off\n\t\treturn fields.stream()\n\t\t\t\t.filter(predicate)\n\t\t\t\t.map(field ->\n\t\t\t\t\ttryToReadFieldValue(field, instance).getOrThrow(ExceptionUtils::throwAsUncheckedException))\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#invokeMethod(Method, Object, Object...)\n\t */\n\tpublic static @Nullable Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) {\n\t\tPreconditions.notNull(method, \"Method must not be null\");\n\t\tPreconditions.condition((target != null || isStatic(method)),\n\t\t\t() -> \"Cannot invoke non-static method [%s] on a null target.\".formatted(method.toGenericString()));\n\n\t\ttry {\n\t\t\treturn makeAccessible(method).invoke(target, args);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));\n\t\t}\n\t}\n\n\t/**\n\t * @since 1.4\n\t * @see org.junit.platform.commons.support.ReflectionSupport#tryToLoadClass(String)\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static Try<Class<?>> tryToLoadClass(String name) {\n\t\treturn tryToLoadClass(name, ClassLoaderUtils.getDefaultClassLoader());\n\t}\n\n\t/**\n\t * Load a class by its <em>primitive name</em> or <em>fully qualified name</em>,\n\t * using the supplied {@link ClassLoader}.\n\t *\n\t * <p>See {@link org.junit.platform.commons.support.ReflectionSupport#tryToLoadClass(String)}\n\t * for details on support for class names for arrays.\n\t *\n\t * @param name the name of the class to load; never {@code null} or blank\n\t * @param classLoader the {@code ClassLoader} to use; never {@code null}\n\t * @throws JUnitException if the class could not be loaded\n\t * @since 1.11\n\t * @see #tryToLoadClass(String, ClassLoader)\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static Class<?> loadRequiredClass(String name, ClassLoader classLoader) throws JUnitException {\n\t\treturn tryToLoadClass(name, classLoader).getNonNullOrThrow(\n\t\t\tcause -> new JUnitException(\"Could not load class [%s]\".formatted(name), cause));\n\t}\n\n\t/**\n\t * Try to load a class by its <em>primitive name</em> or <em>fully qualified\n\t * name</em>, using the supplied {@link ClassLoader}.\n\t *\n\t * <p>See {@link org.junit.platform.commons.support.ReflectionSupport#tryToLoadClass(String)}\n\t * for details on support for class names for arrays.\n\t *\n\t * @param name the name of the class to load; never {@code null} or blank\n\t * @param classLoader the {@code ClassLoader} to use; never {@code null}\n\t * @since 1.4\n\t * @see #tryToLoadClass(String)\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static Try<Class<?>> tryToLoadClass(String name, ClassLoader classLoader) {\n\t\tPreconditions.notBlank(name, \"Class name must not be null or blank\");\n\t\tPreconditions.notNull(classLoader, \"ClassLoader must not be null\");\n\t\tString strippedName = name.strip();\n\n\t\tif (classNameToTypeMap.containsKey(strippedName)) {\n\t\t\treturn Try.success(classNameToTypeMap.get(strippedName));\n\t\t}\n\n\t\treturn Try.call(() -> {\n\t\t\t// Arrays such as \"java.lang.String[]\", \"int[]\", \"int[][][][]\", etc.\n\t\t\tMatcher matcher = SOURCE_CODE_SYNTAX_ARRAY_PATTERN.matcher(strippedName);\n\t\t\tif (matcher.matches()) {\n\t\t\t\tString componentTypeName = matcher.group(1);\n\t\t\t\tString bracketPairs = matcher.group(2);\n\t\t\t\t// Calculate dimensions by counting bracket pairs.\n\t\t\t\tint dimensions = bracketPairs.length() / 2;\n\n\t\t\t\treturn loadArrayType(classLoader, componentTypeName, dimensions);\n\t\t\t}\n\n\t\t\t// Fallback to standard VM class loading\n\t\t\treturn Class.forName(strippedName, false, classLoader);\n\t\t});\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ResourceSupport#tryToGetResources(String)\n\t */\n\t@API(status = INTERNAL, since = \"1.12\")\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName) {\n\t\treturn tryToGetResources(classpathResourceName, ClassLoaderUtils.getDefaultClassLoader());\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ResourceSupport#tryToGetResources(String, ClassLoader)\n\t */\n\t@API(status = INTERNAL, since = \"1.12\")\n\tpublic static Try<Set<Resource>> tryToGetResources(String classpathResourceName, ClassLoader classLoader) {\n\t\tPreconditions.notBlank(classpathResourceName, \"Resource name must not be null or blank\");\n\t\tPreconditions.notNull(classLoader, \"Class loader must not be null\");\n\t\tboolean startsWithSlash = classpathResourceName.startsWith(\"/\");\n\t\tString canonicalClasspathResourceName = (startsWithSlash ? classpathResourceName.substring(1)\n\t\t\t\t: classpathResourceName);\n\n\t\treturn Try.call(() -> {\n\t\t\tList<URL> resources = Collections.list(classLoader.getResources(canonicalClasspathResourceName));\n\t\t\treturn resources.stream().map(url -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn Resource.of(canonicalClasspathResourceName, url.toURI());\n\t\t\t\t}\n\t\t\t\tcatch (URISyntaxException e) {\n\t\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(e);\n\t\t\t\t}\n\t\t\t}).collect(toCollection(LinkedHashSet::new));\n\t\t});\n\t}\n\n\tprivate static Class<?> loadArrayType(ClassLoader classLoader, String componentTypeName, int dimensions)\n\t\t\tthrows ClassNotFoundException {\n\n\t\tClass<?> componentType = classNameToTypeMap.containsKey(componentTypeName)\n\t\t\t\t? classNameToTypeMap.get(componentTypeName)\n\t\t\t\t: Class.forName(componentTypeName, false, classLoader);\n\n\t\treturn Array.newInstance(componentType, new int[dimensions]).getClass();\n\t}\n\n\t/**\n\t * Build the <em>fully qualified method name</em> for the method described by the\n\t * supplied class and method.\n\t *\n\t * <p>Note that the class is not necessarily the class in which the method is\n\t * declared.\n\t *\n\t * @param clazz the class from which the method should be referenced; never {@code null}\n\t * @param method the method; never {@code null}\n\t * @return fully qualified method name; never {@code null}\n\t * @since 1.4\n\t * @see #getFullyQualifiedMethodName(Class, String, Class...)\n\t */\n\tpublic static String getFullyQualifiedMethodName(Class<?> clazz, Method method) {\n\t\tPreconditions.notNull(method, \"Method must not be null\");\n\n\t\treturn getFullyQualifiedMethodName(clazz, method.getName(), method.getParameterTypes());\n\t}\n\n\t/**\n\t * Build the <em>fully qualified method name</em> for the method described by the\n\t * supplied class, method name, and parameter types.\n\t *\n\t * <p>Note that the class is not necessarily the class in which the method is\n\t * declared.\n\t *\n\t * @param clazz the class from which the method should be referenced; never {@code null}\n\t * @param methodName the name of the method; never {@code null} or blank\n\t * @param parameterTypes the parameter types of the method; may be {@code null} or empty\n\t * @return fully qualified method name; never {@code null}\n\t */\n\tpublic static String getFullyQualifiedMethodName(Class<?> clazz, String methodName,\n\t\t\tClass<?> @Nullable... parameterTypes) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\n\t\treturn getFullyQualifiedMethodName(clazz.getName(), methodName, ClassUtils.nullSafeToString(parameterTypes));\n\t}\n\n\t/**\n\t * Build the <em>fully qualified method name</em> for the method described by the\n\t * supplied class name, method name, and parameter types.\n\t *\n\t * <p>Note that the class is not necessarily the class in which the method is\n\t * declared.\n\t *\n\t * @param className the name of the class from which the method should be referenced;\n\t * never {@code null}\n\t * @param methodName the name of the method; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names of the method; may be\n\t * empty but not {@code null}\n\t * @return fully qualified method name; never {@code null}\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static String getFullyQualifiedMethodName(String className, String methodName, String parameterTypeNames) {\n\t\tPreconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypeNames, \"Parameter type names must not be null\");\n\n\t\treturn \"%s#%s(%s)\".formatted(className, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * Parse the supplied <em>fully qualified method name</em> into a 3-element\n\t * {@code String[]} with the following content.\n\t *\n\t * <ul>\n\t *   <li>index {@code 0}: the fully qualified class name</li>\n\t *   <li>index {@code 1}: the name of the method</li>\n\t *   <li>index {@code 2}: a comma-separated list of parameter types, or a\n\t *       blank string if the method does not declare any formal parameters</li>\n\t * </ul>\n\t *\n\t * @param fullyQualifiedMethodName a <em>fully qualified method name</em>,\n\t * never {@code null} or blank\n\t * @return a 3-element array of strings containing the parsed values\n\t */\n\tpublic static String[] parseFullyQualifiedMethodName(String fullyQualifiedMethodName) {\n\t\tPreconditions.notBlank(fullyQualifiedMethodName, \"fullyQualifiedMethodName must not be null or blank\");\n\n\t\tint indexOfFirstHashtag = fullyQualifiedMethodName.indexOf('#');\n\t\tboolean validSyntax = (indexOfFirstHashtag > 0)\n\t\t\t\t&& (indexOfFirstHashtag < fullyQualifiedMethodName.length() - 1);\n\n\t\tPreconditions.condition(validSyntax,\n\t\t\t() -> \"[\" + fullyQualifiedMethodName + \"] is not a valid fully qualified method name: \"\n\t\t\t\t\t+ \"it must start with a fully qualified class name followed by a '#' \"\n\t\t\t\t\t+ \"and then the method name, optionally followed by a parameter list enclosed in parentheses.\");\n\n\t\tString className = fullyQualifiedMethodName.substring(0, indexOfFirstHashtag);\n\t\tString methodPart = fullyQualifiedMethodName.substring(indexOfFirstHashtag + 1);\n\t\tString methodName = methodPart;\n\t\tString methodParameters = \"\";\n\n\t\tif (methodPart.endsWith(\"()\")) {\n\t\t\tmethodName = methodPart.substring(0, methodPart.length() - 2);\n\t\t}\n\t\telse if (methodPart.endsWith(\")\")) {\n\t\t\tint indexOfLastOpeningParenthesis = methodPart.lastIndexOf('(');\n\t\t\tif ((indexOfLastOpeningParenthesis > 0) && (indexOfLastOpeningParenthesis < methodPart.length() - 1)) {\n\t\t\t\tmethodName = methodPart.substring(0, indexOfLastOpeningParenthesis);\n\t\t\t\tmethodParameters = methodPart.substring(indexOfLastOpeningParenthesis + 1, methodPart.length() - 1);\n\t\t\t}\n\t\t}\n\t\treturn new String[] { className, methodName, methodParameters };\n\t}\n\n\t/**\n\t * Parse the supplied <em>fully qualified field name</em> into a 2-element\n\t * {@code String[]} with the following content.\n\t *\n\t * <ul>\n\t *   <li>index {@code 0}: the fully qualified class name</li>\n\t *   <li>index {@code 1}: the name of the field</li>\n\t * </ul>\n\t *\n\t * @param fullyQualifiedFieldName a <em>fully qualified field name</em>,\n\t * never {@code null} or blank\n\t * @return a 2-element array of strings containing the parsed values\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static String[] parseFullyQualifiedFieldName(String fullyQualifiedFieldName) {\n\t\tPreconditions.notBlank(fullyQualifiedFieldName, \"fullyQualifiedFieldName must not be null or blank\");\n\n\t\tint indexOfHashtag = fullyQualifiedFieldName.indexOf('#');\n\t\tboolean validSyntax = (indexOfHashtag > 0) && (indexOfHashtag < fullyQualifiedFieldName.length() - 1);\n\t\tPreconditions.condition(validSyntax,\n\t\t\t() -> \"[\" + fullyQualifiedFieldName + \"] is not a valid fully qualified field name: \"\n\t\t\t\t\t+ \"it must start with a fully qualified class name followed by a '#' \"\n\t\t\t\t\t+ \"and then the field name.\");\n\t\treturn fullyQualifiedFieldName.split(\"#\");\n\t}\n\n\tpublic static Set<Path> getAllClasspathRootDirectories() {\n\t\t// This is quite a hack, since sometimes the classpath is quite different\n\t\tString fullClassPath = System.getProperty(\"java.class.path\");\n\t\t// @formatter:off\n\t\treturn Arrays.stream(fullClassPath.split(File.pathSeparator))\n\t\t\t\t.map(Path::of)\n\t\t\t\t.filter(Files::isDirectory)\n\t\t\t\t.collect(toSet());\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t */\n\tpublic static List<Class<?>> findAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\t// unmodifiable since returned by public, non-internal method(s)\n\t\treturn findAllClassesInClasspathRoot(root, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamAllClassesInClasspathRoot(URI, Predicate, Predicate)\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInClasspathRoot(URI root, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\treturn streamAllClassesInClasspathRoot(root, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 1.1\n\t */\n\tpublic static List<Class<?>> findAllClassesInClasspathRoot(URI root, ClassFilter classFilter) {\n\t\treturn List.copyOf(classpathScanner.scanForClassesInClasspathRoot(root, classFilter));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static List<Resource> findAllResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\treturn List.copyOf(classpathScanner.scanForResourcesInClasspathRoot(root, resourceFilter));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInClasspathRoot(URI root, ClassFilter classFilter) {\n\t\treturn findAllClassesInClasspathRoot(root, classFilter).stream();\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInClasspathRoot(URI root, ResourceFilter resourceFilter) {\n\t\treturn findAllResourcesInClasspathRoot(root, resourceFilter).stream();\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInPackage(String, Predicate, Predicate)\n\t */\n\tpublic static List<Class<?>> findAllClassesInPackage(String basePackageName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\t// unmodifiable since returned by public, non-internal method(s)\n\t\treturn findAllClassesInPackage(basePackageName, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamAllClassesInPackage(String, Predicate, Predicate)\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInPackage(String basePackageName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\treturn streamAllClassesInPackage(basePackageName, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 1.1\n\t */\n\tpublic static List<Class<?>> findAllClassesInPackage(String basePackageName, ClassFilter classFilter) {\n\t\treturn List.copyOf(classpathScanner.scanForClassesInPackage(basePackageName, classFilter));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static List<Resource> findAllResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\treturn List.copyOf(classpathScanner.scanForResourcesInPackage(basePackageName, resourceFilter));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInPackage(String basePackageName, ClassFilter classFilter) {\n\t\treturn findAllClassesInPackage(basePackageName, classFilter).stream();\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInPackage(String basePackageName, ResourceFilter resourceFilter) {\n\t\treturn findAllResourcesInPackage(basePackageName, resourceFilter).stream();\n\t}\n\n\t/**\n\t * @since 1.1.1\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInModule(String, Predicate, Predicate)\n\t */\n\tpublic static List<Class<?>> findAllClassesInModule(String moduleName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\t// unmodifiable since returned by public, non-internal method(s)\n\t\treturn findAllClassesInModule(moduleName, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 6.1\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findAllClassesInModule(Module, Predicate, Predicate)\n\t */\n\tpublic static List<Class<?>> findAllClassesInModule(Module module, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\t// unmodifiable since returned by public, non-internal method(s)\n\t\treturn findAllClassesInModule(module, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamAllClassesInModule(String, Predicate, Predicate)\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInModule(String moduleName, Predicate<Class<?>> classFilter,\n\t\t\tPredicate<String> classNameFilter) {\n\t\treturn streamAllClassesInModule(moduleName, ClassFilter.of(classNameFilter, classFilter));\n\t}\n\n\t/**\n\t * @since 1.1.1\n\t */\n\tpublic static List<Class<?>> findAllClassesInModule(String moduleName, ClassFilter classFilter) {\n\t\treturn List.copyOf(ModuleUtils.findAllClassesInModule(moduleName, classFilter));\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\tpublic static List<Class<?>> findAllClassesInModule(Module module, ClassFilter classFilter) {\n\t\treturn List.copyOf(ModuleUtils.findAllClassesInModule(module, classFilter));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static List<Resource> findAllResourcesInModule(String moduleName, ResourceFilter resourceFilter) {\n\t\treturn List.copyOf(ModuleUtils.findAllResourcesInModule(moduleName, resourceFilter));\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\tpublic static List<Resource> findAllResourcesInModule(Module module, ResourceFilter resourceFilter) {\n\t\treturn List.copyOf(ModuleUtils.findAllResourcesInModule(module, resourceFilter));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tpublic static Stream<Class<?>> streamAllClassesInModule(String moduleName, ClassFilter classFilter) {\n\t\treturn findAllClassesInModule(moduleName, classFilter).stream();\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\tpublic static Stream<Resource> streamAllResourcesInModule(String moduleName, ResourceFilter resourceFilter) {\n\t\treturn findAllResourcesInModule(moduleName, resourceFilter).stream();\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findNestedClasses(Class, Predicate)\n\t */\n\tpublic static List<Class<?>> findNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate) {\n\t\treturn findNestedClasses(clazz, predicate, CycleErrorHandling.THROW_EXCEPTION);\n\t}\n\n\t/**\n\t * @since 1.13.2\n\t */\n\t@API(status = INTERNAL, since = \"1.13.2\")\n\tpublic static List<Class<?>> findNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate,\n\t\t\tCycleErrorHandling errorHandling) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\tSet<Class<?>> candidates = new LinkedHashSet<>();\n\t\tvisitAllNestedClasses(clazz, predicate, candidates::add, errorHandling);\n\t\treturn List.copyOf(candidates);\n\t}\n\n\t/**\n\t * Determine if a nested class within the supplied class, or inherited by the\n\t * supplied class, that conforms to the supplied predicate is present.\n\t *\n\t * <p>This method does <strong>not</strong> search for nested classes\n\t * recursively.\n\t *\n\t * <p>This method detects cycles in <em>inner</em> class hierarchies &mdash;\n\t * from the supplied class up to the outermost enclosing class &mdash; and\n\t * throws a {@link JUnitException} if such a cycle is detected. Cycles within\n\t * inner class hierarchies <em>below</em> the supplied class are not detected\n\t * by this method.\n\t *\n\t * @param clazz the class to be searched; never {@code null}\n\t * @param predicate the predicate against which the list of nested classes is\n\t * checked; never {@code null}\n\t * @return {@code true} if such a nested class is present\n\t * @throws JUnitException if a cycle is detected within an inner class hierarchy\n\t * @since 1.13.2\n\t */\n\t@API(status = INTERNAL, since = \"1.13.2\")\n\tpublic static boolean isNestedClassPresent(Class<?> clazz, Predicate<Class<?>> predicate,\n\t\t\tCycleErrorHandling errorHandling) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\t\tPreconditions.notNull(errorHandling, \"CycleErrorHandling must not be null\");\n\n\t\tAtomicBoolean foundNestedClass = new AtomicBoolean(false);\n\t\tvisitAllNestedClasses(clazz, predicate, __ -> foundNestedClass.setPlain(true), errorHandling);\n\t\treturn foundNestedClass.getPlain();\n\t}\n\n\t/**\n\t * @since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamNestedClasses(Class, Predicate)\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static Stream<Class<?>> streamNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate) {\n\t\treturn findNestedClasses(clazz, predicate).stream();\n\t}\n\n\t/**\n\t * @since 1.13.2\n\t */\n\t@API(status = INTERNAL, since = \"1.13.2\")\n\tpublic static Stream<Class<?>> streamNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate,\n\t\t\tCycleErrorHandling errorHandling) {\n\t\treturn findNestedClasses(clazz, predicate, errorHandling).stream();\n\t}\n\n\t/**\n\t * Visit <em>all</em> nested classes without support for short-circuiting\n\t * in order to ensure all of them are checked for class cycles.\n\t */\n\tprivate static void visitAllNestedClasses(Class<?> clazz, Predicate<Class<?>> predicate,\n\t\t\tConsumer<Class<?>> consumer, CycleErrorHandling errorHandling) {\n\n\t\tif (!isSearchable(clazz)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (isInnerClass(clazz) && predicate.test(clazz)) {\n\t\t\tif (detectInnerClassCycle(clazz, errorHandling)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// Candidates in current class\n\t\t\tfor (Class<?> nestedClass : toSortedMutableList(clazz.getDeclaredClasses())) {\n\t\t\t\tif (predicate.test(nestedClass)) {\n\t\t\t\t\tconsumer.accept(nestedClass);\n\t\t\t\t\tif (detectInnerClassCycle(nestedClass, errorHandling)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (NoClassDefFoundError error) {\n\t\t\tlogger.debug(error, () -> \"Failed to retrieve declared classes for \" + clazz.getName());\n\t\t}\n\n\t\t// Search class hierarchy\n\t\tvisitAllNestedClasses(clazz.getSuperclass(), predicate, consumer, errorHandling);\n\n\t\t// Search interface hierarchy\n\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\t\t\tvisitAllNestedClasses(ifc, predicate, consumer, errorHandling);\n\t\t}\n\t}\n\n\t/**\n\t * Detect a cycle in the inner class hierarchy in which the supplied class\n\t * resides &mdash; from the supplied class up to the outermost enclosing class\n\t * &mdash; and throw a {@link JUnitException} if a cycle is detected.\n\t * <p>This method does <strong>not</strong> detect cycles within inner class\n\t * hierarchies <em>below</em> the supplied class.\n\t * <p>If the supplied class is not an inner class and does not have a\n\t * searchable superclass, this method is effectively a no-op.\n\t *\n\t * @since 1.6\n\t * @see #isInnerClass(Class)\n\t * @see #isSearchable(Class)\n\t */\n\tprivate static boolean detectInnerClassCycle(Class<?> clazz, CycleErrorHandling errorHandling) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tString className = clazz.getName();\n\n\t\tif (noCyclesDetectedCache.contains(className)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tClass<?> superclass = clazz.getSuperclass();\n\t\tif (isInnerClass(clazz) && isSearchable(superclass)) {\n\t\t\tfor (Class<?> enclosing = clazz.getEnclosingClass(); enclosing != null; enclosing = enclosing.getEnclosingClass()) {\n\t\t\t\tif (superclass.equals(enclosing)) {\n\t\t\t\t\terrorHandling.handle(clazz, enclosing);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tnoCyclesDetectedCache.add(className);\n\t\treturn false;\n\t}\n\n\t/**\n\t * Get the sole declared, non-synthetic {@link Constructor} for the supplied class.\n\t *\n\t * <p>Throws a {@link org.junit.platform.commons.PreconditionViolationException}\n\t * if the supplied class declares more than one non-synthetic constructor.\n\t *\n\t * @param clazz the class to get the constructor for\n\t * @return the sole declared constructor; never {@code null}\n\t * @see Class#getDeclaredConstructors()\n\t * @see Class#isSynthetic()\n\t */\n\t@SuppressWarnings(\"unchecked\")\n\tpublic static <T> Constructor<T> getDeclaredConstructor(Class<T> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\ttry {\n\t\t\tConstructor<?>[] constructors = Arrays.stream(clazz.getDeclaredConstructors())//\n\t\t\t\t\t.filter(ctor -> !ctor.isSynthetic())//\n\t\t\t\t\t.toArray(Constructor[]::new);\n\n\t\t\tPreconditions.condition(constructors.length == 1,\n\t\t\t\t() -> \"Class [%s] must declare a single constructor\".formatted(clazz.getName()));\n\n\t\t\treturn (Constructor<T>) constructors[0];\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));\n\t\t}\n\t}\n\n\t/**\n\t * Find all constructors in the supplied class that match the supplied predicate.\n\t *\n\t * <p>Note that this method may return {@linkplain Class#isSynthetic() synthetic}\n\t * constructors. If you wish to ignore synthetic constructors, you may filter\n\t * them out with the supplied {@code predicate} or filter them out of the list\n\t * returned by this method.\n\t *\n\t * @param clazz the class in which to search for constructors; never {@code null}\n\t * @param predicate the predicate to use to test for a match; never {@code null}\n\t * @return an immutable list of all such constructors found; never {@code null}\n\t * but potentially empty\n\t * @see Class#getDeclaredConstructors()\n\t * @see Class#isSynthetic()\n\t */\n\tpublic static List<Constructor<?>> findConstructors(Class<?> clazz, Predicate<Constructor<?>> predicate) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\ttry {\n\t\t\t// @formatter:off\n\t\t\treturn Arrays.stream(clazz.getDeclaredConstructors())\n\t\t\t\t\t.filter(predicate)\n\t\t\t\t\t.toList();\n\t\t\t// @formatter:on\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));\n\t\t}\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findFields(Class, Predicate, org.junit.platform.commons.support.HierarchyTraversalMode)\n\t */\n\tpublic static List<Field> findFields(Class<?> clazz, Predicate<Field> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\treturn streamFields(clazz, predicate, traversalMode).toList();\n\t}\n\n\t/**\n\t * @since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamFields(Class, Predicate, org.junit.platform.commons.support.HierarchyTraversalMode)\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static Stream<Field> streamFields(Class<?> clazz, Predicate<Field> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\t// @formatter:off\n\t\treturn findAllFieldsInHierarchy(clazz, traversalMode).stream()\n\t\t\t\t.filter(predicate)\n\t\t\t\t.distinct();\n\t\t// @formatter:on\n\t}\n\n\tprivate static List<Field> findAllFieldsInHierarchy(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\t// @formatter:off\n\t\tField[] localFields = getDeclaredFields(clazz).stream()\n\t\t\t\t.filter(field -> !field.isSynthetic())\n\t\t\t\t.toArray(Field[]::new);\n\t\t// @formatter:on\n\t\tList<Field> superclassFields = getSuperclassFields(clazz, traversalMode);\n\t\tList<Field> interfaceFields = getInterfaceFields(clazz, traversalMode);\n\n\t\tList<Field> fields = new ArrayList<>(superclassFields.size() + interfaceFields.size() + localFields.length);\n\t\tif (traversalMode == TOP_DOWN) {\n\t\t\tfields.addAll(superclassFields);\n\t\t\tfields.addAll(interfaceFields);\n\t\t}\n\t\tCollections.addAll(fields, localFields);\n\t\tif (traversalMode == BOTTOM_UP) {\n\t\t\tfields.addAll(interfaceFields);\n\t\t\tfields.addAll(superclassFields);\n\t\t}\n\t\treturn fields;\n\t}\n\n\t/**\n\t * Determine if a {@link Method} matching the supplied {@link Predicate}\n\t * is present within the type hierarchy of the specified class, beginning\n\t * with the specified class or interface and traversing up the type\n\t * hierarchy until such a method is found or the type hierarchy is exhausted.\n\t *\n\t * @param clazz the class or interface in which to find the method; never\n\t * {@code null}\n\t * @param predicate the predicate to use to test for a match; never\n\t * {@code null}\n\t * @return {@code true} if such a method is present\n\t * @see #findMethod(Class, String, String)\n\t * @see #findMethod(Class, String, Class...)\n\t */\n\tpublic static boolean isMethodPresent(Class<?> clazz, Predicate<Method> predicate) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\treturn findMethod(clazz, predicate).isPresent();\n\t}\n\n\t/**\n\t * Try to get the {@link Method} in the specified class with the specified\n\t * name and parameter types.\n\t *\n\t * <p>This method delegates to {@link Class#getMethod(String, Class...)} but\n\t * catches any exception thrown.\n\t *\n\t * @param clazz the class in which to search for the method; never {@code null}\n\t * @param methodName the name of the method to get; never {@code null} or blank\n\t * @param parameterTypes the parameter types of the method; may be {@code null}\n\t * or empty\n\t * @return a successful {@link Try} containing the method or a failed\n\t * {@link Try} containing the {@link NoSuchMethodException} thrown by\n\t * {@code Class#getMethod()}; never {@code null}\n\t * @since 1.4\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\tpublic static Try<Method> tryToGetMethod(Class<?> clazz, String methodName, Class<?> @Nullable... parameterTypes) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\n\t\treturn Try.call(() -> clazz.getMethod(methodName, parameterTypes));\n\t}\n\n\t/**\n\t * Determine a corresponding interface method for the given method handle, if possible.\n\t * <p>This is particularly useful for arriving at a public exported type on the Java\n\t * Module System which can be reflectively invoked without an illegal access warning.\n\t * @param method the method to be invoked, potentially from an implementation class;\n\t * never {@code null}\n\t * @param targetClass the target class to check for declared interfaces;\n\t * potentially {@code null}\n\t * @return the corresponding interface method, or the original method if none found\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\t@SuppressWarnings(\"ReferenceEquality\")\n\tpublic static Method getInterfaceMethodIfPossible(Method method, @Nullable Class<?> targetClass) {\n\t\tif (!isPublic(method) || method.getDeclaringClass().isInterface()) {\n\t\t\treturn method;\n\t\t}\n\t\t// Try cached version of method in its declaring class\n\t\tMethod result = interfaceMethodCache.computeIfAbsent(method,\n\t\t\tm -> findInterfaceMethodIfPossible(m, m.getParameterTypes(), m.getDeclaringClass(), Object.class));\n\t\tif (result == method && targetClass != null) {\n\t\t\t// No interface method found yet -> try given target class (possibly a subclass of the\n\t\t\t// declaring class, late-binding a base class method to a subclass-declared interface:\n\t\t\t// see e.g. HashMap.HashIterator.hasNext)\n\t\t\tresult = findInterfaceMethodIfPossible(method, method.getParameterTypes(), targetClass,\n\t\t\t\tmethod.getDeclaringClass());\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate static Method findInterfaceMethodIfPossible(Method method, Class<?>[] parameterTypes, Class<?> startClass,\n\t\t\tClass<?> endClass) {\n\n\t\tClass<?> current = startClass;\n\t\twhile (current != null && current != endClass) {\n\t\t\tfor (Class<?> ifc : current.getInterfaces()) {\n\t\t\t\ttry {\n\t\t\t\t\treturn ifc.getMethod(method.getName(), parameterTypes);\n\t\t\t\t}\n\t\t\t\tcatch (NoSuchMethodException ex) {\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrent = current.getSuperclass();\n\t\t}\n\t\treturn method;\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findMethod(Class, String, String)\n\t */\n\tpublic static Optional<Method> findMethod(Class<?> clazz, String methodName, @Nullable String parameterTypeNames) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\treturn findMethod(clazz, methodName, resolveParameterTypes(clazz, methodName, parameterTypeNames));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static Class<?>[] resolveParameterTypes(Class<?> clazz, String methodName,\n\t\t\t@Nullable String parameterTypeNames) {\n\n\t\tif (parameterTypeNames == null || StringUtils.isBlank(parameterTypeNames)) {\n\t\t\treturn EMPTY_CLASS_ARRAY;\n\t\t}\n\n\t\t// @formatter:off\n\t\treturn Arrays.stream(parameterTypeNames.split(\",\"))\n\t\t\t\t.map(String::strip)\n\t\t\t\t.map(typeName -> loadRequiredParameterType(clazz, methodName, typeName))\n\t\t\t\t.toArray(Class[]::new);\n\t\t// @formatter:on\n\t}\n\n\tprivate static Class<?> loadRequiredParameterType(Class<?> clazz, String methodName, String typeName) {\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(clazz);\n\n\t\t// @formatter:off\n\t\treturn tryToLoadClass(typeName, classLoader)\n\t\t\t\t.getNonNullOrThrow(cause -> new JUnitException(\n\t\t\t\t\t\t\"Failed to load parameter type [%s] for method [%s] in class [%s].\".formatted(\n\t\t\t\t\t\t\t\ttypeName, methodName, clazz.getName()), cause));\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findMethod(Class, String, Class...)\n\t */\n\tpublic static Optional<Method> findMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypes, \"Parameter types array must not be null\");\n\t\tPreconditions.containsNoNullElements(parameterTypes, \"Individual parameter types must not be null\");\n\n\t\treturn findMethod(clazz, method -> hasCompatibleSignature(method, methodName, parameterTypes));\n\t}\n\n\tprivate static Optional<Method> findMethod(Class<?> clazz, Predicate<Method> predicate) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\n\t\tfor (Class<?> current = clazz; isSearchable(current); current = current.getSuperclass()) {\n\t\t\t// Search for match in current type\n\t\t\tList<Method> methods = current.isInterface() ? getMethods(current) : getDeclaredMethods(current, BOTTOM_UP);\n\t\t\tfor (Method method : methods) {\n\t\t\t\tif (predicate.test(method)) {\n\t\t\t\t\treturn Optional.of(method);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Search for match in interfaces implemented by current type\n\t\t\tfor (Class<?> ifc : current.getInterfaces()) {\n\t\t\t\tOptional<Method> optional = findMethod(ifc, predicate);\n\t\t\t\tif (optional.isPresent()) {\n\t\t\t\t\treturn optional;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * Find the first {@link Method} of the supplied class or interface that\n\t * meets the specified criteria, beginning with the specified class or\n\t * interface and traversing up the type hierarchy until such a method is\n\t * found or the type hierarchy is exhausted.\n\t *\n\t * <p>Use this method as an alternative to\n\t * {@link #findMethod(Class, String, Class...)} for use cases in which the\n\t * method is required to be present.\n\t *\n\t * @param clazz the class or interface in which to find the method;\n\t * never {@code null}\n\t * @param methodName the name of the method to find; never {@code null}\n\t * or empty\n\t * @param parameterTypes the types of parameters accepted by the method,\n\t * if any; never {@code null}\n\t * @return the {@code Method} found; never {@code null}\n\t * @throws JUnitException if no method is found\n\t *\n\t * @since 1.7\n\t * @see #findMethod(Class, String, Class...)\n\t */\n\t@API(status = INTERNAL, since = \"1.7\")\n\tpublic static Method getRequiredMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {\n\t\treturn ReflectionUtils.findMethod(clazz, methodName, parameterTypes).orElseThrow(() -> new JUnitException(\n\t\t\t\"Could not find method [%s] in class [%s]\".formatted(methodName, clazz.getName())));\n\t}\n\n\t/**\n\t * Find all {@linkplain Method methods} of the supplied class or interface\n\t * that match the specified {@code predicate}, using top-down search semantics\n\t * within the type hierarchy.\n\t *\n\t * <p>The results will not contain instance methods that are <em>overridden</em>.\n\t *\n\t * @param clazz the class or interface in which to find the methods; never {@code null}\n\t * @param predicate the method filter; never {@code null}\n\t * @return an immutable list of all such methods found; never {@code null}\n\t * @see HierarchyTraversalMode#TOP_DOWN\n\t * @see #findMethods(Class, Predicate, HierarchyTraversalMode)\n\t */\n\tpublic static List<Method> findMethods(Class<?> clazz, Predicate<Method> predicate) {\n\t\treturn findMethods(clazz, predicate, TOP_DOWN);\n\t}\n\n\t/**\n\t * @see org.junit.platform.commons.support.ReflectionSupport#findMethods(Class, Predicate, org.junit.platform.commons.support.HierarchyTraversalMode)\n\t */\n\tpublic static List<Method> findMethods(Class<?> clazz, Predicate<Method> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\treturn streamMethods(clazz, predicate, traversalMode).toList();\n\t}\n\n\t/**\n\t * @since 1.10\n\t * @see org.junit.platform.commons.support.ReflectionSupport#streamMethods(Class, Predicate, org.junit.platform.commons.support.HierarchyTraversalMode)\n\t */\n\t@API(status = INTERNAL, since = \"1.10\")\n\tpublic static Stream<Method> streamMethods(Class<?> clazz, Predicate<Method> predicate,\n\t\t\tHierarchyTraversalMode traversalMode) {\n\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\t// @formatter:off\n\t\treturn findAllMethodsInHierarchy(clazz, traversalMode).stream()\n\t\t\t\t.filter(predicate)\n\t\t\t\t.distinct();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Find all non-synthetic methods in the superclass and interface hierarchy,\n\t * excluding Object.\n\t */\n\tprivate static List<Method> findAllMethodsInHierarchy(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(traversalMode, \"HierarchyTraversalMode must not be null\");\n\n\t\t// @formatter:off\n\t\tMethod[] localMethods = getDeclaredMethods(clazz, traversalMode).stream()\n\t\t\t\t.filter(method -> !method.isSynthetic())\n\t\t\t\t.toArray(Method[]::new);\n\t\tMethod[] superclassMethods = getSuperclassMethods(clazz, traversalMode).stream()\n\t\t\t\t.filter(method -> isNotOverriddenByLocalMethods(method, localMethods))\n\t\t\t\t.toArray(Method[]::new);\n\t\tMethod[] interfaceMethods = getInterfaceMethods(clazz, traversalMode).stream()\n\t\t\t\t.filter(method -> isNotOverriddenByLocalMethods(method, localMethods))\n\t\t\t\t.toArray(Method[]::new);\n\t\t// @formatter:on\n\n\t\tList<Method> methods = new ArrayList<>(\n\t\t\tsuperclassMethods.length + interfaceMethods.length + localMethods.length);\n\t\tif (traversalMode == TOP_DOWN) {\n\t\t\tCollections.addAll(methods, superclassMethods);\n\t\t\tCollections.addAll(methods, interfaceMethods);\n\t\t}\n\t\tCollections.addAll(methods, localMethods);\n\t\tif (traversalMode == BOTTOM_UP) {\n\t\t\tCollections.addAll(methods, interfaceMethods);\n\t\t\tCollections.addAll(methods, superclassMethods);\n\t\t}\n\t\treturn methods;\n\t}\n\n\t/**\n\t * Custom alternative to {@link Class#getDeclaredFields()} that sorts the\n\t * fields and converts them to a mutable list.\n\t */\n\tprivate static List<Field> getDeclaredFields(Class<?> clazz) {\n\t\treturn toSortedMutableList(clazz.getDeclaredFields());\n\t}\n\n\t/**\n\t * Custom alternative to {@link Class#getMethods()} that sorts the methods\n\t * and converts them to a mutable list.\n\t */\n\tprivate static List<Method> getMethods(Class<?> clazz) {\n\t\treturn toSortedMutableList(clazz.getMethods());\n\t}\n\n\t/**\n\t * Custom alternative to {@link Class#getDeclaredMethods()} that sorts the\n\t * methods and converts them to a mutable list.\n\t *\n\t * <p>In addition, the list returned by this method includes interface\n\t * default methods which are either prepended or appended to the list of\n\t * declared methods depending on the supplied traversal mode.\n\t */\n\tprivate static List<Method> getDeclaredMethods(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\t// Note: getDefaultMethods() already sorts the methods,\n\t\tList<Method> defaultMethods = getDefaultMethods(clazz);\n\t\tList<Method> declaredMethods = toSortedMutableList(clazz.getDeclaredMethods());\n\n\t\t// Take the traversal mode into account in order to retain the inherited\n\t\t// nature of interface default methods.\n\t\tif (traversalMode == BOTTOM_UP) {\n\t\t\tdeclaredMethods.addAll(defaultMethods);\n\t\t\treturn declaredMethods;\n\t\t}\n\t\telse {\n\t\t\tdefaultMethods.addAll(declaredMethods);\n\t\t\treturn defaultMethods;\n\t\t}\n\t}\n\n\t/**\n\t * Get a sorted, mutable list of all default methods present in interfaces\n\t * implemented by the supplied class which are also <em>visible</em> within\n\t * the supplied class.\n\t *\n\t * @see <a href=\"https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#d5e9652\">Method Visibility</a>\n\t * in the Java Language Specification\n\t */\n\tprivate static List<Method> getDefaultMethods(Class<?> clazz) {\n\t\t// @formatter:off\n\t\t// Visible default methods are interface default methods that have not\n\t\t// been overridden.\n\t\tList<Method> visibleDefaultMethods = Arrays.stream(clazz.getMethods())\n\t\t\t\t.filter(Method::isDefault)\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\t\tif (visibleDefaultMethods.isEmpty()) {\n\t\t\treturn visibleDefaultMethods;\n\t\t}\n\t\treturn Arrays.stream(clazz.getInterfaces())\n\t\t\t\t.map(ReflectionUtils::getMethods)\n\t\t\t\t.flatMap(List::stream)\n\t\t\t\t.filter(visibleDefaultMethods::contains)\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\t\t// @formatter:on\n\t}\n\n\tprivate static List<Field> toSortedMutableList(Field[] fields) {\n\t\treturn toSortedMutableList(fields, ReflectionUtils::defaultFieldSorter);\n\t}\n\n\tprivate static List<Method> toSortedMutableList(Method[] methods) {\n\t\treturn toSortedMutableList(methods, ReflectionUtils::defaultMethodSorter);\n\t}\n\n\tprivate static List<Class<?>> toSortedMutableList(Class<?>[] classes) {\n\t\treturn toSortedMutableList(classes, ReflectionUtils::defaultClassSorter);\n\t}\n\n\tprivate static <T> List<T> toSortedMutableList(T[] items, Comparator<? super T> comparator) {\n\t\tList<T> result = new ArrayList<>(items.length);\n\t\tCollections.addAll(result, items);\n\t\tresult.sort(comparator);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Field comparator inspired by JUnit 4's {@code org.junit.internal.MethodSorter}\n\t * implementation.\n\t */\n\tprivate static int defaultFieldSorter(Field field1, Field field2) {\n\t\treturn Integer.compare(field1.getName().hashCode(), field2.getName().hashCode());\n\t}\n\n\t/**\n\t * Method comparator based upon JUnit 4's {@code org.junit.internal.MethodSorter}\n\t * implementation.\n\t */\n\tprivate static int defaultMethodSorter(Method method1, Method method2) {\n\t\tString name1 = method1.getName();\n\t\tString name2 = method2.getName();\n\t\tint comparison = Integer.compare(name1.hashCode(), name2.hashCode());\n\t\tif (comparison == 0) {\n\t\t\tcomparison = name1.compareTo(name2);\n\t\t\tif (comparison == 0) {\n\t\t\t\tcomparison = method1.toString().compareTo(method2.toString());\n\t\t\t}\n\t\t}\n\t\treturn comparison;\n\t}\n\n\t/**\n\t * Class comparator to achieve deterministic but nonobvious order.\n\t */\n\tprivate static int defaultClassSorter(Class<?> class1, Class<?> class2) {\n\t\tString name1 = class1.getName();\n\t\tString name2 = class2.getName();\n\t\tint comparison = Integer.compare(name1.hashCode(), name2.hashCode());\n\t\tif (comparison == 0) {\n\t\t\tcomparison = name1.compareTo(name2);\n\t\t}\n\t\treturn comparison;\n\t}\n\n\tprivate static List<Method> getInterfaceMethods(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tList<Method> allInterfaceMethods = new ArrayList<>();\n\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\n\t\t\t// @formatter:off\n\t\t\tMethod[] localInterfaceMethods = getMethods(ifc).stream()\n\t\t\t\t\t.filter(m -> !isAbstract(m))\n\t\t\t\t\t.toArray(Method[]::new);\n\n\t\t\tMethod[] superinterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream()\n\t\t\t\t\t.filter(method -> isNotOverriddenByLocalMethods(method, localInterfaceMethods))\n\t\t\t\t\t.toArray(Method[]::new);\n\t\t\t// @formatter:on\n\n\t\t\tif (traversalMode == TOP_DOWN) {\n\t\t\t\tCollections.addAll(allInterfaceMethods, superinterfaceMethods);\n\t\t\t}\n\t\t\tCollections.addAll(allInterfaceMethods, localInterfaceMethods);\n\t\t\tif (traversalMode == BOTTOM_UP) {\n\t\t\t\tCollections.addAll(allInterfaceMethods, superinterfaceMethods);\n\t\t\t}\n\t\t}\n\t\treturn allInterfaceMethods;\n\t}\n\n\tprivate static List<Field> getInterfaceFields(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tList<Field> allInterfaceFields = new ArrayList<>();\n\t\tfor (Class<?> ifc : clazz.getInterfaces()) {\n\n\t\t\tField[] localInterfaceFields = ifc.getFields();\n\t\t\tArrays.sort(localInterfaceFields, ReflectionUtils::defaultFieldSorter);\n\n\t\t\tList<Field> superinterfaceFields = getInterfaceFields(ifc, traversalMode);\n\n\t\t\tif (traversalMode == TOP_DOWN) {\n\t\t\t\tallInterfaceFields.addAll(superinterfaceFields);\n\t\t\t}\n\t\t\tCollections.addAll(allInterfaceFields, localInterfaceFields);\n\t\t\tif (traversalMode == BOTTOM_UP) {\n\t\t\t\tallInterfaceFields.addAll(superinterfaceFields);\n\t\t\t}\n\t\t}\n\t\treturn allInterfaceFields;\n\t}\n\n\tprivate static List<Field> getSuperclassFields(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tClass<?> superclass = clazz.getSuperclass();\n\t\tif (!isSearchable(superclass)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn findAllFieldsInHierarchy(superclass, traversalMode);\n\t}\n\n\tprivate static List<Method> getSuperclassMethods(Class<?> clazz, HierarchyTraversalMode traversalMode) {\n\t\tClass<?> superclass = clazz.getSuperclass();\n\t\tif (!isSearchable(superclass)) {\n\t\t\treturn Collections.emptyList();\n\t\t}\n\t\treturn findAllMethodsInHierarchy(superclass, traversalMode);\n\t}\n\n\tprivate static boolean isNotOverriddenByLocalMethods(Method method, Method[] localMethods) {\n\t\tfor (Method local : localMethods) {\n\t\t\tif (isMethodOverriddenBy(method, local)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tprivate static boolean isMethodOverriddenBy(Method upper, Method lower) {\n\t\t// A static method cannot override anything.\n\t\tif (Modifier.isStatic(lower.getModifiers())) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Cannot override a private, static, or final method.\n\t\tint modifiers = upper.getModifiers();\n\t\tif (Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Cannot override a package-private method in another package.\n\t\tif (isPackagePrivate(upper) && !isDeclaredInSamePackage(upper, lower)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn hasCompatibleSignature(upper, lower.getName(), lower.getParameterTypes());\n\t}\n\n\t/**\n\t * @since 5.14.1\n\t */\n\t@API(status = INTERNAL, since = \"5.14.1\")\n\tpublic static boolean isPackagePrivate(Member member) {\n\t\tint modifiers = member.getModifiers();\n\t\treturn !(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers) || Modifier.isPrivate(modifiers));\n\t}\n\n\tprivate static boolean isDeclaredInSamePackage(Method m1, Method m2) {\n\t\treturn isDeclaredInSamePackage(m1.getDeclaringClass(), m2.getDeclaringClass());\n\t}\n\n\t/**\n\t * @since 5.14.1\n\t */\n\t@API(status = INTERNAL, since = \"5.14.1\")\n\tpublic static boolean isDeclaredInSamePackage(Class<?> c1, Class<?> c2) {\n\t\treturn c1.getPackageName().equals(c2.getPackageName());\n\t}\n\n\t/**\n\t * Determine if the supplied candidate method (typically a method higher in\n\t * the type hierarchy) has a signature that is compatible with a method that\n\t * has the supplied name and parameter types, taking method sub-signatures\n\t * and generics into account.\n\t */\n\tprivate static boolean hasCompatibleSignature(Method candidate, String methodName, Class<?>[] parameterTypes) {\n\t\tif (!methodName.equals(candidate.getName())) {\n\t\t\treturn false;\n\t\t}\n\t\tif (parameterTypes.length != candidate.getParameterCount()) {\n\t\t\treturn false;\n\t\t}\n\t\tClass<?>[] candidateParameterTypes = candidate.getParameterTypes();\n\t\t// trivial case: parameter types exactly match\n\t\tif (Arrays.equals(parameterTypes, candidateParameterTypes)) {\n\t\t\treturn true;\n\t\t}\n\t\t// param count is equal, but types do not match exactly: check for method sub-signatures\n\t\t// https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2\n\t\tfor (int i = 0; i < parameterTypes.length; i++) {\n\t\t\tClass<?> lowerType = parameterTypes[i];\n\t\t\tClass<?> upperType = candidateParameterTypes[i];\n\t\t\tif (!upperType.isAssignableFrom(lowerType)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\t// lower is sub-signature of upper: check for generics in upper method\n\t\treturn isGeneric(candidate);\n\t}\n\n\tstatic boolean isGeneric(Method method) {\n\t\treturn isGeneric(method.getGenericReturnType())\n\t\t\t\t|| Arrays.stream(method.getGenericParameterTypes()).anyMatch(ReflectionUtils::isGeneric);\n\t}\n\n\tprivate static boolean isGeneric(Type type) {\n\t\treturn type instanceof TypeVariable || type instanceof GenericArrayType;\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\t@SuppressWarnings(\"deprecation\") // \"AccessibleObject.isAccessible()\" is deprecated in Java 9\n\tpublic static <T extends Executable> T makeAccessible(T executable) {\n\t\tif ((!isPublic(executable) || !isPublic(executable.getDeclaringClass())) && !executable.isAccessible()) {\n\t\t\texecutable.setAccessible(true);\n\t\t}\n\t\treturn executable;\n\t}\n\n\t/**\n\t * @since 1.12\n\t */\n\t@API(status = INTERNAL, since = \"1.12\")\n\t@SuppressWarnings(\"deprecation\") // \"AccessibleObject.isAccessible()\" is deprecated in Java 9\n\tpublic static Field makeAccessible(Field field) {\n\t\tif ((!isPublic(field) || !isPublic(field.getDeclaringClass()) || isFinal(field)) && !field.isAccessible()) {\n\t\t\tfield.setAccessible(true);\n\t\t}\n\t\treturn field;\n\t}\n\n\t/**\n\t * Return all classes and interfaces that can be used as assignment types\n\t * for instances of the specified {@link Class}, including itself.\n\t *\n\t * @param clazz the {@code Class} to look up\n\t * @see Class#isAssignableFrom\n\t */\n\tpublic static Set<Class<?>> getAllAssignmentCompatibleClasses(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\n\t\tSet<Class<?>> result = new LinkedHashSet<>();\n\t\tgetAllAssignmentCompatibleClasses(clazz, result);\n\t\treturn result;\n\t}\n\n\tprivate static void getAllAssignmentCompatibleClasses(Class<?> clazz, Set<Class<?>> result) {\n\t\tfor (Class<?> current = clazz; current != null; current = current.getSuperclass()) {\n\t\t\tresult.add(current);\n\t\t\tfor (Class<?> interfaceClass : current.getInterfaces()) {\n\t\t\t\tif (!result.contains(interfaceClass)) {\n\t\t\t\t\tgetAllAssignmentCompatibleClasses(interfaceClass, result);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Determine if the supplied class is <em>searchable</em>: is non-null and is\n\t * not equal to the class reference for {@code java.lang.Object}.\n\t *\n\t * <p>This method is often used to determine if a superclass should be\n\t * searched but may be applicable for other use cases as well.\n\t * @since 1.6\n\t */\n\tprivate static boolean isSearchable(@Nullable Class<?> clazz) {\n\t\treturn (clazz != null && clazz != Object.class);\n\t}\n\n\t/**\n\t * Get the underlying cause of the supplied {@link Throwable}.\n\t *\n\t * <p>If the supplied {@code Throwable} is an instance of\n\t * {@link InvocationTargetException}, this method will be invoked\n\t * recursively with the underlying\n\t * {@linkplain InvocationTargetException#getTargetException() target\n\t * exception}; otherwise, this method returns the supplied {@code Throwable}.\n\t */\n\tstatic Throwable getUnderlyingCause(Throwable t) {\n\t\tif (t instanceof InvocationTargetException ite) {\n\t\t\treturn getUnderlyingCause(ite.getTargetException());\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * @since 1.13.2\n\t */\n\t@API(status = INTERNAL, since = \"1.13.2\")\n\tpublic enum CycleErrorHandling {\n\n\t\tTHROW_EXCEPTION {\n\t\t\t@Override\n\t\t\tvoid handle(Class<?> clazz, Class<?> enclosing) {\n\t\t\t\tthrow new JUnitException(\"Detected cycle in inner class hierarchy between %s and %s\".formatted(\n\t\t\t\t\tclazz.getName(), enclosing.getName()));\n\t\t\t}\n\t\t},\n\n\t\tABORT_VISIT {\n\t\t\t@Override\n\t\t\tvoid handle(Class<?> clazz, Class<?> enclosing) {\n\t\t\t\t// ignore\n\t\t\t}\n\t\t};\n\n\t\tabstract void handle(Class<?> clazz, Class<?> enclosing);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ResourceUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.net.URI;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of static utility methods for working with resources.\n *\n * @since 1.3 (originally in org.junit.platform.engine.support.descriptor)\n */\n@API(status = INTERNAL, since = \"1.12\")\npublic final class ResourceUtils {\n\n\tprivate ResourceUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Strip the {@link URI#getQuery() query} component from the supplied\n\t * {@link URI}.\n\t *\n\t * @param uri the {@code URI} from which to strip the query component\n\t * @return a new {@code URI} with the query component removed, or the\n\t * original {@code URI} unmodified if it does not have a query component\n\t *\n\t * @since 1.3\n\t */\n\tpublic static URI stripQueryComponent(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\n\t\tif (StringUtils.isBlank(uri.getQuery())) {\n\t\t\treturn uri;\n\t\t}\n\n\t\tString uriAsString = uri.toString();\n\t\treturn URI.create(uriAsString.substring(0, uriAsString.indexOf('?')));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/RuntimeUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of utilities for working with {@link Runtime},\n * {@link java.lang.management.RuntimeMXBean}, etc.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.6\n */\n@API(status = INTERNAL, since = \"1.6\")\npublic final class RuntimeUtils {\n\n\tprivate RuntimeUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Try to determine whether the VM was started in debug mode or not.\n\t */\n\tpublic static boolean isDebugMode() {\n\t\treturn getInputArguments() //\n\t\t\t\t.map(args -> args.stream().anyMatch(\n\t\t\t\t\targ -> arg.startsWith(\"-agentlib:jdwp\") || arg.startsWith(\"-Xrunjdwp\"))) //\n\t\t\t\t.orElse(false);\n\t}\n\n\t/**\n\t * Try to get the input arguments the VM was started with.\n\t */\n\tstatic Optional<List<String>> getInputArguments() {\n\t\tOptional<Class<?>> managementFactoryClass = ReflectionUtils.tryToLoadClass(\n\t\t\t\"java.lang.management.ManagementFactory\").toOptional();\n\t\tif (managementFactoryClass.isEmpty()) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\t// Can't use \"java.lang.management.ManagementFactory.getRuntimeMXBean().getInputArguments()\"\n\t\t// directly as module \"java.management\" might not be available and/or the current platform\n\t\t// doesn't support the Java Management Extensions (JMX) API (like Android?).\n\t\t// See https://github.com/junit-team/junit4/pull/1187\n\t\ttry {\n\t\t\tObject bean = managementFactoryClass.get().getMethod(\"getRuntimeMXBean\").invoke(null);\n\t\t\tClass<?> mx = ReflectionUtils.tryToLoadClass(\"java.lang.management.RuntimeMXBean\").getNonNull();\n\t\t\t@SuppressWarnings(\"unchecked\")\n\t\t\tList<String> args = (List<String>) mx.getMethod(\"getInputArguments\").invoke(bean);\n\t\t\treturn Optional.of(args);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/SearchPathUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.nio.file.Path;\nimport java.util.stream.IntStream;\n\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * @since 1.11\n */\nclass SearchPathUtils {\n\n\tstatic final char PACKAGE_SEPARATOR_CHAR = '.';\n\tstatic final String PACKAGE_SEPARATOR_STRING = String.valueOf(PACKAGE_SEPARATOR_CHAR);\n\tprivate static final char FILE_NAME_EXTENSION_SEPARATOR_CHAR = '.';\n\n\tprivate static final String CLASS_FILE_SUFFIX = \".class\";\n\tprivate static final String SOURCE_FILE_SUFFIX = \".java\";\n\n\tprivate static final String PACKAGE_INFO_FILE_NAME = \"package-info\";\n\tprivate static final String MODULE_INFO_FILE_NAME = \"module-info\";\n\n\t// System property defined since Java 12: https://bugs.java/bugdatabase/JDK-8210877\n\tprivate static final boolean SOURCE_MODE = System.getProperty(\"jdk.launcher.sourcefile\") != null;\n\n\tstatic boolean isResourceFile(Path file) {\n\t\treturn !isClassFile(file);\n\t}\n\n\tstatic boolean isClassOrSourceFile(Path file) {\n\t\tvar fileName = file.getFileName().toString();\n\t\treturn isClassOrSourceFile(fileName) && !isModuleInfoOrPackageInfo(fileName);\n\t}\n\n\tprivate static boolean isModuleInfoOrPackageInfo(String fileName) {\n\t\tvar fileNameWithoutExtension = removeExtension(fileName);\n\t\treturn PACKAGE_INFO_FILE_NAME.equals(fileNameWithoutExtension) //\n\t\t\t\t|| MODULE_INFO_FILE_NAME.equals(fileNameWithoutExtension);\n\t}\n\n\tstatic String determineFullyQualifiedClassName(Path path) {\n\t\tvar simpleClassName = determineSimpleClassName(path);\n\t\tvar parent = path.getParent();\n\t\treturn parent == null ? simpleClassName : joinPathNamesWithPackageSeparator(parent.resolve(simpleClassName));\n\t}\n\n\tprivate static String joinPathNamesWithPackageSeparator(Path path) {\n\t\treturn IntStream.range(0, path.getNameCount()) //\n\t\t\t\t.mapToObj(i -> path.getName(i).toString()) //\n\t\t\t\t.collect(joining(PACKAGE_SEPARATOR_STRING));\n\t}\n\n\tstatic String determineSimpleClassName(Path file) {\n\t\treturn removeExtension(file.getFileName().toString());\n\t}\n\n\tprivate static String removeExtension(String fileName) {\n\t\tint lastDot = fileName.lastIndexOf(FILE_NAME_EXTENSION_SEPARATOR_CHAR);\n\t\tif (lastDot < 0) {\n\t\t\tthrow new JUnitException(\"Expected file name with file extension, but got: \" + fileName);\n\t\t}\n\t\treturn fileName.substring(0, lastDot);\n\t}\n\n\tprivate static boolean isClassOrSourceFile(String name) {\n\t\treturn name.endsWith(CLASS_FILE_SUFFIX) || (SOURCE_MODE && name.endsWith(SOURCE_FILE_SUFFIX));\n\t}\n\n\tprivate static boolean isClassFile(Path file) {\n\t\treturn file.getFileName().toString().endsWith(CLASS_FILE_SUFFIX);\n\t}\n\n\tprivate SearchPathUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ServiceLoaderUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ServiceLoader;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of utilities for working with {@link ServiceLoader}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.11\n */\n@API(status = INTERNAL, since = \"1.11\")\npublic class ServiceLoaderUtils {\n\n\tprivate ServiceLoaderUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Filters the supplied service loader using the supplied predicate.\n\t *\n\t * @param <T> the type of the service\n\t * @param serviceLoader the service loader to be filtered\n\t * @param providerPredicate the predicate to filter the loaded services\n\t * @return a stream of loaded services that match the predicate\n\t */\n\tpublic static <T> Stream<T> filter(ServiceLoader<T> serviceLoader,\n\t\t\tPredicate<? super Class<? extends T>> providerPredicate) {\n\n\t\tPreconditions.notNull(serviceLoader, \"serviceLoader must not be null\");\n\t\tPreconditions.notNull(providerPredicate, \"providerPredicate must not be null\");\n\n\t\t// @formatter:off\n\t\treturn serviceLoader\n\t\t\t\t.stream()\n\t\t\t\t.filter(provider -> providerPredicate.test(provider.type()))\n\t\t\t\t.map(ServiceLoader.Provider::get);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/StringUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.regex.Pattern.UNICODE_CHARACTER_CLASS;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Arrays;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\nimport java.util.regex.Pattern;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.annotation.Contract;\n\n/**\n * Collection of utilities for working with {@link String Strings},\n * {@link CharSequence CharSequences}, etc.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic final class StringUtils {\n\n\tprivate static final Pattern ISO_CONTROL_PATTERN = compileIsoControlPattern();\n\tprivate static final Pattern WHITESPACE_PATTERN = Pattern.compile(\"\\\\s\");\n\n\t/**\n\t * Guard against \"IllegalArgumentException: Unsupported flags: 256\" errors.\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1800\">#1800</a>\n\t */\n\tstatic Pattern compileIsoControlPattern() {\n\t\t// https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html#posix\n\t\ttry {\n\t\t\t// All of the characters that Unicode refers to as 'control characters'\n\t\t\treturn Pattern.compile(\"\\\\p{Cntrl}\", UNICODE_CHARACTER_CLASS);\n\t\t}\n\t\tcatch (IllegalArgumentException e) {\n\t\t\t// Fall-back to ASCII control characters only: [\\x00-\\x1F\\x7F]\n\t\t\treturn Pattern.compile(\"\\\\p{Cntrl}\");\n\t\t}\n\t}\n\n\tprivate StringUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} is <em>blank</em> (i.e.,\n\t * {@code null} or consisting only of whitespace characters).\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string is blank\n\t * @see String#isBlank()\n\t * @see #isNotBlank(String)\n\t */\n\t@Contract(\"null -> true\")\n\tpublic static boolean isBlank(@Nullable String str) {\n\t\treturn (str == null || str.isBlank());\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} is not {@linkplain #isBlank\n\t * blank}.\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string is not blank\n\t * @see #isBlank(String)\n\t */\n\t@Contract(\"null -> false\")\n\tpublic static boolean isNotBlank(@Nullable String str) {\n\t\treturn !isBlank(str);\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} contains any whitespace characters.\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string contains whitespace\n\t * @see #containsIsoControlCharacter(String)\n\t * @see Character#isWhitespace(int)\n\t */\n\t@Contract(\"null -> false\")\n\tpublic static boolean containsWhitespace(@Nullable String str) {\n\t\treturn str != null && str.codePoints().anyMatch(Character::isWhitespace);\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} does not contain any whitespace\n\t * characters.\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string does not contain whitespace\n\t * @see #containsWhitespace(String)\n\t * @see #containsIsoControlCharacter(String)\n\t * @see Character#isWhitespace(int)\n\t */\n\t@Contract(\"null -> true\")\n\tpublic static boolean doesNotContainWhitespace(@Nullable String str) {\n\t\treturn !containsWhitespace(str);\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} contains any ISO control characters.\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string contains an ISO control character\n\t * @see #containsWhitespace(String)\n\t * @see Character#isISOControl(int)\n\t */\n\t@Contract(\"null -> false\")\n\tpublic static boolean containsIsoControlCharacter(@Nullable String str) {\n\t\treturn str != null && str.codePoints().anyMatch(Character::isISOControl);\n\t}\n\n\t/**\n\t * Determine if the supplied {@link String} does not contain any ISO control\n\t * characters.\n\t *\n\t * @param str the string to check; may be {@code null}\n\t * @return {@code true} if the string does not contain an ISO control character\n\t * @see #containsIsoControlCharacter(String)\n\t * @see #containsWhitespace(String)\n\t * @see Character#isISOControl(int)\n\t */\n\t@Contract(\"null -> true\")\n\tpublic static boolean doesNotContainIsoControlCharacter(@Nullable String str) {\n\t\treturn !containsIsoControlCharacter(str);\n\t}\n\n\t/**\n\t * Convert the supplied {@code Object} to a {@code String} using the\n\t * following algorithm.\n\t *\n\t * <ul>\n\t * <li>If the supplied object is {@code null}, this method returns {@code \"null\"}.</li>\n\t * <li>If the supplied object is a primitive array, the appropriate\n\t * {@code Arrays#toString(...)} variant will be used to convert it to a String.</li>\n\t * <li>If the supplied object is an object array, {@code Arrays#deepToString(Object[])}\n\t * will be used to convert it to a String.</li>\n\t * <li>Otherwise, {@code toString()} will be invoked on the object. If the\n\t * result is non-null, that result will be returned. If the result is\n\t * {@code null}, {@code \"null\"} will be returned.</li>\n\t * <li>If any of the above results in an exception, this method delegates to\n\t * {@link #defaultToString(Object)}</li>\n\t * </ul>\n\t *\n\t * @param obj the object to convert to a String; may be {@code null}\n\t * @return a String representation of the supplied object; never {@code null}\n\t * @see Arrays#deepToString(Object[])\n\t * @see ClassUtils#nullSafeToString(Class...)\n\t */\n\tpublic static String nullSafeToString(@Nullable Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn \"null\";\n\t\t}\n\n\t\ttry {\n\t\t\tif (obj.getClass().isArray()) {\n\t\t\t\tif (obj.getClass().getComponentType().isPrimitive()) {\n\t\t\t\t\tif (obj instanceof boolean[] booleans) {\n\t\t\t\t\t\treturn Arrays.toString(booleans);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof char[] chars) {\n\t\t\t\t\t\treturn Arrays.toString(chars);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof short[] shorts) {\n\t\t\t\t\t\treturn Arrays.toString(shorts);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof byte[] bytes) {\n\t\t\t\t\t\treturn Arrays.toString(bytes);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof int[] ints) {\n\t\t\t\t\t\treturn Arrays.toString(ints);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof long[] longs) {\n\t\t\t\t\t\treturn Arrays.toString(longs);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof float[] floats) {\n\t\t\t\t\t\treturn Arrays.toString(floats);\n\t\t\t\t\t}\n\t\t\t\t\tif (obj instanceof double[] doubles) {\n\t\t\t\t\t\treturn Arrays.toString(doubles);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn Arrays.deepToString((Object[]) obj);\n\t\t\t}\n\n\t\t\t// else\n\t\t\tString result = obj.toString();\n\t\t\treturn result != null ? result : \"null\";\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\n\t\t\treturn defaultToString(obj);\n\t\t}\n\t}\n\n\t/**\n\t * Convert the supplied {@code Object} to a <em>default</em> {@code String}\n\t * representation using the following algorithm.\n\t *\n\t * <ul>\n\t * <li>If the supplied object is {@code null}, this method returns {@code \"null\"}.</li>\n\t * <li>Otherwise, the String returned by this method will be generated analogous\n\t * to the default implementation of {@link Object#toString()} by using the supplied\n\t * object's class name and hash code as follows:\n\t * {@code obj.getClass().getName() + \"@\" + Integer.toHexString(System.identityHashCode(obj))}</li>\n\t * </ul>\n\t *\n\t * @param obj the object to convert to a String; may be {@code null}\n\t * @return the default String representation of the supplied object; never {@code null}\n\t * @see #nullSafeToString(Object)\n\t * @see ClassUtils#nullSafeToString(Class...)\n\t */\n\tpublic static String defaultToString(@Nullable Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn \"null\";\n\t\t}\n\n\t\treturn obj.getClass().getName() + \"@\" + Integer.toHexString(System.identityHashCode(obj));\n\t}\n\n\t/**\n\t * Replace all ISO control characters in the supplied {@link String}.\n\t *\n\t * @param str the string in which to perform the replacement; may be {@code null}\n\t * @param replacement the replacement string; never {@code null}\n\t * @return the supplied string with all control characters replaced, or\n\t * {@code null} if the supplied string was {@code null}\n\t * @since 1.4\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\t@Contract(\"null, _ -> null; !null, _ -> !null\")\n\tpublic static @Nullable String replaceIsoControlCharacters(@Nullable String str, String replacement) {\n\t\tPreconditions.notNull(replacement, \"replacement must not be null\");\n\t\treturn str == null ? null : ISO_CONTROL_PATTERN.matcher(str).replaceAll(replacement);\n\t}\n\n\t/**\n\t * Replace all whitespace characters in the supplied {@link String}.\n\t *\n\t * @param str the string in which to perform the replacement; may be {@code null}\n\t * @param replacement the replacement string; never {@code null}\n\t * @return the supplied string with all whitespace characters replaced, or\n\t * {@code null} if the supplied string was {@code null}\n\t * @since 1.4\n\t */\n\t@API(status = INTERNAL, since = \"1.4\")\n\t@Contract(\"null, _ -> null; !null, _ -> !null\")\n\tpublic static @Nullable String replaceWhitespaceCharacters(@Nullable String str, String replacement) {\n\t\tPreconditions.notNull(replacement, \"replacement must not be null\");\n\t\treturn str == null ? null : WHITESPACE_PATTERN.matcher(str).replaceAll(replacement);\n\t}\n\n\t/**\n\t * Split the supplied {@link String} into up to two parts using the supplied\n\t * separator character.\n\t *\n\t * @param separator the separator character\n\t * @param value the value to split; never {@code null}\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static TwoPartSplitResult splitIntoTwo(char separator, String value) {\n\t\tPreconditions.notNull(value, \"value must not be null\");\n\t\treturn splitIntoTwo(value, value.indexOf(separator), 1);\n\t}\n\n\t/**\n\t * Split the supplied {@link String} into up to two parts using the supplied\n\t * separator string.\n\t *\n\t * @param separator the separator string; never {@code null}\n\t * @param value the value to split; never {@code null}\n\t * @since 1.11\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static TwoPartSplitResult splitIntoTwo(String separator, String value) {\n\t\tPreconditions.notNull(separator, \"separator must not be null\");\n\t\tPreconditions.notNull(value, \"value must not be null\");\n\t\treturn splitIntoTwo(value, value.indexOf(separator), separator.length());\n\t}\n\n\tprivate static TwoPartSplitResult splitIntoTwo(String value, int index, int length) {\n\t\tif (index == -1) {\n\t\t\treturn new OnePart(value);\n\t\t}\n\t\treturn new TwoParts(value.substring(0, index), value.substring(index + length));\n\t}\n\n\t/**\n\t * The result of splitting a string into up to two parts.\n\t *\n\t * @since 1.11\n\t * @see StringUtils#splitIntoTwo(char, String)\n\t * @see StringUtils#splitIntoTwo(String, String)\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic sealed interface TwoPartSplitResult {\n\n\t\t/**\n\t\t * Map the result of splitting a string into two parts or throw an exception.\n\t\t *\n\t\t * @param onePartExceptionCreator the exception creator to use if the string\n\t\t * was split into a single part\n\t\t * @param twoPartsMapper the mapper to use if the string was split into two parts\n\t\t */\n\t\tdefault <T> T mapTwo(Supplier<? extends RuntimeException> onePartExceptionCreator,\n\t\t\t\tBiFunction<String, String, ? extends T> twoPartsMapper) {\n\t\t\tFunction<String, ? extends T> onePartMapper = __ -> {\n\t\t\t\tthrow onePartExceptionCreator.get();\n\t\t\t};\n\t\t\treturn map(onePartMapper, twoPartsMapper);\n\t\t}\n\n\t\t/**\n\t\t * Map the result of splitting a string into up to two parts.\n\t\t *\n\t\t * @param onePartMapper the mapper to use if the string was split into a single part\n\t\t * @param twoPartsMapper the mapper to use if the string was split into two parts\n\t\t */\n\t\t<T> T map(Function<String, ? extends T> onePartMapper, BiFunction<String, String, ? extends T> twoPartsMapper);\n\n\t}\n\n\tprivate record OnePart(String value) implements TwoPartSplitResult {\n\n\t\t@Override\n\t\tpublic <T> T map(Function<String, ? extends T> onePartMapper,\n\t\t\t\tBiFunction<String, String, ? extends T> twoPartsMapper) {\n\t\t\treturn onePartMapper.apply(this.value);\n\t\t}\n\t}\n\n\tprivate record TwoParts(String first, String second) implements TwoPartSplitResult {\n\n\t\t@Override\n\t\tpublic <T> T map(Function<String, ? extends T> onePartMapper,\n\t\t\t\tBiFunction<String, String, ? extends T> twoPartsMapper) {\n\t\t\treturn twoPartsMapper.apply(this.first, this.second);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/ToStringBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.lang.String.join;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Simple builder for generating strings in custom implementations of\n * {@link Object#toString toString()}.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic class ToStringBuilder {\n\n\tprivate final String typeName;\n\n\tprivate final List<String> values = new ArrayList<>();\n\n\tpublic ToStringBuilder(Object obj) {\n\t\tthis(Preconditions.notNull(obj, \"Object must not be null\").getClass().getSimpleName());\n\t}\n\n\tpublic ToStringBuilder(Class<?> type) {\n\t\tthis(Preconditions.notNull(type, \"Class must not be null\").getSimpleName());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.7\")\n\tpublic ToStringBuilder(String typeName) {\n\t\tthis.typeName = Preconditions.notNull(typeName, \"Type name must not be null\");\n\t}\n\n\tpublic ToStringBuilder append(String name, @Nullable Object value) {\n\t\tPreconditions.notBlank(name, \"Name must not be null or blank\");\n\t\tthis.values.add(name + \" = \" + toString(value));\n\t\treturn this;\n\t}\n\n\tprivate String toString(@Nullable Object obj) {\n\t\treturn (obj instanceof CharSequence) ? (\"'\" + obj + \"'\") : StringUtils.nullSafeToString(obj);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.typeName + \" [\" + join(\", \", this.values) + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/UnrecoverableExceptions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Internal utilities for working with <em>unrecoverable</em> exceptions.\n *\n * <p><em>Unrecoverable</em> exceptions are those that should always terminate\n * test plan execution immediately.\n *\n * <h2>Currently Unrecoverable Exceptions</h2>\n * <ul>\n * <li>{@link OutOfMemoryError}</li>\n * </ul>\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.7\n */\n@API(status = INTERNAL, since = \"1.7\")\npublic final class UnrecoverableExceptions {\n\n\tprivate UnrecoverableExceptions() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Rethrow the supplied {@link Throwable exception} if it is\n\t * <em>unrecoverable</em>.\n\t *\n\t * <p>If the supplied {@code exception} is not <em>unrecoverable</em>, this\n\t * method does nothing.\n\t */\n\tpublic static void rethrowIfUnrecoverable(@Nullable Throwable exception) {\n\t\tif (exception instanceof OutOfMemoryError) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(exception);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/main/java/org/junit/platform/commons/util/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal common utilities for JUnit.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n */\n\n@NullMarked\npackage org.junit.platform.commons.util;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-commons/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/ConcurrencyTestingUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.test;\n\nimport static java.util.concurrent.TimeUnit.SECONDS;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CompletionException;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.jspecify.annotations.Nullable;\n\npublic class ConcurrencyTestingUtils {\n\n\tpublic static void executeConcurrently(int threads, Runnable action) throws Exception {\n\t\tConcurrencyTestingUtils.<@Nullable Object> executeConcurrently(threads, () -> {\n\t\t\taction.run();\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t@SuppressWarnings(\"Finally\")\n\tpublic static <T extends @Nullable Object> List<T> executeConcurrently(int threads, Callable<T> action)\n\t\t\tthrows Exception {\n\t\tExecutorService executorService = Executors.newFixedThreadPool(threads);\n\t\ttry {\n\t\t\tCountDownLatch latch = new CountDownLatch(threads);\n\t\t\tList<CompletableFuture<T>> futures = new ArrayList<>();\n\t\t\tfor (int i = 0; i < threads; i++) {\n\t\t\t\tfutures.add(CompletableFuture.supplyAsync(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlatch.countDown();\n\t\t\t\t\t\tlatch.await();\n\t\t\t\t\t\treturn action.call();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t\t\tthrow new CompletionException(e);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tthrow new CompletionException(\"Action failed\", e);\n\t\t\t\t\t}\n\t\t\t\t}, executorService));\n\t\t\t}\n\t\t\tList<T> list = new ArrayList<>();\n\t\t\tfor (CompletableFuture<T> future : futures) {\n\t\t\t\tlist.add(future.get(5, SECONDS));\n\t\t\t}\n\t\t\treturn list;\n\t\t}\n\t\tfinally {\n\t\t\texecutorService.shutdownNow();\n\t\t\tvar terminated = executorService.awaitTermination(5, SECONDS);\n\t\t\tif (!terminated) {\n\t\t\t\t//noinspection ThrowFromFinallyBlock\n\t\t\t\tthrow new AssertionError(\"ExecutorService did not cleanly shut down\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate ConcurrencyTestingUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/IdeUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.test;\n\n/**\n * Collection of test utilities for IDEs.\n */\npublic class IdeUtils {\n\n\t/**\n\t * Determine if the current code is running in the Eclipse IDE.\n\t * <p>Copied from {@code org.springframework.core.testfixture.ide.IdeUtils}.\n\t */\n\tpublic static boolean runningInEclipse() {\n\t\treturn StackWalker.getInstance().walk(\n\t\t\tstream -> stream.anyMatch(stackFrame -> stackFrame.getClassName().startsWith(\"org.eclipse.jdt\")));\n\t}\n\n\tprivate IdeUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/PreconditionAssertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.test;\n\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\nimport org.assertj.core.api.ThrowableAssert.ThrowingCallable;\nimport org.assertj.core.api.ThrowableAssertAlternative;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Collection of assertions for working with {@link Preconditions}.\n *\n * @since 1.14\n */\npublic final class PreconditionAssertions {\n\n\tprivate PreconditionAssertions() {\n\t\t/* no-op */\n\t}\n\n\tpublic static void assertPreconditionViolationNotNullFor(String name, ThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessage(\"%s must not be null\", name);\n\t}\n\n\tpublic static void assertPreconditionViolationNotBlankFor(String name, ThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessageContaining(\"%s must not be blank\", name);\n\t}\n\n\tpublic static void assertPreconditionViolationNotEmptyFor(String name, ThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessage(\"%s must not be empty\", name);\n\t}\n\n\tpublic static void assertPreconditionViolationNotNullOrBlankFor(String name, ThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessage(\"%s must not be null or blank\", name);\n\t}\n\n\tpublic static void assertPreconditionViolationNotNullOrEmptyFor(String name, ThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessage(\"%s must not be null or empty\", name);\n\t}\n\n\tpublic static void assertPreconditionViolationContainsNoNullElementsFor(String name,\n\t\t\tThrowingCallable throwingCallable) {\n\t\tassertPreconditionViolationFor(throwingCallable).withMessage(\"%s must not contain null elements\", name);\n\t}\n\n\tpublic static ThrowableAssertAlternative<PreconditionViolationException> assertPreconditionViolationFor(\n\t\t\tThrowingCallable throwingCallable) {\n\n\t\treturn assertThatExceptionOfType(PreconditionViolationException.class).isThrownBy(throwingCallable);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/TestClassLoader.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.test;\n\nimport java.lang.StackWalker.Option;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.security.CodeSource;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.util.ClassLoaderUtils;\n\n/**\n * Test {@link ClassLoader} which accepts a class name {@link Predicate} to\n * filter classes that should be loaded by this {@code ClassLoader} instead of\n * the {@linkplain ClassLoaderUtils#getDefaultClassLoader() default ClassLoader}.\n *\n * <p>This class loader is only suitable for specific testing scenarios, where\n * you need to load particular classes from a different class loader.\n *\n * @since 1.10\n */\npublic class TestClassLoader extends URLClassLoader {\n\n\tprivate static final StackWalker stackWalker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);\n\n\tstatic {\n\t\tClassLoader.registerAsParallelCapable();\n\t}\n\n\t/**\n\t * Create a {@link TestClassLoader} that filters the provided classes.\n\t *\n\t * @see #forClasses(List)\n\t * @see #forClassNamePrefix(String)\n\t */\n\tpublic static TestClassLoader forClasses(Class<?>... classes) {\n\t\tPredicate<String> classNameFilter = name -> Arrays.stream(classes).map(Class::getName).anyMatch(name::equals);\n\t\treturn new TestClassLoader(getCodeSourceUrl(stackWalker.getCallerClass()), classNameFilter);\n\t}\n\n\t/**\n\t * Create a {@link TestClassLoader} that filters the provided classes.\n\t *\n\t * @see #forClasses(Class...)\n\t * @see #forClassNamePrefix(String)\n\t */\n\tpublic static TestClassLoader forClasses(List<Class<?>> classes) {\n\t\tPredicate<String> classNameFilter = name -> classes.stream().map(Class::getName).anyMatch(name::equals);\n\t\treturn new TestClassLoader(getCodeSourceUrl(stackWalker.getCallerClass()), classNameFilter);\n\t}\n\n\t/**\n\t * Create a {@link TestClassLoader} that filters classes whose fully\n\t * qualified names start with the provided prefix.\n\t *\n\t * @see #forClasses(Class...)\n\t * @see #forClasses(List)\n\t */\n\tpublic static TestClassLoader forClassNamePrefix(String prefix) {\n\t\treturn new TestClassLoader(getCodeSourceUrl(stackWalker.getCallerClass()), name -> name.startsWith(prefix));\n\t}\n\n\tprivate final Predicate<String> classNameFilter;\n\n\tprivate TestClassLoader(URL codeSourceUrl, Predicate<String> classNameFilter) {\n\t\tsuper(new URL[] { codeSourceUrl }, ClassLoaderUtils.getDefaultClassLoader());\n\n\t\tthis.classNameFilter = classNameFilter;\n\t}\n\n\t@Override\n\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\t\tsynchronized (getClassLoadingLock(name)) {\n\t\t\tClass<?> clazz = findLoadedClass(name);\n\t\t\tif (clazz != null) {\n\t\t\t\treturn clazz;\n\t\t\t}\n\t\t\treturn this.classNameFilter.test(name) ? findClass(name) : super.loadClass(name);\n\t\t}\n\t}\n\n\t/**\n\t * Get the {@link CodeSource} {@link URL} of the supplied class.\n\t */\n\tprivate static URL getCodeSourceUrl(Class<?> clazz) {\n\t\treturn clazz.getProtectionDomain().getCodeSource().getLocation();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-commons/src/testFixtures/java/org/junit/platform/commons/test/package-info.java",
    "content": "\n@NullMarked\npackage org.junit.platform.commons.test;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-console/LICENSE-picocli.md",
    "content": "Apache License\n==============\n\n_Version 2.0, January 2004_\n_&lt;<https://www.apache.org/licenses/>&gt;_\n\n### Terms and Conditions for use, reproduction, and distribution\n\n#### 1. Definitions\n\n“License” shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, “control” means **(i)** the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the\noutstanding shares, or **(iii)** beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n“Object” form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n“submitted” means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n#### 2. Grant of Copyright License\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n#### 3. Grant of Patent License\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n#### 4. Redistribution\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\n* **(a)** You must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\n* **(b)** You must cause any modified files to carry prominent notices stating that You\nchanged the files; and\n* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\n* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\n\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n#### 5. Submission of Contributions\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n#### 6. Trademarks\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n#### 7. Disclaimer of Warranty\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n#### 8. Limitation of Liability\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n#### 9. Accepting Warranty or Additional Liability\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\n_END OF TERMS AND CONDITIONS_\n\n### APPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets `[]` replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same “printed page” as the copyright notice for easier identification within\nthird-party archives.\n\n    Copyright [yyyy] [name of copyright owner]\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"
  },
  {
    "path": "junit-platform-console/junit-platform-console.gradle.kts",
    "content": "import junitbuild.extensions.javaModuleName\nimport junitbuild.java.UpdateJarAction\nimport net.ltgt.gradle.errorprone.errorprone\nimport net.ltgt.gradle.nullaway.nullaway\n\nplugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"junitbuild.shadow-conventions\")\n}\n\ndescription = \"JUnit Platform Console\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformReporting)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tshadowed(libs.picocli)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n\tosgiVerification(libs.openTestReporting.tooling.spi)\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.compilerArgs.addAll(listOf(\n\t\t\t\"-Xlint:-module\", // due to qualified exports\n\t\t\t\"--add-modules\", \"info.picocli\",\n\t\t\t\"--add-reads\", \"${javaModuleName}=info.picocli\"\n\t\t))\n\t\toptions.errorprone.nullaway {\n\t\t\texcludedFieldAnnotations.addAll(\n\t\t\t\t\"picocli.CommandLine.ArgGroup\",\n\t\t\t\t\"picocli.CommandLine.Mixin\",\n\t\t\t\t\"picocli.CommandLine.Spec\",\n\t\t\t)\n\t\t}\n\t}\n\tjavadoc {\n\t\t(options as StandardJavadocDocletOptions).apply {\n\t\t\taddStringOption(\"-add-modules\", \"info.picocli\")\n\t\t\taddStringOption(\"-add-reads\", \"${javaModuleName}=info.picocli\")\n\t\t}\n\t}\n\tshadowJar {\n\t\texclude(\"META-INF/**/module-info.class\")\n\t\trelocate(\"picocli\", \"org.junit.platform.console.shadow.picocli\")\n\t\tfrom(projectDir) {\n\t\t\tinclude(\"LICENSE-picocli.md\")\n\t\t\tinto(\"META-INF\")\n\t\t}\n\t\tdoLast(objects.newInstance(UpdateJarAction::class).apply {\n\t\t\tjavaLauncher = project.javaToolchains.launcherFor(java.toolchain)\n\t\t\targs.addAll(\n\t\t\t\t\"--file\", archiveFile.get().asFile.absolutePath,\n\t\t\t\t\"--main-class\", \"org.junit.platform.console.ConsoleLauncher\",\n\t\t\t)\n\t\t})\n\t\tbundle {\n\t\t\t// Ignore warning for package that is only exported as \"INTERNAL\"\n\t\t\tbnd(\"\"\"\n\t\t\t\t-fixupmessages.picocli.export: \"Export org.junit.platform.console.options\";is:=ignore\n\t\t\t\"\"\")\n\t\t}\n\t}\n\tcodeCoverageClassesJar {\n\t\texclude(\"org/junit/platform/console/options/ConsoleUtils.class\")\n\t}\n\tjar {\n\t\tmanifest {\n\t\t\tattributes(\"Main-Class\" to \"org.junit.platform.console.ConsoleLauncher\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for launching the JUnit Platform from the console.\n *\n * @since 1.0\n * @provides java.util.spi.ToolProvider\n */\nmodule org.junit.platform.console {\n\n\trequires static org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires org.junit.platform.commons;\n\trequires org.junit.platform.engine;\n\trequires org.junit.platform.launcher;\n\trequires org.junit.platform.reporting;\n\n\texports org.junit.platform.console.output to org.junit.start;\n\n\tprovides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider;\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.PrintWriter;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.console.command.CommandFacade;\nimport org.junit.platform.console.command.CommandResult;\nimport org.junit.platform.console.command.ConsoleTestExecutor;\nimport org.junit.platform.console.command.CustomClassLoaderCloseStrategy;\n\n/**\n * The {@code ConsoleLauncher} is a stand-alone application for launching the\n * JUnit Platform from the console.\n *\n * @since 1.0\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic class ConsoleLauncher {\n\n\tpublic static void main(String... args) {\n\t\tCommandFacade facade = newCommandFacade(CustomClassLoaderCloseStrategy.KEEP_OPEN);\n\t\tCommandResult<?> result = facade.run(args);\n\t\tSystem.exit(result.getExitCode());\n\t}\n\n\t@API(status = INTERNAL, since = \"1.0\")\n\tpublic static CommandResult<?> run(PrintWriter out, PrintWriter err, String... args) {\n\t\tCommandFacade facade = newCommandFacade(CustomClassLoaderCloseStrategy.CLOSE_AFTER_CALLING_LAUNCHER);\n\t\treturn facade.run(args, out, err);\n\t}\n\n\tprivate static CommandFacade newCommandFacade(CustomClassLoaderCloseStrategy classLoaderCleanupStrategy) {\n\t\treturn new CommandFacade((discoveryOptions, outputOptions) -> new ConsoleTestExecutor(discoveryOptions,\n\t\t\toutputOptions, classLoaderCleanupStrategy));\n\t}\n\n\tprivate ConsoleLauncher() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/ConsoleLauncherToolProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.PrintWriter;\nimport java.util.spi.ToolProvider;\n\nimport org.apiguardian.api.API;\n\n/**\n * Run the JUnit Platform Console Launcher as a service.\n *\n * @since 1.6\n */\n@API(status = STABLE, since = \"1.10\")\npublic class ConsoleLauncherToolProvider implements ToolProvider {\n\n\t@Override\n\tpublic String name() {\n\t\treturn \"junit\";\n\t}\n\n\t@Override\n\tpublic int run(PrintWriter out, PrintWriter err, String... args) {\n\t\treturn ConsoleLauncher.run(out, err, args).getExitCode();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/BaseCommand.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.io.PrintWriter;\nimport java.util.concurrent.Callable;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.console.options.AnsiColorOptionMixin;\n\nimport picocli.CommandLine;\nimport picocli.CommandLine.Mixin;\nimport picocli.CommandLine.Model.CommandSpec;\nimport picocli.CommandLine.Option;\nimport picocli.CommandLine.ParameterException;\nimport picocli.CommandLine.Spec;\n\nabstract class BaseCommand<T> implements Callable<T> {\n\n\t@Spec\n\tCommandSpec commandSpec;\n\n\t@Mixin\n\tAnsiColorOptionMixin ansiColorOption;\n\n\t@Option(names = \"--disable-banner\", description = \"Disable print out of the welcome message.\")\n\tprivate boolean disableBanner;\n\n\t@SuppressWarnings(\"unused\")\n\t@Option(names = { \"-h\", \"--help\" }, usageHelp = true, description = \"Display help information.\")\n\tprivate boolean helpRequested;\n\n\t@SuppressWarnings(\"unused\")\n\t@Option(names = \"--version\", versionHelp = true, description = \"Display version information.\")\n\tprivate boolean versionRequested;\n\n\tvoid execute(String... args) {\n\t\ttoCommandLine().execute(args);\n\t}\n\n\tvoid parseArgs(String... args) {\n\t\ttoCommandLine().parseArgs(args);\n\t}\n\n\tprivate CommandLine toCommandLine() {\n\t\treturn BaseCommand.initialize(new CommandLine(this));\n\t}\n\n\tstatic CommandLine initialize(CommandLine commandLine) {\n\t\tCommandLine.IParameterExceptionHandler defaultParameterExceptionHandler = commandLine.getParameterExceptionHandler();\n\t\treturn commandLine //\n\t\t\t\t.setParameterExceptionHandler((ex, args) -> {\n\t\t\t\t\tdefaultParameterExceptionHandler.handleParseException(ex, args);\n\t\t\t\t\treturn ExitCode.ANY_ERROR;\n\t\t\t\t}) //\n\t\t\t\t.setExecutionExceptionHandler((ex, cmd, __) -> {\n\t\t\t\t\tcommandLine.getErr().println(cmd.getColorScheme().richStackTraceString(ex));\n\t\t\t\t\tcommandLine.getErr().println();\n\t\t\t\t\tcommandLine.getErr().flush();\n\t\t\t\t\tcmd.usage(commandLine.getOut());\n\t\t\t\t\treturn ExitCode.ANY_ERROR;\n\t\t\t\t}) //\n\t\t\t\t.setCaseInsensitiveEnumValuesAllowed(true) //\n\t\t\t\t.setAtFileCommentChar(null);\n\t}\n\n\t@Override\n\tpublic final T call() {\n\t\tPrintWriter out = getOut();\n\t\tif (!disableBanner) {\n\t\t\tdisplayBanner(out);\n\t\t}\n\t\ttry {\n\t\t\treturn execute(out);\n\t\t}\n\t\tcatch (PreconditionViolationException e) {\n\t\t\tthrow new ParameterException(commandSpec.commandLine(), e.getMessage(), e.getCause());\n\t\t}\n\t}\n\n\tprivate PrintWriter getOut() {\n\t\treturn commandSpec.commandLine().getOut();\n\t}\n\n\tprivate void displayBanner(PrintWriter out) {\n\t\tout.println();\n\t\tCommandLine.Help.ColorScheme colorScheme = getColorScheme();\n\t\tif (colorScheme.ansi().enabled()) {\n\t\t\tout.print(\"💚 \");\n\t\t}\n\t\tout.println(colorScheme.string(\n\t\t\t\"@|italic Thanks for using JUnit!|@ Support its development at @|underline https://junit.org/sponsoring|@\"));\n\t\tout.println();\n\t\tout.flush();\n\t}\n\n\tprotected final CommandLine.Help.ColorScheme getColorScheme() {\n\t\treturn commandSpec.commandLine().getColorScheme();\n\t}\n\n\tprotected abstract T execute(PrintWriter out);\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/CommandFacade.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.PrintWriter;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * Internal facade to run a CLI command that exists to hide implementation\n * details such as the used library.\n *\n * @since 1.10\n */\n@API(status = INTERNAL, since = \"1.10\")\npublic class CommandFacade {\n\n\tprivate final ConsoleTestExecutor.Factory consoleTestExecutorFactory;\n\n\tpublic CommandFacade(ConsoleTestExecutor.Factory consoleTestExecutorFactory) {\n\t\tthis.consoleTestExecutorFactory = consoleTestExecutorFactory;\n\t}\n\n\tpublic CommandResult<?> run(String[] args) {\n\t\treturn run(args, Optional.empty());\n\t}\n\n\tpublic CommandResult<?> run(String[] args, PrintWriter out, PrintWriter err) {\n\t\ttry {\n\t\t\treturn run(args, Optional.of(new OutputStreamConfig(out, err)));\n\t\t}\n\t\tfinally {\n\t\t\tout.flush();\n\t\t\terr.flush();\n\t\t}\n\t}\n\n\tprivate CommandResult<?> run(String[] args, Optional<OutputStreamConfig> outputStreamConfig) {\n\t\tOptional<String> version = ManifestVersionProvider.getImplementationVersion();\n\t\tSystem.setProperty(\"junit.docs.version\",\n\t\t\tversion.map(it -> it.endsWith(\"-SNAPSHOT\") ? \"snapshot\" : it).orElse(\"current\"));\n\t\treturn new MainCommand(consoleTestExecutorFactory).run(args, outputStreamConfig);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/CommandResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.10\n */\n@API(status = INTERNAL, since = \"1.10\")\npublic class CommandResult<T> {\n\tpublic static <T> CommandResult<T> success() {\n\t\treturn create(ExitCode.SUCCESS, null);\n\t}\n\n\tpublic static <T> CommandResult<T> create(int exitCode, @Nullable T value) {\n\t\treturn new CommandResult<>(exitCode, value);\n\t}\n\n\tprivate final int exitCode;\n\n\tprivate final @Nullable T value;\n\n\tprivate CommandResult(int exitCode, @Nullable T value) {\n\t\tthis.exitCode = exitCode;\n\t\tthis.value = value;\n\t}\n\n\tpublic int getExitCode() {\n\t\treturn this.exitCode;\n\t}\n\n\tpublic Optional<T> getValue() {\n\t\treturn Optional.ofNullable(this.value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/ConsoleTestExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static java.util.Objects.requireNonNullElseGet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.console.command.DiscoveryRequestCreator.toDiscoveryRequestBuilder;\nimport static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;\n\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Path;\nimport java.util.EnumSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.console.options.Details;\nimport org.junit.platform.console.options.TestConsoleOutputOptions;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.console.output.ColorPalette;\nimport org.junit.platform.console.output.DetailsPrintingListener;\nimport org.junit.platform.console.output.FlatPrintingListener;\nimport org.junit.platform.console.output.TestFeedPrintingListener;\nimport org.junit.platform.console.output.Theme;\nimport org.junit.platform.console.output.TreePrintingListener;\nimport org.junit.platform.console.output.VerboseTreePrintingListener;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\nimport org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic class ConsoleTestExecutor {\n\n\tprivate final TestDiscoveryOptions discoveryOptions;\n\tprivate final TestConsoleOutputOptions outputOptions;\n\tprivate final Supplier<Launcher> launcherSupplier;\n\tprivate final CustomClassLoaderCloseStrategy classLoaderCloseStrategy;\n\n\tpublic ConsoleTestExecutor(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions) {\n\t\tthis(discoveryOptions, outputOptions, CustomClassLoaderCloseStrategy.CLOSE_AFTER_CALLING_LAUNCHER);\n\t}\n\n\tpublic ConsoleTestExecutor(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions,\n\t\t\tCustomClassLoaderCloseStrategy classLoaderCloseStrategy) {\n\t\tthis(discoveryOptions, outputOptions, classLoaderCloseStrategy, LauncherFactory::create);\n\t}\n\n\t// for tests only\n\tConsoleTestExecutor(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions,\n\t\t\tSupplier<Launcher> launcherSupplier) {\n\t\tthis(discoveryOptions, outputOptions, CustomClassLoaderCloseStrategy.CLOSE_AFTER_CALLING_LAUNCHER,\n\t\t\tlauncherSupplier);\n\t}\n\n\tprivate ConsoleTestExecutor(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions,\n\t\t\tCustomClassLoaderCloseStrategy classLoaderCloseStrategy, Supplier<Launcher> launcherSupplier) {\n\t\tthis.discoveryOptions = discoveryOptions;\n\t\tthis.outputOptions = outputOptions;\n\t\tthis.launcherSupplier = launcherSupplier;\n\t\tthis.classLoaderCloseStrategy = classLoaderCloseStrategy;\n\t}\n\n\tpublic void discover(PrintWriter out) {\n\t\tcreateCustomContextClassLoaderExecutor().invoke(() -> {\n\t\t\tdiscoverTests(out);\n\t\t\treturn null;\n\t\t});\n\t}\n\n\tpublic TestExecutionSummary execute(PrintWriter out, Optional<Path> reportsDir, boolean failFast) {\n\t\treturn createCustomContextClassLoaderExecutor() //\n\t\t\t\t.invoke(() -> executeTests(out, reportsDir, failFast));\n\t}\n\n\tprivate CustomContextClassLoaderExecutor createCustomContextClassLoaderExecutor() {\n\t\treturn new CustomContextClassLoaderExecutor(createCustomClassLoader(), classLoaderCloseStrategy);\n\t}\n\n\tprivate void discoverTests(PrintWriter out) {\n\t\tLauncher launcher = launcherSupplier.get();\n\t\tOptional<DetailsPrintingListener> commandLineTestPrinter = createDetailsPrintingListener(out);\n\n\t\tLauncherDiscoveryRequest discoveryRequest = toDiscoveryRequestBuilder(discoveryOptions).build();\n\t\tTestPlan testPlan = launcher.discover(discoveryRequest);\n\n\t\tcommandLineTestPrinter.ifPresent(printer -> printer.listTests(testPlan));\n\t\tif (outputOptions.getDetails() != Details.NONE) {\n\t\t\tprintFoundTestsSummary(out, testPlan);\n\t\t}\n\t}\n\n\tprivate static void printFoundTestsSummary(PrintWriter out, TestPlan testPlan) {\n\t\tSummaryGeneratingListener summaryListener = new SummaryGeneratingListener();\n\t\tsummaryListener.testPlanExecutionStarted(testPlan);\n\t\tTestExecutionSummary summary = summaryListener.getSummary();\n\n\t\tout.printf(\"%n[%10d containers found ]%n[%10d tests found      ]%n%n\", summary.getContainersFoundCount(),\n\t\t\tsummary.getTestsFoundCount());\n\t\tout.flush();\n\t}\n\n\tprivate TestExecutionSummary executeTests(PrintWriter out, Optional<Path> reportsDir, boolean failFast) {\n\t\tLauncher launcher = launcherSupplier.get();\n\t\tCancellationToken cancellationToken = failFast ? CancellationToken.create() : null;\n\t\tSummaryGeneratingListener summaryListener = registerListeners(out, reportsDir, launcher, cancellationToken);\n\n\t\tPrintStream originalOut = System.out;\n\t\tPrintStream originalErr = System.err;\n\t\ttry (StandardStreamsHandler standardStreamsHandler = new StandardStreamsHandler()) {\n\t\t\tstandardStreamsHandler.redirectStandardStreams(outputOptions.getStdoutPath(),\n\t\t\t\toutputOptions.getStderrPath());\n\t\t\tlaunchTests(launcher, reportsDir, cancellationToken);\n\t\t}\n\t\tfinally {\n\t\t\tSystem.setOut(originalOut);\n\t\t\tSystem.setErr(originalErr);\n\t\t}\n\n\t\tTestExecutionSummary summary = summaryListener.getSummary();\n\t\tif (summary.getTotalFailureCount() > 0 || outputOptions.getDetails() != Details.NONE) {\n\t\t\tprintSummary(summary, out);\n\t\t}\n\n\t\tif (cancellationToken != null && cancellationToken.isCancellationRequested()) {\n\t\t\tout.println(\"Test execution was cancelled due to --fail-fast mode.\");\n\t\t\tout.println();\n\t\t}\n\n\t\treturn summary;\n\t}\n\n\tprivate void launchTests(Launcher launcher, Optional<Path> reportsDir,\n\t\t\t@Nullable CancellationToken cancellationToken) {\n\n\t\tvar discoveryRequestBuilder = toDiscoveryRequestBuilder(discoveryOptions);\n\t\treportsDir.ifPresent(dir -> discoveryRequestBuilder.configurationParameter(OUTPUT_DIR_PROPERTY_NAME,\n\t\t\tdir.toAbsolutePath().toString()));\n\t\tvar executionRequest = discoveryRequestBuilder.forExecution() //\n\t\t\t\t.cancellationToken(requireNonNullElseGet(cancellationToken, CancellationToken::disabled)) //\n\t\t\t\t.build();\n\t\tlauncher.execute(executionRequest);\n\t}\n\n\tprivate Optional<ClassLoader> createCustomClassLoader() {\n\t\tList<Path> additionalClasspathEntries = discoveryOptions.getExistingAdditionalClasspathEntries();\n\t\tif (!additionalClasspathEntries.isEmpty()) {\n\t\t\tURL[] urls = additionalClasspathEntries.stream().map(this::toURL).toArray(URL[]::new);\n\t\t\tClassLoader parentClassLoader = ClassLoaderUtils.getDefaultClassLoader();\n\t\t\tClassLoader customClassLoader = URLClassLoader.newInstance(urls, parentClassLoader);\n\t\t\treturn Optional.of(customClassLoader);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate URL toURL(Path path) {\n\t\ttry {\n\t\t\treturn path.toUri().toURL();\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new JUnitException(\"Invalid classpath entry: \" + path, ex);\n\t\t}\n\t}\n\n\tprivate SummaryGeneratingListener registerListeners(PrintWriter out, Optional<Path> reportsDir, Launcher launcher,\n\t\t\t@Nullable CancellationToken cancellationToken) {\n\n\t\t// always register summary generating listener\n\t\tSummaryGeneratingListener summaryListener = new SummaryGeneratingListener();\n\t\tlauncher.registerTestExecutionListeners(summaryListener);\n\t\t// optionally, register test plan execution details printing listener\n\t\tcreateDetailsPrintingListener(out).ifPresent(launcher::registerTestExecutionListeners);\n\t\t// optionally, register XML reports writing listener\n\t\tcreateXmlWritingListener(out, reportsDir).ifPresent(launcher::registerTestExecutionListeners);\n\t\tcreateFailFastListener(cancellationToken).ifPresent(launcher::registerTestExecutionListeners);\n\t\treturn summaryListener;\n\t}\n\n\tprivate Optional<DetailsPrintingListener> createDetailsPrintingListener(PrintWriter out) {\n\t\tColorPalette colorPalette = getColorPalette();\n\t\tTheme theme = outputOptions.getTheme();\n\t\treturn switch (outputOptions.getDetails()) {\n\t\t\tcase SUMMARY ->\n\t\t\t\t\t// summary listener is always created and registered\n\t\t\t\t\tOptional.empty();\n\t\t\tcase FLAT -> Optional.of(new FlatPrintingListener(out, colorPalette));\n\t\t\tcase TREE -> Optional.of(new TreePrintingListener(out, colorPalette, theme));\n\t\t\tcase VERBOSE -> Optional.of(new VerboseTreePrintingListener(out, colorPalette, 16, theme));\n\t\t\tcase TESTFEED -> Optional.of(new TestFeedPrintingListener(out, colorPalette));\n\t\t\tcase NONE -> Optional.empty();\n\t\t};\n\t}\n\n\tprivate ColorPalette getColorPalette() {\n\t\tif (outputOptions.isAnsiColorOutputDisabled()) {\n\t\t\treturn ColorPalette.NONE;\n\t\t}\n\t\tif (outputOptions.getColorPalettePath() != null) {\n\t\t\treturn new ColorPalette(outputOptions.getColorPalettePath());\n\t\t}\n\t\tif (outputOptions.isSingleColorPalette()) {\n\t\t\treturn ColorPalette.SINGLE_COLOR;\n\t\t}\n\t\treturn ColorPalette.DEFAULT;\n\t}\n\n\tprivate Optional<TestExecutionListener> createXmlWritingListener(PrintWriter out, Optional<Path> reportsDir) {\n\t\treturn reportsDir.map(it -> new LegacyXmlReportGeneratingListener(it, out));\n\t}\n\n\tprivate Optional<TestExecutionListener> createFailFastListener(@Nullable CancellationToken cancellationToken) {\n\t\treturn Optional.ofNullable(cancellationToken).map(FailFastListener::new);\n\t}\n\n\tprivate void printSummary(TestExecutionSummary summary, PrintWriter out) {\n\t\t// Otherwise the failures have already been printed in detail\n\t\tif (EnumSet.of(Details.NONE, Details.SUMMARY, Details.TREE).contains(outputOptions.getDetails())) {\n\t\t\tsummary.printFailuresTo(out);\n\t\t}\n\t\tsummary.printTo(out);\n\t}\n\n\t@FunctionalInterface\n\tpublic interface Factory {\n\t\tConsoleTestExecutor create(TestDiscoveryOptions discoveryOptions, TestConsoleOutputOptions outputOptions);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/CustomClassLoaderCloseStrategy.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Defines the strategy for closing custom class loaders created for test\n * discovery and execution.\n */\n@API(status = INTERNAL, since = \"1.13\")\npublic enum CustomClassLoaderCloseStrategy {\n\n\t/**\n\t * Close the custom class loader after calling the\n\t * {@link org.junit.platform.launcher.Launcher} for test discovery or\n\t * execution.\n\t */\n\tCLOSE_AFTER_CALLING_LAUNCHER {\n\n\t\t@Override\n\t\tpublic void handle(ClassLoader customClassLoader) {\n\t\t\tif (customClassLoader instanceof @SuppressWarnings(\"resource\") AutoCloseable closeable) {\n\t\t\t\ttry {\n\t\t\t\t\tcloseable.close();\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow new JUnitException(\"Failed to close custom class loader\", ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Rely on the JVM to release resources held by the custom class loader when\n\t * it terminates.\n\t *\n\t * <p>This mode is only safe to use when calling {@link System#exit(int)}\n\t * afterward.\n\t */\n\tKEEP_OPEN {\n\n\t\t@Override\n\t\tpublic void handle(ClassLoader customClassLoader) {\n\t\t\t// do nothing\n\t\t}\n\t};\n\n\t/**\n\t * Handle the class loader according to the strategy.\n\t */\n\tpublic abstract void handle(ClassLoader classLoader);\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/CustomContextClassLoaderExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\n/**\n * @since 1.0\n */\nclass CustomContextClassLoaderExecutor {\n\n\tprivate final Optional<ClassLoader> customClassLoader;\n\tprivate final CustomClassLoaderCloseStrategy closeStrategy;\n\n\tCustomContextClassLoaderExecutor(Optional<ClassLoader> customClassLoader) {\n\t\tthis(customClassLoader, CustomClassLoaderCloseStrategy.CLOSE_AFTER_CALLING_LAUNCHER);\n\t}\n\n\tCustomContextClassLoaderExecutor(Optional<ClassLoader> customClassLoader,\n\t\t\tCustomClassLoaderCloseStrategy closeStrategy) {\n\t\tthis.customClassLoader = customClassLoader;\n\t\tthis.closeStrategy = closeStrategy;\n\t}\n\n\t<T> T invoke(Supplier<T> supplier) {\n\t\tif (customClassLoader.isPresent()) {\n\t\t\t// Only get/set context class loader when necessary to prevent problems with\n\t\t\t// security managers\n\t\t\treturn replaceThreadContextClassLoaderAndInvoke(customClassLoader.get(), supplier);\n\t\t}\n\t\treturn supplier.get();\n\t}\n\n\tprivate <T> T replaceThreadContextClassLoaderAndInvoke(ClassLoader customClassLoader, Supplier<T> supplier) {\n\t\tClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\ttry {\n\t\t\tThread.currentThread().setContextClassLoader(customClassLoader);\n\t\t\treturn supplier.get();\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t\tcloseStrategy.handle(customClassLoader);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/DiscoverTestsCommand.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.io.PrintWriter;\n\nimport org.junit.platform.console.options.TestConsoleOutputOptions;\nimport org.junit.platform.console.options.TestConsoleOutputOptionsMixin;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.console.options.TestDiscoveryOptionsMixin;\n\nimport picocli.CommandLine.Command;\nimport picocli.CommandLine.Mixin;\n\n@Command(//\n\t\tname = \"discover\", //\n\t\tdescription = \"Discover tests\" //\n)\nclass DiscoverTestsCommand extends BaseCommand<Void> {\n\n\tprivate final ConsoleTestExecutor.Factory consoleTestExecutorFactory;\n\n\t@Mixin\n\tTestDiscoveryOptionsMixin discoveryOptions;\n\n\t@Mixin\n\tTestConsoleOutputOptionsMixin testOutputOptions;\n\n\tDiscoverTestsCommand(ConsoleTestExecutor.Factory consoleTestExecutorFactory) {\n\t\tthis.consoleTestExecutorFactory = consoleTestExecutorFactory;\n\t}\n\n\t@Override\n\tprotected Void execute(PrintWriter out) {\n\t\tTestDiscoveryOptions discoveryOptions = this.discoveryOptions.toTestDiscoveryOptions();\n\t\tTestConsoleOutputOptions testOutputOptions = this.testOutputOptions.toTestConsoleOutputOptions();\n\t\ttestOutputOptions.setAnsiColorOutputDisabled(this.ansiColorOption.isDisableAnsiColors());\n\t\tthis.consoleTestExecutorFactory.create(discoveryOptions, testOutputOptions).discover(out);\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/DiscoveryRequestCreator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.platform.engine.discovery.ClassNameFilter.excludeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathRoots;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModules;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.excludePackageNames;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.includePackageNames;\nimport static org.junit.platform.launcher.EngineFilter.excludeEngines;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.MethodFilter.excludeMethodNamePatterns;\nimport static org.junit.platform.launcher.MethodFilter.includeMethodNamePatterns;\nimport static org.junit.platform.launcher.TagFilter.excludeTags;\nimport static org.junit.platform.launcher.TagFilter.includeTags;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ModuleUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\n\n/**\n * @since 1.0\n */\nclass DiscoveryRequestCreator {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DiscoveryRequestCreator.class);\n\n\tstatic LauncherDiscoveryRequestBuilder toDiscoveryRequestBuilder(TestDiscoveryOptions options) {\n\t\tLauncherDiscoveryRequestBuilder requestBuilder = request();\n\t\tList<? extends DiscoverySelector> selectors = createDiscoverySelectors(options);\n\t\trequestBuilder.selectors(selectors);\n\t\taddFilters(requestBuilder, options, selectors);\n\t\trequestBuilder.configurationParameters(options.getConfigurationParameters());\n\t\trequestBuilder.configurationParametersResources(\n\t\t\toptions.getConfigurationParametersResources().toArray(new String[0]));\n\t\treturn requestBuilder;\n\t}\n\n\tprivate static List<? extends DiscoverySelector> createDiscoverySelectors(TestDiscoveryOptions options) {\n\t\tList<DiscoverySelector> explicitSelectors = options.getExplicitSelectors();\n\t\tif (options.isScanClasspath()) {\n\t\t\tPreconditions.condition(explicitSelectors.isEmpty(),\n\t\t\t\t\"Scanning the classpath and using explicit selectors at the same time is not supported\");\n\t\t\treturn createClasspathRootSelectors(options);\n\t\t}\n\t\tif (options.isScanModulepath()) {\n\t\t\tPreconditions.condition(explicitSelectors.isEmpty(),\n\t\t\t\t\"Scanning the module-path and using explicit selectors at the same time is not supported\");\n\t\t\treturn selectModules(ModuleUtils.findAllNonSystemBootModuleNames());\n\t\t}\n\t\treturn Preconditions.notEmpty(explicitSelectors,\n\t\t\t\"Please specify an explicit selector option or use --scan-class-path or --scan-modules\");\n\t}\n\n\tprivate static List<ClasspathRootSelector> createClasspathRootSelectors(TestDiscoveryOptions options) {\n\t\tSet<Path> classpathRoots = validateAndLogInvalidRoots(determineClasspathRoots(options));\n\t\treturn selectClasspathRoots(classpathRoots);\n\t}\n\n\tprivate static Set<Path> determineClasspathRoots(TestDiscoveryOptions options) {\n\t\tvar selectedClasspathEntries = Preconditions.notNull(options.getSelectedClasspathEntries(),\n\t\t\t() -> \"No classpath entries selected\");\n\t\tif (selectedClasspathEntries.isEmpty()) {\n\t\t\tSet<Path> rootDirs = new LinkedHashSet<>(ReflectionUtils.getAllClasspathRootDirectories());\n\t\t\trootDirs.addAll(options.getAdditionalClasspathEntries());\n\t\t\treturn rootDirs;\n\t\t}\n\t\treturn new LinkedHashSet<>(selectedClasspathEntries);\n\t}\n\n\tprivate static Set<Path> validateAndLogInvalidRoots(Set<Path> roots) {\n\t\tLinkedHashSet<Path> valid = new LinkedHashSet<>();\n\t\tHashSet<Path> seen = new HashSet<>();\n\n\t\tfor (Path root : roots) {\n\t\t\tif (!seen.add(root)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (Files.exists(root)) {\n\t\t\t\tvalid.add(root);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlogger.warn(() -> \"Ignoring nonexistent classpath root: %s\".formatted(root));\n\t\t\t}\n\t\t}\n\n\t\treturn valid;\n\t}\n\n\tprivate static void addFilters(LauncherDiscoveryRequestBuilder requestBuilder, TestDiscoveryOptions options,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\t\trequestBuilder.filters(includedClassNamePatterns(options, selectors));\n\n\t\tif (!options.getExcludedClassNamePatterns().isEmpty()) {\n\t\t\trequestBuilder.filters(\n\t\t\t\texcludeClassNamePatterns(options.getExcludedClassNamePatterns().toArray(new String[0])));\n\t\t}\n\n\t\tif (!options.getIncludedPackages().isEmpty()) {\n\t\t\trequestBuilder.filters(includePackageNames(options.getIncludedPackages()));\n\t\t}\n\n\t\tif (!options.getExcludedPackages().isEmpty()) {\n\t\t\trequestBuilder.filters(excludePackageNames(options.getExcludedPackages()));\n\t\t}\n\n\t\tif (!options.getIncludedMethodNamePatterns().isEmpty()) {\n\t\t\trequestBuilder.filters(includeMethodNamePatterns(options.getIncludedMethodNamePatterns()));\n\t\t}\n\n\t\tif (!options.getExcludedMethodNamePatterns().isEmpty()) {\n\t\t\trequestBuilder.filters(excludeMethodNamePatterns(options.getExcludedMethodNamePatterns()));\n\t\t}\n\n\t\tif (!options.getIncludedTagExpressions().isEmpty()) {\n\t\t\trequestBuilder.filters(includeTags(options.getIncludedTagExpressions()));\n\t\t}\n\n\t\tif (!options.getExcludedTagExpressions().isEmpty()) {\n\t\t\trequestBuilder.filters(excludeTags(options.getExcludedTagExpressions()));\n\t\t}\n\n\t\tif (!options.getIncludedEngines().isEmpty()) {\n\t\t\trequestBuilder.filters(includeEngines(options.getIncludedEngines()));\n\t\t}\n\n\t\tif (!options.getExcludedEngines().isEmpty()) {\n\t\t\trequestBuilder.filters(excludeEngines(options.getExcludedEngines()));\n\t\t}\n\t}\n\n\tprivate static ClassNameFilter includedClassNamePatterns(TestDiscoveryOptions options,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\t\tStream<String> patternStreams = Stream.concat( //\n\t\t\toptions.getIncludedClassNamePatterns().stream(), //\n\t\t\tselectors.stream() //\n\t\t\t\t\t.map(selector -> selector instanceof IterationSelector iterationSelector\n\t\t\t\t\t\t\t? iterationSelector.getParentSelector()\n\t\t\t\t\t\t\t: selector) //\n\t\t\t\t\t.map(selector -> {\n\t\t\t\t\t\tif (selector instanceof ClassSelector classSelector) {\n\t\t\t\t\t\t\treturn classSelector.getClassName();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (selector instanceof MethodSelector methodSelector) {\n\t\t\t\t\t\t\treturn methodSelector.getClassName();\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}) //\n\t\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t\t.map(Pattern::quote));\n\t\treturn includeClassNamePatterns(patternStreams.toArray(String[]::new));\n\t}\n\n\tprivate DiscoveryRequestCreator() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/ExecuteTestsCommand.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.platform.console.command.ExitCode.NO_TESTS_FOUND;\nimport static org.junit.platform.console.command.ExitCode.SUCCESS;\nimport static org.junit.platform.console.command.ExitCode.TEST_FAILED;\n\nimport java.io.PrintWriter;\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.console.options.TestConsoleOutputOptions;\nimport org.junit.platform.console.options.TestConsoleOutputOptionsMixin;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.console.options.TestDiscoveryOptionsMixin;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\n\nimport picocli.CommandLine;\nimport picocli.CommandLine.ArgGroup;\nimport picocli.CommandLine.Command;\nimport picocli.CommandLine.Mixin;\nimport picocli.CommandLine.Option;\n\n@Command(//\n\t\tname = \"execute\", //\n\t\tdescription = \"Execute tests\" //\n)\nclass ExecuteTestsCommand extends BaseCommand<TestExecutionSummary> implements CommandLine.IExitCodeGenerator {\n\tprivate final ConsoleTestExecutor.Factory consoleTestExecutorFactory;\n\n\t@Mixin\n\tTestDiscoveryOptionsMixin discoveryOptions;\n\n\t@Mixin\n\tTestConsoleOutputOptionsMixin testOutputOptions;\n\n\t@ArgGroup(validate = false, order = 6, heading = \"%n@|bold REPORTING|@%n%n\")\n\tReportingOptions reportingOptions;\n\n\tExecuteTestsCommand(ConsoleTestExecutor.Factory consoleTestExecutorFactory) {\n\t\tthis.consoleTestExecutorFactory = consoleTestExecutorFactory;\n\t}\n\n\t@Override\n\tprotected TestExecutionSummary execute(PrintWriter out) {\n\t\treturn consoleTestExecutorFactory.create(toTestDiscoveryOptions(), toTestConsoleOutputOptions()) //\n\t\t\t\t.execute(out, getReportsDir(), isFailFast());\n\t}\n\n\tOptional<Path> getReportsDir() {\n\t\treturn getReportingOptions().flatMap(ReportingOptions::getReportsDir);\n\t}\n\n\tboolean isFailFast() {\n\t\treturn getReportingOptions().map(options -> options.failFast).orElse(false);\n\t}\n\n\tprivate Optional<ReportingOptions> getReportingOptions() {\n\t\treturn Optional.ofNullable(reportingOptions);\n\t}\n\n\tTestDiscoveryOptions toTestDiscoveryOptions() {\n\t\treturn this.discoveryOptions == null //\n\t\t\t\t? new TestDiscoveryOptions() //\n\t\t\t\t: this.discoveryOptions.toTestDiscoveryOptions();\n\t}\n\n\tTestConsoleOutputOptions toTestConsoleOutputOptions() {\n\t\tTestConsoleOutputOptions testOutputOptions = this.testOutputOptions.toTestConsoleOutputOptions();\n\t\ttestOutputOptions.setAnsiColorOutputDisabled(this.ansiColorOption.isDisableAnsiColors());\n\t\treturn testOutputOptions;\n\t}\n\n\t@Override\n\tpublic int getExitCode() {\n\t\tTestExecutionSummary executionResult = commandSpec.commandLine().getExecutionResult();\n\t\tboolean failIfNoTests = getReportingOptions().map(it -> it.failIfNoTests).orElse(false);\n\t\tif (failIfNoTests && executionResult.getTestsFoundCount() == 0) {\n\t\t\treturn NO_TESTS_FOUND;\n\t\t}\n\t\treturn executionResult.getTotalFailureCount() == 0 ? SUCCESS : TEST_FAILED;\n\t}\n\n\tstatic class ReportingOptions {\n\n\t\t@Option(names = \"--fail-if-no-tests\", description = \"Fail and return exit status code 2 if no tests are found.\")\n\t\tprivate boolean failIfNoTests;\n\n\t\t/**\n\t\t * @since 6.0\n\t\t */\n\t\t@Option(names = \"--fail-fast\", description = \"Stops test execution after the first failed test.\")\n\t\tprivate boolean failFast;\n\n\t\t@Option(names = \"--reports-dir\", paramLabel = \"DIR\", description = \"Enable report output into a specified local directory (will be created if it does not exist).\")\n\t\tprivate @Nullable Path reportsDir;\n\n\t\tOptional<Path> getReportsDir() {\n\t\t\treturn Optional.ofNullable(reportsDir);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/ExitCode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\n/**\n * Well-known exit codes of the {@code junit} tool.\n *\n * @since 6.0.1\n */\nfinal class ExitCode {\n\t/**\n\t * Exit code indicating a successful tool run.\n\t */\n\tpublic static final int SUCCESS = 0;\n\n\t/**\n\t * Exit code indicating an unsuccessful run.\n\t */\n\tpublic static final int ANY_ERROR = -1;\n\n\t/**\n\t * Exit code indicating test failure(s).\n\t */\n\tpublic static final int TEST_FAILED = 1;\n\n\t/**\n\t * Exit code indicating no tests found.\n\t */\n\tpublic static final int NO_TESTS_FOUND = 2;\n\n\t/**\n\t * Exit code indicating invalid user input.\n\t */\n\tpublic static final int INVALID_INPUT = 3;\n\n\tprivate ExitCode() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/FailFastListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\n\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * @since 6.0\n */\nclass FailFastListener implements TestExecutionListener {\n\n\tprivate final CancellationToken cancellationToken;\n\n\tFailFastListener(CancellationToken cancellationToken) {\n\t\tthis.cancellationToken = cancellationToken;\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tif (testExecutionResult.getStatus() == FAILED) {\n\t\t\tcancellationToken.cancel();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/ListTestEnginesCommand.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.io.PrintWriter;\nimport java.util.Comparator;\nimport java.util.StringJoiner;\nimport java.util.stream.StreamSupport;\n\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry;\n\nimport picocli.CommandLine.Command;\n\n@Command(//\n\t\tname = \"engines\", //\n\t\tdescription = \"List available test engines\" //\n)\nclass ListTestEnginesCommand extends BaseCommand<Void> {\n\n\t@Override\n\tprotected Void execute(PrintWriter out) {\n\t\tdisplayEngines(out);\n\t\treturn null;\n\t}\n\n\tvoid displayEngines(PrintWriter out) {\n\t\tServiceLoaderTestEngineRegistry registry = new ServiceLoaderTestEngineRegistry();\n\t\tIterable<TestEngine> engines = registry.loadTestEngines();\n\t\tStreamSupport.stream(engines.spliterator(), false) //\n\t\t\t\t.sorted(Comparator.comparing(TestEngine::getId)) //\n\t\t\t\t.forEach(engine -> displayEngine(out, engine));\n\t\tout.flush();\n\t}\n\n\tprivate void displayEngine(PrintWriter out, TestEngine engine) {\n\t\tStringJoiner details = new StringJoiner(\":\", \" (\", \")\");\n\t\tengine.getGroupId().ifPresent(details::add);\n\t\tengine.getArtifactId().ifPresent(details::add);\n\t\tengine.getVersion().ifPresent(details::add);\n\t\tout.println(getColorScheme().string(\"@|bold %s|@%s\".formatted(engine.getId(), details)));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/MainCommand.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.console.options.AnsiColorOptionMixin;\n\nimport picocli.CommandLine;\nimport picocli.CommandLine.Command;\nimport picocli.CommandLine.IExitCodeGenerator;\nimport picocli.CommandLine.Mixin;\nimport picocli.CommandLine.Model.CommandSpec;\nimport picocli.CommandLine.Option;\nimport picocli.CommandLine.ParameterException;\nimport picocli.CommandLine.Spec;\n\n@Command(//\n\t\tname = \"junit\", //\n\t\tabbreviateSynopsis = true, //\n\t\tsynopsisSubcommandLabel = \"COMMAND\", //\n\t\tsortOptions = false, //\n\t\tusageHelpWidth = 95, //\n\t\tshowAtFileInUsageHelp = true, //\n\t\tusageHelpAutoWidth = true, //\n\t\tdescription = \"Launches the JUnit Platform for test discovery and execution.\", //\n\t\tfooterHeading = \"%n\", //\n\t\tfooter = \"For more information, please refer to the JUnit User Guide at%n\" //\n\t\t\t\t+ \"@|underline https://docs.junit.org/${junit.docs.version}/|@\", //\n\t\tscope = CommandLine.ScopeType.INHERIT, //\n\t\texitCodeOnInvalidInput = ExitCode.INVALID_INPUT, //\n\t\texitCodeOnExecutionException = ExitCode.ANY_ERROR, //\n\t\tversionProvider = ManifestVersionProvider.class //\n)\nclass MainCommand implements Runnable, IExitCodeGenerator {\n\n\tprivate final ConsoleTestExecutor.Factory consoleTestExecutorFactory;\n\n\t@Option(names = { \"-h\", \"--help\" }, help = true, description = \"Display help information.\")\n\tprivate boolean helpRequested;\n\n\t@Option(names = \"--version\", versionHelp = true, description = \"Display version information.\")\n\tprivate boolean versionRequested;\n\n\t@Mixin\n\tAnsiColorOptionMixin ansiColorOption;\n\n\t@Spec\n\tCommandSpec commandSpec;\n\n\t@Nullable\n\tCommandResult<?> commandResult;\n\n\tMainCommand(ConsoleTestExecutor.Factory consoleTestExecutorFactory) {\n\t\tthis.consoleTestExecutorFactory = consoleTestExecutorFactory;\n\t}\n\n\t@Override\n\tpublic void run() {\n\t\tif (helpRequested) {\n\t\t\tcommandSpec.commandLine().usage(commandSpec.commandLine().getOut());\n\t\t\tcommandResult = CommandResult.success();\n\t\t}\n\t\telse if (versionRequested) {\n\t\t\tcommandSpec.commandLine().printVersionHelp(commandSpec.commandLine().getOut());\n\t\t\tcommandResult = CommandResult.success();\n\t\t}\n\t\telse {\n\t\t\tthrow new ParameterException(commandSpec.commandLine(), \"Missing required subcommand\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getExitCode() {\n\t\treturn requireNonNull(commandResult).getExitCode();\n\t}\n\n\tCommandResult<?> run(String[] args,\n\t\t\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\") Optional<OutputStreamConfig> outputStreamConfig) {\n\t\tCommandLine commandLine = new CommandLine(this) //\n\t\t\t\t.addSubcommand(new DiscoverTestsCommand(consoleTestExecutorFactory)) //\n\t\t\t\t.addSubcommand(new ExecuteTestsCommand(consoleTestExecutorFactory)) //\n\t\t\t\t.addSubcommand(new ListTestEnginesCommand());\n\t\treturn runCommand(commandLine, args, outputStreamConfig);\n\t}\n\n\tprivate static CommandResult<?> runCommand(CommandLine commandLine, String[] args,\n\t\t\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\") Optional<OutputStreamConfig> outputStreamConfig) {\n\t\tBaseCommand.initialize(commandLine);\n\t\toutputStreamConfig.ifPresent(it -> it.applyTo(commandLine));\n\t\tint exitCode = commandLine.execute(args);\n\t\treturn CommandResult.create(exitCode, getLikelyExecutedCommand(commandLine).getExecutionResult());\n\t}\n\n\t/**\n\t * Get the most likely executed subcommand, if any, or the main command otherwise.\n\t * @see <a href=\"https://picocli.info/#_executing_commands_with_subcommands\">Executing Commands with Subcommands</a>\n\t */\n\tprivate static CommandLine getLikelyExecutedCommand(final CommandLine commandLine) {\n\t\treturn Optional.ofNullable(commandLine.getParseResult().subcommand()) //\n\t\t\t\t.map(parseResult -> parseResult.commandSpec().commandLine()) //\n\t\t\t\t.orElse(commandLine);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/ManifestVersionProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.util.Optional;\n\nimport org.junit.platform.commons.util.PackageUtils;\n\nimport picocli.CommandLine;\n\nclass ManifestVersionProvider implements CommandLine.IVersionProvider {\n\n\tpublic static Optional<String> getImplementationVersion() {\n\t\treturn PackageUtils.getModuleOrImplementationVersion(ManifestVersionProvider.class);\n\t}\n\n\t@Override\n\tpublic String[] getVersion() {\n\t\treturn new String[] { //\n\t\t\t\t\"@|bold JUnit Platform Console Launcher %s|@\".formatted(getImplementationVersion().orElse(\"<unknown>\")), //\n\t\t\t\t\"JVM: ${java.version} (${java.vendor} ${java.vm.name} ${java.vm.version})\", //\n\t\t\t\t\"OS: ${os.name} ${os.version} ${os.arch}\" //\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/OutputStreamConfig.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.io.PrintWriter;\n\nimport picocli.CommandLine;\n\nclass OutputStreamConfig {\n\n\tprivate final PrintWriter out;\n\tprivate final PrintWriter err;\n\n\tOutputStreamConfig(PrintWriter out, PrintWriter err) {\n\t\tthis.out = out;\n\t\tthis.err = err;\n\t}\n\n\tvoid applyTo(CommandLine commandLine) {\n\t\tcommandLine.setOut(this.out).setErr(this.err);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/StandardStreamsHandler.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport java.io.IOException;\nimport java.io.PrintStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\n\nclass StandardStreamsHandler implements AutoCloseable {\n\n\tprivate @Nullable PrintStream stdout;\n\n\tprivate @Nullable PrintStream stderr;\n\n\tStandardStreamsHandler() {\n\t}\n\n\t/**\n\t * Redirect standard output (stdout) and standard error (stderr) to the specified\n\t * file paths.\n\t *\n\t * <p>If the paths are the same, both streams are redirected to the same file.\n\t *\n\t * <p>The default charset is used for writing to the files.\n\t *\n\t * @param stdoutPath the file path for standard output, or {@code null} to\n\t * indicate no redirection\n\t * @param stderrPath the file path for standard error, or {@code null} to\n\t * indicate no redirection\n\t */\n\tpublic void redirectStandardStreams(@Nullable Path stdoutPath, @Nullable Path stderrPath) {\n\t\tif (isSameFile(stdoutPath, stderrPath)) {\n\t\t\ttry {\n\t\t\t\tPrintStream commonStream = new PrintStream(Files.newOutputStream(stdoutPath), true);\n\t\t\t\tthis.stdout = commonStream;\n\t\t\t\tthis.stderr = commonStream;\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new JUnitException(\"Error redirecting stdout and stderr to file: \" + stdoutPath, ex);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (stdoutPath != null) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.stdout = new PrintStream(Files.newOutputStream(stdoutPath), true);\n\t\t\t\t}\n\t\t\t\tcatch (IOException ex) {\n\t\t\t\t\tthrow new JUnitException(\"Error redirecting stdout to file: \" + stdoutPath, ex);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (stderrPath != null) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.stderr = new PrintStream(Files.newOutputStream(stderrPath), true);\n\t\t\t\t}\n\t\t\t\tcatch (IOException ex) {\n\t\t\t\t\tthrow new JUnitException(\"Error redirecting stderr to file: \" + stderrPath, ex);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (this.stdout != null) {\n\t\t\tSystem.setOut(this.stdout);\n\t\t}\n\t\tif (this.stderr != null) {\n\t\t\tSystem.setErr(this.stderr);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\tif (this.stdout != null) {\n\t\t\t\tthis.stdout.close();\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\tif (this.stderr != null) {\n\t\t\t\tthis.stderr.close();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean isSameFile(@Nullable Path path1, @Nullable Path path2) {\n\t\tif (path1 == null || path2 == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn path1.normalize().toAbsolutePath().equals(path2.normalize().toAbsolutePath());\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/command/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Commands of JUnit's console launcher.\n */\n\n@NullMarked\npackage org.junit.platform.console.command;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/AnsiColorOptionMixin.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static picocli.CommandLine.Help.defaultColorScheme;\nimport static picocli.CommandLine.Spec.Target.MIXEE;\n\nimport org.apiguardian.api.API;\n\nimport picocli.CommandLine.Help.Ansi;\nimport picocli.CommandLine.Model.CommandSpec;\nimport picocli.CommandLine.Option;\nimport picocli.CommandLine.Spec;\n\n@API(status = INTERNAL, since = \"1.14\")\npublic class AnsiColorOptionMixin {\n\n\t@Spec(MIXEE)\n\tCommandSpec commandSpec;\n\n\t// https://no-color.org\n\t// ANSI is disabled when environment variable NO_COLOR is defined (regardless of its value).\n\tprivate boolean disableAnsiColors = System.getenv(\"NO_COLOR\") != null;\n\n\tpublic boolean isDisableAnsiColors() {\n\t\treturn this.disableAnsiColors;\n\t}\n\n\t@Option(names = \"--disable-ansi-colors\", description = \"Disable ANSI colors in output (not supported by all terminals).\")\n\tpublic void setDisableAnsiColors(boolean disableAnsiColors) {\n\t\tif (disableAnsiColors) {\n\t\t\tthis.commandSpec.commandLine().setColorScheme(defaultColorScheme(Ansi.OFF));\n\t\t}\n\t\tthis.disableAnsiColors = disableAnsiColors;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/ClasspathEntriesConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport java.io.File;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport picocli.CommandLine;\n\nclass ClasspathEntriesConverter implements CommandLine.ITypeConverter<List<Path>> {\n\n\t@Override\n\tpublic List<Path> convert(String value) {\n\t\treturn Stream.of(value.split(File.pathSeparator)).map(Path::of).toList();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/ConsoleUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.Console;\nimport java.nio.charset.Charset;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of utilities for working with {@code java.io.Console}\n * and friends.\n *\n * <h2>DISCLAIMER</h2>\n *\n * <p>These utilities are intended solely for usage within the JUnit framework\n * itself. <strong>Any usage by external parties is not supported.</strong>\n * Use at your own risk!\n *\n * @since 1.9\n */\n@API(status = INTERNAL, since = \"1.9\")\npublic class ConsoleUtils {\n\n\t/**\n\t * {@return the charset of the console}\n\t */\n\t@SuppressWarnings(\"SystemConsoleNull\")\n\tpublic static Charset charset() {\n\t\tConsole console = System.console();\n\t\treturn console != null ? console.charset() : Charset.defaultCharset();\n\t}\n\n\tprivate ConsoleUtils() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/Details.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic enum Details {\n\n\t/**\n\t * No test plan execution details are printed.\n\t */\n\tNONE,\n\n\t/**\n\t * Print summary table of counts only.\n\t */\n\tSUMMARY,\n\n\t/**\n\t * Test plan execution details are rendered in a flat, line-by-line mode.\n\t */\n\tFLAT,\n\n\t/**\n\t * Test plan execution details are rendered as a simple tree.\n\t */\n\tTREE,\n\n\t/**\n\t * Combines {@link #TREE} and {@link #FLAT} modes.\n\t */\n\tVERBOSE,\n\n\t/**\n\t * Test plan execution events are rendered as they occur in a concise format.\n\t *\n\t * @since 1.10\n\t */\n\tTESTFEED;\n\n\t/**\n\t * Return lower case {@link #name()} for easier usage in help text for\n\t * available options.\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn name().toLowerCase(Locale.ROOT);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/SelectorConverter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri;\n\nimport java.net.URI;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ResourceUtils;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.FilePosition;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\n\nimport picocli.CommandLine.ITypeConverter;\n\nclass SelectorConverter {\n\n\tstatic class Module implements ITypeConverter<ModuleSelector> {\n\t\t@Override\n\t\tpublic ModuleSelector convert(String value) {\n\t\t\treturn selectModule(value);\n\t\t}\n\t}\n\n\tstatic class Uri implements ITypeConverter<UriSelector> {\n\t\t@Override\n\t\tpublic UriSelector convert(String value) {\n\t\t\treturn selectUri(value);\n\t\t}\n\t}\n\n\tstatic class File implements ITypeConverter<FileSelector> {\n\t\t@Override\n\t\tpublic FileSelector convert(String value) {\n\t\t\tURI uri = URI.create(value);\n\t\t\tString path = ResourceUtils.stripQueryComponent(uri).getPath();\n\t\t\tFilePosition filePosition = FilePosition.fromQuery(uri.getQuery()).orElse(null);\n\t\t\treturn selectFile(path, filePosition);\n\t\t}\n\t}\n\n\tstatic class Directory implements ITypeConverter<DirectorySelector> {\n\t\t@Override\n\t\tpublic DirectorySelector convert(String value) {\n\t\t\treturn selectDirectory(value);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JavaLangClash\")\n\tstatic class Package implements ITypeConverter<PackageSelector> {\n\t\t@Override\n\t\tpublic PackageSelector convert(String value) {\n\t\t\treturn selectPackage(value);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JavaLangClash\")\n\tstatic class Class implements ITypeConverter<ClassSelector> {\n\t\t@Override\n\t\tpublic ClassSelector convert(String value) {\n\t\t\treturn selectClass(value);\n\t\t}\n\t}\n\n\tstatic class Method implements ITypeConverter<MethodSelector> {\n\t\t@Override\n\t\tpublic MethodSelector convert(String value) {\n\t\t\treturn selectMethod(value);\n\t\t}\n\t}\n\n\tstatic class ClasspathResource implements ITypeConverter<ClasspathResourceSelector> {\n\t\t@Override\n\t\tpublic ClasspathResourceSelector convert(String value) {\n\t\t\tURI uri = URI.create(value);\n\t\t\tString path = ResourceUtils.stripQueryComponent(uri).getPath();\n\t\t\tFilePosition filePosition = FilePosition.fromQuery(uri.getQuery()).orElse(null);\n\t\t\treturn selectClasspathResource(path, filePosition);\n\t\t}\n\t}\n\n\tstatic class Iteration implements ITypeConverter<IterationSelector> {\n\t\t@Override\n\t\tpublic IterationSelector convert(String value) {\n\t\t\tDiscoverySelectorIdentifier identifier = DiscoverySelectorIdentifier.create(\n\t\t\t\tIterationSelector.IdentifierParser.PREFIX, value);\n\t\t\treturn (IterationSelector) DiscoverySelectors.parse(identifier) //\n\t\t\t\t\t.orElseThrow(() -> new PreconditionViolationException(\"Invalid format: Failed to parse selector\"));\n\t\t}\n\t}\n\n\tstatic class UniqueId implements ITypeConverter<UniqueIdSelector> {\n\t\t@Override\n\t\tpublic UniqueIdSelector convert(String value) {\n\t\t\treturn selectUniqueId(value);\n\t\t}\n\t}\n\n\tstatic class Identifier implements ITypeConverter<DiscoverySelectorIdentifier> {\n\t\t@Override\n\t\tpublic DiscoverySelectorIdentifier convert(String value) {\n\t\t\treturn DiscoverySelectorIdentifier.parse(value);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.nio.file.Path;\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.console.output.Theme;\n\n/**\n * @since 1.10\n */\n@API(status = INTERNAL, since = \"1.10\")\npublic class TestConsoleOutputOptions {\n\n\tstatic final String DEFAULT_DETAILS_NAME = \"tree\";\n\tpublic static final Details DEFAULT_DETAILS = Details.valueOf(DEFAULT_DETAILS_NAME.toUpperCase(Locale.ROOT));\n\tstatic final Theme DEFAULT_THEME = Theme.valueOf(ConsoleUtils.charset());\n\n\tprivate boolean ansiColorOutputDisabled;\n\n\tprivate @Nullable Path colorPalettePath;\n\n\tprivate boolean isSingleColorPalette;\n\tprivate Details details = DEFAULT_DETAILS;\n\tprivate Theme theme = DEFAULT_THEME;\n\n\tprivate @Nullable Path stdoutPath;\n\n\tprivate @Nullable Path stderrPath;\n\n\tpublic boolean isAnsiColorOutputDisabled() {\n\t\treturn this.ansiColorOutputDisabled;\n\t}\n\n\tpublic void setAnsiColorOutputDisabled(boolean ansiColorOutputDisabled) {\n\t\tthis.ansiColorOutputDisabled = ansiColorOutputDisabled;\n\t}\n\n\tpublic @Nullable Path getColorPalettePath() {\n\t\treturn colorPalettePath;\n\t}\n\n\tpublic void setColorPalettePath(@Nullable Path colorPalettePath) {\n\t\tthis.colorPalettePath = colorPalettePath;\n\t}\n\n\tpublic boolean isSingleColorPalette() {\n\t\treturn isSingleColorPalette;\n\t}\n\n\tpublic void setSingleColorPalette(boolean singleColorPalette) {\n\t\tthis.isSingleColorPalette = singleColorPalette;\n\t}\n\n\tpublic Details getDetails() {\n\t\treturn this.details;\n\t}\n\n\tpublic void setDetails(Details details) {\n\t\tthis.details = details;\n\t}\n\n\tpublic Theme getTheme() {\n\t\treturn this.theme;\n\t}\n\n\tpublic void setTheme(Theme theme) {\n\t\tthis.theme = theme;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic @Nullable Path getStdoutPath() {\n\t\treturn this.stdoutPath;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic void setStdoutPath(@Nullable Path stdoutPath) {\n\t\tthis.stdoutPath = stdoutPath;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic @Nullable Path getStderrPath() {\n\t\treturn this.stderrPath;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic void setStderrPath(@Nullable Path stderrPath) {\n\t\tthis.stderrPath = stderrPath;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/TestConsoleOutputOptionsMixin.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.console.options.TestConsoleOutputOptions.DEFAULT_DETAILS;\nimport static org.junit.platform.console.options.TestConsoleOutputOptions.DEFAULT_DETAILS_NAME;\nimport static org.junit.platform.console.options.TestConsoleOutputOptions.DEFAULT_THEME;\n\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.console.output.Theme;\n\nimport picocli.CommandLine.ArgGroup;\nimport picocli.CommandLine.Option;\n\n@API(status = INTERNAL, since = \"1.14\")\npublic class TestConsoleOutputOptionsMixin {\n\n\t@ArgGroup(validate = false, order = 5, heading = \"%n@|bold CONSOLE OUTPUT|@%n%n\")\n\tConsoleOutputOptions consoleOutputOptions = new ConsoleOutputOptions();\n\n\tpublic static class ConsoleOutputOptions {\n\n\t\t@Option(names = \"--color-palette\", paramLabel = \"FILE\", description = \"Specify a path to a properties file to customize ANSI style of output (not supported by all terminals).\")\n\t\tprivate @Nullable Path colorPalette;\n\n\t\t@Option(names = \"--single-color\", description = \"Style test output using only text attributes, no color (not supported by all terminals).\")\n\t\tprivate boolean singleColorPalette;\n\n\t\t@Option(names = \"--details\", paramLabel = \"MODE\", defaultValue = DEFAULT_DETAILS_NAME, description = \"Select an output details mode for when tests are executed. \" //\n\t\t\t\t+ \"Use one of: ${COMPLETION-CANDIDATES}. If 'none' is selected, \" //\n\t\t\t\t+ \"then only the summary and test failures are shown. Default: ${DEFAULT-VALUE}.\")\n\t\tprivate Details details = DEFAULT_DETAILS;\n\n\t\t@Option(names = \"--details-theme\", paramLabel = \"THEME\", description = \"Select an output details tree theme for when tests are executed. \"\n\t\t\t\t+ \"Use one of: ${COMPLETION-CANDIDATES}. Default is detected based on default character encoding.\")\n\t\tprivate Theme theme = DEFAULT_THEME;\n\n\t\t@Option(names = \"--redirect-stdout\", paramLabel = \"FILE\", description = \"Redirect test output to stdout to a file.\")\n\t\tprivate @Nullable Path stdout;\n\n\t\t@Option(names = \"--redirect-stderr\", paramLabel = \"FILE\", description = \"Redirect test output to stderr to a file.\")\n\t\tprivate @Nullable Path stderr;\n\n\t\tprivate void applyTo(TestConsoleOutputOptions result) {\n\t\t\tresult.setColorPalettePath(colorPalette);\n\t\t\tresult.setSingleColorPalette(singleColorPalette);\n\t\t\tresult.setDetails(details);\n\t\t\tresult.setTheme(theme);\n\t\t\tresult.setStdoutPath(stdout);\n\t\t\tresult.setStderrPath(stderr);\n\t\t}\n\t}\n\n\tpublic TestConsoleOutputOptions toTestConsoleOutputOptions() {\n\t\tTestConsoleOutputOptions result = new TestConsoleOutputOptions();\n\t\tif (this.consoleOutputOptions != null) {\n\t\t\tthis.consoleOutputOptions.applyTo(result);\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Collections.emptyMap;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.STANDARD_INCLUDE_PATTERN;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\n\n/**\n * @since 1.10\n */\n@API(status = INTERNAL, since = \"1.10\")\npublic class TestDiscoveryOptions {\n\n\tprivate boolean scanClasspath;\n\tprivate List<Path> additionalClasspathEntries = emptyList();\n\n\tprivate @Nullable List<Path> selectedClasspathEntries = emptyList();\n\n\tprivate boolean scanModulepath;\n\n\tprivate List<ModuleSelector> selectedModules = emptyList();\n\tprivate List<UriSelector> selectedUris = emptyList();\n\tprivate List<FileSelector> selectedFiles = emptyList();\n\tprivate List<DirectorySelector> selectedDirectories = emptyList();\n\tprivate List<PackageSelector> selectedPackages = emptyList();\n\tprivate List<ClassSelector> selectedClasses = emptyList();\n\tprivate List<MethodSelector> selectedMethods = emptyList();\n\tprivate List<ClasspathResourceSelector> selectedClasspathResources = emptyList();\n\tprivate List<IterationSelector> selectedIterations = emptyList();\n\tprivate List<UniqueIdSelector> selectedUniqueIds = emptyList();\n\tprivate List<DiscoverySelectorIdentifier> selectorIdentifiers = emptyList();\n\n\tprivate List<String> includedClassNamePatterns = List.of(STANDARD_INCLUDE_PATTERN);\n\tprivate List<String> excludedClassNamePatterns = emptyList();\n\tprivate List<String> includedPackages = emptyList();\n\tprivate List<String> excludedPackages = emptyList();\n\tprivate List<String> includedMethodNamePatterns = emptyList();\n\tprivate List<String> excludedMethodNamePatterns = emptyList();\n\tprivate List<String> includedEngines = emptyList();\n\tprivate List<String> excludedEngines = emptyList();\n\tprivate List<String> includedTagExpressions = emptyList();\n\tprivate List<String> excludedTagExpressions = emptyList();\n\n\tprivate List<String> configurationParametersResources = emptyList();\n\tprivate Map<String, String> configurationParameters = emptyMap();\n\n\tpublic boolean isScanModulepath() {\n\t\treturn this.scanModulepath;\n\t}\n\n\tpublic void setScanModulepath(boolean scanModulepath) {\n\t\tthis.scanModulepath = scanModulepath;\n\t}\n\n\tpublic boolean isScanClasspath() {\n\t\treturn this.scanClasspath;\n\t}\n\n\tpublic void setScanClasspath(boolean scanClasspath) {\n\t\tthis.scanClasspath = scanClasspath;\n\t}\n\n\tpublic List<Path> getExistingAdditionalClasspathEntries() {\n\t\treturn this.additionalClasspathEntries.stream().filter(Files::exists).toList();\n\t}\n\n\tpublic List<Path> getAdditionalClasspathEntries() {\n\t\treturn this.additionalClasspathEntries;\n\t}\n\n\tpublic void setAdditionalClasspathEntries(List<Path> additionalClasspathEntries) {\n\t\tthis.additionalClasspathEntries = additionalClasspathEntries;\n\t}\n\n\tpublic @Nullable List<Path> getSelectedClasspathEntries() {\n\t\treturn this.selectedClasspathEntries;\n\t}\n\n\tpublic void setSelectedClasspathEntries(@Nullable List<Path> selectedClasspathEntries) {\n\t\tthis.selectedClasspathEntries = selectedClasspathEntries;\n\t}\n\n\tpublic List<UriSelector> getSelectedUris() {\n\t\treturn selectedUris;\n\t}\n\n\tpublic void setSelectedUris(List<UriSelector> selectedUris) {\n\t\tthis.selectedUris = selectedUris;\n\t}\n\n\tpublic List<FileSelector> getSelectedFiles() {\n\t\treturn selectedFiles;\n\t}\n\n\tpublic void setSelectedFiles(List<FileSelector> selectedFiles) {\n\t\tthis.selectedFiles = selectedFiles;\n\t}\n\n\tpublic List<DirectorySelector> getSelectedDirectories() {\n\t\treturn selectedDirectories;\n\t}\n\n\tpublic void setSelectedDirectories(List<DirectorySelector> selectedDirectories) {\n\t\tthis.selectedDirectories = selectedDirectories;\n\t}\n\n\tpublic List<ModuleSelector> getSelectedModules() {\n\t\treturn selectedModules;\n\t}\n\n\tpublic void setSelectedModules(List<ModuleSelector> selectedModules) {\n\t\tthis.selectedModules = selectedModules;\n\t}\n\n\tpublic List<PackageSelector> getSelectedPackages() {\n\t\treturn selectedPackages;\n\t}\n\n\tpublic void setSelectedPackages(List<PackageSelector> selectedPackages) {\n\t\tthis.selectedPackages = selectedPackages;\n\t}\n\n\tpublic List<ClassSelector> getSelectedClasses() {\n\t\treturn selectedClasses;\n\t}\n\n\tpublic void setSelectedClasses(List<ClassSelector> selectedClasses) {\n\t\tthis.selectedClasses = selectedClasses;\n\t}\n\n\tpublic List<MethodSelector> getSelectedMethods() {\n\t\treturn selectedMethods;\n\t}\n\n\tpublic void setSelectedMethods(List<MethodSelector> selectedMethods) {\n\t\tthis.selectedMethods = selectedMethods;\n\t}\n\n\tpublic List<ClasspathResourceSelector> getSelectedClasspathResources() {\n\t\treturn selectedClasspathResources;\n\t}\n\n\tpublic void setSelectedClasspathResources(List<ClasspathResourceSelector> selectedClasspathResources) {\n\t\tthis.selectedClasspathResources = selectedClasspathResources;\n\t}\n\n\tpublic List<IterationSelector> getSelectedIterations() {\n\t\treturn selectedIterations;\n\t}\n\n\tpublic void setSelectedIterations(List<IterationSelector> selectedIterations) {\n\t\tthis.selectedIterations = selectedIterations;\n\t}\n\n\tpublic List<UniqueIdSelector> getSelectedUniqueIds() {\n\t\treturn selectedUniqueIds;\n\t}\n\n\tpublic void setSelectedUniqueId(List<UniqueIdSelector> selectedUniqueIds) {\n\t\tthis.selectedUniqueIds = selectedUniqueIds;\n\t}\n\n\tpublic List<DiscoverySelectorIdentifier> getSelectorIdentifiers() {\n\t\treturn selectorIdentifiers;\n\t}\n\n\tpublic void setSelectorIdentifiers(List<DiscoverySelectorIdentifier> selectorIdentifiers) {\n\t\tthis.selectorIdentifiers = selectorIdentifiers;\n\t}\n\n\tpublic List<DiscoverySelector> getExplicitSelectors() {\n\t\tList<DiscoverySelector> selectors = new ArrayList<>();\n\t\tselectors.addAll(getSelectedUniqueIds());\n\t\tselectors.addAll(getSelectedUris());\n\t\tselectors.addAll(getSelectedFiles());\n\t\tselectors.addAll(getSelectedDirectories());\n\t\tselectors.addAll(getSelectedModules());\n\t\tselectors.addAll(getSelectedPackages());\n\t\tselectors.addAll(getSelectedClasses());\n\t\tselectors.addAll(getSelectedMethods());\n\t\tselectors.addAll(getSelectedClasspathResources());\n\t\tselectors.addAll(getSelectedIterations());\n\t\tDiscoverySelectors.parseAll(getSelectorIdentifiers()).forEach(selectors::add);\n\t\treturn selectors;\n\t}\n\n\tpublic List<String> getIncludedClassNamePatterns() {\n\t\treturn this.includedClassNamePatterns;\n\t}\n\n\tpublic void setIncludedClassNamePatterns(List<String> includedClassNamePatterns) {\n\t\tthis.includedClassNamePatterns = includedClassNamePatterns;\n\t}\n\n\tpublic List<String> getExcludedClassNamePatterns() {\n\t\treturn this.excludedClassNamePatterns;\n\t}\n\n\tpublic void setExcludedClassNamePatterns(List<String> excludedClassNamePatterns) {\n\t\tthis.excludedClassNamePatterns = excludedClassNamePatterns;\n\t}\n\n\tpublic List<String> getIncludedPackages() {\n\t\treturn this.includedPackages;\n\t}\n\n\tpublic void setIncludedPackages(List<String> includedPackages) {\n\t\tthis.includedPackages = includedPackages;\n\t}\n\n\tpublic List<String> getExcludedPackages() {\n\t\treturn this.excludedPackages;\n\t}\n\n\tpublic void setExcludedPackages(List<String> excludedPackages) {\n\t\tthis.excludedPackages = excludedPackages;\n\t}\n\n\tpublic List<String> getIncludedMethodNamePatterns() {\n\t\treturn includedMethodNamePatterns;\n\t}\n\n\tpublic void setIncludedMethodNamePatterns(List<String> includedMethodNamePatterns) {\n\t\tthis.includedMethodNamePatterns = includedMethodNamePatterns;\n\t}\n\n\tpublic List<String> getExcludedMethodNamePatterns() {\n\t\treturn excludedMethodNamePatterns;\n\t}\n\n\tpublic void setExcludedMethodNamePatterns(List<String> excludedMethodNamePatterns) {\n\t\tthis.excludedMethodNamePatterns = excludedMethodNamePatterns;\n\t}\n\n\tpublic List<String> getIncludedEngines() {\n\t\treturn this.includedEngines;\n\t}\n\n\tpublic void setIncludedEngines(List<String> includedEngines) {\n\t\tthis.includedEngines = includedEngines;\n\t}\n\n\tpublic List<String> getExcludedEngines() {\n\t\treturn this.excludedEngines;\n\t}\n\n\tpublic void setExcludedEngines(List<String> excludedEngines) {\n\t\tthis.excludedEngines = excludedEngines;\n\t}\n\n\tpublic List<String> getIncludedTagExpressions() {\n\t\treturn this.includedTagExpressions;\n\t}\n\n\tpublic void setIncludedTagExpressions(List<String> includedTags) {\n\t\tthis.includedTagExpressions = includedTags;\n\t}\n\n\tpublic List<String> getExcludedTagExpressions() {\n\t\treturn this.excludedTagExpressions;\n\t}\n\n\tpublic void setExcludedTagExpressions(List<String> excludedTags) {\n\t\tthis.excludedTagExpressions = excludedTags;\n\t}\n\n\tpublic Map<String, String> getConfigurationParameters() {\n\t\treturn this.configurationParameters;\n\t}\n\n\tpublic void setConfigurationParameters(Map<String, String> configurationParameters) {\n\t\tthis.configurationParameters = configurationParameters;\n\t}\n\n\tpublic List<String> getConfigurationParametersResources() {\n\t\treturn this.configurationParametersResources;\n\t}\n\n\tpublic TestDiscoveryOptions setConfigurationParametersResources(List<String> configurationParametersResources) {\n\t\tthis.configurationParametersResources = configurationParametersResources;\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.options;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\n\nimport picocli.CommandLine;\nimport picocli.CommandLine.ArgGroup;\nimport picocli.CommandLine.Option;\n\n@API(status = INTERNAL, since = \"1.14\")\npublic class TestDiscoveryOptionsMixin {\n\n\tprivate static final String CP_OPTION = \"cp\";\n\n\t@ArgGroup(validate = false, order = 2, heading = \"%n@|bold SELECTORS|@%n%n\")\n\tSelectorOptions selectorOptions;\n\n\t@ArgGroup(validate = false, order = 3, heading = \"%n  For more information on selectors including syntax examples, see\"\n\t\t\t+ \"%n  @|underline https://docs.junit.org/${junit.docs.version}/running-tests/discovery-selectors.html|@\"\n\t\t\t+ \"%n%n@|bold FILTERS|@%n%n\")\n\tFilterOptions filterOptions;\n\n\t@ArgGroup(validate = false, order = 4, heading = \"%n@|bold RUNTIME CONFIGURATION|@%n%n\")\n\tRuntimeConfigurationOptions runtimeConfigurationOptions;\n\n\tpublic static class SelectorOptions {\n\n\t\t@Option(names = { \"--scan-classpath\",\n\t\t\t\t\"--scan-class-path\" }, converter = ClasspathEntriesConverter.class, paramLabel = \"PATH\", arity = \"0..1\", description = \"Scan all directories on the classpath or explicit classpath roots. \" //\n\t\t\t\t\t\t+ \"Without arguments, only directories on the system classpath as well as additional classpath \" //\n\t\t\t\t\t\t+ \"entries supplied via -\" + CP_OPTION + \" (directories and JAR files) are scanned. \" //\n\t\t\t\t\t\t+ \"Explicit classpath roots that are not on the classpath will be silently ignored. \" //\n\t\t\t\t\t\t+ \"This option can be repeated.\")\n\t\tprivate @Nullable List<Path> selectedClasspathEntries;\n\n\t\t@Option(names = \"--scan-modules\", description = \"Scan all resolved modules for test discovery.\")\n\t\tprivate boolean scanModulepath;\n\n\t\t@Option(names = { \"-u\",\n\t\t\t\t\"--select-uri\" }, paramLabel = \"URI\", arity = \"1..*\", converter = SelectorConverter.Uri.class, description = \"Select a URI for test discovery. This option can be repeated.\")\n\t\tprivate List<UriSelector> selectedUris = new ArrayList<>();\n\n\t\t@Option(names = { \"-f\",\n\t\t\t\t\"--select-file\" }, paramLabel = \"FILE\", arity = \"1..*\", converter = SelectorConverter.File.class, //\n\t\t\t\tdescription = \"Select a file for test discovery. \"\n\t\t\t\t\t\t+ \"The line and column numbers can be provided as URI query parameters (e.g. foo.txt?line=12&column=34). \"\n\t\t\t\t\t\t+ \"This option can be repeated.\")\n\t\tprivate List<FileSelector> selectedFiles = new ArrayList<>();\n\n\t\t@Option(names = { \"-d\",\n\t\t\t\t\"--select-directory\" }, paramLabel = \"DIR\", arity = \"1..*\", converter = SelectorConverter.Directory.class, description = \"Select a directory for test discovery. This option can be repeated.\")\n\t\tprivate List<DirectorySelector> selectedDirectories = new ArrayList<>();\n\n\t\t@Option(names = { \"-o\",\n\t\t\t\t\"--select-module\" }, paramLabel = \"NAME\", arity = \"1..*\", converter = SelectorConverter.Module.class, description = \"Select single module for test discovery. This option can be repeated.\")\n\t\tprivate List<ModuleSelector> selectedModules = new ArrayList<>();\n\n\t\t@Option(names = { \"-p\",\n\t\t\t\t\"--select-package\" }, paramLabel = \"PKG\", arity = \"1..*\", converter = SelectorConverter.Package.class, description = \"Select a package for test discovery. This option can be repeated.\")\n\t\tprivate List<PackageSelector> selectedPackages = new ArrayList<>();\n\n\t\t@Option(names = { \"-c\",\n\t\t\t\t\"--select-class\" }, paramLabel = \"CLASS\", arity = \"1..*\", converter = SelectorConverter.Class.class, description = \"Select a class for test discovery. This option can be repeated.\")\n\t\tprivate List<ClassSelector> selectedClasses = new ArrayList<>();\n\n\t\t@Option(names = { \"-m\",\n\t\t\t\t\"--select-method\" }, paramLabel = \"NAME\", arity = \"1..*\", converter = SelectorConverter.Method.class, description = \"Select a method for test discovery. This option can be repeated.\")\n\t\tprivate List<MethodSelector> selectedMethods = new ArrayList<>();\n\n\t\t@Option(names = { \"-r\",\n\t\t\t\t\"--select-resource\" }, paramLabel = \"RESOURCE\", arity = \"1..*\", converter = SelectorConverter.ClasspathResource.class, description = \"Select a classpath resource for test discovery. This option can be repeated.\")\n\t\tprivate List<ClasspathResourceSelector> selectedClasspathResources = new ArrayList<>();\n\n\t\t@Option(names = { \"-i\",\n\t\t\t\t\"--select-iteration\" }, paramLabel = \"PREFIX:VALUE[INDEX(..INDEX)?(,INDEX(..INDEX)?)*]\", arity = \"1..*\", converter = SelectorConverter.Iteration.class, //\n\t\t\t\tdescription = \"Select iterations for test discovery via a prefixed identifier and a list of indexes or index ranges \"\n\t\t\t\t\t\t+ \"(e.g. method:com.acme.Foo#m()[1..2] selects the first and second iteration of the m() method in the com.acme.Foo class). \"\n\t\t\t\t\t\t+ \"This option can be repeated.\")\n\t\tprivate List<IterationSelector> selectedIterations = new ArrayList<>();\n\n\t\t@Option(names = { \"--select-unique-id\",\n\t\t\t\t\"--uid\" }, paramLabel = \"UNIQUE-ID\", arity = \"1..*\", converter = SelectorConverter.UniqueId.class, //\n\t\t\t\tdescription = \"Select a unique id for test discovery. This option can be repeated.\")\n\t\tprivate List<UniqueIdSelector> selectedUniqueIds = new ArrayList<>();\n\n\t\t@Option(names = \"--select\", paramLabel = \"PREFIX:VALUE\", arity = \"1..*\", converter = SelectorConverter.Identifier.class, //\n\t\t\t\tdescription = \"Select via a prefixed identifier (e.g. method:com.acme.Foo#m selects the m() method in the com.acme.Foo class). \"\n\t\t\t\t\t\t+ \"This option can be repeated.\")\n\t\tprivate List<DiscoverySelectorIdentifier> selectorIdentifiers = new ArrayList<>();\n\n\t\tSelectorOptions() {\n\t\t}\n\n\t\tprivate void applyTo(TestDiscoveryOptions result) {\n\t\t\tresult.setScanClasspath(this.selectedClasspathEntries != null); // flag was specified\n\t\t\tresult.setScanModulepath(this.scanModulepath);\n\t\t\tresult.setSelectedModules(this.selectedModules);\n\t\t\tresult.setSelectedClasspathEntries(this.selectedClasspathEntries);\n\t\t\tresult.setSelectedUris(this.selectedUris);\n\t\t\tresult.setSelectedFiles(this.selectedFiles);\n\t\t\tresult.setSelectedDirectories(this.selectedDirectories);\n\t\t\tresult.setSelectedPackages(this.selectedPackages);\n\t\t\tresult.setSelectedClasses(this.selectedClasses);\n\t\t\tresult.setSelectedMethods(this.selectedMethods);\n\t\t\tresult.setSelectedClasspathResources(this.selectedClasspathResources);\n\t\t\tresult.setSelectedIterations(this.selectedIterations);\n\t\t\tresult.setSelectedUniqueId(this.selectedUniqueIds);\n\t\t\tresult.setSelectorIdentifiers(this.selectorIdentifiers);\n\t\t}\n\t}\n\n\tpublic static class FilterOptions {\n\n\t\t@Option(names = { \"-n\",\n\t\t\t\t\"--include-classname\" }, paramLabel = \"PATTERN\", defaultValue = ClassNameFilter.STANDARD_INCLUDE_PATTERN, arity = \"1\", description = \"Provide a regular expression to include only classes whose fully qualified names match. \" //\n\t\t\t\t\t\t+ \"To avoid loading classes unnecessarily, the default pattern only includes class \" //\n\t\t\t\t\t\t+ \"names that begin with \\\"Test\\\" or end with \\\"Test\\\" or \\\"Tests\\\". \" //\n\t\t\t\t\t\t+ \"When this option is repeated, all patterns will be combined using OR semantics. \" //\n\t\t\t\t\t\t+ \"Default: ${DEFAULT-VALUE}\")\n\t\tprivate List<String> includeClassNamePatterns = new ArrayList<>();\n\n\t\t@Option(names = { \"-N\",\n\t\t\t\t\"--exclude-classname\" }, paramLabel = \"PATTERN\", arity = \"1\", description = \"Provide a regular expression to exclude those classes whose fully qualified names match. \" //\n\t\t\t\t\t\t+ \"When this option is repeated, all patterns will be combined using OR semantics.\")\n\t\tprivate List<String> excludeClassNamePatterns = new ArrayList<>();\n\n\t\t@Option(names = \"--include-package\", paramLabel = \"PKG\", arity = \"1\", description = \"?Provide a package to be included in the test run. This option can be repeated.\")\n\t\tprivate List<String> includePackages = new ArrayList<>();\n\n\t\t@Option(names = \"--exclude-package\", paramLabel = \"PKG\", arity = \"1\", description = \"Provide a package to be excluded from the test run. This option can be repeated.\")\n\t\tprivate List<String> excludePackages = new ArrayList<>();\n\n\t\t@Option(names = \"--include-methodname\", paramLabel = \"PATTERN\", arity = \"1\", description = \"Provide a regular expression to include only methods whose fully qualified names without parameters match. \" //\n\t\t\t\t+ \"When this option is repeated, all patterns will be combined using OR semantics.\")\n\t\tprivate List<String> includeMethodNamePatterns = new ArrayList<>();\n\n\t\t@Option(names = \"--exclude-methodname\", paramLabel = \"PATTERN\", arity = \"1\", description = \"Provide a regular expression to exclude those methods whose fully qualified names without parameters match. \" //\n\t\t\t\t+ \"When this option is repeated, all patterns will be combined using OR semantics.\")\n\t\tprivate List<String> excludeMethodNamePatterns = new ArrayList<>();\n\n\t\t@Option(names = { \"-t\",\n\t\t\t\t\"--include-tag\" }, paramLabel = \"TAG\", arity = \"1\", description = \"Provide a tag or tag expression to include only tests whose tags match. \"\n\t\t\t\t\t\t+ //\n\t\t\t\t\t\t\"When this option is repeated, all patterns will be combined using OR semantics.\")\n\t\tprivate List<String> includedTags = new ArrayList<>();\n\n\t\t@Option(names = { \"-T\",\n\t\t\t\t\"--exclude-tag\" }, paramLabel = \"TAG\", arity = \"1\", description = \"Provide a tag or tag expression to exclude those tests whose tags match. \"\n\t\t\t\t\t\t+ //\n\t\t\t\t\t\t\"When this option is repeated, all patterns will be combined using OR semantics.\")\n\t\tprivate List<String> excludedTags = new ArrayList<>();\n\n\t\t@Option(names = { \"-e\",\n\t\t\t\t\"--include-engine\" }, paramLabel = \"ID\", arity = \"1\", description = \"Provide the ID of an engine to be included in the test run. This option can be repeated.\")\n\t\tprivate List<String> includedEngines = new ArrayList<>();\n\n\t\t@Option(names = { \"-E\",\n\t\t\t\t\"--exclude-engine\" }, paramLabel = \"ID\", arity = \"1\", description = \"Provide the ID of an engine to be excluded from the test run. This option can be repeated.\")\n\t\tprivate List<String> excludedEngines = new ArrayList<>();\n\n\t\tprivate void applyTo(TestDiscoveryOptions result) {\n\t\t\tresult.setIncludedClassNamePatterns(this.includeClassNamePatterns);\n\t\t\tresult.setExcludedClassNamePatterns(this.excludeClassNamePatterns);\n\t\t\tresult.setIncludedPackages(this.includePackages);\n\t\t\tresult.setExcludedPackages(this.excludePackages);\n\t\t\tresult.setIncludedMethodNamePatterns(new ArrayList<>(this.includeMethodNamePatterns));\n\t\t\tresult.setExcludedMethodNamePatterns(new ArrayList<>(this.excludeMethodNamePatterns));\n\t\t\tresult.setIncludedTagExpressions(this.includedTags);\n\t\t\tresult.setExcludedTagExpressions(this.excludedTags);\n\t\t\tresult.setIncludedEngines(this.includedEngines);\n\t\t\tresult.setExcludedEngines(this.excludedEngines);\n\t\t}\n\t}\n\n\tpublic static class RuntimeConfigurationOptions {\n\n\t\t@Option(names = { \"-\" + CP_OPTION, \"--classpath\",\n\t\t\t\t\"--class-path\" }, converter = ClasspathEntriesConverter.class, paramLabel = \"PATH\", arity = \"1\", description = \"Provide additional classpath entries \"\n\t\t\t\t\t\t+ \"-- for example, for adding engines and their dependencies. This option can be repeated.\")\n\t\tprivate List<Path> additionalClasspathEntries = new ArrayList<>();\n\n\t\t// Implementation note: the @Option annotation is on a setter method to allow validation.\n\t\tprivate Map<String, String> configurationParameters = new LinkedHashMap<>();\n\n\t\t@Option(names = \"--config-resource\", paramLabel = \"PATH\", arity = \"1\", description = \"Set configuration parameters for test discovery and execution via a classpath resource. This option can be repeated.\")\n\t\tprivate List<String> configurationParametersResources = new ArrayList<>();\n\n\t\t@CommandLine.Spec\n\t\tprivate CommandLine.Model.CommandSpec spec;\n\n\t\t/**\n\t\t * Adds the specified key-value pair (or pairs) to the configuration parameters.\n\t\t * A {@code ParameterException} is thrown if the same key is specified multiple times\n\t\t * on the command line.\n\t\t *\n\t\t * @param map the key-value pairs to add\n\t\t * @throws CommandLine.ParameterException if the map already contains this key\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1308\">#1308</a>\n\t\t */\n\t\t@Option(names = \"--config\", paramLabel = \"KEY=VALUE\", arity = \"1\", description = \"Set a configuration parameter for test discovery and execution. This option can be repeated.\")\n\t\tpublic void setConfigurationParameters(Map<String, String> map) {\n\t\t\tfor (String key : map.keySet()) {\n\t\t\t\tString newValue = map.get(key);\n\t\t\t\tvalidateUnique(key, newValue);\n\t\t\t\tconfigurationParameters.put(key, newValue);\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateUnique(String key, String newValue) {\n\t\t\tString existing = configurationParameters.get(key);\n\t\t\tif (existing != null && !existing.equals(newValue)) {\n\t\t\t\tthrow new CommandLine.ParameterException(spec.commandLine(),\n\t\t\t\t\t\"Duplicate key '%s' for values '%s' and '%s'.\".formatted(key, existing, newValue));\n\t\t\t}\n\t\t}\n\n\t\tprivate void applyTo(TestDiscoveryOptions result) {\n\t\t\tresult.setAdditionalClasspathEntries(additionalClasspathEntries);\n\t\t\tresult.setConfigurationParametersResources(configurationParametersResources);\n\t\t\tresult.setConfigurationParameters(configurationParameters);\n\t\t}\n\t}\n\n\tpublic TestDiscoveryOptions toTestDiscoveryOptions() {\n\t\tTestDiscoveryOptions result = new TestDiscoveryOptions();\n\t\tif (this.selectorOptions != null) {\n\t\t\tthis.selectorOptions.applyTo(result);\n\t\t}\n\t\tif (this.filterOptions != null) {\n\t\t\tthis.filterOptions.applyTo(result);\n\t\t}\n\t\tif (this.runtimeConfigurationOptions != null) {\n\t\t\tthis.runtimeConfigurationOptions.applyTo(result);\n\t\t}\n\t\treturn result;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/options/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Command-line option utility classes of JUnit's console launcher.\n */\n\n@NullMarked\npackage org.junit.platform.console.options;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/ColorPalette.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.EnumMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 1.9\n */\n@API(status = INTERNAL, since = \"1.14\")\npublic class ColorPalette {\n\n\tpublic static final ColorPalette SINGLE_COLOR = new ColorPalette(singleColorPalette(), false);\n\tpublic static final ColorPalette DEFAULT = new ColorPalette(defaultPalette(), false);\n\tpublic static final ColorPalette NONE = new ColorPalette(new EnumMap<>(Style.class), true);\n\n\tprivate final Map<Style, String> colorsToAnsiSequences;\n\tprivate final boolean disableAnsiColors;\n\n\t// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters\n\tprivate static Map<Style, String> defaultPalette() {\n\t\tMap<Style, String> colorsToAnsiSequences = new EnumMap<>(Style.class);\n\t\tcolorsToAnsiSequences.put(Style.NONE, \"0\");\n\t\tcolorsToAnsiSequences.put(Style.SUCCESSFUL, \"32\");\n\t\tcolorsToAnsiSequences.put(Style.ABORTED, \"33\");\n\t\tcolorsToAnsiSequences.put(Style.FAILED, \"31\");\n\t\tcolorsToAnsiSequences.put(Style.SKIPPED, \"35\");\n\t\tcolorsToAnsiSequences.put(Style.CONTAINER, \"36\");\n\t\tcolorsToAnsiSequences.put(Style.TEST, \"34\");\n\t\tcolorsToAnsiSequences.put(Style.DYNAMIC, \"35\");\n\t\tcolorsToAnsiSequences.put(Style.REPORTED, \"37\");\n\t\treturn colorsToAnsiSequences;\n\t}\n\n\tprivate static Map<Style, String> singleColorPalette() {\n\t\tMap<Style, String> colorsToAnsiSequences = new EnumMap<>(Style.class);\n\t\tcolorsToAnsiSequences.put(Style.NONE, \"0\");\n\t\tcolorsToAnsiSequences.put(Style.SUCCESSFUL, \"1\");\n\t\tcolorsToAnsiSequences.put(Style.ABORTED, \"4\");\n\t\tcolorsToAnsiSequences.put(Style.FAILED, \"7\");\n\t\tcolorsToAnsiSequences.put(Style.SKIPPED, \"9\");\n\t\tcolorsToAnsiSequences.put(Style.CONTAINER, \"1\");\n\t\tcolorsToAnsiSequences.put(Style.TEST, \"0\");\n\t\tcolorsToAnsiSequences.put(Style.DYNAMIC, \"0\");\n\t\tcolorsToAnsiSequences.put(Style.REPORTED, \"2\");\n\t\treturn colorsToAnsiSequences;\n\t}\n\n\tColorPalette(Map<Style, String> overrides) {\n\t\tthis(defaultPalette(), false);\n\n\t\tif (overrides.containsKey(Style.NONE)) {\n\t\t\tthrow new IllegalArgumentException(\"Cannot override the standard style 'NONE'\");\n\t\t}\n\t\tthis.colorsToAnsiSequences.putAll(overrides);\n\t}\n\n\tColorPalette(Properties properties) {\n\t\tthis(toOverrideMap(properties));\n\t}\n\n\tColorPalette(Reader reader) {\n\t\tthis(getProperties(reader));\n\t}\n\n\tpublic ColorPalette(Path path) {\n\t\tthis(getProperties(path));\n\t}\n\n\tprivate ColorPalette(Map<Style, String> colorsToAnsiSequences, boolean disableAnsiColors) {\n\t\tthis.colorsToAnsiSequences = colorsToAnsiSequences;\n\t\tthis.disableAnsiColors = disableAnsiColors;\n\t}\n\n\tprivate static Map<Style, String> toOverrideMap(Properties properties) {\n\t\tMap<String, String> upperCaseProperties = properties.entrySet().stream().collect(Collectors.toMap(\n\t\t\tentry -> ((String) entry.getKey()).toUpperCase(Locale.ROOT), entry -> (String) entry.getValue()));\n\n\t\treturn Arrays.stream(Style.values()).filter(style -> upperCaseProperties.containsKey(style.name())).collect(\n\t\t\tCollectors.toMap(Function.identity(), style -> upperCaseProperties.get(style.name())));\n\t}\n\n\tprivate static Properties getProperties(Reader reader) {\n\t\tProperties properties = new Properties();\n\t\ttry {\n\t\t\tproperties.load(reader);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new IllegalArgumentException(\"Could not read color palette properties\", e);\n\t\t}\n\t\treturn properties;\n\t}\n\n\tprivate static Properties getProperties(Path path) {\n\t\ttry (FileReader fileReader = new FileReader(path.toFile(), StandardCharsets.UTF_8)) {\n\t\t\treturn getProperties(fileReader);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new IllegalArgumentException(\"Could not open color palette properties file\", e);\n\t\t}\n\t}\n\n\tpublic String paint(Style style, String text) {\n\t\treturn this.disableAnsiColors || style == Style.NONE ? text\n\t\t\t\t: getAnsiFormatter(style) + text + getAnsiFormatter(Style.NONE);\n\t}\n\n\tprivate String getAnsiFormatter(Style style) {\n\t\treturn \"\\u001B[%sm\".formatted(this.colorsToAnsiSequences.get(style));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/DetailsPrintingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.regex.Pattern;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.14\")\npublic interface DetailsPrintingListener extends TestExecutionListener {\n\n\tPattern LINE_START_PATTERN = Pattern.compile(\"(?m)^\");\n\n\tvoid listTests(TestPlan testPlan);\n\n\tstatic String indented(String message, String indentation) {\n\t\treturn LINE_START_PATTERN.matcher(message).replaceAll(indentation).strip();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/FlatPrintingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.PrintWriter;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.14\")\npublic class FlatPrintingListener implements DetailsPrintingListener {\n\n\tstatic final String INDENTATION = \"             \";\n\n\tprivate final PrintWriter out;\n\tprivate final ColorPalette colorPalette;\n\n\tpublic FlatPrintingListener(PrintWriter out, ColorPalette colorPalette) {\n\t\tthis.out = out;\n\t\tthis.colorPalette = colorPalette;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tthis.out.printf(\"Test execution started. Number of static tests: %d%n\",\n\t\t\ttestPlan.countTestIdentifiers(TestIdentifier::isTest));\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tthis.out.println(\"Test execution finished.\");\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\tprintlnTestDescriptor(Style.DYNAMIC, \"Registered:\", testIdentifier);\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tprintlnTestDescriptor(Style.SKIPPED, \"Skipped:\", testIdentifier);\n\t\tprintlnMessage(Style.SKIPPED, \"Reason\", reason);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tprintlnTestDescriptor(Style.valueOf(testIdentifier), \"Started:\", testIdentifier);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tStyle style = Style.valueOf(testExecutionResult);\n\t\tprintlnTestDescriptor(style, \"Finished:\", testIdentifier);\n\t\ttestExecutionResult.getThrowable().ifPresent(t -> printlnException(style, t));\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tprintlnTestDescriptor(Style.REPORTED, \"Reported:\", testIdentifier);\n\t\tprintlnMessage(Style.REPORTED, \"Reported values\", entry.toString());\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t\tprintlnTestDescriptor(Style.REPORTED, \"Reported:\", testIdentifier);\n\t\tprintlnMessage(Style.REPORTED, \"Reported file\", file.toString());\n\t}\n\n\tprivate void printlnTestDescriptor(Style style, String message, TestIdentifier testIdentifier) {\n\t\tprintln(style, \"%-10s   %s (%s)\", message, testIdentifier.getDisplayName(), testIdentifier.getUniqueId());\n\t}\n\n\tprivate void printlnException(Style style, Throwable throwable) {\n\t\tprintlnMessage(style, \"Exception\", ExceptionUtils.readStackTrace(throwable));\n\t}\n\n\tprivate void printlnMessage(Style style, String message, String detail) {\n\t\tprintln(style, INDENTATION + \"=> \" + message + \": %s\", indented(detail));\n\t}\n\n\tprivate void println(Style style, String format, Object... args) {\n\t\tthis.out.println(colorPalette.paint(style, format.formatted(args)));\n\t}\n\n\t/**\n\t * Indent the given message if it is a multi-line string.\n\t *\n\t * <p>{@link #INDENTATION} is used to prefix the start of each new line\n\t * except the first one.\n\t *\n\t * @param message the message to indent\n\t * @return indented message\n\t */\n\tprivate static String indented(String message) {\n\t\treturn DetailsPrintingListener.indented(message, INDENTATION);\n\t}\n\n\t@Override\n\tpublic void listTests(TestPlan testPlan) {\n\t\ttestPlan.accept(new TestPlan.Visitor() {\n\t\t\t@Override\n\t\t\tpublic void visit(TestIdentifier testIdentifier) {\n\t\t\t\tprintln(Style.valueOf(testIdentifier), \"%s (%s)\", testIdentifier.getDisplayName(),\n\t\t\t\t\ttestIdentifier.getUniqueId());\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/Style.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * @since 1.9\n */\nenum Style {\n\n\tNONE, SUCCESSFUL, ABORTED, FAILED, SKIPPED, CONTAINER, TEST, DYNAMIC, REPORTED;\n\n\tstatic Style valueOf(TestExecutionResult result) {\n\t\treturn switch (result.getStatus()) {\n\t\t\tcase SUCCESSFUL -> Style.SUCCESSFUL;\n\t\t\tcase ABORTED -> Style.ABORTED;\n\t\t\tcase FAILED -> Style.FAILED;\n\t\t};\n\t}\n\n\tstatic Style valueOf(TestIdentifier testIdentifier) {\n\t\treturn testIdentifier.isContainer() ? CONTAINER : TEST;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/TestFeedPrintingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\n\nimport java.io.PrintWriter;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n@API(status = INTERNAL, since = \"1.14\")\npublic class TestFeedPrintingListener implements DetailsPrintingListener {\n\n\tprivate static final String INDENTATION = \"\\t\";\n\tprivate static final String STATUS_SEPARATOR = \" :: \";\n\n\tprivate final PrintWriter out;\n\tprivate final ColorPalette colorPalette;\n\n\tprivate @Nullable TestPlan testPlan;\n\n\tpublic TestFeedPrintingListener(PrintWriter out, ColorPalette colorPalette) {\n\t\tthis.out = out;\n\t\tthis.colorPalette = colorPalette;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tthis.testPlan = testPlan;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tthis.testPlan = null;\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tif (shouldPrint(testIdentifier)) {\n\t\t\tString msg = formatTestIdentifier(testIdentifier);\n\t\t\tString indentedReason = indented(\"Reason: %s\".formatted(reason));\n\t\t\tprintln(Style.SKIPPED,\n\t\t\t\t(\"%s\" + STATUS_SEPARATOR + \"SKIPPED%n\" + INDENTATION + \"%s\").formatted(msg, indentedReason));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tif (shouldPrint(testIdentifier)) {\n\t\t\tString msg = formatTestIdentifier(testIdentifier);\n\t\t\tprintln(Style.NONE, (\"%s\" + STATUS_SEPARATOR + \"STARTED\").formatted(msg));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tTestExecutionResult.Status status = testExecutionResult.getStatus();\n\t\tif (testExecutionResult.getThrowable().isPresent()) {\n\t\t\tStyle style = Style.valueOf(testExecutionResult);\n\t\t\tString msg = formatTestIdentifier(testIdentifier);\n\t\t\tThrowable throwable = testExecutionResult.getThrowable().get();\n\t\t\tString stacktrace = indented(ExceptionUtils.readStackTrace(throwable));\n\t\t\tprintln(style, (\"%s\" + STATUS_SEPARATOR + \"%s%n\" + INDENTATION + \"%s\").formatted(msg, status, stacktrace));\n\t\t}\n\t\telse if (shouldPrint(testIdentifier) || testExecutionResult.getStatus() != SUCCESSFUL) {\n\t\t\tStyle style = Style.valueOf(testExecutionResult);\n\t\t\tString msg = formatTestIdentifier(testIdentifier);\n\t\t\tprintln(style, (\"%s\" + STATUS_SEPARATOR + \"%s\").formatted(msg, status));\n\t\t}\n\t}\n\n\tprivate String formatTestIdentifier(TestIdentifier testIdentifier) {\n\t\treturn String.join(\" > \", collectDisplayNames(testIdentifier.getUniqueIdObject()));\n\t}\n\n\tprivate void println(Style style, String message) {\n\t\tthis.out.println(colorPalette.paint(style, message));\n\t}\n\n\tprivate List<String> collectDisplayNames(UniqueId uniqueId) {\n\t\tint size = uniqueId.getSegments().size();\n\t\tList<String> displayNames = new ArrayList<>(size);\n\t\tfor (int i = 0; i < size; i++) {\n\t\t\tdisplayNames.add(0, requireNonNull(testPlan).getTestIdentifier(uniqueId).getDisplayName());\n\t\t\tif (i < size - 1) {\n\t\t\t\tuniqueId = uniqueId.removeLastSegment();\n\t\t\t}\n\t\t}\n\t\treturn displayNames;\n\t}\n\n\tprivate static String indented(String message) {\n\t\treturn DetailsPrintingListener.indented(message, INDENTATION);\n\t}\n\n\t@Override\n\tpublic void listTests(TestPlan testPlan) {\n\t\tthis.testPlan = testPlan;\n\t\ttry {\n\t\t\ttestPlan.accept(new TestPlan.Visitor() {\n\t\t\t\t@Override\n\t\t\t\tpublic void visit(TestIdentifier testIdentifier) {\n\t\t\t\t\tif (shouldPrint(testIdentifier)) {\n\t\t\t\t\t\tprintln(Style.NONE, formatTestIdentifier(testIdentifier));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tfinally {\n\t\t\tthis.testPlan = null;\n\t\t}\n\t}\n\n\tprivate static boolean shouldPrint(TestIdentifier testIdentifier) {\n\t\treturn testIdentifier.isTest();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/Theme.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\")\npublic enum Theme {\n\n\t/**\n\t * ASCII 7-bit characters form the tree branch.\n\t *\n\t * <p>Example test plan execution tree:\n\t * <pre class=\"code\">\n\t * +-- engine alpha\n\t * | '-- container BEGIN\n\t * |   +-- test 00 [OK]\n\t * |   '-- test 01 [OK]\n\t * '-- engine omega\n\t *   +-- container END\n\t *   | +-- test 10 [OK]\n\t *   | '-- test 11 [A] aborted\n\t *   '-- container FINAL\n\t *     +-- skipped [S] because\n\t *     '-- failing [X] BäMM\n\t * </pre>\n\t */\n\tASCII(\".\", \"| \", \"+--\", \"'--\", \"[OK]\", \"[A]\", \"[X]\", \"[S]\"),\n\n\t/**\n\t * Unicode (extended ASCII) characters are used to display the test execution tree.\n\t *\n\t * <p>Example test plan execution tree:\n\t * <pre class=\"code\">\n\t * ├─ engine alpha ✔\n\t * │  └─ container BEGIN ✔\n\t * │     ├─ test 00 ✔\n\t * │     └─ test 01 ✔\n\t * └─ engine omega ✔\n\t *    ├─ container END ✔\n\t *    │  ├─ test 10 ✔\n\t *    │  └─ test 11 ■ aborted\n\t *    └─ container FINAL ✔\n\t *       ├─ skipped ↷ because\n\t *       └─ failing ✘ BäMM\n\t * </pre>\n\t */\n\tUNICODE(\"╷\", \"│  \", \"├─\", \"└─\", \"✔\", \"■\", \"✘\", \"↷\");\n\n\tpublic static Theme valueOf(Charset charset) {\n\t\tif (StandardCharsets.UTF_8.equals(charset)) {\n\t\t\treturn UNICODE;\n\t\t}\n\t\treturn ASCII;\n\t}\n\n\tprivate final String[] tiles;\n\tprivate final String blank;\n\n\tTheme(String... tiles) {\n\t\tthis.tiles = tiles;\n\t\tthis.blank = new String(new char[vertical().length()]).replace('\\0', ' ');\n\t}\n\n\tpublic final String root() {\n\t\treturn tiles[0];\n\t}\n\n\tpublic final String vertical() {\n\t\treturn tiles[1];\n\t}\n\n\tpublic final String blank() {\n\t\treturn blank;\n\t}\n\n\tpublic final String entry() {\n\t\treturn tiles[2];\n\t}\n\n\tpublic final String end() {\n\t\treturn tiles[3];\n\t}\n\n\tpublic final String successful() {\n\t\treturn tiles[4];\n\t}\n\n\tpublic final String aborted() {\n\t\treturn tiles[5];\n\t}\n\n\tpublic final String failed() {\n\t\treturn tiles[6];\n\t}\n\n\tpublic final String skipped() {\n\t\treturn tiles[7];\n\t}\n\n\tpublic final String status(TestExecutionResult result) {\n\t\treturn switch (result.getStatus()) {\n\t\t\tcase SUCCESSFUL -> successful();\n\t\t\tcase ABORTED -> aborted();\n\t\t\tcase FAILED -> failed();\n\t\t};\n\t}\n\n\t/**\n\t * Return lower case {@link #name()} for easier usage in help text for\n\t * available options.\n\t */\n\t@Override\n\tpublic final String toString() {\n\t\treturn name().toLowerCase(Locale.ROOT);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/TreeNode.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.concurrent.ConcurrentLinkedQueue;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * @since 1.0\n */\nclass TreeNode {\n\n\tprivate final String caption;\n\tprivate final long creation;\n\tlong duration;\n\n\tprivate @Nullable String reason;\n\n\tprivate @Nullable TestIdentifier identifier;\n\n\tprivate @Nullable TestExecutionResult result;\n\n\tfinal Queue<ReportEntry> reports = new ConcurrentLinkedQueue<>();\n\tfinal Queue<FileEntry> files = new ConcurrentLinkedQueue<>();\n\tfinal Queue<TreeNode> children = new ConcurrentLinkedQueue<>();\n\tboolean visible;\n\n\tTreeNode(String caption) {\n\t\tthis.caption = caption;\n\t\tthis.creation = System.currentTimeMillis();\n\t\tthis.visible = false;\n\t}\n\n\tTreeNode(TestIdentifier identifier) {\n\t\tthis(createCaption(identifier.getDisplayName()));\n\t\tthis.identifier = identifier;\n\t\tthis.visible = true;\n\t}\n\n\tTreeNode(TestIdentifier identifier, String reason) {\n\t\tthis(identifier);\n\t\tthis.reason = reason;\n\t}\n\n\tTreeNode addChild(TreeNode node) {\n\t\tchildren.add(node);\n\t\treturn this;\n\t}\n\n\tTreeNode addReportEntry(ReportEntry reportEntry) {\n\t\treports.add(reportEntry);\n\t\treturn this;\n\t}\n\n\tTreeNode addFileEntry(FileEntry file) {\n\t\tfiles.add(file);\n\t\treturn this;\n\t}\n\n\tTreeNode setResult(TestExecutionResult result) {\n\t\tthis.result = result;\n\t\tthis.duration = System.currentTimeMillis() - creation;\n\t\treturn this;\n\t}\n\n\tpublic String caption() {\n\t\treturn caption;\n\t}\n\n\tOptional<String> reason() {\n\t\treturn Optional.ofNullable(reason);\n\t}\n\n\tOptional<TestExecutionResult> result() {\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\tOptional<TestIdentifier> identifier() {\n\t\treturn Optional.ofNullable(identifier);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tstatic String createCaption(String displayName) {\n\t\tboolean normal = displayName.length() <= 80;\n\t\tString caption = normal ? displayName : displayName.substring(0, 80) + \"...\";\n\t\tString whites = StringUtils.replaceWhitespaceCharacters(caption, \" \");\n\t\treturn StringUtils.replaceIsoControlCharacters(whites, \".\");\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/TreePrinter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.io.PrintWriter;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * @since 1.0\n */\nclass TreePrinter {\n\n\tprivate final PrintWriter out;\n\tprivate final Theme theme;\n\tprivate final ColorPalette colorPalette;\n\n\tTreePrinter(PrintWriter out, Theme theme, ColorPalette colorPalette) {\n\t\tthis.out = out;\n\t\tthis.theme = theme;\n\t\tthis.colorPalette = colorPalette;\n\t}\n\n\tvoid print(TreeNode node) {\n\t\tout.println(color(Style.CONTAINER, theme.root()));\n\t\tprint(node, \"\", true);\n\t\tout.flush();\n\t}\n\n\tprivate void print(TreeNode node, String indent, boolean continuous) {\n\t\tif (node.visible) {\n\t\t\tprintVisible(node, indent, continuous);\n\t\t}\n\t\tif (node.children.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tif (node.visible) {\n\t\t\tindent += continuous ? theme.vertical() : theme.blank();\n\t\t}\n\t\tIterator<TreeNode> iterator = node.children.iterator();\n\t\twhile (iterator.hasNext()) {\n\t\t\tprint(iterator.next(), indent, iterator.hasNext());\n\t\t}\n\t}\n\n\tprivate void printVisible(TreeNode node, String indent, boolean continuous) {\n\t\tString bullet = continuous ? theme.entry() : theme.end();\n\t\tString prefix = color(Style.CONTAINER, indent + bullet);\n\t\tString tabbed = color(Style.CONTAINER, indent + tab(node, continuous));\n\t\tString caption = colorCaption(node);\n\t\tString duration = color(Style.CONTAINER, node.duration + \" ms\");\n\t\tString icon = color(Style.SKIPPED, theme.skipped());\n\t\tif (node.result().isPresent()) {\n\t\t\tTestExecutionResult result = node.result().get();\n\t\t\tStyle resultStyle = Style.valueOf(result);\n\t\t\ticon = color(resultStyle, theme.status(result));\n\t\t}\n\t\tout.print(prefix);\n\t\tout.print(\" \");\n\t\tout.print(caption);\n\t\tif (node.duration > 10000 && node.children.isEmpty()) {\n\t\t\tout.print(\" \");\n\t\t\tout.print(duration);\n\t\t}\n\t\tboolean nodeIsBeingListed = node.duration == 0 && node.result().isEmpty() && node.reason().isEmpty();\n\t\tif (!nodeIsBeingListed) {\n\t\t\tout.print(\" \");\n\t\t\tout.print(icon);\n\t\t}\n\t\tnode.result().ifPresent(result -> printThrowable(tabbed, result));\n\t\tnode.reason().ifPresent(reason -> printMessage(Style.SKIPPED, tabbed, reason));\n\t\tnode.reports.forEach(e -> printReportEntry(tabbed, e));\n\t\tout.println();\n\t\tnode.files.forEach(e -> printFileEntry(tabbed, e));\n\t}\n\n\tprivate String tab(TreeNode node, boolean continuous) {\n\t\t// We might be the \"last\" node in this level, that means\n\t\t// `continuous == false`, but still need to include a vertical\n\t\t// bar for printing stack traces, messages and reports.\n\t\t// See https://github.com/junit-team/junit-framework/issues/1531\n\t\tif (node.children.size() > 0) {\n\t\t\treturn theme.blank() + theme.vertical();\n\t\t}\n\t\treturn (continuous ? theme.vertical() : theme.blank()) + theme.blank();\n\t}\n\n\tprivate String colorCaption(TreeNode node) {\n\t\tString caption = node.caption();\n\t\tif (node.result().isPresent()) {\n\t\t\tTestExecutionResult result = node.result().get();\n\t\t\tStyle resultStyle = Style.valueOf(result);\n\t\t\tif (result.getStatus() != Status.SUCCESSFUL) {\n\t\t\t\treturn color(resultStyle, caption);\n\t\t\t}\n\t\t}\n\t\tif (node.reason().isPresent()) {\n\t\t\treturn color(Style.SKIPPED, caption);\n\t\t}\n\t\tStyle style = node.identifier().map(Style::valueOf).orElse(Style.NONE);\n\t\treturn color(style, caption);\n\t}\n\n\tprivate void printThrowable(String indent, TestExecutionResult result) {\n\t\tif (result.getThrowable().isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tThrowable throwable = result.getThrowable().get();\n\t\tString message = throwable.getMessage();\n\t\tif (StringUtils.isBlank(message)) {\n\t\t\tmessage = throwable.toString();\n\t\t}\n\t\tprintMessage(Style.FAILED, indent, message);\n\t}\n\n\tprivate void printReportEntry(String indent, ReportEntry reportEntry) {\n\t\tout.println();\n\t\tout.print(indent);\n\t\tout.print(reportEntry.getTimestamp());\n\t\tSet<Map.Entry<String, String>> entries = reportEntry.getKeyValuePairs().entrySet();\n\t\tif (entries.size() == 1) {\n\t\t\tprintReportEntry(\" \", getOnlyElement(entries));\n\t\t\treturn;\n\t\t}\n\t\tfor (Map.Entry<String, String> entry : entries) {\n\t\t\tout.println();\n\t\t\tprintReportEntry(indent + theme.blank(), entry);\n\t\t}\n\t}\n\n\tprivate void printReportEntry(String indent, Map.Entry<String, String> mapEntry) {\n\t\tout.print(indent);\n\t\tout.print(color(Style.ABORTED, mapEntry.getKey()));\n\t\tout.print(\" = `\");\n\t\tout.print(color(Style.SUCCESSFUL, mapEntry.getValue()));\n\t\tout.print(\"`\");\n\t}\n\n\tprivate void printFileEntry(String indent, FileEntry fileEntry) {\n\t\tout.print(indent);\n\t\tout.print(fileEntry.getTimestamp());\n\t\tout.print(\" \");\n\t\tout.print(color(Style.SUCCESSFUL, fileEntry.getPath().toUri().toString()));\n\t\tout.println();\n\t}\n\n\tprivate void printMessage(Style style, String indent, String message) {\n\t\tString[] lines = message.split(\"\\\\R\");\n\t\tout.print(\" \");\n\t\tout.print(color(style, lines[0]));\n\t\tif (lines.length > 1) {\n\t\t\tfor (int i = 1; i < lines.length; i++) {\n\t\t\t\tout.println();\n\t\t\t\tout.print(indent);\n\t\t\t\tif (StringUtils.isNotBlank(lines[i])) {\n\t\t\t\t\tString extra = theme.blank();\n\t\t\t\t\tout.print(color(style, extra + lines[i]));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate String color(Style style, String text) {\n\t\treturn colorPalette.paint(style, text);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/TreePrintingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.Objects.requireNonNullElse;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.PrintWriter;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.14\")\npublic class TreePrintingListener implements DetailsPrintingListener {\n\n\tprivate final Map<UniqueId, TreeNode> nodesByUniqueId = new ConcurrentHashMap<>();\n\tprivate final TreePrinter treePrinter;\n\n\tprivate @Nullable TreeNode root;\n\n\tpublic TreePrintingListener(PrintWriter out, ColorPalette colorPalette, Theme theme) {\n\t\tthis.treePrinter = new TreePrinter(out, theme, colorPalette);\n\t}\n\n\tprivate void addNode(TestIdentifier testIdentifier, TreeNode node) {\n\t\tnodesByUniqueId.put(testIdentifier.getUniqueIdObject(), node);\n\t\tTreeNode parent = testIdentifier.getParentIdObject().map(nodesByUniqueId::get).orElse(null);\n\t\trequireNonNullElse(parent, root).addChild(node);\n\t}\n\n\tprivate TreeNode getNode(TestIdentifier testIdentifier) {\n\t\treturn requireNonNull(nodesByUniqueId.get(testIdentifier.getUniqueIdObject()));\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\troot = new TreeNode(testPlan.toString());\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\ttreePrinter.print(requireNonNull(root));\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\taddNode(testIdentifier, new TreeNode(testIdentifier));\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tgetNode(testIdentifier).setResult(testExecutionResult);\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\taddNode(testIdentifier, new TreeNode(testIdentifier, reason));\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tgetNode(testIdentifier).addReportEntry(entry);\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t\tgetNode(testIdentifier).addFileEntry(file);\n\t}\n\n\t@Override\n\tpublic void listTests(TestPlan testPlan) {\n\t\troot = new TreeNode(testPlan.toString());\n\t\ttestPlan.accept(new TestPlan.Visitor() {\n\t\t\t@Override\n\t\t\tpublic void visit(TestIdentifier testIdentifier) {\n\t\t\t\taddNode(testIdentifier, new TreeNode(testIdentifier));\n\t\t\t}\n\t\t});\n\t\ttreePrinter.print(root);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/VerboseTreePrintingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;\nimport static org.junit.platform.console.output.Style.NONE;\n\nimport java.io.PrintWriter;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.14\")\npublic class VerboseTreePrintingListener implements DetailsPrintingListener {\n\n\tprivate final PrintWriter out;\n\tprivate final Theme theme;\n\tprivate final ColorPalette colorPalette;\n\tprivate final Deque<Long> frames;\n\tprivate final String[] verticals;\n\tprivate long executionStartedMillis;\n\n\tpublic VerboseTreePrintingListener(PrintWriter out, ColorPalette colorPalette, int maxContainerNestingLevel,\n\t\t\tTheme theme) {\n\t\tthis.out = out;\n\t\tthis.colorPalette = colorPalette;\n\t\tthis.theme = theme;\n\n\t\t// create frame stack and push initial root frame\n\t\tthis.frames = new ArrayDeque<>();\n\t\tthis.frames.push(0L);\n\n\t\t// create and populate vertical indentation lookup table\n\t\tthis.verticals = new String[Math.max(10, maxContainerNestingLevel) + 1];\n\t\tthis.verticals[0] = \"\"; // no frame\n\t\tthis.verticals[1] = \"\"; // synthetic root \"/\" level\n\t\tthis.verticals[2] = \"\"; // \"engine\" level\n\n\t\tfor (int i = 3; i < verticals.length; i++) {\n\t\t\tverticals[i] = verticals[i - 1] + theme.vertical();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tframes.push(System.currentTimeMillis());\n\n\t\tString prefix = \"Test plan execution started. Number of static tests: \";\n\t\tprintNumberOfTests(testPlan, prefix);\n\t\tprintf(Style.CONTAINER, \"%s%n\", theme.root());\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tframes.pop();\n\n\t\tprintNumberOfTests(testPlan, \"Test plan execution finished. Number of all tests: \");\n\t}\n\n\tprivate void printNumberOfTests(TestPlan testPlan, String prefix) {\n\t\tlong tests = testPlan.countTestIdentifiers(TestIdentifier::isTest);\n\t\tprintf(NONE, \"%s\", prefix);\n\t\tprintf(Style.TEST, \"%d%n\", tests);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tthis.executionStartedMillis = System.currentTimeMillis();\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tprintVerticals(theme.entry());\n\t\t\tprintf(Style.CONTAINER, \" %s\", testIdentifier.getDisplayName());\n\t\t\tprintf(NONE, \"%n\");\n\t\t\tframes.push(System.currentTimeMillis());\n\t\t}\n\t\tif (testIdentifier.isContainer()) {\n\t\t\treturn;\n\t\t}\n\t\tprintVerticals(theme.entry());\n\t\tprintf(Style.valueOf(testIdentifier), \" %s%n\", testIdentifier.getDisplayName());\n\t\tprintDetails(testIdentifier);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\ttestExecutionResult.getThrowable().ifPresent(t -> printDetail(Style.FAILED, \"caught\", readStackTrace(t)));\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tLong creationMillis = frames.pop();\n\t\t\tprintVerticals(theme.end());\n\t\t\tprintf(Style.CONTAINER, \" %s\", testIdentifier.getDisplayName());\n\t\t\tprintf(NONE, \" finished after %d ms.%n\", System.currentTimeMillis() - creationMillis);\n\t\t\treturn;\n\t\t}\n\t\tprintDetail(NONE, \"duration\", \"%d ms%n\", System.currentTimeMillis() - executionStartedMillis);\n\t\tString status = theme.status(testExecutionResult) + \" \" + testExecutionResult.getStatus();\n\t\tprintDetail(Style.valueOf(testExecutionResult), \"status\", \"%s%n\", status);\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tprintVerticals(theme.entry());\n\t\tprintf(Style.valueOf(testIdentifier), \" %s%n\", testIdentifier.getDisplayName());\n\t\tprintDetails(testIdentifier);\n\t\tprintDetail(Style.SKIPPED, \"reason\", reason);\n\t\tprintDetail(Style.SKIPPED, \"status\", theme.skipped() + \" SKIPPED\");\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\tprintVerticals(theme.entry());\n\t\tprintf(Style.DYNAMIC, \" %s\", testIdentifier.getDisplayName());\n\t\tprintf(NONE, \"%s%n\", \" dynamically registered\");\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tprintDetail(Style.REPORTED, \"reports\", entry.toString());\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t\tprintDetail(Style.REPORTED, \"reports\", file.toString());\n\t}\n\n\t/**\n\t * Print static information about the test identifier.\n\t */\n\tprivate void printDetails(TestIdentifier testIdentifier) {\n\t\tprintDetail(NONE, \"tags\", \"%s%n\", testIdentifier.getTags());\n\t\tprintDetail(NONE, \"uniqueId\", \"%s%n\", testIdentifier.getUniqueId());\n\t\tprintDetail(NONE, \"parent\", \"%s%n\", testIdentifier.getParentId().orElse(\"[]\"));\n\t\ttestIdentifier.getSource().ifPresent(source -> printDetail(NONE, \"source\", \"%s%n\", source));\n\t}\n\n\tprivate String verticals() {\n\t\treturn verticals(frames.size());\n\t}\n\n\tprivate String verticals(int index) {\n\t\treturn verticals[Math.min(index, verticals.length - 1)];\n\t}\n\n\tprivate void printVerticals(String tile) {\n\t\tprintf(NONE, verticals());\n\t\tprintf(NONE, tile);\n\t}\n\n\tprivate void printf(Style style, String message, Object... args) {\n\t\tout.printf(colorPalette.paint(style, message), args);\n\t\tout.flush();\n\t}\n\n\t/**\n\t * Print single detail with a potential multi-line message.\n\t */\n\tprivate void printDetail(Style style, String detail, String format, Object... args) {\n\t\t// print initial verticals - expecting to be at start of the line\n\t\tString verticals = verticals(frames.size() + 1);\n\t\tprintf(NONE, verticals);\n\t\tString detailFormat = \"%9s\";\n\t\t// omit detail string if it's empty\n\t\tif (!detail.isEmpty()) {\n\t\t\tprintf(NONE, \"%s\", (detailFormat + \": \").formatted(detail));\n\t\t}\n\t\t// trivial case: at least one arg is given? Let printf do the entire work\n\t\tif (args.length > 0) {\n\t\t\tprintf(style, format, args);\n\t\t\treturn;\n\t\t}\n\t\t// still here? Split format into separate lines and indent them from the second line on\n\t\tString[] lines = format.split(\"\\\\R\");\n\t\tprintf(style, \"%s\", lines[0]);\n\t\tif (lines.length > 1) {\n\t\t\tString delimiter = System.lineSeparator() + verticals + (detailFormat + \"    \").formatted(\"\");\n\t\t\tfor (int i = 1; i < lines.length; i++) {\n\t\t\t\tprintf(NONE, \"%s\", delimiter);\n\t\t\t\tprintf(style, \"%s\", lines[i]);\n\t\t\t}\n\t\t}\n\t\tprintf(NONE, \"%n\");\n\t}\n\n\t@Override\n\tpublic void listTests(TestPlan testPlan) {\n\t\tframes.push(0L);\n\t\ttestPlan.accept(new TestPlan.Visitor() {\n\t\t\t@Override\n\t\t\tpublic void preVisitContainer(TestIdentifier testIdentifier) {\n\t\t\t\tif (!testPlan.getChildren(testIdentifier).isEmpty()) {\n\t\t\t\t\tprintVerticals(theme.entry());\n\t\t\t\t\tprintf(Style.CONTAINER, \" %s\", testIdentifier.getDisplayName());\n\t\t\t\t\tprintf(NONE, \"%n\");\n\t\t\t\t\tframes.push(0L);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void visit(TestIdentifier testIdentifier) {\n\t\t\t\tif (testPlan.getChildren(testIdentifier).isEmpty()) {\n\t\t\t\t\tprintVerticals(theme.entry());\n\t\t\t\t\tprintf(Style.valueOf(testIdentifier), \" %s%n\", testIdentifier.getDisplayName());\n\t\t\t\t\tprintDetails(testIdentifier);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void postVisitContainer(TestIdentifier testIdentifier) {\n\t\t\t\tif (!testPlan.getChildren(testIdentifier).isEmpty()) {\n\t\t\t\t\tframes.pop();\n\t\t\t\t\tprintVerticals(theme.end());\n\t\t\t\t\tprintf(Style.CONTAINER, \" %s%n\", testIdentifier.getDisplayName());\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tframes.pop();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/output/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Output printing utility classes of JUnit's console launcher.\n */\n\n@NullMarked\npackage org.junit.platform.console.output;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-console/src/main/java/org/junit/platform/console/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for launching the JUnit Platform from the console.\n */\n\n@NullMarked\npackage org.junit.platform.console;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-console/src/main/resources/META-INF/services/java.util.spi.ToolProvider",
    "content": "org.junit.platform.console.ConsoleLauncherToolProvider\n"
  },
  {
    "path": "junit-platform-console/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-console-standalone/junit-platform-console-standalone.gradle.kts",
    "content": "import junitbuild.extensions.withArchiveOperations\nimport junitbuild.java.WriteArtifactsFile\n\nplugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"junitbuild.shadow-conventions\")\n}\n\ndescription = \"JUnit Platform Console Standalone\"\n\ndependencies {\n\tshadowed(projects.junitPlatformReporting)\n\tshadowed(projects.junitPlatformConsole)\n\tshadowed(projects.junitPlatformSuiteEngine)\n\tshadowed(projects.junitJupiterEngine)\n\tshadowed(projects.junitJupiterParams)\n\tshadowed(projects.junitVintageEngine)\n\tshadowed(libs.apiguardian) {\n\t\tbecause(\"downstream projects need it to avoid compiler warnings\")\n\t}\n\n\tosgiVerification(libs.openTestReporting.tooling.spi)\n}\n\nbackwardCompatibilityChecks {\n\tenabled = false // already checked by individual projects\n}\n\ntasks {\n\tjar {\n\t\tmanifest {\n\t\t\tattributes(\"Automatic-Module-Name\" to \"org.junit.platform.console.standalone\")\n\t\t\tattributes(\"Main-Class\" to \"org.junit.platform.console.ConsoleLauncher\")\n\t\t}\n\t}\n\tval shadowedArtifactsFile by registering(WriteArtifactsFile::class) {\n\t\tfrom(configurations.shadowedClasspath)\n\t\toutputFile = layout.buildDirectory.file(\"shadowed-artifacts\")\n\t}\n\tval extractThirdPartyLicenses by registering(Sync::class) {\n\t\tfrom(withArchiveOperations { ops -> configurations.shadowedClasspath.flatMap { it.elements }.map { it.map(ops::zipTree) } })\n\t\tinto(layout.buildDirectory.dir(\"thirdPartyLicenses\"))\n\t\tinclude(\"LICENSE.txt\")\n\t\tinclude(\"LICENSE-junit.txt\")\n\t\tinclude(\"META-INF/LICENSE-*\")\n\t\texclude(\"META-INF/LICENSE-notice.md\")\n\t\teachFile {\n\t\t\tval fileName = relativePath.lastName\n\t\t\trelativePath = RelativePath(true, when (fileName) {\n\t\t\t\t\"LICENSE.txt\" -> \"LICENSE-hamcrest\"\n\t\t\t\t\"LICENSE-junit.txt\" -> \"LICENSE-junit4\"\n\t\t\t\telse -> fileName\n\t\t\t})\n\t\t}\n\t\tincludeEmptyDirs = false\n\t}\n\tshadowJar {\n\t\t// https://github.com/junit-team/junit-framework/issues/2557\n\t\t// exclude compiled module declarations from any source (e.g. /*, /META-INF/versions/N/*)\n\t\texclude(\"**/module-info.class\")\n\t\t// https://github.com/junit-team/junit-framework/issues/761\n\t\t// prevent duplicates, add 3rd-party licenses explicitly\n\t\texclude(\"**/COPYRIGHT*\")\n\t\texclude(\"META-INF/LICENSE*\")\n\t\texclude(\"LICENSE*.txt\") // JUnit 4 and Hamcrest\n\t\tfrom(extractThirdPartyLicenses) {\n\t\t\tinto(\"META-INF\")\n\t\t}\n\t\tfrom(shadowedArtifactsFile) {\n\t\t\tinto(\"META-INF\")\n\t\t}\n\n\t\tbundle {\n\t\t\tval importAPIGuardian: String by extra\n\t\t\tval importJSpecify: String by extra\n\t\t\tbnd(\"\"\"\n\t\t\t\t# Customize the imports because this is an aggregate jar\n\t\t\t\tImport-Package: \\\n\t\t\t\t\t$importAPIGuardian,\\\n\t\t\t\t\t$importJSpecify,\\\n\t\t\t\t\tkotlin.*;resolution:=\"optional\",\\\n\t\t\t\t\tkotlinx.*;resolution:=\"optional\",\\\n\t\t\t\t\t*\n\t\t\t\t# Disable the APIGuardian plugin since everything was already\n\t\t\t\t# processed, again because this is an aggregate jar\n\t\t\t\t-export-apiguardian:\n\t\t\t\"\"\")\n\t\t}\n\n\t\tduplicatesStrategy = DuplicatesStrategy.INCLUDE\n\t\tmergeServiceFiles()\n\t\tfailOnDuplicateEntries = true\n\n\t\tmanifest.apply {\n\t\t\tattributes(mapOf(\n\t\t\t\t\t\"Specification-Title\" to project.name,\n\t\t\t\t\t\"Implementation-Title\" to project.name,\n\t\t\t\t\t// Generate test engine version information in single shared manifest file.\n\t\t\t\t\t// Pattern of key and value: `\"Engine-Version-{YourTestEngine#getId()}\": \"47.11\"`\n\t\t\t\t\t\"Engine-Version-junit-jupiter\" to project.version,\n\t\t\t\t\t\"Engine-Version-junit-vintage\" to project.version,\n\t\t\t))\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/junit-platform-engine.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Platform Engine API\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(libs.opentest4j)\n\tapi(projects.junitPlatformCommons)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\ttestImplementation(libs.assertj)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\njavadocConventions {\n\taddExtraModuleReferences(projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Public API for test engines.\n *\n * <p>Provides the {@link org.junit.platform.engine.TestEngine} interface,\n * test discovery, and execution reporting support.\n *\n * @since 1.0\n */\nmodule org.junit.platform.engine {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.junit.platform.commons;\n\trequires transitive org.opentest4j;\n\n\texports org.junit.platform.engine;\n\texports org.junit.platform.engine.discovery;\n\texports org.junit.platform.engine.reporting;\n\t// exports org.junit.platform.engine.support; empty package\n\texports org.junit.platform.engine.support.config;\n\texports org.junit.platform.engine.support.descriptor;\n\texports org.junit.platform.engine.support.discovery;\n\texports org.junit.platform.engine.support.hierarchical;\n\texports org.junit.platform.engine.support.store;\n\n\tuses org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser;\n\n\tprovides org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser with\n\t\t\torg.junit.platform.engine.discovery.ClassSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.ClasspathResourceSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.ClasspathRootSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.DirectorySelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.FileSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.IterationSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.MethodSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.ModuleSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.NestedClassSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.NestedMethodSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.PackageSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.UniqueIdSelector.IdentifierParser,\n\t\t\torg.junit.platform.engine.discovery.UriSelector.IdentifierParser;\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/CancellationToken.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport org.apiguardian.api.API;\n\n/**\n * Token that should be checked to determine whether an operation was requested\n * to be cancelled.\n *\n * <p>For example, this is used by the\n * {@link org.junit.platform.launcher.Launcher} and\n * {@link org.junit.platform.engine.TestEngine} implementations to determine\n * whether the current test execution should be cancelled.\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 6.0\n * @see org.junit.platform.launcher.core.LauncherExecutionRequestBuilder#cancellationToken(CancellationToken)\n * @see org.junit.platform.launcher.LauncherExecutionRequest#getCancellationToken()\n * @see ExecutionRequest#getCancellationToken()\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic sealed interface CancellationToken permits RegularCancellationToken, DisabledCancellationToken {\n\n\t/**\n\t * Create a new, uncancelled cancellation token.\n\t */\n\tstatic CancellationToken create() {\n\t\treturn new RegularCancellationToken();\n\t}\n\n\t/**\n\t * Get a new cancellation token that cannot be cancelled.\n\t *\n\t * <p>This is only useful for cases when a cancellation token is required\n\t * but is not supported or irrelevant, for example, in tests.\n\t */\n\tstatic CancellationToken disabled() {\n\t\treturn DisabledCancellationToken.INSTANCE;\n\t}\n\n\t/**\n\t * {@return whether cancellation has been requested}\n\t *\n\t * <p>Once this method returns {@code true}, it will never return\n\t * {@code false} in a subsequent call.\n\t */\n\tboolean isCancellationRequested();\n\n\t/**\n\t * Request cancellation.\n\t *\n\t * <p>This will call subsequent calls to {@link #isCancellationRequested()}\n\t * to return {@code true}.\n\t */\n\tvoid cancel();\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Combines a collection of {@link Filter Filters} into a new filter that will\n * include elements if and only if all of the filters in the specified collection\n * include it.\n *\n * @since 1.0\n */\nclass CompositeFilter<T> implements Filter<T> {\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate static final Filter ALWAYS_INCLUDED_FILTER = new Filter() {\n\t\t@Override\n\t\tpublic FilterResult apply(Object obj) {\n\t\t\treturn ALWAYS_INCLUDED_RESULT;\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate toPredicate() {\n\t\t\treturn obj -> true;\n\t\t}\n\t};\n\n\tprivate static final FilterResult ALWAYS_INCLUDED_RESULT = included(\"Always included\");\n\tprivate static final FilterResult INCLUDED_BY_ALL_FILTERS = included(\"Element was included by all filters.\");\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic <T> Filter<T> alwaysIncluded() {\n\t\treturn ALWAYS_INCLUDED_FILTER;\n\t}\n\n\tprivate final Collection<Filter<T>> filters;\n\n\tCompositeFilter(Collection<? extends Filter<T>> filters) {\n\t\tthis.filters = new ArrayList<>(Preconditions.notEmpty(filters, \"filters must not be empty\"));\n\t}\n\n\t@Override\n\tpublic FilterResult apply(T element) {\n\t\t// @formatter:off\n\t\treturn filters.stream()\n\t\t\t\t.map(filter -> filter.apply(element))\n\t\t\t\t.filter(FilterResult::excluded)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElse(INCLUDED_BY_ALL_FILTERS);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Predicate<T> toPredicate() {\n\t\t// @formatter:off\n\t\treturn filters.stream()\n\t\t\t\t.map(Filter::toPredicate)\n\t\t\t\t.reduce(Predicate::and)\n\t\t\t\t.get(); // it's safe to call get() here because the constructor ensures filters is not empty\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn filters.stream()\n\t\t\t\t.map(Object::toString)\n\t\t\t\t.map(\"(%s)\"::formatted)\n\t\t\t\t.collect(joining(\" and \"));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/CompositeTestDescriptorVisitor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport java.util.Arrays;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 1.13\n */\nfinal class CompositeTestDescriptorVisitor implements TestDescriptor.Visitor {\n\n\tprivate final TestDescriptor.Visitor[] visitors;\n\n\tstatic TestDescriptor.Visitor from(TestDescriptor.Visitor... visitors) {\n\t\tPreconditions.notNull(visitors, \"visitors must not be null\");\n\t\tPreconditions.notEmpty(visitors, \"visitors must not be empty\");\n\t\tPreconditions.containsNoNullElements(visitors, \"visitors must not contain any null elements\");\n\t\treturn visitors.length == 1 ? visitors[0] : new CompositeTestDescriptorVisitor(visitors);\n\t}\n\n\tprivate CompositeTestDescriptorVisitor(TestDescriptor.Visitor[] visitors) {\n\t\tthis.visitors = Arrays.copyOf(visitors, visitors.length);\n\t}\n\n\t@Override\n\tpublic void visit(TestDescriptor descriptor) {\n\t\tfor (TestDescriptor.Visitor visitor : visitors) {\n\t\t\tvisitor.visit(descriptor);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/ConfigurationParameters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Configuration parameters that {@link TestEngine TestEngines} may use to\n * influence test discovery and execution.\n *\n * <p>For example, the JUnit Jupiter engine uses a configuration parameter to\n * enable IDEs and build tools to deactivate conditional test execution.\n *\n * <p>Configuration parameters are also made available to implementations of the\n * {@link org.junit.platform.launcher.TestExecutionListener} API via the\n * {@link org.junit.platform.launcher.TestPlan}.\n *\n * @since 1.0\n * @see TestEngine\n * @see EngineDiscoveryRequest\n * @see ExecutionRequest\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface ConfigurationParameters {\n\n\t/**\n\t * Name of the JUnit Platform configuration file: {@value}.\n\t *\n\t * <p>If a properties file with this name is present in the root of the\n\t * classpath, it will be used as a source for <em>configuration\n\t * parameters</em>. If multiple files are present, only the first one\n\t * detected in the classpath will be used.\n\t *\n\t * @see java.util.Properties\n\t */\n\tString CONFIG_FILE_NAME = \"junit-platform.properties\";\n\n\t/**\n\t * Get the configuration parameter stored under the specified {@code key}.\n\t *\n\t * <p>If no such key is present in this {@code ConfigurationParameters},\n\t * an attempt will be made to look up the value as a JVM system property.\n\t * If no such system property exists, an attempt will be made to look up\n\t * the value in the {@linkplain #CONFIG_FILE_NAME JUnit Platform properties\n\t * file}.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @see #getBoolean(String)\n\t * @see System#getProperty(String)\n\t * @see #CONFIG_FILE_NAME\n\t */\n\tOptional<String> get(String key);\n\n\t/**\n\t * Get the boolean configuration parameter stored under the specified\n\t * {@code key}.\n\t *\n\t * <p>If no such key is present in this {@code ConfigurationParameters},\n\t * an attempt will be made to look up the value as a JVM system property.\n\t * If no such system property exists, an attempt will be made to look up\n\t * the value in the {@linkplain #CONFIG_FILE_NAME JUnit Platform properties\n\t * file}.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @see #get(String)\n\t * @see Boolean#parseBoolean(String)\n\t * @see System#getProperty(String)\n\t * @see #CONFIG_FILE_NAME\n\t */\n\tOptional<Boolean> getBoolean(String key);\n\n\t/**\n\t * Get and transform the configuration parameter stored under the specified\n\t * {@code key} using the specified {@code transformer}.\n\t *\n\t * <p>If no such key is present in this {@code ConfigurationParameters},\n\t * an attempt will be made to look up the value as a JVM system property.\n\t * If no such system property exists, an attempt will be made to look up\n\t * the value in the {@linkplain #CONFIG_FILE_NAME JUnit Platform properties\n\t * file}.\n\t *\n\t * <p>In case the transformer throws an exception, it will be wrapped in a\n\t * {@link JUnitException} with a helpful message.\n\t *\n\t * @param key the key to look up; never {@code null} or blank\n\t * @param transformer the transformer to apply in case a value is found;\n\t * never {@code null}\n\t * @return an {@code Optional} containing the value; never {@code null}\n\t * but potentially empty\n\t *\n\t * @since 1.3\n\t * @see #getBoolean(String)\n\t * @see System#getProperty(String)\n\t * @see #CONFIG_FILE_NAME\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\tdefault <T> Optional<T> get(String key, Function<? super String, ? extends @Nullable T> transformer) {\n\t\tPreconditions.notNull(transformer, \"transformer must not be null\");\n\t\treturn get(key).map(input -> {\n\t\t\ttry {\n\t\t\t\treturn transformer.apply(input);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tString message = \"Failed to transform configuration parameter with key '%s' and initial value '%s'\".formatted(\n\t\t\t\t\tkey, input);\n\t\t\t\tthrow new JUnitException(message, ex);\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Get the keys of all configuration parameters stored in this\n\t * {@code ConfigurationParameters}.\n\t *\n\t * @return the set of keys contained in this {@code ConfigurationParameters}\n\t */\n\t@API(status = STABLE, since = \"1.9\")\n\tSet<String> keySet();\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DefaultDiscoveryIssue.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * @since 1.13\n */\nfinal class DefaultDiscoveryIssue implements DiscoveryIssue {\n\n\tprivate final Severity severity;\n\tprivate final String message;\n\n\tprivate final @Nullable TestSource source;\n\n\tprivate final @Nullable Throwable cause;\n\n\tDefaultDiscoveryIssue(Builder builder) {\n\t\tthis.severity = builder.severity;\n\t\tthis.message = builder.message;\n\t\tthis.source = builder.source;\n\t\tthis.cause = builder.cause;\n\t}\n\n\t@Override\n\tpublic Severity severity() {\n\t\treturn this.severity;\n\t}\n\n\t@Override\n\tpublic String message() {\n\t\treturn this.message;\n\t}\n\n\t@Override\n\tpublic Optional<TestSource> source() {\n\t\treturn Optional.ofNullable(this.source);\n\t}\n\n\t@Override\n\tpublic Optional<Throwable> cause() {\n\t\treturn Optional.ofNullable(this.cause);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tDefaultDiscoveryIssue that = (DefaultDiscoveryIssue) o;\n\t\treturn this.severity == that.severity //\n\t\t\t\t&& Objects.equals(this.message, that.message) //\n\t\t\t\t&& Objects.equals(this.source, that.source) //\n\t\t\t\t&& Objects.equals(this.cause, that.cause);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.severity, this.message, this.source, this.cause);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tToStringBuilder builder = new ToStringBuilder(DiscoveryIssue.class.getSimpleName()) //\n\t\t\t\t.append(\"severity\", this.severity) //\n\t\t\t\t.append(\"message\", this.message);\n\t\tif (this.source != null) {\n\t\t\tbuilder.append(\"source\", this.source);\n\t\t}\n\t\tif (this.cause != null) {\n\t\t\tbuilder.append(\"cause\", this.cause);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tstatic class Builder implements DiscoveryIssue.Builder {\n\n\t\tprivate final Severity severity;\n\t\tprivate final String message;\n\n\t\t@Nullable\n\t\tprivate TestSource source;\n\n\t\t@Nullable\n\t\tpublic Throwable cause;\n\n\t\tBuilder(Severity severity, String message) {\n\t\t\tthis.severity = severity;\n\t\t\tthis.message = message;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder source(@Nullable TestSource source) {\n\t\t\tthis.source = source;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic Builder cause(@Nullable Throwable cause) {\n\t\t\tthis.cause = cause;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic DiscoveryIssue build() {\n\t\t\treturn new DefaultDiscoveryIssue(this);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DisabledCancellationToken.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\n/**\n * @since 6.0\n */\nfinal class DisabledCancellationToken implements CancellationToken {\n\n\tstatic final DisabledCancellationToken INSTANCE = new DisabledCancellationToken();\n\n\tprivate DisabledCancellationToken() {\n\t}\n\n\t@Override\n\tpublic boolean isCancellationRequested() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * A {@link DiscoveryFilter} is applied during test discovery to determine if\n * a given container or test should be included in the test plan.\n *\n * <p>{@link TestEngine TestEngines} should apply {@code DiscoveryFilters}\n * during the test discovery phase.\n *\n * @since 1.0\n * @see EngineDiscoveryRequest\n * @see TestEngine\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface DiscoveryFilter<T> extends Filter<T> {\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoveryIssue.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.Optional;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * {@code DiscoveryIssue} represents an issue that was encountered during test\n * discovery by a {@link TestEngine}.\n *\n * @since 1.13\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface DiscoveryIssue {\n\n\t/**\n\t * Create a new {@code DiscoveryIssue} with the supplied {@link Severity} and\n\t * message.\n\t *\n\t * @param severity the severity of the issue; never {@code null}\n\t * @param message the message of the issue; never blank\n\t * @see #builder(Severity, String)\n\t */\n\tstatic DiscoveryIssue create(Severity severity, String message) {\n\t\treturn builder(severity, message).build();\n\t}\n\n\t/**\n\t * Create a new {@link Builder} for creating a {@code DiscoveryIssue} with\n\t * the supplied {@link Severity} and message.\n\t *\n\t * @param severity the severity of the issue; never {@code null}\n\t * @param message the message of the issue; never blank\n\t * @see Builder\n\t * @see #create(Severity, String)\n\t */\n\tstatic Builder builder(Severity severity, String message) {\n\t\tPreconditions.notNull(severity, \"severity must not be null\");\n\t\tPreconditions.notBlank(message, \"message must not be blank\");\n\t\treturn new DefaultDiscoveryIssue.Builder(severity, message);\n\t}\n\n\t/**\n\t * {@return the severity of this issue}\n\t */\n\tSeverity severity();\n\n\t/**\n\t * {@return the message of this issue}\n\t */\n\tString message();\n\n\t/**\n\t * {@return the source of this issue}\n\t */\n\tOptional<TestSource> source();\n\n\t/**\n\t * {@return the cause of this issue}\n\t */\n\tOptional<Throwable> cause();\n\n\t/**\n\t * Create a copy of this issue with the modified message produced by the\n\t * supplied operator.\n\t */\n\tdefault DiscoveryIssue withMessage(UnaryOperator<String> messageModifier) {\n\t\tString oldMessage = message();\n\t\tString newMessage = messageModifier.apply(oldMessage);\n\t\tif (oldMessage.equals(newMessage)) {\n\t\t\treturn this;\n\t\t}\n\t\treturn DiscoveryIssue.builder(severity(), newMessage) //\n\t\t\t\t.source(source()) //\n\t\t\t\t.cause(cause()) //\n\t\t\t\t.build();\n\t}\n\n\t/**\n\t * The severity of a {@code DiscoveryIssue}.\n\t */\n\tenum Severity {\n\n\t\t/**\n\t\t * Indicates that the engine encountered something that could be\n\t\t * potentially problematic, but could also happen due to a valid setup\n\t\t * or configuration.\n\t\t */\n\t\tINFO,\n\n\t\t/**\n\t\t * Indicates that the engine encountered something that is problematic\n\t\t * and might lead to unexpected behavior or will be removed or changed\n\t\t * in a future release.\n\t\t */\n\t\tWARNING,\n\n\t\t/**\n\t\t * Indicates that the engine encountered something that is definitely\n\t\t * problematic and will lead to unexpected behavior.\n\t\t */\n\t\tERROR\n\t}\n\n\t/**\n\t * Builder for creating a {@code DiscoveryIssue}.\n\t */\n\tinterface Builder {\n\n\t\t/**\n\t\t * Set the {@link TestSource} for the {@code DiscoveryIssue}.\n\t\t *\n\t\t * @param source the {@link TestSource} for the {@code DiscoveryIssue};\n\t\t * never {@code null} but potentially empty\n\t\t */\n\t\tdefault Builder source(Optional<TestSource> source) {\n\t\t\tsource.ifPresent(this::source);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link TestSource} for the {@code DiscoveryIssue}.\n\t\t *\n\t\t * @param source the {@link TestSource} for the {@code DiscoveryIssue};\n\t\t * may be {@code null}\n\t\t */\n\t\tBuilder source(@Nullable TestSource source);\n\n\t\t/**\n\t\t * Set the {@link Throwable} that caused the {@code DiscoveryIssue}.\n\t\t *\n\t\t * @param cause the {@link Throwable} that caused the\n\t\t * {@code DiscoveryIssue}; never {@code null} but potentially empty\n\t\t */\n\t\tdefault Builder cause(Optional<Throwable> cause) {\n\t\t\tcause.ifPresent(this::cause);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link Throwable} that caused the {@code DiscoveryIssue}.\n\t\t *\n\t\t * @param cause the {@link Throwable} that caused the\n\t\t * {@code DiscoveryIssue}; may be {@code null}\n\t\t */\n\t\tBuilder cause(@Nullable Throwable cause);\n\n\t\t/**\n\t\t * Build the {@code DiscoveryIssue}.\n\t\t */\n\t\tDiscoveryIssue build();\n\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser;\n\n/**\n * A selector defines what a {@link TestEngine} can use to discover tests\n * &mdash; for example, the name of a Java class, the path to a file or\n * directory, etc.\n *\n * @since 1.0\n * @see EngineDiscoveryRequest\n * @see org.junit.platform.engine.discovery.DiscoverySelectors\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface DiscoverySelector {\n\n\t/**\n\t * Return the {@linkplain DiscoverySelectorIdentifier identifier} of this\n\t * selector.\n\t *\n\t * <p>The returned identifier must be parsable by a corresponding\n\t * {@link DiscoverySelectorIdentifierParser}.\n\t *\n\t * <p>The default implementation returns {@link Optional#empty()}. Can be\n\t * overridden by concrete implementations.\n\t *\n\t * @return an {@link Optional} containing the identifier of this selector;\n\t * never {@code null} but potentially empty if the selector does not support\n\t * identifiers\n\t * @since 1.11\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tdefault Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.empty();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/DiscoverySelectorIdentifier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Objects;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * Identifier for a {@link DiscoverySelector} with a specific prefix.\n *\n * <p>The {@linkplain #toString() string representation} of an identifier is\n * intended to be human-readable and is formatted as {@code prefix:value}.\n *\n * @since 1.11\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#parse(String)\n * @see org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic final class DiscoverySelectorIdentifier {\n\n\tprivate final String prefix;\n\tprivate final String value;\n\n\t/**\n\t * Create a new {@code DiscoverySelectorIdentifier} with the supplied prefix and\n\t * value.\n\t *\n\t * @param prefix the prefix; never {@code null} or blank\n\t * @param value the value; never {@code null} or blank\n\t */\n\tpublic static DiscoverySelectorIdentifier create(String prefix, String value) {\n\t\treturn new DiscoverySelectorIdentifier(prefix, value);\n\t}\n\n\t/**\n\t * Parse the supplied string representation of a\n\t * {@link DiscoverySelectorIdentifier} in the format {@code prefix:value}.\n\t *\n\t * @param string the string representation of a {@code DiscoverySelectorIdentifier}\n\t * @return the parsed {@code DiscoverySelectorIdentifier}\n\t * @throws PreconditionViolationException if the supplied string does not\n\t * conform to the expected format\n\t */\n\tpublic static DiscoverySelectorIdentifier parse(String string) {\n\t\treturn StringUtils.splitIntoTwo(':', string).mapTwo( //\n\t\t\t() -> new PreconditionViolationException(\"Identifier string must be 'prefix:value', but was \" + string),\n\t\t\tDiscoverySelectorIdentifier::new //\n\t\t);\n\t}\n\n\tprivate DiscoverySelectorIdentifier(String prefix, String value) {\n\t\tthis.prefix = Preconditions.notBlank(prefix, \"prefix must not be blank\");\n\t\tthis.value = Preconditions.notBlank(value, \"value must not be blank\");\n\t}\n\n\t/**\n\t * Get the prefix of this identifier.\n\t *\n\t * @return the prefix; never {@code null} or blank\n\t */\n\tpublic String getPrefix() {\n\t\treturn this.prefix;\n\t}\n\n\t/**\n\t * Get the value of this identifier.\n\t *\n\t * @return the value; never {@code null} or blank\n\t */\n\tpublic String getValue() {\n\t\treturn this.value;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tDiscoverySelectorIdentifier that = (DiscoverySelectorIdentifier) o;\n\t\treturn Objects.equals(this.prefix, that.prefix) && Objects.equals(this.value, that.value);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.prefix, this.value);\n\t}\n\n\t/**\n\t * Get the string representation of this identifier in the format\n\t * {@code prefix:value}.\n\t */\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s:%s\".formatted(this.prefix, this.value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code EngineDiscoveryListener} defines the API which enables a {@link TestEngine}\n * to publish information about events that occur during test discovery.\n *\n * <p>All methods in this interface have empty <em>default</em> implementations.\n * Concrete implementations may therefore override one or more of these methods\n * to be notified of the selected events.\n *\n * @since 1.6\n * @see EngineDiscoveryRequest#getDiscoveryListener()\n * @see org.junit.platform.launcher.LauncherDiscoveryListener\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface EngineDiscoveryListener {\n\n\t/**\n\t * No-op implementation of {@code EngineDiscoveryListener}\n\t */\n\tEngineDiscoveryListener NOOP = new EngineDiscoveryListener() {\n\t};\n\n\t/**\n\t * Must be called after a discovery selector has been processed by a test\n\t * engine.\n\t *\n\t * <p>Exceptions thrown by implementations of this method will cause test\n\t * discovery of the current engine to be aborted.\n\t *\n\t * @param engineId the unique ID of the engine descriptor\n\t * @param selector the processed selector\n\t * @param result the resolution result of the supplied engine and selector\n\t * @see SelectorResolutionResult\n\t */\n\tdefault void selectorProcessed(UniqueId engineId, DiscoverySelector selector, SelectorResolutionResult result) {\n\t}\n\n\t/**\n\t * Called when the engine with the supplied {@code engineId} encountered an\n\t * issue during test discovery.\n\t *\n\t * @param engineId the unique ID of the engine descriptor\n\t * @param issue the encountered issue\n\t * @since 1.13\n\t * @see DiscoveryIssue\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tdefault void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/EngineDiscoveryRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code EngineDiscoveryRequest} provides a {@link TestEngine} access to the\n * information necessary to discover tests and containers.\n *\n * <p>A request is comprised of {@linkplain DiscoverySelector selectors} and\n * {@linkplain DiscoveryFilter filters}. While the former <em>select</em>\n * resources that engines can use to discover tests, the latter specify how\n * such resources are to be <em>filtered</em>. All of the <em>filters</em>\n * have to include a resource for it to end up in the test plan.\n *\n * <p>In addition, the supplied {@linkplain ConfigurationParameters\n * configuration parameters} can be used to influence the discovery process.\n *\n * @since 1.0\n * @see TestEngine\n * @see TestDescriptor\n * @see DiscoverySelector\n * @see DiscoveryFilter\n * @see ConfigurationParameters\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface EngineDiscoveryRequest {\n\n\t/**\n\t * Get the {@link DiscoverySelector DiscoverySelectors} for this request,\n\t * filtered by a particular type.\n\t *\n\t * @param selectorType the type of {@link DiscoverySelector} to filter by;\n\t * never {@code null}\n\t * @return all selectors of this request that are instances of\n\t * {@code selectorType}; never {@code null} but potentially empty\n\t */\n\t<T extends DiscoverySelector> List<T> getSelectorsByType(Class<T> selectorType);\n\n\t/**\n\t * Get the {@link DiscoveryFilter DiscoveryFilters} for this request,\n\t * filtered by a particular type.\n\t *\n\t * <p>The returned filters are to be combined using AND semantics, i.e. all\n\t * of them have to include a resource for it to end up in the test plan.\n\t *\n\t * @param filterType the type of {@link DiscoveryFilter} to filter by;\n\t * never {@code null}\n\t * @return all filters of this request that are instances of\n\t * {@code filterType}; never {@code null} but potentially empty\n\t */\n\t<T extends DiscoveryFilter<?>> List<T> getFiltersByType(Class<T> filterType);\n\n\t/**\n\t * Get the {@link ConfigurationParameters} for this request.\n\t *\n\t * @return the configuration parameters; never {@code null}\n\t */\n\tConfigurationParameters getConfigurationParameters();\n\n\t/**\n\t * Get the {@link EngineDiscoveryListener} for this request.\n\t *\n\t * @return the discovery listener; never {@code null}\n\t * @since 1.6\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tdefault EngineDiscoveryListener getDiscoveryListener() {\n\t\treturn EngineDiscoveryListener.NOOP;\n\t}\n\n\t/**\n\t * Get the\n\t * {@link org.junit.platform.engine.reporting.OutputDirectoryProvider} for\n\t * this request.\n\t *\n\t * @return the output directory provider; never {@code null}\n\t * @since 1.12\n\t * @deprecated Please use {@link #getOutputDirectoryCreator()} instead\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"1.14\")\n\tdefault org.junit.platform.engine.reporting.OutputDirectoryProvider getOutputDirectoryProvider() {\n\t\treturn org.junit.platform.engine.reporting.OutputDirectoryProvider.castOrAdapt(getOutputDirectoryCreator());\n\t}\n\n\t/**\n\t * Get the {@link OutputDirectoryCreator} for this request.\n\t *\n\t * @return the output directory creator; never {@code null}\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tdefault OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\tthrow new JUnitException(\n\t\t\t\"OutputDirectoryCreator not available; probably due to unaligned versions of the junit-platform-engine and junit-platform-launcher jars on the classpath/module path.\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/EngineExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * Listener to be notified of test execution events by\n * {@linkplain TestEngine test engines}.\n *\n * <p>Contrary to JUnit 4, {@linkplain TestEngine test engines}\n * must report events not only for {@linkplain TestDescriptor test descriptors}\n * that represent executable leaves but also for all intermediate containers.\n *\n * @since 1.0\n * @see TestEngine\n * @see ExecutionRequest\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface EngineExecutionListener {\n\n\t/**\n\t * No-op implementation of {@code EngineExecutionListener}\n\t */\n\tEngineExecutionListener NOOP = new EngineExecutionListener() {\n\t};\n\n\t/**\n\t * Must be called when a new, dynamic {@link TestDescriptor} has been\n\t * registered.\n\t *\n\t * <p>A <em>dynamic test</em> is a test that is not known a-priori and\n\t * therefore was not present in the test tree when discovering tests.\n\t *\n\t * @param testDescriptor the descriptor of the newly registered test\n\t * or container\n\t */\n\tdefault void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t}\n\n\t/**\n\t * Must be called when the execution of a leaf or subtree of the test tree\n\t * has been skipped.\n\t *\n\t * <p>The {@link TestDescriptor} may represent a test or a container. In the\n\t * case of a container, an engine must not fire any additional events for its\n\t * descendants.\n\t *\n\t * <p>A skipped test or subtree of tests must not be reported as\n\t * {@linkplain #executionStarted started} or\n\t * {@linkplain #executionFinished finished}.\n\t *\n\t * @param testDescriptor the descriptor of the skipped test or container\n\t * @param reason a human-readable message describing why the execution\n\t * has been skipped\n\t */\n\tdefault void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t}\n\n\t/**\n\t * Must be called when the execution of a leaf or subtree of the test tree\n\t * is about to be started.\n\t *\n\t * <p>The {@link TestDescriptor} may represent a test or a container. In the\n\t * case of a container, an engine must fire additional events for its\n\t * children.\n\t *\n\t * <p>This method may only be called if the test or container has not been\n\t * {@linkplain #executionSkipped skipped}.\n\t *\n\t * <p>This method must be called for a container {@code TestDescriptor}\n\t * <em>before</em> {@linkplain #executionStarted starting} or\n\t * {@linkplain #executionSkipped skipping} any of its children.\n\t *\n\t * @param testDescriptor the descriptor of the started test or container\n\t */\n\tdefault void executionStarted(TestDescriptor testDescriptor) {\n\t}\n\n\t/**\n\t * Must be called when the execution of a leaf or subtree of the test tree\n\t * has finished, regardless of the outcome.\n\t *\n\t * <p>The {@link TestDescriptor} may represent a test or a container.\n\t *\n\t * <p>This method may only be called if the test or container has not\n\t * been {@linkplain #executionSkipped skipped}.\n\t *\n\t * <p>This method must be called for a container {@code TestIdentifier}\n\t * <em>after</em> all of its children have been\n\t * {@linkplain #executionSkipped skipped} or have\n\t * {@linkplain #executionFinished finished}.\n\t *\n\t * <p>The {@link TestExecutionResult} describes the result of the execution\n\t * for the supplied {@code testDescriptor}. The result does not include or\n\t * aggregate the results of its children. For example, a container with a\n\t * failing test must be reported as {@link Status#SUCCESSFUL SUCCESSFUL} even\n\t * if one or more of its children are reported as {@link Status#FAILED FAILED}.\n\t *\n\t * @param testDescriptor the descriptor of the finished test or container\n\t * @param testExecutionResult the (unaggregated) result of the execution for\n\t * the supplied {@code TestDescriptor}\n\t *\n\t * @see TestExecutionResult\n\t */\n\tdefault void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t}\n\n\t/**\n\t * Can be called for any {@link TestDescriptor} in order to publish additional\n\t * information to the reporting infrastructure &mdash; for example:\n\t *\n\t * <ul>\n\t *     <li>Output that would otherwise go to {@code System.out}</li>\n\t *     <li>Information about test context or test data</li>\n\t * </ul>\n\t *\n\t * <p>The current lifecycle state of the supplied {@code TestDescriptor} is\n\t * not relevant: reporting events can occur at any time.\n\t *\n\t * @param testDescriptor the descriptor of the test or container to which\n\t * the reporting entry belongs\n\t * @param reportEntry the {@link ReportEntry} to be published\n\t */\n\tdefault void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry reportEntry) {\n\t}\n\n\t/**\n\t * Can be called for any {@link TestDescriptor} in order to publish a file or\n\t * directory by attaching it to a test or container &mdash; for example:\n\t *\n\t * <ul>\n\t *     <li>Screenshots</li>\n\t *     <li>Logs</li>\n\t *     <li>Output files written by the code under test</li>\n\t *     <li>Test output directory</li>\n\t * </ul>\n\t *\n\t * <p>The current lifecycle state of the supplied {@code TestDescriptor} is\n\t * not relevant: file events can occur at any time.\n\t *\n\t * @param testDescriptor the descriptor of the test or container to which\n\t * the file entry belongs\n\t * @param fileEntry the {@link FileEntry} to be published\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tdefault void fileEntryPublished(TestDescriptor testDescriptor, FileEntry fileEntry) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/ExecutionRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * Provides a single {@link TestEngine} access to the information necessary to\n * execute its tests.\n *\n * <p>A request contains an engine's root {@link TestDescriptor}, the\n * {@link EngineExecutionListener} to be notified of test execution events, the\n * {@link ConfigurationParameters} that the engine may use to influence test\n * execution, and an {@link OutputDirectoryCreator} for writing reports and\n * other output files.\n *\n * @since 1.0\n * @see TestEngine\n */\n@API(status = STABLE, since = \"1.0\")\npublic class ExecutionRequest {\n\n\tprivate final TestDescriptor rootTestDescriptor;\n\tprivate final EngineExecutionListener engineExecutionListener;\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final @Nullable OutputDirectoryCreator outputDirectoryCreator;\n\tprivate final @Nullable NamespacedHierarchicalStore<Namespace> requestLevelStore;\n\tprivate final CancellationToken cancellationToken;\n\n\t/**\n\t * @deprecated without replacement because it's an internal API.\n\t */\n\t@Deprecated(since = \"1.11\")\n\t@API(status = DEPRECATED, since = \"1.11\")\n\tpublic ExecutionRequest(TestDescriptor rootTestDescriptor, EngineExecutionListener engineExecutionListener,\n\t\t\tConfigurationParameters configurationParameters) {\n\t\tthis(rootTestDescriptor, engineExecutionListener, configurationParameters, null, null,\n\t\t\tCancellationToken.disabled());\n\t}\n\n\tprivate ExecutionRequest(TestDescriptor rootTestDescriptor, EngineExecutionListener engineExecutionListener,\n\t\t\tConfigurationParameters configurationParameters, @Nullable OutputDirectoryCreator outputDirectoryCreator,\n\t\t\t@Nullable NamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken) {\n\n\t\tthis.rootTestDescriptor = Preconditions.notNull(rootTestDescriptor, \"rootTestDescriptor must not be null\");\n\t\tthis.engineExecutionListener = Preconditions.notNull(engineExecutionListener,\n\t\t\t\"engineExecutionListener must not be null\");\n\t\tthis.configurationParameters = Preconditions.notNull(configurationParameters,\n\t\t\t\"configurationParameters must not be null\");\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t\tthis.requestLevelStore = requestLevelStore;\n\t\tthis.cancellationToken = Preconditions.notNull(cancellationToken, \"cancellationToken must not be null\");\n\t}\n\n\t/**\n\t * Factory for creating an execution request.\n\t *\n\t * @param rootTestDescriptor the engine's root {@link TestDescriptor}\n\t * @param engineExecutionListener the {@link EngineExecutionListener} to be\n\t * notified of test execution events\n\t * @param configurationParameters {@link ConfigurationParameters} that the\n\t * engine may use to influence test execution\n\t * @return a new {@code ExecutionRequest}; never {@code null}\n\t * @since 1.9\n\t * @deprecated without replacement\n\t */\n\t@Deprecated(since = \"1.11\")\n\t@API(status = DEPRECATED, since = \"1.11\")\n\tpublic static ExecutionRequest create(TestDescriptor rootTestDescriptor,\n\t\t\tEngineExecutionListener engineExecutionListener, ConfigurationParameters configurationParameters) {\n\t\treturn new ExecutionRequest(rootTestDescriptor, engineExecutionListener, configurationParameters);\n\t}\n\n\t/**\n\t * Factory for creating an execution request.\n\t *\n\t * @param rootTestDescriptor the engine's root {@link TestDescriptor}; never\n\t * {@code null}\n\t * @param engineExecutionListener the {@link EngineExecutionListener} to be\n\t * notified of test execution events; never {@code null}\n\t * @param configurationParameters {@link ConfigurationParameters} that the\n\t * engine may use to influence test execution; never {@code null}\n\t * @param outputDirectoryCreator {@link OutputDirectoryCreator} for\n\t * writing reports and other output files; never {@code null}\n\t * @param requestLevelStore {@link NamespacedHierarchicalStore} for storing\n\t * request-scoped data; never {@code null}\n\t * @return a new {@code ExecutionRequest}; never {@code null}\n\t * @since 6.0\n\t */\n\t@API(status = INTERNAL, since = \"6.0\")\n\tpublic static ExecutionRequest create(TestDescriptor rootTestDescriptor,\n\t\t\tEngineExecutionListener engineExecutionListener, ConfigurationParameters configurationParameters,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\treturn new ExecutionRequest(rootTestDescriptor, engineExecutionListener, configurationParameters,\n\t\t\tPreconditions.notNull(outputDirectoryCreator, \"outputDirectoryCreator must not be null\"),\n\t\t\tPreconditions.notNull(requestLevelStore, \"requestLevelStore must not be null\"),\n\t\t\tPreconditions.notNull(cancellationToken, \"cancellationToken must not be null\"));\n\t}\n\n\t/**\n\t * {@return the root {@link TestDescriptor} of the engine that processes this\n\t * request}\n\t *\n\t * <p><strong>Note</strong>: the <em>root</em> descriptor is the\n\t * {@code TestDescriptor} returned by\n\t * {@link TestEngine#discover(EngineDiscoveryRequest, UniqueId)}.\n\t */\n\tpublic TestDescriptor getRootTestDescriptor() {\n\t\treturn this.rootTestDescriptor;\n\t}\n\n\t/**\n\t * {@return the {@link EngineExecutionListener} to be notified of test execution\n\t * events}\n\t */\n\tpublic EngineExecutionListener getEngineExecutionListener() {\n\t\treturn this.engineExecutionListener;\n\t}\n\n\t/**\n\t * {@return the {@link ConfigurationParameters} that the engine may use to\n\t * influence test execution}\n\t */\n\tpublic ConfigurationParameters getConfigurationParameters() {\n\t\treturn this.configurationParameters;\n\t}\n\n\t/**\n\t * {@return the\n\t * {@link org.junit.platform.engine.reporting.OutputDirectoryProvider} for\n\t * this request for writing reports and other output files}\n\t *\n\t * @throws PreconditionViolationException if the output directory provider\n\t * is not available\n\t * @since 1.12\n\t * @deprecated Please use {@link #getOutputDirectoryCreator()} instead\n\t */\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@SuppressWarnings(\"removal\")\n\tpublic org.junit.platform.engine.reporting.OutputDirectoryProvider getOutputDirectoryProvider() {\n\t\treturn org.junit.platform.engine.reporting.OutputDirectoryProvider.castOrAdapt(getOutputDirectoryCreator());\n\t}\n\n\t/**\n\t * {@return the {@link OutputDirectoryCreator} for this request for writing\n\t * reports and other output files}\n\t *\n\t * @throws PreconditionViolationException if the output directory creator is\n\t * not available\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn Preconditions.notNull(this.outputDirectoryCreator,\n\t\t\t\"No OutputDirectoryCreator was configured for this request\");\n\t}\n\n\t/**\n\t * {@return the {@link NamespacedHierarchicalStore} for this request for\n\t * storing request-scoped data}\n\t *\n\t * <p>All stored values that implement {@link AutoCloseable} are notified by\n\t * invoking their {@code close()} methods when this request has been\n\t * executed.\n\t *\n\t * @since 1.13\n\t * @see NamespacedHierarchicalStore\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic NamespacedHierarchicalStore<Namespace> getStore() {\n\t\treturn Preconditions.notNull(this.requestLevelStore,\n\t\t\t\"No NamespacedHierarchicalStore was configured for this request\");\n\t}\n\n\t/**\n\t * {@return the {@link CancellationToken} for this request for engines to\n\t * check whether they should cancel execution}\n\t *\n\t * @since 6.0\n\t * @see CancellationToken#isCancellationRequested()\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic CancellationToken getCancellationToken() {\n\t\treturn cancellationToken;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/Filter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static java.util.Arrays.asList;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.CompositeFilter.alwaysIncluded;\n\nimport java.util.Collection;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * A {@link Filter} can be applied to determine if an object should be\n * <em>included</em> or <em>excluded</em> in a result set.\n *\n * <p>For example, tests may be filtered during or after test discovery\n * based on certain criteria.\n *\n * <p>Clients should not implement this interface directly but rather one of\n * its subinterfaces.\n *\n * @since 1.0\n * @see DiscoveryFilter\n */\n@FunctionalInterface\n@API(status = STABLE, since = \"1.0\")\npublic interface Filter<T> {\n\n\t/**\n\t * Return a filter that will include elements if and only if all of the\n\t * filters in the supplied array of {@link Filter filters} include it.\n\t *\n\t * <p>If the array is empty, the returned filter will include all elements\n\t * it is asked to filter.\n\t *\n\t * @param filters the array of filters to compose; never {@code null}\n\t * @see #composeFilters(Collection)\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tstatic <T> Filter<T> composeFilters(Filter<T>... filters) {\n\t\tPreconditions.notNull(filters, \"filters array must not be null\");\n\t\tPreconditions.containsNoNullElements(filters, \"individual filters must not be null\");\n\n\t\tif (filters.length == 0) {\n\t\t\treturn alwaysIncluded();\n\t\t}\n\t\tif (filters.length == 1) {\n\t\t\treturn filters[0];\n\t\t}\n\t\treturn new CompositeFilter<>(asList(filters));\n\t}\n\n\t/**\n\t * Return a filter that will include elements if and only if all of the\n\t * filters in the supplied collection of {@link Filter filters} include it.\n\t *\n\t * <p>If the collection is empty, the returned filter will include all\n\t * elements it is asked to filter.\n\t *\n\t * @param filters the collection of filters to compose; never {@code null}\n\t * @see #composeFilters(Filter...)\n\t */\n\tstatic <T> Filter<T> composeFilters(Collection<? extends Filter<T>> filters) {\n\t\tPreconditions.notNull(filters, \"Filters must not be null\");\n\n\t\tif (filters.isEmpty()) {\n\t\t\treturn alwaysIncluded();\n\t\t}\n\t\tif (filters.size() == 1) {\n\t\t\treturn getOnlyElement(filters);\n\t\t}\n\t\treturn new CompositeFilter<>(filters);\n\t}\n\n\t/**\n\t * Return a filter that will include elements if and only if the adapted\n\t * {@code Filter} includes the value converted using the supplied\n\t * {@link Function}.\n\t *\n\t * @param adaptee the filter to be adapted\n\t * @param converter the converter function to apply\n\t * @deprecated without replacement\n\t */\n\t@API(status = DEPRECATED, since = \"6.0\")\n\t@Deprecated(since = \"6.0\", forRemoval = true)\n\tstatic <T, V> Filter<T> adaptFilter(Filter<V> adaptee, Function<T, V> converter) {\n\t\treturn input -> adaptee.apply(converter.apply(input));\n\t}\n\n\t/**\n\t * Apply this filter to the supplied object.\n\t */\n\tFilterResult apply(T object);\n\n\t/**\n\t * Return a {@link Predicate} that returns {@code true} if this filter\n\t * <em>includes</em> the object supplied to the predicate's\n\t * {@link Predicate#test test} method.\n\t */\n\tdefault Predicate<T> toPredicate() {\n\t\treturn object -> apply(object).included();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/FilterResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * The result of applying a {@link Filter}.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic class FilterResult {\n\n\t/**\n\t * Factory for creating <em>included</em> results.\n\t *\n\t * @param reason the reason why the filtered object was included\n\t * @return an included {@code FilterResult} with the given reason\n\t */\n\tpublic static FilterResult included(@Nullable String reason) {\n\t\treturn new FilterResult(true, reason);\n\t}\n\n\t/**\n\t * Factory for creating <em>excluded</em> results.\n\t *\n\t * @param reason the reason why the filtered object was excluded\n\t * @return an excluded {@code FilterResult} with the given reason\n\t */\n\tpublic static FilterResult excluded(@Nullable String reason) {\n\t\treturn new FilterResult(false, reason);\n\t}\n\n\t/**\n\t * Factory for creating filter results based on the condition given.\n\t *\n\t * @param included whether the filtered object should be included\n\t * @return a valid {@code FilterResult} for the given condition\n\t */\n\tpublic static FilterResult includedIf(boolean included) {\n\t\treturn includedIf(included, () -> null, () -> null);\n\t}\n\n\t/**\n\t * Factory for creating filter results based on the condition given.\n\t *\n\t * @param included whether the filtered object should be included\n\t * @param inclusionReasonSupplier supplier for the reason in case of inclusion\n\t * @param exclusionReasonSupplier supplier for the reason in case of exclusion\n\t * @return a valid {@code FilterResult} for the given condition\n\t */\n\tpublic static FilterResult includedIf(boolean included, Supplier<@Nullable String> inclusionReasonSupplier,\n\t\t\tSupplier<@Nullable String> exclusionReasonSupplier) {\n\t\treturn included ? included(inclusionReasonSupplier.get()) : excluded(exclusionReasonSupplier.get());\n\t}\n\n\tprivate final boolean included;\n\n\tprivate final Optional<String> reason;\n\n\tprivate FilterResult(boolean included, @Nullable String reason) {\n\t\tthis.included = included;\n\t\tthis.reason = Optional.ofNullable(reason);\n\t}\n\n\t/**\n\t * {@return {@code true} if the filtered object should be included}\n\t */\n\tpublic boolean included() {\n\t\treturn this.included;\n\t}\n\n\t/**\n\t * {@return {@code true} if the filtered object should be excluded}\n\t */\n\tpublic boolean excluded() {\n\t\treturn !included();\n\t}\n\n\t/**\n\t * Get the reason why the filtered object should be included or excluded,\n\t * if available.\n\t */\n\tpublic Optional<String> getReason() {\n\t\treturn this.reason;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"included\", this.included)\n\t\t\t\t.append(\"reason\", this.reason.orElse(\"<unknown>\"))\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/OutputDirectoryCreator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\n\n/**\n * Provider of output directories for test engines to write reports and other\n * output files to.\n *\n * @since 1.14\n * @see EngineDiscoveryRequest#getOutputDirectoryCreator()\n */\n@API(status = MAINTAINED, since = \"1.14\")\npublic interface OutputDirectoryCreator {\n\n\t/**\n\t * {@return the root directory for all output files; never {@code null}}\n\t */\n\tPath getRootDirectory();\n\n\t/**\n\t * Create an output directory for the supplied test descriptor.\n\t *\n\t * @param testDescriptor the test descriptor for which to create an output\n\t * directory; never {@code null}\n\t * @return the output directory\n\t * @throws IOException if the output directory could not be created\n\t */\n\tPath createOutputDirectory(TestDescriptor testDescriptor) throws IOException;\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/RegularCancellationToken.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * @since 6.0\n */\nfinal class RegularCancellationToken implements CancellationToken {\n\n\tprivate final AtomicBoolean cancelled = new AtomicBoolean();\n\n\t@Override\n\tpublic boolean isCancellationRequested() {\n\t\treturn cancelled.get();\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t\tcancelled.set(true);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/SelectorResolutionResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code SelectorResolutionResult} encapsulates the result of resolving a\n * {@link DiscoverySelector} by a {@link TestEngine}.\n *\n * <p>A {@code SelectorResolutionResult} consists of a mandatory\n * {@link #getStatus() Status} and an optional {@link #getThrowable() Throwable}.\n *\n * @since 1.6\n */\n@API(status = STABLE, since = \"1.10\")\npublic class SelectorResolutionResult {\n\n\t/**\n\t * Status of resolving a {@link DiscoverySelector}.\n\t */\n\tpublic enum Status {\n\n\t\t/**\n\t\t * Indicates that the {@link TestEngine} has successfully resolved the\n\t\t * selector.\n\t\t */\n\t\tRESOLVED,\n\n\t\t/**\n\t\t * Indicates that the {@link TestEngine} was unable to resolve the\n\t\t * selector.\n\t\t */\n\t\tUNRESOLVED,\n\n\t\t/**\n\t\t * Indicates that the {@link TestEngine} has encountered an error while\n\t\t * resolving the selector.\n\t\t */\n\t\tFAILED\n\n\t}\n\n\tprivate static final SelectorResolutionResult RESOLVED_RESULT = new SelectorResolutionResult(Status.RESOLVED, null);\n\tprivate static final SelectorResolutionResult UNRESOLVED_RESULT = new SelectorResolutionResult(Status.UNRESOLVED,\n\t\tnull);\n\n\t/**\n\t * Create a {@code SelectorResolutionResult} for a <em>resolved</em>\n\t * selector.\n\t * @return the {@code SelectorResolutionResult}; never {@code null}\n\t */\n\tpublic static SelectorResolutionResult resolved() {\n\t\treturn RESOLVED_RESULT;\n\t}\n\n\t/**\n\t * Create a {@code SelectorResolutionResult} for an <em>unresolved</em>\n\t * selector.\n\t * @return the {@code SelectorResolutionResult}; never {@code null}\n\t */\n\tpublic static SelectorResolutionResult unresolved() {\n\t\treturn UNRESOLVED_RESULT;\n\t}\n\n\t/**\n\t * Create a {@code SelectorResolutionResult} for a <em>failed</em>\n\t * selector resolution.\n\t * @return the {@code SelectorResolutionResult}; never {@code null}\n\t */\n\tpublic static SelectorResolutionResult failed(Throwable throwable) {\n\t\treturn new SelectorResolutionResult(Status.FAILED, throwable);\n\t}\n\n\tprivate final Status status;\n\n\tprivate final @Nullable Throwable throwable;\n\n\tprivate SelectorResolutionResult(Status status, @Nullable Throwable throwable) {\n\t\tthis.status = status;\n\t\tthis.throwable = throwable;\n\t}\n\n\t/**\n\t * Get the {@linkplain Status status} of this result.\n\t *\n\t * @return the status; never {@code null}\n\t */\n\tpublic Status getStatus() {\n\t\treturn status;\n\t}\n\n\t/**\n\t * Get the throwable that caused this result, if available.\n\t *\n\t * @return an {@code Optional} containing the throwable; never {@code null}\n\t * but potentially empty\n\t */\n\tpublic Optional<Throwable> getThrowable() {\n\t\treturn Optional.ofNullable(throwable);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"status\", status)\n\t\t\t\t.append(\"throwable\", throwable)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/TestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * A descriptor with a mutable hierarchy for a test or container that has been\n * discovered by a {@link TestEngine}.\n *\n * @since 1.0\n * @see TestEngine\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface TestDescriptor {\n\n\t/**\n\t * Get the unique identifier (UID) for this descriptor.\n\t *\n\t * <p>Uniqueness must be guaranteed across an entire test plan,\n\t * regardless of how many engines are used behind the scenes.\n\t *\n\t * <p>The implementation must treat this property as immutable after test\n\t * discovery has completed.\n\t *\n\t * @return the {@code UniqueId} for this descriptor; never {@code null}\n\t */\n\tUniqueId getUniqueId();\n\n\t/**\n\t * Get the display name for this descriptor.\n\t *\n\t * <p>A <em>display name</em> is a human-readable name for a test or\n\t * container that is typically used for test reporting in IDEs and build\n\t * tools. Display names may contain spaces, special characters, and emoji,\n\t * and the format may be customized by {@link TestEngine TestEngines} or\n\t * potentially by end users as well. Consequently, display names should\n\t * never be parsed; rather, they should be used for display purposes only.\n\t *\n\t * @return the display name for this descriptor; never {@code null} or blank\n\t * @see #getSource()\n\t */\n\tString getDisplayName();\n\n\t/**\n\t * Get the name of this descriptor in a format that is suitable for legacy\n\t * reporting infrastructure &mdash; for example, for reporting systems built\n\t * on the Ant-based XML reporting format for JUnit 4.\n\t *\n\t * <p>The default implementation delegates to {@link #getDisplayName()}.\n\t *\n\t * @return the legacy reporting name; never {@code null} or blank\n\t */\n\tdefault String getLegacyReportingName() {\n\t\treturn getDisplayName();\n\t}\n\n\t/**\n\t * Get the set of {@linkplain TestTag tags} associated with this descriptor.\n\t *\n\t * <p>The implementation must treat this property as immutable after test\n\t * discovery has completed.\n\t *\n\t * @return the set of tags associated with this descriptor; never {@code null}\n\t * but potentially empty\n\t * @see TestTag\n\t */\n\tSet<TestTag> getTags();\n\n\t/**\n\t * Get the {@linkplain TestSource source} of the test or container described\n\t * by this descriptor, if available.\n\t *\n\t * <p>The implementation must treat this property as immutable after test\n\t * discovery has completed.\n\t *\n\t * @see TestSource\n\t */\n\tOptional<TestSource> getSource();\n\n\t/**\n\t * Get the <em>parent</em> of this descriptor, if available.\n\t */\n\tOptional<TestDescriptor> getParent();\n\n\t/**\n\t * Set the <em>parent</em> of this descriptor.\n\t *\n\t * @param parent the new parent of this descriptor; may be {@code null}.\n\t */\n\tvoid setParent(@Nullable TestDescriptor parent);\n\n\t/**\n\t * Get the immutable set of <em>children</em> of this descriptor.\n\t *\n\t * <p>The implementation must be consistent with {@link #isContainer()} such that\n\t * {@code !x.container()} implies {@code x.getChildren().isEmpty()}.\n\t *\n\t * @return the set of children of this descriptor; neither {@code null}\n\t * nor mutable, but potentially empty\n\t * @see #getDescendants()\n\t */\n\tSet<? extends TestDescriptor> getChildren();\n\n\t/**\n\t * Get the immutable set of all <em>ancestors</em> of this descriptor.\n\t *\n\t * <p>An <em>ancestor</em> is the parent of this descriptor or the parent of\n\t * one of its parents, recursively.\n\t *\n\t * @see #getParent()\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tdefault Set<? extends TestDescriptor> getAncestors() {\n\t\tif (getParent().isEmpty()) {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\t\tTestDescriptor parent = getParent().get();\n\t\tSet<TestDescriptor> ancestors = new LinkedHashSet<>();\n\t\tancestors.add(parent);\n\t\t// Need to recurse?\n\t\tif (parent.getParent().isPresent()) {\n\t\t\tancestors.addAll(parent.getAncestors());\n\t\t}\n\t\treturn Collections.unmodifiableSet(ancestors);\n\t}\n\n\t/**\n\t * Get the immutable set of all <em>descendants</em> of this descriptor.\n\t *\n\t * <p>A <em>descendant</em> is a child of this descriptor or a child of one of\n\t * its children, recursively.\n\t *\n\t * <p>The implementation must be consistent with {@link #isContainer()} such that\n\t * {@code !x.container()} implies {@code x.getDescendants().isEmpty()}.\n\t *\n\t * @see #getChildren()\n\t */\n\tdefault Set<? extends TestDescriptor> getDescendants() {\n\t\tSet<TestDescriptor> descendants = new LinkedHashSet<>();\n\t\tdescendants.addAll(getChildren());\n\t\tfor (TestDescriptor child : getChildren()) {\n\t\t\tdescendants.addAll(child.getDescendants());\n\t\t}\n\t\treturn Collections.unmodifiableSet(descendants);\n\t}\n\n\t/**\n\t * Add a <em>child</em> to this descriptor.\n\t *\n\t * @param descriptor the child to add to this descriptor; never {@code null}\n\t */\n\tvoid addChild(TestDescriptor descriptor);\n\n\t/**\n\t * Remove a <em>child</em> from this descriptor.\n\t *\n\t * @param descriptor the child to remove from this descriptor; never\n\t * {@code null}\n\t */\n\tvoid removeChild(TestDescriptor descriptor);\n\n\t/**\n\t * Remove this non-root descriptor from its parent and remove all the\n\t * children from this descriptor.\n\t *\n\t * <p>If this method is invoked on a {@linkplain #isRoot root} descriptor,\n\t * this method must throw a {@link org.junit.platform.commons.JUnitException\n\t * JUnitException} explaining that a root cannot be removed from the\n\t * hierarchy.\n\t */\n\tvoid removeFromHierarchy();\n\n\t/**\n\t * Order the children of this descriptor.\n\t *\n\t * <p>The {@code orderer} is provided a modifiable list of child test\n\t * descriptors of this test descriptor; never {@code null}. The\n\t * {@code orderer} must return a list containing the same descriptors in any\n\t * order; potentially the same list, but never {@code null}. If descriptors\n\t * are added or removed, an exception is thrown.\n\t *\n\t * @param orderer a unary operator to order the children of this test descriptor\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tdefault void orderChildren(UnaryOperator<List<TestDescriptor>> orderer) {\n\t\tPreconditions.notNull(orderer, \"orderer must not be null\");\n\t\tSet<? extends TestDescriptor> originalChildren = getChildren();\n\t\tList<TestDescriptor> suggestedOrder = orderer.apply(new ArrayList<>(originalChildren));\n\t\tPreconditions.notNull(suggestedOrder, \"orderer may not return null\");\n\n\t\tSet<? extends TestDescriptor> orderedChildren = new LinkedHashSet<>(suggestedOrder);\n\t\tboolean unmodified = originalChildren.equals(orderedChildren);\n\t\tPreconditions.condition(unmodified && originalChildren.size() == suggestedOrder.size(),\n\t\t\t\"orderer may not add or remove test descriptors\");\n\n\t\tsuggestedOrder.stream() //\n\t\t\t\t.distinct() //\n\t\t\t\t.filter(originalChildren::contains)//\n\t\t\t\t.forEach(testDescriptor -> {\n\t\t\t\t\tremoveChild(testDescriptor);\n\t\t\t\t\taddChild(testDescriptor);\n\t\t\t\t});\n\t}\n\n\t/**\n\t * Determine if this descriptor is a <em>root</em> descriptor.\n\t *\n\t * <p>A <em>root</em> descriptor is a descriptor without a parent.\n\t */\n\tdefault boolean isRoot() {\n\t\treturn getParent().isEmpty();\n\t}\n\n\t/**\n\t * Determine the {@link Type} of this descriptor.\n\t *\n\t * <p>The implementation must treat this property as immutable after test\n\t * discovery has completed.\n\t *\n\t * @return the descriptor type; never {@code null}.\n\t * @see #isContainer()\n\t * @see #isTest()\n\t */\n\tType getType();\n\n\t/**\n\t * Determine if this descriptor describes a <em>container</em>.\n\t *\n\t * <p>A test descriptor is a <em>container</em> when it may contain other\n\t * containers or tests as its children. In addition to being a\n\t * <em>container</em> this test descriptor may also be a <em>test</em>.\n\t *\n\t * <p>The implementation must be consistent with {@link #getType()} such\n\t * that {@code x.isContainer()} equals {@code x.getType().isContainer()}.\n\t *\n\t * <p>The default implementation delegates to {@link Type#isContainer()}.\n\t */\n\tdefault boolean isContainer() {\n\t\treturn getType().isContainer();\n\t}\n\n\t/**\n\t * Determine if this descriptor describes a <em>test</em>.\n\t *\n\t * <p>A test descriptor is a <em>test</em> when it verifies expected\n\t * behavior when executed. In addition to being a <em>test</em> this\n\t * test descriptor may also be a <em>container</em>.\n\t *\n\t * <p>The implementation must be consistent with {@link #getType()} such\n\t * that {@code x.isTest()} equals {@code x.getType().isTest()}.\n\t *\n\t * <p>The default implementation delegates to {@link Type#isTest()}.\n\t */\n\tdefault boolean isTest() {\n\t\treturn getType().isTest();\n\t}\n\n\t/**\n\t * Determine if this descriptor may register dynamic tests during execution.\n\t *\n\t * <p>The implementation must treat this property as immutable after test\n\t * discovery has completed and must be consistent with {@link #isContainer()}\n\t * such that {@code !x.container()} implies {@code !x.mayRegisterTests()}.\n\t *\n\t * <p>The default implementation assumes tests are usually known during\n\t * discovery and thus returns {@code false}.\n\t */\n\tdefault boolean mayRegisterTests() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Determine if the supplied descriptor (or any of its descendants)\n\t * {@linkplain TestDescriptor#isTest() is a <em>test</em>} or\n\t * {@linkplain TestDescriptor#mayRegisterTests() may potentially register\n\t * tests dynamically}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} to check for tests; never\n\t * {@code null}\n\t * @return {@code true} if the descriptor is a test, contains tests, or may\n\t * later register tests dynamically\n\t */\n\tstatic boolean containsTests(TestDescriptor testDescriptor) {\n\t\tPreconditions.notNull(testDescriptor, \"TestDescriptor must not be null\");\n\t\treturn testDescriptor.isTest() || testDescriptor.mayRegisterTests()\n\t\t\t\t|| testDescriptor.getChildren().stream().anyMatch(TestDescriptor::containsTests);\n\t}\n\n\t/**\n\t * Remove this descriptor from the hierarchy unless it is a root or contains\n\t * tests.\n\t *\n\t * <p>A concrete {@link TestEngine} may override this method in order to\n\t * implement a different algorithm or to skip pruning altogether.\n\t *\n\t * @see #isRoot()\n\t * @see #containsTests(TestDescriptor)\n\t * @see #removeFromHierarchy()\n\t */\n\tdefault void prune() {\n\t\tif (!isRoot() && !containsTests(this)) {\n\t\t\tremoveFromHierarchy();\n\t\t}\n\t}\n\n\t/**\n\t * Find the descriptor with the supplied unique ID.\n\t *\n\t * <p>The search algorithm begins with this descriptor and then searches\n\t * through its descendants.\n\t *\n\t * @param uniqueId the {@code UniqueId} to search for; never {@code null}\n\t */\n\tOptional<? extends TestDescriptor> findByUniqueId(UniqueId uniqueId);\n\n\t/**\n\t * Accept a {@link Visitor} to the subtree starting with this descriptor.\n\t *\n\t * @param visitor the {@code Visitor} to accept; never {@code null}\n\t */\n\tdefault void accept(Visitor visitor) {\n\t\tPreconditions.notNull(visitor, \"Visitor must not be null\");\n\t\tvisitor.visit(this);\n\t\tthis.getChildren().forEach(child -> child.accept(visitor));\n\t}\n\n\t/**\n\t * Visitor for the tree-like {@link TestDescriptor} structure.\n\t *\n\t * @see TestDescriptor#accept(Visitor)\n\t */\n\t@FunctionalInterface\n\tinterface Visitor {\n\n\t\t/**\n\t\t * Combine the supplied {@code visitors} into a single {@code Visitor}.\n\t\t *\n\t\t * <p>If the supplied array contains only a single {@code Visitor}, that\n\t\t * {@code Visitor} is returned as is.\n\t\t *\n\t\t * @param visitors the {@code Visitor}s to combine; never {@code null}\n\t\t * or empty\n\t\t * @return the combined {@code Visitor}\n\t\t * @throws org.junit.platform.commons.PreconditionViolationException if\n\t\t * {@code visitors} is {@code null}, contains {@code null} elements, or\n\t\t * is empty\n\t\t * @since 1.13\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tstatic Visitor composite(Visitor... visitors) {\n\t\t\treturn CompositeTestDescriptorVisitor.from(visitors);\n\t\t}\n\n\t\t/**\n\t\t * Visit a {@link TestDescriptor}.\n\t\t *\n\t\t * @param descriptor the {@code TestDescriptor} to visit; never {@code null}\n\t\t */\n\t\tvoid visit(TestDescriptor descriptor);\n\n\t}\n\n\t/**\n\t * Supported types for {@link TestDescriptor TestDescriptors}.\n\t */\n\tenum Type {\n\n\t\t/**\n\t\t * Denotes that the {@link TestDescriptor} is strictly for a\n\t\t * <em>container</em>. I.e. it is not also a <em>test</em>.\n\t\t */\n\t\tCONTAINER,\n\n\t\t/**\n\t\t * Denotes that the {@link TestDescriptor} is strictly for a\n\t\t * <em>test</em>. I.e. it is not also a <em>container</em>.\n\t\t */\n\t\tTEST,\n\n\t\t/**\n\t\t * Denotes that the {@link TestDescriptor} is for a <em>test</em>\n\t\t * that may potentially also be a <em>container</em>.\n\t\t */\n\t\tCONTAINER_AND_TEST;\n\n\t\t/**\n\t\t * {@return {@code true} if this type represents a descriptor that can\n\t\t * contain other descriptors}\n\t\t */\n\t\tpublic boolean isContainer() {\n\t\t\treturn this == CONTAINER || this == CONTAINER_AND_TEST;\n\t\t}\n\n\t\t/**\n\t\t * {@return {@code true} if this type represents a descriptor for a test}\n\t\t */\n\t\tpublic boolean isTest() {\n\t\t\treturn this == TEST || this == CONTAINER_AND_TEST;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/TestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.module.ModuleDescriptor;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ModuleUtils;\nimport org.junit.platform.commons.util.PackageUtils;\n\n/**\n * A {@code TestEngine} facilitates <em>discovery</em> and <em>execution</em> of\n * tests for a particular programming model.\n *\n * <p>For example, JUnit provides a {@code TestEngine} that discovers and\n * executes tests written using the JUnit Jupiter programming model.\n *\n * <p>Every {@code TestEngine} must {@linkplain #getId provide its own unique ID},\n * {@linkplain #discover discover tests} from\n * {@link EngineDiscoveryRequest EngineDiscoveryRequests},\n * and {@linkplain #execute execute those tests} according to\n * {@link ExecutionRequest ExecutionRequests}.\n *\n * <p>In order to facilitate test discovery within IDEs and tools prior\n * to launching the JUnit Platform, {@code TestEngine} implementations are\n * encouraged to make use of the\n * {@link org.junit.platform.commons.annotation.Testable @Testable} annotation.\n * For example, the {@code @Test} and {@code @TestFactory} annotations in JUnit\n * Jupiter are meta-annotated with {@code @Testable}. Consult the Javadoc for\n * {@code @Testable} for further details.\n *\n * @since 1.0\n * @see org.junit.platform.engine.EngineDiscoveryRequest\n * @see org.junit.platform.engine.ExecutionRequest\n * @see org.junit.platform.commons.annotation.Testable\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface TestEngine {\n\n\t/**\n\t * Get the ID that uniquely identifies this test engine.\n\t *\n\t * <p>Each test engine must provide a unique ID. For example, JUnit Vintage\n\t * and JUnit Jupiter use {@code \"junit-vintage\"} and {@code \"junit-jupiter\"},\n\t * respectively. When in doubt, you may use the fully qualified name of your\n\t * custom {@code TestEngine} implementation class.\n\t *\n\t * @return the ID of this test engine; never {@code null} or blank\n\t */\n\tString getId();\n\n\t/**\n\t * Discover tests according to the supplied {@link EngineDiscoveryRequest}.\n\t *\n\t * <p>The supplied {@link UniqueId} must be used as the unique ID of the\n\t * returned root {@link TestDescriptor}. In addition, the {@code UniqueId}\n\t * must be used to create unique IDs for children of the root's descriptor\n\t * by calling {@link UniqueId#append}.\n\t *\n\t * <p>Furthermore, implementations must publish events about test discovery\n\t * via the supplied {@link EngineDiscoveryRequest#getDiscoveryListener()\n\t * EngineDiscoveryListener}.\n\t *\n\t * @param discoveryRequest the discovery request; never {@code null}\n\t * @param uniqueId the unique ID to be used for this test engine's\n\t * {@code TestDescriptor}; never {@code null}\n\t * @return the root {@code TestDescriptor} of this engine, typically an\n\t * instance of {@code EngineDescriptor}\n\t * @see org.junit.platform.engine.support.descriptor.EngineDescriptor\n\t */\n\tTestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId);\n\n\t/**\n\t * Execute tests according to the supplied {@link ExecutionRequest}.\n\t *\n\t * <p>The {@code request} passed to this method contains the root\n\t * {@link TestDescriptor} that was previously returned by {@link #discover},\n\t * the {@link EngineExecutionListener} to be notified of test execution\n\t * events, and {@link ConfigurationParameters} that may influence test execution.\n\t *\n\t * @param request the request to execute tests for; never {@code null}\n\t */\n\tvoid execute(ExecutionRequest request);\n\n\t/**\n\t * Get the <em>Group ID</em> of the JAR in which this test engine is packaged.\n\t *\n\t * <p>This information is used solely for debugging and reporting purposes.\n\t *\n\t * <p>The default implementation returns an empty {@link Optional},\n\t * signaling that the group ID is unknown.\n\t *\n\t * <p>Concrete test engine implementations may override this method in\n\t * order to provide a known group ID.\n\t *\n\t * @return an {@code Optional} containing the group ID; never {@code null}\n\t * but potentially empty if the group ID is unknown\n\t * @see #getArtifactId()\n\t * @see #getVersion()\n\t */\n\tdefault Optional<String> getGroupId() {\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * Get the <em>Module Name</em> or <em>Artifact ID</em> of the JAR in which\n\t * this test engine is packaged.\n\t *\n\t * <p>This information is used solely for debugging and reporting purposes.\n\t *\n\t * <p>The default implementation first attempts to retrieve the\n\t * {@linkplain Module#getName() name} of the {@link Module} in which the\n\t * engine resides. If the module name is not available, the default\n\t * implementation then attempts to retrieve the artifact ID as explained\n\t * below.\n\t *\n\t * <p>The default implementation assumes the implementation title is equivalent\n\t * to the artifact ID and therefore attempts to query the\n\t * {@linkplain Package#getImplementationTitle() implementation title}\n\t * from the package attributes for the {@link Package} in which the engine\n\t * resides. Note that a package only has attributes if the information is\n\t * defined in the {@link java.util.jar.Manifest Manifest} of the JAR containing\n\t * that package, and if the class loader created the {@link Package} instance\n\t * with the attributes from the manifest.\n\t *\n\t * <p>If the implementation title cannot be queried from the package\n\t * attributes, the default implementation returns an empty {@link Optional}.\n\t *\n\t * <p>Concrete test engine implementations may override this method in\n\t * order to determine the artifact ID by some other means.\n\t *\n\t * @return an {@code Optional} containing the module name or artifact ID; never\n\t * {@code null} but potentially empty if neither the module name nor the artifact\n\t * ID can be determined\n\t * @see Module#getName()\n\t * @see Class#getPackage()\n\t * @see Package#getImplementationTitle()\n\t * @see #getGroupId()\n\t * @see #getVersion()\n\t */\n\tdefault Optional<String> getArtifactId() {\n\t\tOptional<String> moduleName = ModuleUtils.getModuleName(getClass());\n\t\tif (moduleName.isPresent()) {\n\t\t\treturn moduleName;\n\t\t}\n\t\treturn PackageUtils.getAttribute(getClass(), Package::getImplementationTitle);\n\t}\n\n\t/**\n\t * Get the version of this test engine.\n\t *\n\t * <p>This information is used solely for debugging and reporting purposes.\n\t *\n\t * <p>The default implementation first attempts to retrieve the version\n\t * from the JAR manifest attribute named {@code \"Engine-Version-\" + getId()}.\n\t *\n\t * <p>If the manifest attribute is not available, an attempt is made to retrieve\n\t * the {@linkplain ModuleDescriptor#rawVersion() raw version} of the\n\t * {@link Module} in which the engine resides.\n\t *\n\t * <p>If the module version is not available, an attempt is made to query the\n\t * {@linkplain Package#getImplementationVersion() implementation version}\n\t * from the package attributes for the {@link Package} in which the engine\n\t * resides. Note that a package only has attributes if the information is\n\t * defined in the {@link java.util.jar.Manifest Manifest} of the JAR\n\t * containing that package, and if the class loader created the\n\t * {@link Package} instance with the attributes from the manifest.\n\t *\n\t * <p>If the implementation version cannot be determined, the default\n\t * implementation returns {@code \"DEVELOPMENT\"}.\n\t *\n\t * <p>Concrete test engine implementations may override this method to\n\t * determine the version by some other means.\n\t *\n\t * @return an {@code Optional} containing the version; never {@code null}\n\t * but potentially empty if the version is unknown\n\t * @see Class#getPackage()\n\t * @see Package#getImplementationVersion()\n\t * @see #getGroupId()\n\t * @see #getArtifactId()\n\t */\n\tdefault Optional<String> getVersion() {\n\t\tOptional<String> standalone = PackageUtils.getAttribute(getClass(), \"Engine-Version-\" + getId());\n\t\tif (standalone.isPresent()) {\n\t\t\treturn standalone;\n\t\t}\n\t\treturn Optional.of(PackageUtils.getModuleOrImplementationVersion(getClass()).orElse(\"DEVELOPMENT\"));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/TestExecutionResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code TestExecutionResult} encapsulates the result of executing a single test\n * or container.\n *\n * <p>A {@code TestExecutionResult} consists of a mandatory\n * {@link #getStatus() Status} and an optional {@link #getThrowable() Throwable}.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic class TestExecutionResult {\n\n\t/**\n\t * Status of executing a single test or container.\n\t */\n\tpublic enum Status {\n\n\t\t/**\n\t\t * Indicates that the execution of a test or container was\n\t\t * <em>successful</em>.\n\t\t */\n\t\tSUCCESSFUL,\n\n\t\t/**\n\t\t * Indicates that the execution of a test or container was\n\t\t * <em>aborted</em> (started but not finished).\n\t\t */\n\t\tABORTED,\n\n\t\t/**\n\t\t * Indicates that the execution of a test or container <em>failed</em>.\n\t\t */\n\t\tFAILED\n\n\t}\n\n\tprivate static final TestExecutionResult SUCCESSFUL_RESULT = new TestExecutionResult(SUCCESSFUL, null);\n\n\t/**\n\t * Create a {@code TestExecutionResult} for a <em>successful</em> execution\n\t * of a test or container.\n\t *\n\t * @return the {@code TestExecutionResult}; never {@code null}\n\t */\n\tpublic static TestExecutionResult successful() {\n\t\treturn SUCCESSFUL_RESULT;\n\t}\n\n\t/**\n\t * Create a {@code TestExecutionResult} for an <em>aborted</em> execution\n\t * of a test or container with the supplied {@link Throwable throwable}.\n\t *\n\t * @param throwable the throwable that caused the aborted execution; may be\n\t * {@code null}\n\t * @return the {@code TestExecutionResult}; never {@code null}\n\t */\n\tpublic static TestExecutionResult aborted(@Nullable Throwable throwable) {\n\t\treturn new TestExecutionResult(ABORTED, throwable);\n\t}\n\n\t/**\n\t * Create a {@code TestExecutionResult} for a <em>failed</em> execution\n\t * of a test or container with the supplied {@link Throwable throwable}.\n\t *\n\t * @param throwable the throwable that caused the failed execution; may be\n\t * {@code null}\n\t * @return the {@code TestExecutionResult}; never {@code null}\n\t */\n\tpublic static TestExecutionResult failed(@Nullable Throwable throwable) {\n\t\treturn new TestExecutionResult(FAILED, throwable);\n\t}\n\n\tprivate final Status status;\n\n\tprivate final @Nullable Throwable throwable;\n\n\tprivate TestExecutionResult(Status status, @Nullable Throwable throwable) {\n\t\tthis.status = Preconditions.notNull(status, \"Status must not be null\");\n\t\tthis.throwable = throwable;\n\t}\n\n\t/**\n\t * Get the {@linkplain Status status} of this result.\n\t *\n\t * @return the status; never {@code null}\n\t */\n\tpublic Status getStatus() {\n\t\treturn status;\n\t}\n\n\t/**\n\t * Get the throwable that caused this result, if available.\n\t *\n\t * @return an {@code Optional} containing the throwable; never {@code null}\n\t * but potentially empty\n\t */\n\tpublic Optional<Throwable> getThrowable() {\n\t\treturn Optional.ofNullable(throwable);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"status\", status)\n\t\t\t\t.append(\"throwable\", throwable)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/TestSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serializable;\n\nimport org.apiguardian.api.API;\n\n/**\n * Representation of the source of a test or container used to navigate to\n * its location by IDEs and build tools.\n *\n * <p>This is a marker interface. Clients need to check instances for concrete\n * subclasses or subinterfaces.\n *\n * <p>Implementations of this interface need to ensure that they are\n * <em>serializable</em> and <em>immutable</em> since they may be used as data\n * transfer objects.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface TestSource extends Serializable {\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/TestTag.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Objects;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * Immutable value object for a <em>tag</em> that is assigned to a test or\n * container.\n *\n * @since 1.0\n * @see #isValid(String)\n * @see #create(String)\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class TestTag implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate final String name;\n\n\t/**\n\t * <em>Reserved characters</em> that are not permissible as part of a tag name.\n\t *\n\t * <ul>\n\t * <li>{@code ,}: <em>comma</em></li>\n\t * <li>{@code (}: <em>left parenthesis</em></li>\n\t * <li>{@code )}: <em>right parenthesis</em></li>\n\t * <li>{@code &}: <em>ampersand</em></li>\n\t * <li>{@code |}: <em>vertical bar</em></li>\n\t * <li>{@code !}: <em>exclamation point</em></li>\n\t * </ul>\n\t */\n\tpublic static final Set<String> RESERVED_CHARACTERS = Set.of(\",\", \"(\", \")\", \"&\", \"|\", \"!\");\n\n\t/**\n\t * Determine if the supplied tag name is valid with regard to the supported\n\t * syntax for tags.\n\t *\n\t * <h4>Syntax Rules for Tags</h4>\n\t * <ul>\n\t * <li>A tag must not be {@code null}.</li>\n\t * <li>A tag must not be blank.</li>\n\t * <li>A stripped tag must not contain whitespace.</li>\n\t * <li>A stripped tag must not contain ISO control characters.</li>\n\t * <li>A stripped tag must not contain {@linkplain #RESERVED_CHARACTERS\n\t * reserved characters}.</li>\n\t * </ul>\n\t *\n\t * <p>If this method returns {@code true} for a given name, it is then a\n\t * valid candidate for the {@link TestTag#create(String) create()} factory\n\t * method.\n\t *\n\t * @param name the name of the tag to validate; may be {@code null} or blank\n\t * @return {@code true} if the supplied tag name conforms to the supported\n\t * syntax for tags\n\t * @see StringUtils#isNotBlank(String)\n\t * @see String#strip()\n\t * @see StringUtils#doesNotContainWhitespace(String)\n\t * @see StringUtils#doesNotContainIsoControlCharacter(String)\n\t * @see #RESERVED_CHARACTERS\n\t * @see TestTag#create(String)\n\t */\n\tpublic static boolean isValid(@Nullable String name) {\n\t\tif (name == null) {\n\t\t\treturn false;\n\t\t}\n\t\tname = name.strip();\n\n\t\treturn !name.isEmpty() && //\n\t\t\t\tStringUtils.doesNotContainWhitespace(name) && //\n\t\t\t\tStringUtils.doesNotContainIsoControlCharacter(name) && //\n\t\t\t\tdoesNotContainReservedCharacter(name);\n\t}\n\n\tprivate static boolean doesNotContainReservedCharacter(String str) {\n\t\treturn RESERVED_CHARACTERS.stream().noneMatch(str::contains);\n\t}\n\n\t/**\n\t * Create a {@code TestTag} from the supplied {@code name}.\n\t *\n\t * <p>Consider checking whether the syntax of the supplied {@code name}\n\t * is {@linkplain #isValid(String) valid} before attempting to create a\n\t * {@code TestTag} using this factory method.\n\t *\n\t * <p>Note: the supplied {@code name} will be {@linkplain String#strip() stripped}.\n\t *\n\t * @param name the name of the tag; must be syntactically <em>valid</em>\n\t * @throws PreconditionViolationException if the supplied tag name is not\n\t * syntactically <em>valid</em>\n\t * @see TestTag#isValid(String)\n\t */\n\tpublic static TestTag create(String name) throws PreconditionViolationException {\n\t\treturn new TestTag(name);\n\t}\n\n\tprivate TestTag(String name) {\n\t\tPreconditions.condition(TestTag.isValid(name),\n\t\t\t() -> \"Tag name [%s] must be syntactically valid\".formatted(name));\n\t\tthis.name = name.strip();\n\t}\n\n\t/**\n\t * Get the name of this tag.\n\t *\n\t * @return the name of this tag; never {@code null} or blank\n\t */\n\tpublic String getName() {\n\t\treturn this.name;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\treturn (obj instanceof TestTag that && Objects.equals(this.name, that.name));\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.name.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn this.name;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueId.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.ObjectStreamClass;\nimport java.io.ObjectStreamField;\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.lang.ref.SoftReference;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code UniqueId} encapsulates the creation, parsing, and display of unique IDs\n * for {@link TestDescriptor TestDescriptors}.\n *\n * <p>Instances of this class have value semantics and are immutable.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class UniqueId implements Cloneable, Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t@Serial\n\t@SuppressWarnings(\"UnusedVariable\")\n\tprivate static final ObjectStreamField[] serialPersistentFields = ObjectStreamClass.lookup(\n\t\tSerializedForm.class).getFields();\n\n\tprivate static final String ENGINE_SEGMENT_TYPE = \"engine\";\n\n\t/**\n\t * Parse a {@code UniqueId} from the supplied string representation using the\n\t * default format.\n\t *\n\t * @param uniqueId the string representation to parse; never {@code null} or blank\n\t * @return a properly constructed {@code UniqueId}\n\t * @throws JUnitException if the string cannot be parsed\n\t */\n\tpublic static UniqueId parse(String uniqueId) throws JUnitException {\n\t\tPreconditions.notBlank(uniqueId, \"Unique ID string must not be null or blank\");\n\t\treturn UniqueIdFormat.getDefault().parse(uniqueId);\n\t}\n\n\t/**\n\t * Create an engine's unique ID from its {@code engineId} using the default\n\t * format.\n\t *\n\t * <p>The engine ID will be stored in a {@link Segment} with\n\t * {@link Segment#getType type} {@code \"engine\"}.\n\t *\n\t * @param engineId the engine ID; never {@code null} or blank\n\t * @see #root(String, String)\n\t */\n\tpublic static UniqueId forEngine(String engineId) {\n\t\tPreconditions.notBlank(engineId, \"engineId must not be null or blank\");\n\t\treturn root(ENGINE_SEGMENT_TYPE, engineId);\n\t}\n\n\t/**\n\t * Create a root unique ID from the supplied {@code segmentType} and\n\t * {@code value} using the default format.\n\t *\n\t * @param segmentType the segment type; never {@code null} or blank\n\t * @param value the value; never {@code null} or blank\n\t * @see #forEngine(String)\n\t */\n\tpublic static UniqueId root(String segmentType, String value) {\n\t\treturn new UniqueId(new Segment(segmentType, value));\n\t}\n\n\t@SuppressWarnings({ \"serial\", \"RedundantSuppression\" })\n\t// always used with serializable implementation (List.copyOf())\n\t// This is effectively final but not technically due to late initialization when deserializing\n\tprivate /* final */ List<Segment> segments;\n\n\t// lazily computed\n\tprivate transient int hashCode;\n\n\t// lazily computed\n\tprivate transient @Nullable SoftReference<String> toString;\n\n\tprivate UniqueId(Segment segment) {\n\t\tthis(List.of(segment));\n\t}\n\n\t/**\n\t * Initialize a {@code UniqueId} instance.\n\t */\n\tUniqueId(List<Segment> segments) {\n\t\tthis.segments = List.copyOf(segments);\n\t}\n\n\tOptional<Segment> getRoot() {\n\t\treturn this.segments.isEmpty() ? Optional.empty() : Optional.of(this.segments.get(0));\n\t}\n\n\t/**\n\t * Get the engine ID stored in this {@code UniqueId}, if available.\n\t *\n\t * @see #forEngine(String)\n\t */\n\tpublic Optional<String> getEngineId() {\n\t\treturn getRoot().filter(segment -> ENGINE_SEGMENT_TYPE.equals(segment.getType())).map(Segment::getValue);\n\t}\n\n\t/**\n\t * Get the immutable list of {@linkplain Segment segments} that make up this\n\t * {@code UniqueId}.\n\t */\n\tpublic List<Segment> getSegments() {\n\t\treturn this.segments;\n\t}\n\n\t/**\n\t * Construct a new {@code UniqueId} by appending a new {@link Segment}, based\n\t * on the supplied {@code segmentType} and {@code value}, to the end of this\n\t * {@code UniqueId}.\n\t *\n\t * <p>This {@code UniqueId} will not be modified.\n\t *\n\t * <p>Neither the {@code segmentType} nor the {@code value} may contain any\n\t * of the special characters used for constructing the string representation\n\t * of this {@code UniqueId}.\n\t *\n\t * @param segmentType the type of the segment; never {@code null} or blank\n\t * @param value the value of the segment; never {@code null} or blank\n\t */\n\tpublic UniqueId append(String segmentType, String value) {\n\t\treturn append(new Segment(segmentType, value));\n\t}\n\n\t/**\n\t * Construct a new {@code UniqueId} by appending a new {@link Segment} to\n\t * the end of this {@code UniqueId}.\n\t *\n\t * <p>This {@code UniqueId} will not be modified.\n\t *\n\t * @param segment the segment to be appended; never {@code null}\n\t *\n\t * @since 1.1\n\t */\n\t@API(status = STABLE, since = \"1.1\")\n\tpublic UniqueId append(Segment segment) {\n\t\tPreconditions.notNull(segment, \"segment must not be null\");\n\t\tList<Segment> baseSegments = new ArrayList<>(this.segments.size() + 1);\n\t\tbaseSegments.addAll(this.segments);\n\t\tbaseSegments.add(segment);\n\t\treturn new UniqueId(baseSegments);\n\t}\n\n\t/**\n\t * Construct a new {@code UniqueId} by appending a new {@link Segment}, based\n\t * on the supplied {@code engineId}, to the end of this {@code UniqueId}.\n\t *\n\t * <p>This {@code UniqueId} will not be modified.\n\t *\n\t * <p>The engine ID will be stored in a {@link Segment} with\n\t * {@link Segment#getType type} {@value #ENGINE_SEGMENT_TYPE}.\n\t *\n\t * @param engineId the engine ID; never {@code null} or blank\n\t *\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic UniqueId appendEngine(String engineId) {\n\t\treturn append(new Segment(ENGINE_SEGMENT_TYPE, engineId));\n\t}\n\n\t/**\n\t * Determine if the supplied {@code UniqueId} is a prefix for this\n\t * {@code UniqueId}.\n\t *\n\t * @param potentialPrefix the {@code UniqueId} to be checked; never {@code null}\n\t *\n\t * @since 1.1\n\t */\n\t@API(status = STABLE, since = \"1.1\")\n\tpublic boolean hasPrefix(UniqueId potentialPrefix) {\n\t\tPreconditions.notNull(potentialPrefix, \"potentialPrefix must not be null\");\n\t\tint size = this.segments.size();\n\t\tint prefixSize = potentialPrefix.segments.size();\n\t\treturn size >= prefixSize && this.segments.subList(0, prefixSize).equals(potentialPrefix.segments);\n\t}\n\n\t/**\n\t * Construct a new {@code UniqueId} and removing the last {@link Segment} of\n\t * this {@code UniqueId}.\n\t *\n\t * <p>This {@code UniqueId} will not be modified.\n\t *\n\t * @return a new {@code UniqueId}; never {@code null}\n\t * @throws org.junit.platform.commons.PreconditionViolationException\n\t * if this {@code UniqueId} contains a single segment\n\t * @since 1.5\n\t */\n\t@API(status = STABLE, since = \"1.5\")\n\tpublic UniqueId removeLastSegment() {\n\t\tPreconditions.condition(this.segments.size() > 1, \"Cannot remove last remaining segment\");\n\t\treturn new UniqueId(this.segments.subList(0, this.segments.size() - 1));\n\t}\n\n\t/**\n\t * Get the last {@link Segment} of this {@code UniqueId}.\n\t *\n\t * @return the last {@code Segment}; never {@code null}\n\t * @since 1.5\n\t */\n\t@API(status = STABLE, since = \"1.5\")\n\tpublic Segment getLastSegment() {\n\t\treturn this.segments.get(this.segments.size() - 1);\n\t}\n\n\t@Override\n\tprotected Object clone() throws CloneNotSupportedException {\n\t\treturn super.clone();\n\t}\n\n\t@Serial\n\tprivate void writeObject(ObjectOutputStream s) throws IOException {\n\t\tSerializedForm serializedForm = new SerializedForm(this);\n\t\tserializedForm.serialize(s);\n\t}\n\n\t@Serial\n\tprivate void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {\n\t\tSerializedForm serializedForm = SerializedForm.deserialize(s);\n\t\tsegments = serializedForm.segments;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tUniqueId that = (UniqueId) o;\n\t\treturn this.segments.equals(that.segments);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint value = this.hashCode;\n\t\tif (value == 0) {\n\t\t\tvalue = this.segments.hashCode();\n\t\t\tif (value == 0) {\n\t\t\t\t// handle the edge case of the computed hashCode being 0\n\t\t\t\tvalue = 1;\n\t\t\t}\n\t\t\t// this is a benign race like String#hash\n\t\t\t// we potentially read and write values from multiple threads\n\t\t\t// without a happens-before relationship\n\t\t\t// however the JMM guarantees us that we only ever see values\n\t\t\t// that were valid at one point, either 0 or the hash code\n\t\t\t// so we might end up not seeing a value that a different thread\n\t\t\t// has computed or multiple threads writing the same value\n\t\t\tthis.hashCode = value;\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Generate the unique, formatted string representation of this {@code UniqueId}\n\t * using the configured {@link UniqueIdFormat}.\n\t */\n\t@Override\n\tpublic String toString() {\n\t\tSoftReference<String> s = this.toString;\n\t\tString value = s == null ? null : s.get();\n\t\tif (value == null) {\n\t\t\tvalue = UniqueIdFormat.getDefault().format(this);\n\t\t\t// this is a benign race like String#hash\n\t\t\t// we potentially read and write values from multiple threads\n\t\t\t// without a happens-before relationship\n\t\t\t// however the JMM guarantees us that we only ever see values\n\t\t\t// that were valid at one point, either null or the toString value\n\t\t\t// so we might end up not seeing a value that a different thread\n\t\t\t// has computed or multiple threads writing the same value\n\t\t\tthis.toString = new SoftReference<>(value);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * A segment of a {@link UniqueId} comprises a <em>type</em> and a\n\t * <em>value</em>.\n\t */\n\t@API(status = STABLE, since = \"1.0\")\n\tpublic static final class Segment implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tprivate final String type;\n\t\tprivate final String value;\n\n\t\t/**\n\t\t * Create a new {@code Segment} using the supplied {@code type} and\n\t\t * {@code value}.\n\t\t *\n\t\t * @param type the type of this segment\n\t\t * @param value the value of this segment\n\t\t */\n\t\tSegment(String type, String value) {\n\t\t\tPreconditions.notBlank(type, \"type must not be null or blank\");\n\t\t\tPreconditions.notBlank(value, \"value must not be null or blank\");\n\t\t\tthis.type = type;\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t/**\n\t\t * Get the type of this segment.\n\t\t */\n\t\tpublic String getType() {\n\t\t\treturn this.type;\n\t\t}\n\n\t\t/**\n\t\t * Get the value of this segment.\n\t\t */\n\t\tpublic String getValue() {\n\t\t\treturn this.value;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(this.type, this.value);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object o) {\n\t\t\tif (this == o) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tSegment that = (Segment) o;\n\t\t\treturn Objects.equals(this.type, that.type) && Objects.equals(this.value, that.value);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\t// @formatter:off\n\t\t\treturn new ToStringBuilder(this)\n\t\t\t\t\t.append(\"type\", this.type)\n\t\t\t\t\t.append(\"value\", this.value)\n\t\t\t\t\t.toString();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t/**\n\t * Represents the serialized output of {@code UniqueId}. The fields on this\n\t * class match the fields that {@code UniqueId} had prior to 6.1.\n\t */\n\tprivate static final class SerializedForm implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t@SuppressWarnings({ \"serial\", \"RedundantSuppression\" })\n\t\t// always used with serializable implementation (List.copyOf())\n\t\tprivate final List<Segment> segments;\n\t\tprivate final UniqueIdFormat uniqueIdFormat;\n\n\t\tSerializedForm(UniqueId uniqueId) {\n\t\t\tthis.segments = uniqueId.segments;\n\t\t\tthis.uniqueIdFormat = UniqueIdFormat.getDefault();\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate SerializedForm(ObjectInputStream.GetField fields) throws IOException, ClassNotFoundException {\n\t\t\tthis.segments = (List<Segment>) fields.get(\"segments\", null);\n\t\t\tthis.uniqueIdFormat = UniqueIdFormat.getDefault();\n\t\t}\n\n\t\tvoid serialize(ObjectOutputStream s) throws IOException {\n\t\t\tObjectOutputStream.PutField fields = s.putFields();\n\t\t\tfields.put(\"segments\", segments);\n\t\t\tfields.put(\"uniqueIdFormat\", uniqueIdFormat);\n\t\t\ts.writeFields();\n\t\t}\n\n\t\tstatic SerializedForm deserialize(ObjectInputStream s) throws IOException, ClassNotFoundException {\n\t\t\tObjectInputStream.GetField fields = s.readFields();\n\t\t\treturn new SerializedForm(fields);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.UniqueId.Segment;\n\n/**\n * Used to {@link #parse} a {@link UniqueId} from a string representation\n * or to {@link #format} a {@link UniqueId} into a string representation.\n *\n * @since 1.0\n */\nclass UniqueIdFormat implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate static final UniqueIdFormat defaultFormat = new UniqueIdFormat('[', ':', ']', '/');\n\n\tstatic UniqueIdFormat getDefault() {\n\t\treturn defaultFormat;\n\t}\n\n\tprivate static String quote(char c) {\n\t\treturn Pattern.quote(String.valueOf(c));\n\t}\n\n\tprivate static String encode(char c) {\n\t\treturn URLEncoder.encode(String.valueOf(c), StandardCharsets.UTF_8);\n\t}\n\n\tprivate final char openSegment;\n\tprivate final char closeSegment;\n\tprivate final char segmentDelimiter;\n\tprivate final char typeValueSeparator;\n\tprivate final Pattern segmentPattern;\n\tprivate final HashMap<Character, String> encodedCharacterMap = new HashMap<>();\n\n\tUniqueIdFormat(char openSegment, char typeValueSeparator, char closeSegment, char segmentDelimiter) {\n\t\tthis.openSegment = openSegment;\n\t\tthis.typeValueSeparator = typeValueSeparator;\n\t\tthis.closeSegment = closeSegment;\n\t\tthis.segmentDelimiter = segmentDelimiter;\n\t\tthis.segmentPattern = Pattern.compile(\n\t\t\t\"%s(.+)%s(.+)%s\".formatted(quote(openSegment), quote(typeValueSeparator), quote(closeSegment)),\n\t\t\tPattern.DOTALL);\n\n\t\t// Compute \"forbidden\" character encoding map.\n\t\t// Note that the map is always empty at this point. Thus the use of\n\t\t// computeIfAbsent() is purely syntactic sugar.\n\t\tencodedCharacterMap.computeIfAbsent('%', UniqueIdFormat::encode);\n\t\tencodedCharacterMap.computeIfAbsent('+', UniqueIdFormat::encode);\n\t\tencodedCharacterMap.computeIfAbsent(openSegment, UniqueIdFormat::encode);\n\t\tencodedCharacterMap.computeIfAbsent(typeValueSeparator, UniqueIdFormat::encode);\n\t\tencodedCharacterMap.computeIfAbsent(closeSegment, UniqueIdFormat::encode);\n\t\tencodedCharacterMap.computeIfAbsent(segmentDelimiter, UniqueIdFormat::encode);\n\t}\n\n\t/**\n\t * Parse a {@code UniqueId} from the supplied string representation.\n\t *\n\t * @return a properly constructed {@code UniqueId}\n\t * @throws JUnitException if the string cannot be parsed\n\t */\n\tUniqueId parse(String source) throws JUnitException {\n\t\tString[] parts = source.split(String.valueOf(this.segmentDelimiter));\n\t\tList<Segment> segments = Arrays.stream(parts).map(this::createSegment).toList();\n\t\treturn new UniqueId(segments);\n\t}\n\n\tprivate Segment createSegment(String segmentString) throws JUnitException {\n\t\tMatcher segmentMatcher = this.segmentPattern.matcher(segmentString);\n\t\tif (!segmentMatcher.matches()) {\n\t\t\tthrow new JUnitException(\"'%s' is not a well-formed UniqueId segment\".formatted(segmentString));\n\t\t}\n\t\tString type = decode(checkAllowed(segmentMatcher.group(1)));\n\t\tString value = decode(checkAllowed(segmentMatcher.group(2)));\n\t\treturn new Segment(type, value);\n\t}\n\n\tprivate String checkAllowed(String typeOrValue) {\n\t\tcheckDoesNotContain(typeOrValue, this.segmentDelimiter);\n\t\tcheckDoesNotContain(typeOrValue, this.typeValueSeparator);\n\t\tcheckDoesNotContain(typeOrValue, this.openSegment);\n\t\tcheckDoesNotContain(typeOrValue, this.closeSegment);\n\t\treturn typeOrValue;\n\t}\n\n\tprivate void checkDoesNotContain(String typeOrValue, char forbiddenCharacter) {\n\t\tPreconditions.condition(typeOrValue.indexOf(forbiddenCharacter) < 0,\n\t\t\t() -> \"type or value '%s' must not contain '%s'\".formatted(typeOrValue, forbiddenCharacter));\n\t}\n\n\t/**\n\t * Format and return the string representation of the supplied {@code UniqueId}.\n\t */\n\tString format(UniqueId uniqueId) {\n\t\t// @formatter:off\n\t\treturn uniqueId.getSegments().stream()\n\t\t\t.map(this::describe)\n\t\t\t.collect(joining(String.valueOf(this.segmentDelimiter)));\n\t\t// @formatter:on\n\t}\n\n\tprivate String describe(Segment segment) {\n\t\tString body = encode(segment.getType()) + typeValueSeparator + encode(segment.getValue());\n\t\treturn openSegment + body + closeSegment;\n\t}\n\n\tprivate String encode(String s) {\n\t\tStringBuilder builder = new StringBuilder(s.length());\n\t\tfor (int i = 0; i < s.length(); i++) {\n\t\t\tchar c = s.charAt(i);\n\t\t\tString value = encodedCharacterMap.get(c);\n\t\t\tif (value == null) {\n\t\t\t\tbuilder.append(c);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbuilder.append(value);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tprivate static String decode(String s) {\n\t\treturn URLDecoder.decode(s, StandardCharsets.UTF_8);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/AbstractClassNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * Abstract {@link ClassNameFilter} that servers as a superclass\n * for filters including or excluding fully qualified class names\n * based on pattern-matching.\n *\n * @since 1.0\n */\nabstract class AbstractClassNameFilter implements ClassNameFilter {\n\n\tprotected final List<Pattern> patterns;\n\tprotected final String patternDescription;\n\n\tAbstractClassNameFilter(String... patterns) {\n\t\tPreconditions.notEmpty(patterns, \"patterns array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(patterns, \"patterns array must not contain null elements\");\n\t\tthis.patterns = Arrays.stream(patterns).map(Pattern::compile).toList();\n\t\tthis.patternDescription = Arrays.stream(patterns).collect(joining(\"' OR '\", \"'\", \"'\"));\n\t}\n\n\t@Override\n\tpublic abstract Predicate<String> toPredicate();\n\n\tprotected Optional<Pattern> findMatchingPattern(String className) {\n\t\treturn this.patterns.stream().filter(pattern -> pattern.matcher(className).matches()).findAny();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.DiscoveryFilter;\n\n/**\n * {@link DiscoveryFilter} that is applied to the name of a {@link Class}.\n *\n * @since 1.0\n * @see #includeClassNamePatterns(String...)\n * @see #excludeClassNamePatterns(String...)\n * @see PackageNameFilter\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface ClassNameFilter extends DiscoveryFilter<String> {\n\n\t/**\n\t * Standard include pattern in the form of a regular expression that is\n\t * used to match against fully qualified class names:\n\t * {@value org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN}\n\t *\n\t * <p>This pattern matches against class names beginning with {@code Test}\n\t * or ending with {@code Test} or {@code Tests} (in any package).\n\t */\n\t// Implementation notes:\n\t// - Test.* :: \"Test\" prefix for classes in default package\n\t// - .+[.$]Test.* :: \"Test\" prefix for top-level and nested classes in a named package\n\t// - .*Tests? :: \"Test\" and \"Tests\" suffixes in any package\n\tString STANDARD_INCLUDE_PATTERN = \"^(Test.*|.+[.$]Test.*|.*Tests?)$\";\n\n\t/**\n\t * Create a new <em>include</em> {@link ClassNameFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a class matches against at least one of the patterns,\n\t * the class will be included in the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * class names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see #excludeClassNamePatterns(String...)\n\t */\n\tstatic ClassNameFilter includeClassNamePatterns(String... patterns) {\n\t\treturn new IncludeClassNameFilter(patterns);\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@link ClassNameFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a class matches against at least one of the patterns,\n\t * the class will be excluded from the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * class names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see #includeClassNamePatterns(String...)\n\t */\n\tstatic ClassNameFilter excludeClassNamePatterns(String... patterns) {\n\t\treturn new ExcludeClassNameFilter(patterns);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClassSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a {@link Class} or class name so\n * that {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * tests or containers based on classes.\n *\n * <p>If a Java {@link Class} reference is provided, the selector will return\n * that {@code Class} and its class name accordingly. If a class name is\n * provided, the selector will only attempt to lazily load the {@link Class}\n * if {@link #getJavaClass()} is invoked.\n *\n * <p>In this context, Java {@link Class} means anything that can be referenced\n * as a {@link Class} on the JVM &mdash; for example, classes from other JVM\n * languages such Groovy, Scala, etc.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectClass(String)\n * @see DiscoverySelectors#selectClass(Class)\n * @see org.junit.platform.engine.support.descriptor.ClassSource\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ClassSelector implements DiscoverySelector {\n\n\tprivate final @Nullable ClassLoader classLoader;\n\n\tprivate final String className;\n\n\tprivate @Nullable Class<?> javaClass;\n\n\tClassSelector(@Nullable ClassLoader classLoader, String className) {\n\t\tthis.className = className;\n\t\tthis.classLoader = classLoader;\n\t}\n\n\tClassSelector(Class<?> javaClass) {\n\t\tthis.className = javaClass.getName();\n\t\tthis.classLoader = javaClass.getClassLoader();\n\t\tthis.javaClass = javaClass;\n\t}\n\n\t/**\n\t * Get the {@link ClassLoader} used to load the selected class.\n\t *\n\t * @return the {@code ClassLoader}; potentially {@code null}\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic @Nullable ClassLoader getClassLoader() {\n\t\treturn this.classLoader;\n\t}\n\n\t/**\n\t * Get the selected class name.\n\t */\n\tpublic String getClassName() {\n\t\treturn this.className;\n\t}\n\n\t/**\n\t * Get the selected {@link Class}.\n\t *\n\t * <p>If the {@link Class} was not provided, but only the name, this method\n\t * attempts to lazily load the {@link Class} based on its name and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t */\n\tpublic Class<?> getJavaClass() {\n\t\tif (this.javaClass == null) {\n\t\t\t// @formatter:off\n\t\t\tTry<Class<?>> tryToLoadClass = this.classLoader == null\n\t\t\t\t? ReflectionSupport.tryToLoadClass(this.className)\n\t\t\t\t: ReflectionSupport.tryToLoadClass(this.className, this.classLoader);\n\t\t\tthis.javaClass = tryToLoadClass.getNonNullOrThrow(cause ->\n\t\t\t\tnew PreconditionViolationException(\"Could not load class with name: \" + this.className, cause));\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn this.javaClass;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClassSelector that = (ClassSelector) o;\n\t\treturn Objects.equals(this.className, that.className);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.className.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"className\", this.className)\n\t\t\t\t.append(\"classLoader\", this.classLoader)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.className));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link ClassSelector\n\t * ClassSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"class\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ClassSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectClass(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathResourceSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.Collections.unmodifiableSet;\nimport static java.util.stream.Collectors.toCollection;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.LinkedHashSet;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.support.ResourceSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects the name of a <em>classpath resource</em>\n * so that {@link org.junit.platform.engine.TestEngine TestEngines} can load resources\n * from the classpath &mdash; for example, to load XML or JSON files from the classpath,\n * potentially within JARs.\n *\n * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n * expected to modify the classpath, the classpath resource represented by this\n * selector must be on the classpath of the\n * {@linkplain Thread#getContextClassLoader() context class loader} of the\n * {@linkplain Thread thread} that uses it.\n *\n * <p>Note: Since Java 9, all resources are on the module path. Either in\n * named or unnamed modules. These resources are also considered to be\n * classpath resources.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectClasspathResource(String)\n * @see ClasspathRootSelector\n * @see #getClasspathResourceName()\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ClasspathResourceSelector implements DiscoverySelector {\n\n\tprivate final String classpathResourceName;\n\n\tprivate final @Nullable FilePosition position;\n\n\tprivate @Nullable Set<Resource> resources;\n\n\tClasspathResourceSelector(String classpathResourceName, @Nullable FilePosition position) {\n\t\tboolean startsWithSlash = classpathResourceName.startsWith(\"/\");\n\t\tthis.classpathResourceName = (startsWithSlash ? classpathResourceName.substring(1) : classpathResourceName);\n\t\tPreconditions.notBlank(this.classpathResourceName,\n\t\t\t\"classpath resource name must not be blank after removing leading slash\");\n\t\tthis.position = position;\n\t}\n\n\tClasspathResourceSelector(Set<? extends Resource> resources) {\n\t\tthis(resources.iterator().next().getName(), null);\n\t\tthis.resources = unmodifiableSet(new LinkedHashSet<>(resources));\n\t}\n\n\t/**\n\t * Get the name of the selected classpath resource.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * @see ClassLoader#getResource(String)\n\t * @see ClassLoader#getResourceAsStream(String)\n\t * @see ClassLoader#getResources(String)\n\t */\n\tpublic String getClasspathResourceName() {\n\t\treturn this.classpathResourceName;\n\t}\n\n\t/**\n\t * Get the selected {@link Resource resources}.\n\t *\n\t * <p>If the {@link Resource resources} were not provided, but only their name,\n\t * this method attempts to lazily load the {@link Resource resources} based on\n\t * their name and throws a {@link PreconditionViolationException} if the\n\t * resource cannot be loaded.\n\t *\n\t * @since 1.12\n\t * @deprecated Please use {{@link #getResources()}} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic Set<org.junit.platform.commons.support.Resource> getClasspathResources() {\n\t\treturn getResources().stream() //\n\t\t\t\t.map(org.junit.platform.commons.support.Resource::of) //\n\t\t\t\t.collect(toCollection(LinkedHashSet::new));\n\t}\n\n\t/**\n\t * Get the selected {@link Resource resources}.\n\t *\n\t * <p>If the {@link Resource resources} were not provided, but only their name,\n\t * this method attempts to lazily load the {@link Resource resources} based on\n\t * their name and throws a {@link PreconditionViolationException} if the\n\t * resource cannot be loaded.\n\t *\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic Set<Resource> getResources() {\n\t\tif (this.resources == null) {\n\t\t\tTry<Set<Resource>> tryToGetResource = ResourceSupport.tryToGetResources(this.classpathResourceName);\n\t\t\tSet<Resource> classpathResources = tryToGetResource.getNonNullOrThrow( //\n\t\t\t\tcause -> new PreconditionViolationException( //\n\t\t\t\t\t\"Could not load resource(s) with name: \" + this.classpathResourceName, cause));\n\t\t\tif (classpathResources.isEmpty()) {\n\t\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\t\"Could not find any resource(s) with name: \" + this.classpathResourceName);\n\t\t\t}\n\t\t\tthis.resources = unmodifiableSet(classpathResources);\n\t\t}\n\t\treturn this.resources;\n\t}\n\n\t/**\n\t * Get the selected {@code FilePosition} within the classpath resource.\n\t */\n\tpublic Optional<FilePosition> getPosition() {\n\t\treturn Optional.ofNullable(this.position);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClasspathResourceSelector that = (ClasspathResourceSelector) o;\n\t\treturn Objects.equals(this.classpathResourceName, that.classpathResourceName)\n\t\t\t\t&& Objects.equals(this.position, that.position);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.classpathResourceName, this.position);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"classpathResourceName\", this.classpathResourceName)\n\t\t\t\t.append(\"position\", this.position)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\tif (this.position == null) {\n\t\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.classpathResourceName));\n\t\t}\n\t\telse {\n\t\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX,\n\t\t\t\t\"%s?%s\".formatted(this.classpathResourceName, this.position.toQueryPart())));\n\t\t}\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link ClasspathResourceSelector ClasspathResourceSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"resource\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ClasspathResourceSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(StringUtils.splitIntoTwo('?', identifier.getValue()).map( //\n\t\t\t\tDiscoverySelectors::selectClasspathResource, //\n\t\t\t\t(resourceName, query) -> {\n\t\t\t\t\tFilePosition position = FilePosition.fromQuery(query).orElse(null);\n\t\t\t\t\treturn DiscoverySelectors.selectClasspathResource(resourceName, position);\n\t\t\t\t} //\n\t\t\t));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ClasspathRootSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.commons.util.CollectionUtils.getFirstElement;\n\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a <em>classpath root</em> so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can search for class\n * files or resources within the physical classpath &mdash; for example, to\n * scan for test classes.\n *\n * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n * expected to modify the classpath, the classpath root represented by this\n * selector must be on the classpath of the\n * {@linkplain Thread#getContextClassLoader() context class loader} of the\n * {@linkplain Thread thread} that uses this selector.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectClasspathRoots(java.util.Set)\n * @see ClasspathResourceSelector\n * @see Thread#getContextClassLoader()\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ClasspathRootSelector implements DiscoverySelector {\n\n\tprivate final URI classpathRoot;\n\n\tClasspathRootSelector(URI classpathRoot) {\n\t\tthis.classpathRoot = Preconditions.notNull(classpathRoot, \"classpathRoot must not be null\");\n\t}\n\n\t/**\n\t * Get the selected classpath root directory as an {@link URI}.\n\t */\n\tpublic URI getClasspathRoot() {\n\t\treturn this.classpathRoot;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClasspathRootSelector that = (ClasspathRootSelector) o;\n\t\treturn Objects.equals(this.classpathRoot, that.classpathRoot);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.classpathRoot.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"classpathRoot\", this.classpathRoot).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.classpathRoot.toString()));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link ClasspathRootSelector ClasspathRootSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"classpath-root\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ClasspathRootSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\tPath path = Path.of(URI.create(identifier.getValue()));\n\t\t\treturn getFirstElement(DiscoverySelectors.selectClasspathRoots(Set.of(path)));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DirectorySelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a directory so that\n * {@link org.junit.platform.engine.TestEngine TestEngines}\n * can discover tests or containers based on directories in the\n * file system.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectDirectory(String)\n * @see DiscoverySelectors#selectDirectory(File)\n * @see FileSelector\n * @see #getDirectory()\n * @see #getPath()\n * @see #getRawPath()\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class DirectorySelector implements DiscoverySelector {\n\n\tprivate final String path;\n\n\tDirectorySelector(String path) {\n\t\tthis.path = path;\n\t}\n\n\t/**\n\t * Get the selected directory as a {@link java.io.File}.\n\t *\n\t * @see #getPath()\n\t * @see #getRawPath()\n\t */\n\tpublic File getDirectory() {\n\t\treturn new File(this.path);\n\t}\n\n\t/**\n\t * Get the selected directory as a {@link java.nio.file.Path} using the\n\t * {@linkplain FileSystems#getDefault default} {@link FileSystem}.\n\t *\n\t * @see #getDirectory()\n\t * @see #getRawPath()\n\t */\n\tpublic Path getPath() {\n\t\treturn Path.of(this.path);\n\t}\n\n\t/**\n\t * Get the selected directory as a <em>raw path</em>.\n\t *\n\t * @see #getDirectory()\n\t * @see #getPath()\n\t */\n\tpublic String getRawPath() {\n\t\treturn this.path;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tDirectorySelector that = (DirectorySelector) o;\n\t\treturn Objects.equals(this.path, that.path);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.path.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"path\", this.path).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.path));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link DirectorySelector DirectorySelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"directory\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<DirectorySelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectDirectory(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParser.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * Parser for a {@link DiscoverySelectorIdentifier} with a specific prefix.\n *\n * <p>Implementations of this interface can be registered using the Java service\n * loader mechanism to extend the set of supported prefixes for\n * {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}.\n *\n * @since 1.11\n * @see DiscoverySelectors#parse(String)\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic interface DiscoverySelectorIdentifierParser {\n\n\t/**\n\t * Get the prefix that this parser supports.\n\t *\n\t * @return the prefix that this parser supports; never {@code null} or blank\n\t */\n\tString getPrefix();\n\n\t/**\n\t * Parse the supplied {@link DiscoverySelectorIdentifier}.\n\t *\n\t * <p>The JUnit Platform will only invoke this method if the supplied\n\t * {@link DiscoverySelectorIdentifier} has a prefix that matches the value\n\t * returned by {@link #getPrefix()}.\n\t *\n\t * @param identifier the {@link DiscoverySelectorIdentifier} to parse\n\t * @param context the {@link Context} to use for parsing\n\t * @return an {@link Optional} containing the parsed {@link DiscoverySelector};\n\t * never {@code null} but potentially empty\n\t */\n\tOptional<? extends DiscoverySelector> parse(DiscoverySelectorIdentifier identifier, Context context);\n\n\t/**\n\t * Context for parsing {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}.\n\t */\n\tinterface Context {\n\n\t\t/**\n\t\t * Parse the supplied selector.\n\t\t *\n\t\t * <p>This method is intended to be used by implementations of\n\t\t * {@link DiscoverySelectorIdentifierParser#parse} for selectors that\n\t\t * contain other selectors.\n\t\t */\n\t\tOptional<? extends DiscoverySelector> parse(String selector);\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectorIdentifierParsers.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.Collections.unmodifiableMap;\nimport static java.util.Objects.requireNonNull;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.ServiceLoader;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * Utility class for parsing {@link DiscoverySelectorIdentifier\n * DiscoverySelectorIdentifiers}.\n *\n * @since 1.11\n */\nclass DiscoverySelectorIdentifierParsers {\n\n\tstatic Stream<? extends DiscoverySelector> parseAll(String... identifiers) {\n\t\tPreconditions.notNull(identifiers, \"identifiers must not be null\");\n\t\treturn Stream.of(identifiers) //\n\t\t\t\t.map(DiscoverySelectorIdentifierParsers::parse) //\n\t\t\t\t.flatMap(Optional::stream);\n\t}\n\n\tstatic Stream<? extends DiscoverySelector> parseAll(Collection<DiscoverySelectorIdentifier> identifiers) {\n\t\tPreconditions.notNull(identifiers, \"identifiers must not be null\");\n\t\treturn identifiers.stream() //\n\t\t\t\t.map(DiscoverySelectorIdentifierParsers::parse) //\n\t\t\t\t.flatMap(Optional::stream);\n\t}\n\n\tstatic Optional<? extends DiscoverySelector> parse(String identifier) {\n\t\tPreconditions.notNull(identifier, \"identifier must not be null\");\n\t\treturn parse(DiscoverySelectorIdentifier.parse(identifier));\n\t}\n\n\tstatic Optional<? extends DiscoverySelector> parse(DiscoverySelectorIdentifier identifier) {\n\t\tPreconditions.notNull(identifier, \"identifier must not be null\");\n\t\tDiscoverySelectorIdentifierParser parser = Preconditions.notNull(\n\t\t\tSingleton.INSTANCE.parsersByPrefix.get(identifier.getPrefix()),\n\t\t\t\"No parser for prefix: \" + identifier.getPrefix());\n\n\t\treturn parser.parse(identifier, DiscoverySelectorIdentifierParsers::parse);\n\t}\n\n\tprivate enum Singleton {\n\n\t\tINSTANCE;\n\n\t\tprivate final Map<String, DiscoverySelectorIdentifierParser> parsersByPrefix;\n\n\t\tSingleton() {\n\t\t\tMap<String, DiscoverySelectorIdentifierParser> parsersByPrefix = new HashMap<>();\n\t\t\tIterable<DiscoverySelectorIdentifierParser> loadedParsers = ServiceLoader.load(\n\t\t\t\tDiscoverySelectorIdentifierParser.class, ClassLoaderUtils.getDefaultClassLoader());\n\t\t\tfor (DiscoverySelectorIdentifierParser parser : loadedParsers) {\n\t\t\t\tDiscoverySelectorIdentifierParser previous = parsersByPrefix.put(parser.getPrefix(), parser);\n\t\t\t\tPreconditions.condition(previous == null,\n\t\t\t\t\t() -> \"Duplicate parser for prefix: [%s]; candidate a: [%s]; candidate b: [%s]\".formatted(\n\t\t\t\t\t\tparser.getPrefix(), requireNonNull(previous).getClass().getName(),\n\t\t\t\t\t\tparser.getClass().getName()));\n\t\t\t}\n\t\t\tthis.parsersByPrefix = unmodifiableMap(parsersByPrefix);\n\t\t}\n\n\t}\n\n\tprivate DiscoverySelectorIdentifierParsers() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/DiscoverySelectors.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Collection of {@code static} factory methods for creating\n * {@link DiscoverySelector DiscoverySelectors}.\n *\n * @since 1.0\n * @see UriSelector\n * @see FileSelector\n * @see DirectorySelector\n * @see ClasspathRootSelector\n * @see ClasspathResourceSelector\n * @see ModuleSelector\n * @see PackageSelector\n * @see ClassSelector\n * @see MethodSelector\n * @see NestedClassSelector\n * @see NestedMethodSelector\n * @see UniqueIdSelector\n * @see DiscoverySelectorIdentifier\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class DiscoverySelectors {\n\n\tprivate DiscoverySelectors() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Create a {@code UriSelector} for the supplied URI.\n\t *\n\t * @param uri the URI to select; never {@code null} or blank\n\t * @see UriSelector\n\t * @see #selectUri(URI)\n\t * @see #selectFile(String)\n\t * @see #selectFile(File)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static UriSelector selectUri(String uri) {\n\t\tPreconditions.notBlank(uri, \"URI must not be null or blank\");\n\t\ttry {\n\t\t\treturn new UriSelector(new URI(uri));\n\t\t}\n\t\tcatch (URISyntaxException ex) {\n\t\t\tthrow new PreconditionViolationException(\"Failed to create a java.net.URI from: \" + uri, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Create a {@code UriSelector} for the supplied {@link URI}.\n\t *\n\t * @param uri the URI to select; never {@code null}\n\t * @see UriSelector\n\t * @see #selectUri(String)\n\t * @see #selectFile(String)\n\t * @see #selectFile(File)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static UriSelector selectUri(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t\treturn new UriSelector(uri);\n\t}\n\n\t/**\n\t * Create a {@code FileSelector} for the supplied file path.\n\t *\n\t * <p>This method selects the file using the supplied path <em>as is</em>,\n\t * without verifying if the file exists.\n\t *\n\t * @param path the path to the file to select; never {@code null} or blank\n\t * @see FileSelector\n\t * @see #selectFile(File)\n\t * @see #selectFile(String, FilePosition)\n\t * @see #selectFile(File, FilePosition)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static FileSelector selectFile(String path) {\n\t\treturn selectFile(path, null);\n\t}\n\n\t/**\n\t * Create a {@code FileSelector} for the supplied {@linkplain File file}.\n\t *\n\t * <p>This method selects the file in its {@linkplain File#getCanonicalPath()\n\t * canonical} form and throws a {@link PreconditionViolationException} if the\n\t * file does not exist.\n\t *\n\t * @param file the file to select; never {@code null}\n\t * @see FileSelector\n\t * @see #selectFile(String)\n\t * @see #selectFile(File, FilePosition)\n\t * @see #selectFile(String, FilePosition)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static FileSelector selectFile(File file) {\n\t\treturn selectFile(file, null);\n\t}\n\n\t/**\n\t * Create a {@code FileSelector} for the supplied file path.\n\t *\n\t * <p>This method selects the file using the supplied path <em>as is</em>,\n\t * without verifying if the file exists.\n\t *\n\t * @param path the path to the file to select; never {@code null} or blank\n\t * @param position the position inside the file; may be {@code null}\n\t * @see FileSelector\n\t * @see #selectFile(String)\n\t * @see #selectFile(File)\n\t * @see #selectFile(File, FilePosition)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static FileSelector selectFile(String path, @Nullable FilePosition position) {\n\t\tPreconditions.notBlank(path, \"File path must not be null or blank\");\n\t\treturn new FileSelector(path, position);\n\t}\n\n\t/**\n\t * Create a {@code FileSelector} for the supplied {@linkplain File file}.\n\t *\n\t * <p>This method selects the file in its {@linkplain File#getCanonicalPath()\n\t * canonical} form and throws a {@link PreconditionViolationException} if the\n\t * file does not exist.\n\t *\n\t * @param file the file to select; never {@code null}\n\t * @param position the position inside the file; may be {@code null}\n\t * @see FileSelector\n\t * @see #selectFile(File)\n\t * @see #selectFile(String)\n\t * @see #selectFile(String, FilePosition)\n\t * @see #selectDirectory(String)\n\t * @see #selectDirectory(File)\n\t */\n\tpublic static FileSelector selectFile(File file, @Nullable FilePosition position) {\n\t\tPreconditions.notNull(file, \"File must not be null\");\n\t\tPreconditions.condition(file.isFile(),\n\t\t\t() -> \"The supplied java.io.File [%s] must represent an existing file\".formatted(file));\n\t\ttry {\n\t\t\treturn new FileSelector(file.getCanonicalPath(), position);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new PreconditionViolationException(\"Failed to retrieve canonical path for file: \" + file, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Create a {@code DirectorySelector} for the supplied directory path.\n\t *\n\t * <p>This method selects the directory using the supplied path <em>as is</em>,\n\t * without verifying if the directory exists.\n\t *\n\t * @param path the path to the directory to select; never {@code null} or blank\n\t * @see DirectorySelector\n\t * @see #selectDirectory(File)\n\t * @see #selectFile(String)\n\t * @see #selectFile(File)\n\t */\n\tpublic static DirectorySelector selectDirectory(String path) {\n\t\tPreconditions.notBlank(path, \"Directory path must not be null or blank\");\n\t\treturn new DirectorySelector(path);\n\t}\n\n\t/**\n\t * Create a {@code DirectorySelector} for the supplied {@linkplain File directory}.\n\t *\n\t * <p>This method selects the directory in its {@linkplain File#getCanonicalPath()\n\t * canonical} form and throws a {@link PreconditionViolationException} if the\n\t * directory does not exist.\n\t *\n\t * @param directory the directory to select; never {@code null}\n\t * @see DirectorySelector\n\t * @see #selectDirectory(String)\n\t * @see #selectFile(String)\n\t * @see #selectFile(File)\n\t */\n\tpublic static DirectorySelector selectDirectory(File directory) {\n\t\tPreconditions.notNull(directory, \"Directory must not be null\");\n\t\tPreconditions.condition(directory.isDirectory(),\n\t\t\t() -> \"The supplied java.io.File [%s] must represent an existing directory\".formatted(directory));\n\t\ttry {\n\t\t\treturn new DirectorySelector(directory.getCanonicalPath());\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new PreconditionViolationException(\"Failed to retrieve canonical path for directory: \" + directory,\n\t\t\t\tex);\n\t\t}\n\t}\n\n\t/**\n\t * Create a list of {@code ClasspathRootSelectors} for the supplied\n\t * <em>classpath roots</em> (directories or JAR files).\n\t *\n\t * <p>Since the supplied paths are converted to {@link URI URIs}, the\n\t * {@link java.nio.file.FileSystem} that created them must be the\n\t * {@linkplain java.nio.file.FileSystems#getDefault() default} or one that\n\t * has been created by an installed\n\t * {@link java.nio.file.spi.FileSystemProvider}.\n\t *\n\t * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n\t * expected to modify the classpath, the classpath roots represented by the\n\t * resulting selectors must be on the classpath of the\n\t * {@linkplain Thread#getContextClassLoader() context class loader} of the\n\t * {@linkplain Thread thread} that uses these selectors.\n\t *\n\t * <p>The {@link Set} supplied to this method should have a reliable iteration\n\t * order to support reliable discovery and execution order. It is therefore\n\t * recommended that the set be a {@link java.util.SequencedSet} (on Java 21\n\t * or higher), {@link java.util.SortedSet}, {@link java.util.LinkedHashSet},\n\t * or similar. Note that {@link Set#of(Object[])} and related {@code Set.of()}\n\t * methods do not guarantee a reliable iteration order.\n\t *\n\t * @param classpathRoots set of directories and JAR files in the filesystem\n\t * that represent classpath roots; never {@code null}\n\t * @return a list of selectors for the supplied classpath roots; elements\n\t * which do not physically exist in the filesystem will be filtered out\n\t * @see ClasspathRootSelector\n\t * @see Thread#getContextClassLoader()\n\t */\n\tpublic static List<ClasspathRootSelector> selectClasspathRoots(Set<Path> classpathRoots) {\n\t\tPreconditions.notNull(classpathRoots, \"classpathRoots must not be null\");\n\n\t\t// @formatter:off\n\t\treturn classpathRoots.stream()\n\t\t\t\t.filter(Files::exists)\n\t\t\t\t.map(Path::toUri)\n\t\t\t\t.map(ClasspathRootSelector::new)\n\t\t\t\t// unmodifiable since selectClasspathRoots is a public, non-internal method\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for the supplied classpath\n\t * resource name.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n\t * expected to modify the classpath, the supplied classpath resource must be\n\t * on the classpath of the\n\t * {@linkplain Thread#getContextClassLoader() context class loader} of the\n\t * {@linkplain Thread thread} that uses the resulting selector.\n\t *\n\t * @param classpathResourceName the name of the classpath resource; never\n\t * {@code null} or blank\n\t * @see #selectClasspathResource(String, FilePosition)\n\t * @see #selectClasspathResources(String...)\n\t * @see #selectClasspathResourceByName(Set)\n\t * @see ClasspathResourceSelector\n\t * @see ClassLoader#getResource(String)\n\t * @see ClassLoader#getResourceAsStream(String)\n\t * @see ClassLoader#getResources(String)\n\t */\n\tpublic static ClasspathResourceSelector selectClasspathResource(String classpathResourceName) {\n\t\treturn selectClasspathResource(classpathResourceName, null);\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for the supplied classpath\n\t * resource name.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n\t * expected to modify the classpath, the supplied classpath resource must be\n\t * on the classpath of the\n\t * {@linkplain Thread#getContextClassLoader() context class loader} of the\n\t * {@linkplain Thread thread} that uses the resulting selector.\n\t *\n\t * @param classpathResourceName the name of the classpath resource; never\n\t * {@code null} or blank\n\t * @param position the position inside the classpath resource; may be {@code null}\n\t * @see #selectClasspathResource(String)\n\t * @see #selectClasspathResources(String...)\n\t * @see #selectClasspathResourceByName(Set)\n\t * @see ClasspathResourceSelector\n\t * @see ClassLoader#getResource(String)\n\t * @see ClassLoader#getResourceAsStream(String)\n\t * @see ClassLoader#getResources(String)\n\t */\n\tpublic static ClasspathResourceSelector selectClasspathResource(String classpathResourceName,\n\t\t\t@Nullable FilePosition position) {\n\t\tPreconditions.notBlank(classpathResourceName, \"classpath resource name must not be null or blank\");\n\t\treturn new ClasspathResourceSelector(classpathResourceName, position);\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for the supplied classpath\n\t * resources.\n\t *\n\t * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n\t * expected to modify the classpath, the supplied resource must be on the\n\t * classpath of the\n\t * {@linkplain Thread#getContextClassLoader() context class loader} of the\n\t * {@linkplain Thread thread} that uses the resulting selector.\n\t *\n\t * <p>Note: Since Java 9, all resources are on the module path. Either in\n\t * named or unnamed modules. These resources are also considered to be\n\t * classpath resources.\n\t *\n\t * <p>The {@link Set} supplied to this method should have a reliable iteration\n\t * order to support reliable discovery and execution order. It is therefore\n\t * recommended that the set be a {@link java.util.SequencedSet} (on Java 21\n\t * or higher), {@link java.util.SortedSet}, {@link java.util.LinkedHashSet},\n\t * or similar. Note that {@link Set#of(Object[])} and related {@code Set.of()}\n\t * methods do not guarantee a reliable iteration order.\n\t *\n\t * @param classpathResources a set of classpath resources; never\n\t * {@code null} or empty. All resources must have the same name, may not\n\t * be {@code null} or blank.\n\t * @since 1.12\n\t * @see #selectClasspathResource(String, FilePosition)\n\t * @see #selectClasspathResource(String)\n\t * @see ClasspathResourceSelector\n\t * @see ReflectionSupport#tryToGetResources(String)\n\t * @deprecated Please use {@link #selectClasspathResourceByName(Set)} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t@SuppressWarnings(\"removal\")\n\tpublic static ClasspathResourceSelector selectClasspathResource(\n\t\t\tSet<org.junit.platform.commons.support.Resource> classpathResources) {\n\t\treturn selectClasspathResourceByName(classpathResources);\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for the supplied classpath\n\t * resources.\n\t *\n\t * <p>Since {@linkplain org.junit.platform.engine.TestEngine engines} are not\n\t * expected to modify the classpath, the supplied resource must be on the\n\t * classpath of the\n\t * {@linkplain Thread#getContextClassLoader() context class loader} of the\n\t * {@linkplain Thread thread} that uses the resulting selector.\n\t *\n\t * <p>Note: Since Java 9, all resources are on the module path. Either in\n\t * named or unnamed modules. These resources are also considered to be\n\t * classpath resources.\n\t *\n\t * <p>The {@link Set} supplied to this method should have a reliable iteration\n\t * order to support reliable discovery and execution order. It is therefore\n\t * recommended that the set be a {@link java.util.SequencedSet} (on Java 21\n\t * or higher), {@link java.util.SortedSet}, {@link java.util.LinkedHashSet},\n\t * or similar. Note that {@link Set#of(Object[])} and related {@code Set.of()}\n\t * methods do not guarantee a reliable iteration order.\n\t *\n\t * @param classpathResources a set of classpath resources; never\n\t * {@code null} or empty. All resources must have the same name, may not\n\t * be {@code null} or blank.\n\t * @since 1.14\n\t * @see #selectClasspathResource(String, FilePosition)\n\t * @see #selectClasspathResource(String)\n\t * @see ClasspathResourceSelector\n\t * @see org.junit.platform.commons.support.ResourceSupport#tryToGetResources(String)\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic static ClasspathResourceSelector selectClasspathResourceByName(Set<? extends Resource> classpathResources) {\n\t\tPreconditions.notEmpty(classpathResources, \"classpath resources must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(classpathResources, \"individual classpath resources must not be null\");\n\t\tList<String> resourceNames = classpathResources.stream().map(Resource::getName).distinct().toList();\n\t\tPreconditions.condition(resourceNames.size() == 1, \"all classpath resources must have the same name\");\n\t\tPreconditions.notBlank(resourceNames.get(0), \"classpath resource names must not be null or blank\");\n\t\treturn new ClasspathResourceSelector(classpathResources);\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for each supplied classpath\n\t * resource name.\n\t *\n\t * @param classpathResourceNames the names of the classpath resource; never\n\t * {@code null} and never containing {@code null} or blank references.\n\t * @since 6.1\n\t * @see #selectClasspathResource(String)\n\t * @see #selectClasspathResources(String...)\n\t * @see ClasspathResourceSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static List<ClasspathResourceSelector> selectClasspathResources(String... classpathResourceNames) {\n\t\tPreconditions.notNull(classpathResourceNames, \"classpathResourceNames must not be null\");\n\t\treturn selectClasspathResources(Arrays.asList(classpathResourceNames));\n\t}\n\n\t/**\n\t * Create a {@code ClasspathResourceSelector} for each supplied classpath\n\t * resource name.\n\t *\n\t * @param classpathResourceNames the names of the classpath resource; never\n\t * {@code null} and never containing {@code null} or blank references.\n\t * @since 6.1\n\t * @see #selectClasspathResource(String)\n\t * @see #selectClasspathResources(String...)\n\t * @see ClasspathResourceSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static List<ClasspathResourceSelector> selectClasspathResources(List<String> classpathResourceNames) {\n\t\tPreconditions.notNull(classpathResourceNames, \"classpathResourceNames must not be null\");\n\t\tPreconditions.containsNoBlankElements(classpathResourceNames,\n\t\t\t\"Individual classpathResourceNames must not be null or blank\");\n\t\treturn classpathResourceNames.stream() //\n\t\t\t\t.distinct() //\n\t\t\t\t.map(DiscoverySelectors::selectClasspathResource) //\n\t\t\t\t.toList();\n\t}\n\n\t/**\n\t * Create a {@code ModuleSelector} for the supplied module name.\n\t *\n\t * <p>The unnamed module is not supported.\n\t *\n\t * @param moduleName the module name to select; never {@code null} or blank\n\t * @since 1.1\n\t * @see ModuleSelector\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static ModuleSelector selectModule(String moduleName) {\n\t\tPreconditions.notBlank(moduleName, \"Module name must not be null or blank\");\n\t\treturn new ModuleSelector(moduleName.strip());\n\t}\n\n\t/**\n\t * Create a {@code ModuleSelector} for the supplied module.\n\t *\n\t * <p>The unnamed module is not supported.\n\t *\n\t * @param module the module to select; never {@code null} or <em>unnamed</em>\n\t * @since 6.1\n\t * @see ModuleSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static ModuleSelector selectModule(Module module) {\n\t\tPreconditions.notNull(module, \"Module must not be null\");\n\t\tPreconditions.condition(module.isNamed(), \"Module must be named\");\n\t\treturn new ModuleSelector(module);\n\t}\n\n\t/**\n\t * Create a list of {@code ModuleSelectors} for the supplied module names.\n\t *\n\t * <p>The unnamed module is not supported.\n\t *\n\t * <p>The {@link Set} supplied to this method should have a reliable iteration\n\t * order to support reliable discovery and execution order. It is therefore\n\t * recommended that the set be a {@link java.util.SequencedSet} (on Java 21\n\t * or higher), {@link java.util.SortedSet}, {@link java.util.LinkedHashSet},\n\t * or similar. Note that {@link Set#of(Object[])} and related {@code Set.of()}\n\t * methods do not guarantee a reliable iteration order.\n\t *\n\t * @param moduleNames the module names to select; never {@code null}, never\n\t * containing {@code null} or blank\n\t * @since 1.1\n\t * @see ModuleSelector\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static List<ModuleSelector> selectModules(Set<String> moduleNames) {\n\t\tPreconditions.notNull(moduleNames, \"Module names must not be null\");\n\t\tPreconditions.containsNoNullElements(moduleNames, \"Individual module name must not be null\");\n\n\t\t// @formatter:off\n\t\treturn moduleNames.stream()\n\t\t\t\t.map(DiscoverySelectors::selectModule)\n\t\t\t\t// unmodifiable since this is a public, non-internal method\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Create a {@code PackageSelector} for the supplied package name.\n\t *\n\t * <p>The default package is represented by an empty string ({@code \"\"}).\n\t *\n\t * @param packageName the package name to select; never {@code null} and\n\t * never containing whitespace only\n\t * @see PackageSelector\n\t */\n\tpublic static PackageSelector selectPackage(String packageName) {\n\t\tPreconditions.notNull(packageName, \"Package name must not be null\");\n\t\tPreconditions.condition(packageName.isEmpty() || !packageName.isBlank(),\n\t\t\t\"Package name must not contain only whitespace\");\n\t\treturn new PackageSelector(packageName.strip());\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for the supplied {@link Class}.\n\t *\n\t * @param clazz the class to select; never {@code null}\n\t * @see ClassSelector\n\t */\n\tpublic static ClassSelector selectClass(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn new ClassSelector(clazz);\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for the supplied class name.\n\t *\n\t * @param className the fully qualified name of the class to select; never\n\t * {@code null} or blank\n\t * @see ClassSelector\n\t */\n\tpublic static ClassSelector selectClass(String className) {\n\t\treturn selectClass(null, className);\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for the supplied class name and class loader.\n\t *\n\t * @param classLoader the class loader to use to load the class, or {@code null}\n\t * to signal that the default {@code ClassLoader} should be used\n\t * @param className the fully qualified name of the class to select; never\n\t * {@code null} or blank\n\t * @since 1.10\n\t * @see ClassSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static ClassSelector selectClass(@Nullable ClassLoader classLoader, String className) {\n\t\tPreconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\treturn new ClassSelector(classLoader, className);\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied {@link Class}.\n\t *\n\t * @param classes the classes to select; never {@code null} and never containing\n\t * {@code null} class references\n\t * @since 6.0\n\t * @see #selectClass(Class)\n\t * @see #selectClasses(List)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClasses(Class<?>... classes) {\n\t\tPreconditions.notNull(classes, \"classes must not be null\");\n\t\treturn selectClasses(Arrays.asList(classes));\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied {@link Class}.\n\t *\n\t * @param classes the classes to select; never {@code null} and never containing\n\t * {@code null} class references\n\t * @since 6.0\n\t * @see #selectClass(Class)\n\t * @see #selectClasses(Class...)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClasses(List<Class<?>> classes) {\n\t\tPreconditions.notNull(classes, \"classes must not be null\");\n\t\tPreconditions.containsNoNullElements(classes, \"Individual classes must not be null\");\n\n\t\t// @formatter:off\n\t\treturn classes.stream()\n\t\t\t\t.distinct()\n\t\t\t\t.map(DiscoverySelectors::selectClass)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied class name.\n\t *\n\t * @param classNames the fully qualified names of the classes to select;\n\t * never {@code null} and never containing {@code null} or blank names\n\t * @since 6.0\n\t * @see #selectClass(String)\n\t * @see #selectClassesByName(List)\n\t * @see #selectClassesByName(ClassLoader, String...)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClassesByName(String... classNames) {\n\t\tPreconditions.notNull(classNames, \"classNames must not be null\");\n\t\treturn selectClassesByName(Arrays.asList(classNames));\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied class name.\n\t *\n\t * @param classNames the fully qualified names of the classes to select;\n\t * never {@code null} and never containing {@code null} or blank names\n\t * @since 6.0\n\t * @see #selectClass(String)\n\t * @see #selectClassesByName(String...)\n\t * @see #selectClassesByName(ClassLoader, List)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClassesByName(List<String> classNames) {\n\t\tPreconditions.notNull(classNames, \"classNames must not be null\");\n\t\treturn selectClassesByName(null, classNames);\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied class name, using the\n\t * supplied class loader.\n\t *\n\t * @param classLoader the class loader to use to load the classes, or {@code null}\n\t * to signal that the default {@code ClassLoader} should be used\n\t * @param classNames the fully qualified names of the classes to select;\n\t * never {@code null} and never containing {@code null} or blank names\n\t * @since 6.0\n\t * @see #selectClass(ClassLoader, String)\n\t * @see #selectClassesByName(ClassLoader, List)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClassesByName(@Nullable ClassLoader classLoader, String... classNames) {\n\t\tPreconditions.notNull(classNames, \"classNames must not be null\");\n\t\treturn selectClassesByName(classLoader, Arrays.asList(classNames));\n\t}\n\n\t/**\n\t * Create a {@code ClassSelector} for each supplied class name, using the\n\t * supplied class loader.\n\t *\n\t * @param classLoader the class loader to use to load the classes, or {@code null}\n\t * to signal that the default {@code ClassLoader} should be used\n\t * @param classNames the fully qualified names of the classes to select;\n\t * never {@code null} and never containing {@code null} or blank names\n\t * @since 6.0\n\t * @see #selectClass(ClassLoader, String)\n\t * @see ClassSelector\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static List<ClassSelector> selectClassesByName(@Nullable ClassLoader classLoader, List<String> classNames) {\n\t\tPreconditions.notNull(classNames, \"classNames must not be null\");\n\t\tPreconditions.containsNoBlankElements(classNames, \"Individual class names must not be null or blank\");\n\n\t\t// @formatter:off\n\t\treturn classNames.stream()\n\t\t\t\t.distinct()\n\t\t\t\t.map(className -> selectClass(classLoader, className))\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied <em>fully qualified\n\t * method name</em>.\n\t *\n\t * <p>The following formats are supported.\n\t *\n\t * <ul>\n\t * <li>{@code [fully qualified class name]#[methodName]}</li>\n\t * <li>{@code [fully qualified class name]#[methodName](parameter type list)}\n\t * </ul>\n\t *\n\t * <p>The <em>parameter type list</em> is a comma-separated list of primitive\n\t * names or fully qualified class names for the types of parameters accepted\n\t * by the method.\n\t *\n\t * <p>Array parameter types may be specified using either the JVM's internal\n\t * String representation (e.g., {@code [[I} for {@code int[][]},\n\t * {@code [Ljava.lang.String;} for {@code java.lang.String[]}, etc.) or\n\t * <em>source code syntax</em> (e.g., {@code int[][]}, {@code java.lang.String[]},\n\t * etc.).\n\t *\n\t * <table class=\"plain\">\n\t * <caption>Examples</caption>\n\t * <tr><th>Method</th><th>Fully Qualified Method Name</th></tr>\n\t * <tr><td>{@code java.lang.String.chars()}</td><td>{@code java.lang.String#chars}</td></tr>\n\t * <tr><td>{@code java.lang.String.chars()}</td><td>{@code java.lang.String#chars()}</td></tr>\n\t * <tr><td>{@code java.lang.String.equalsIgnoreCase(String)}</td><td>{@code java.lang.String#equalsIgnoreCase(java.lang.String)}</td></tr>\n\t * <tr><td>{@code java.lang.String.substring(int, int)}</td><td>{@code java.lang.String#substring(int, int)}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code example.Calc#avg([I)}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code example.Calc#avg(int[])}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code example.Matrix#multiply([[D)}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code example.Matrix#multiply(double[][])}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code example.Service#process([Ljava.lang.String;)}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code example.Service#process(java.lang.String[])}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code example.Service#process([[Ljava.lang.String;)}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code example.Service#process(java.lang.String[][])}</td></tr>\n\t * </table>\n\t *\n\t * @param fullyQualifiedMethodName the fully qualified name of the method to\n\t * select; never {@code null} or blank\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(String fullyQualifiedMethodName) throws PreconditionViolationException {\n\t\treturn selectMethod((ClassLoader) null, fullyQualifiedMethodName);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied <em>fully qualified\n\t * method name</em> and class loader.\n\t *\n\t * <p>See {@link #selectMethod(String)} for the supported formats for a\n\t * fully qualified method name.\n\t *\n\t * @param classLoader the class loader to use to load the method's declaring\n\t * class, or {@code null} to signal that the default {@code ClassLoader}\n\t * should be used\n\t * @param fullyQualifiedMethodName the fully qualified name of the method to\n\t * select; never {@code null} or blank\n\t * @since 1.10\n\t * @see #selectMethod(String)\n\t * @see MethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static MethodSelector selectMethod(@Nullable ClassLoader classLoader, String fullyQualifiedMethodName)\n\t\t\tthrows PreconditionViolationException {\n\t\tString[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(fullyQualifiedMethodName);\n\t\treturn selectMethod(classLoader, methodParts[0], methodParts[1], methodParts[2]);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied class name and method name\n\t * using the default class loader.\n\t *\n\t * @param className the fully qualified name of the class in which the method\n\t * is declared, or a subclass thereof; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(String className, String methodName) {\n\t\treturn selectMethod((ClassLoader) null, className, methodName);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied class name, method name,\n\t * and class loader.\n\t *\n\t * @param classLoader the class loader to use to load the class, or {@code null}\n\t * to signal that the default {@code ClassLoader} should be used\n\t * @param className the fully qualified name of the class in which the method\n\t * is declared, or a subclass thereof; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @since 1.10\n\t * @see MethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static MethodSelector selectMethod(@Nullable ClassLoader classLoader, String className, String methodName) {\n\t\treturn selectMethod(classLoader, className, methodName, \"\");\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied class name, method name,\n\t * and parameter type names.\n\t *\n\t * <p>The parameter type names {@code String} is typically a comma-separated\n\t * list of atomic types, fully qualified class names, or array types; however,\n\t * the exact syntax depends on the underlying test engine.\n\t *\n\t * @param className the fully qualified name of the class in which the method\n\t * is declared, or a subclass thereof; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * parameters\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(String className, String methodName, String parameterTypeNames) {\n\t\treturn selectMethod(null, className, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied class name, method name,\n\t * parameter type names, and class loader.\n\t *\n\t * <p>The parameter type names {@code String} is typically a comma-separated\n\t * list of atomic types, fully qualified class names, or array types; however,\n\t * the exact syntax depends on the underlying test engine.\n\t *\n\t * @param classLoader the class loader to use to load the class, or {@code null}\n\t * to signal that the default {@code ClassLoader} should be used\n\t * @param className the fully qualified name of the class in which the method\n\t * is declared, or a subclass thereof; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * any parameters\n\t * @since 1.10\n\t * @see MethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static MethodSelector selectMethod(@Nullable ClassLoader classLoader, String className, String methodName,\n\t\t\tString parameterTypeNames) {\n\t\tPreconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypeNames, \"Parameter type names must not be null\");\n\t\treturn new MethodSelector(classLoader, className, methodName, parameterTypeNames.strip());\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied {@link Class} and method name.\n\t *\n\t * @param javaClass the class in which the method is declared, or a subclass thereof;\n\t * never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(Class<?> javaClass, String methodName) {\n\t\treturn selectMethod(javaClass, methodName, \"\");\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied {@link Class}, method name,\n\t * and parameter type names.\n\t *\n\t * <p>The parameter type names {@code String} is typically a comma-separated\n\t * list of atomic types, fully qualified class names, or array types; however,\n\t * the exact syntax depends on the underlying test engine.\n\t *\n\t * @param javaClass the class in which the method is declared, or a subclass thereof;\n\t * never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * any parameters\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(Class<?> javaClass, String methodName, String parameterTypeNames) {\n\t\tPreconditions.notNull(javaClass, \"Class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypeNames, \"Parameter type names must not be null\");\n\t\treturn new MethodSelector(javaClass, methodName, parameterTypeNames.strip());\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied class name, method name,\n\t * and parameter types.\n\t *\n\t * @param className the fully qualified name of the class in which the method\n\t * is declared, or a subclass thereof; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypes the formal parameter types of the method; never\n\t * {@code null} though potentially empty if the method does not declare parameters\n\t * @since 1.10\n\t * @see MethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static MethodSelector selectMethod(String className, String methodName, Class<?>... parameterTypes) {\n\t\tPreconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypes, \"Parameter types array must not be null\");\n\t\tPreconditions.containsNoNullElements(parameterTypes, \"Parameter types array must not contain null elements\");\n\t\treturn new MethodSelector(null, className, methodName, parameterTypes);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied {@link Class}, method name,\n\t * and parameter types.\n\t *\n\t * @param javaClass the class in which the method is declared, or a subclass thereof;\n\t * never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypes the formal parameter types of the method; never\n\t * {@code null} though potentially empty if the method does not declare parameters\n\t * @since 1.10\n\t * @see MethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static MethodSelector selectMethod(Class<?> javaClass, String methodName, Class<?>... parameterTypes) {\n\t\tPreconditions.notNull(javaClass, \"Class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypes, \"Parameter types array must not be null\");\n\t\tPreconditions.containsNoNullElements(parameterTypes, \"Parameter types array must not contain null elements\");\n\t\treturn new MethodSelector(javaClass, methodName, parameterTypes);\n\t}\n\n\t/**\n\t * Create a {@code MethodSelector} for the supplied {@link Class} and {@link Method}.\n\t *\n\t * @param javaClass the class in which the method is declared, or a subclass thereof;\n\t * never {@code null}\n\t * @param method the method to select; never {@code null}\n\t * @see MethodSelector\n\t */\n\tpublic static MethodSelector selectMethod(Class<?> javaClass, Method method) {\n\t\tPreconditions.notNull(javaClass, \"Class must not be null\");\n\t\tPreconditions.notNull(method, \"Method must not be null\");\n\t\treturn new MethodSelector(javaClass, method);\n\t}\n\n\t/**\n\t * Create a {@code NestedClassSelector} for the supplied nested {@link Class} and its\n\t * enclosing classes.\n\t *\n\t * @param enclosingClasses the path to the nested class to select; never {@code null} or empty\n\t * @param nestedClass the nested class to select; never {@code null}\n\t * @since 1.6\n\t * @see NestedClassSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedClassSelector selectNestedClass(List<Class<?>> enclosingClasses, Class<?> nestedClass) {\n\t\tPreconditions.notEmpty(enclosingClasses, \"Enclosing classes must not be null or empty\");\n\t\tPreconditions.notNull(nestedClass, \"Nested class must not be null\");\n\t\treturn new NestedClassSelector(enclosingClasses, nestedClass);\n\t}\n\n\t/**\n\t * Create a {@code NestedClassSelector} for the supplied class name and its enclosing\n\t * classes' names.\n\t *\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @since 1.6\n\t * @see NestedClassSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedClassSelector selectNestedClass(List<String> enclosingClassNames, String nestedClassName) {\n\t\treturn selectNestedClass(null, enclosingClassNames, nestedClassName);\n\t}\n\n\t/**\n\t * Create a {@code NestedClassSelector} for the supplied class name, its enclosing\n\t * classes' names, and class loader.\n\t *\n\t * @param classLoader the class loader to use to load the enclosing and nested classes, or\n\t * {@code null} to signal that the default {@code ClassLoader} should be used\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @since 1.10\n\t * @see NestedClassSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static NestedClassSelector selectNestedClass(@Nullable ClassLoader classLoader,\n\t\t\tList<String> enclosingClassNames, String nestedClassName) {\n\t\tPreconditions.notEmpty(enclosingClassNames, \"Enclosing class names must not be null or empty\");\n\t\tPreconditions.notBlank(nestedClassName, \"Nested class name must not be null or blank\");\n\t\treturn new NestedClassSelector(classLoader, enclosingClassNames, nestedClassName);\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested class name and method name.\n\t *\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @since 1.6\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<String> enclosingClassNames, String nestedClassName,\n\t\t\tString methodName) {\n\t\treturn selectNestedMethod(null, enclosingClassNames, nestedClassName, methodName);\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested class name, method name,\n\t * and class loader.\n\t *\n\t * @param classLoader the class loader to use to load the method's declaring\n\t * class, or {@code null} to signal that the default {@code ClassLoader}\n\t * should be used\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @since 1.10\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static NestedMethodSelector selectNestedMethod(@Nullable ClassLoader classLoader,\n\t\t\tList<String> enclosingClassNames, String nestedClassName, String methodName)\n\t\t\tthrows PreconditionViolationException {\n\t\tPreconditions.notEmpty(enclosingClassNames, \"Enclosing class names must not be null or empty\");\n\t\tPreconditions.notBlank(nestedClassName, \"Nested class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\treturn new NestedMethodSelector(classLoader, enclosingClassNames, nestedClassName, methodName, \"\");\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested class name, method name,\n\t * and parameter type names.\n\t *\n\t * <p>The parameter type names {@code String} is typically a comma-separated\n\t * list of atomic types, fully qualified class names, or array types; however,\n\t * the exact syntax depends on the underlying test engine.\n\t *\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * parameters\n\t * @since 1.6\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<String> enclosingClassNames, String nestedClassName,\n\t\t\tString methodName, String parameterTypeNames) {\n\t\treturn selectNestedMethod(null, enclosingClassNames, nestedClassName, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested class name, method name,\n\t * parameter type names, and class loader.\n\t *\n\t * @param classLoader the class loader to use to load the method's declaring\n\t * class, or {@code null} to signal that the default {@code ClassLoader}\n\t * should be used\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null} or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null} or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * parameters\n\t * @since 1.10\n\t * @see #selectNestedMethod(List, String, String, String)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static NestedMethodSelector selectNestedMethod(@Nullable ClassLoader classLoader,\n\t\t\tList<String> enclosingClassNames, String nestedClassName, String methodName, String parameterTypeNames) {\n\n\t\tPreconditions.notEmpty(enclosingClassNames, \"Enclosing class names must not be null or empty\");\n\t\tPreconditions.notBlank(nestedClassName, \"Nested class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypeNames, \"Parameter types must not be null\");\n\t\treturn new NestedMethodSelector(classLoader, enclosingClassNames, nestedClassName, methodName,\n\t\t\tparameterTypeNames.strip());\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied enclosing class names,\n\t * nested class name, method name, and parameter types.\n\t *\n\t * @param enclosingClassNames the names of the enclosing classes; never {@code null}\n\t * or empty\n\t * @param nestedClassName the name of the nested class to select; never {@code null}\n\t * or blank\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypes the formal parameter types of the method; never {@code null}\n\t * though potentially empty if the method does not declare parameters\n\t * @since 1.10\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<String> enclosingClassNames, String nestedClassName,\n\t\t\tString methodName, Class<?>... parameterTypes) {\n\n\t\tPreconditions.notEmpty(enclosingClassNames, \"Enclosing class names must not be null or empty\");\n\t\tPreconditions.notBlank(nestedClassName, \"Nested class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypes, \"Parameter types array must not be null\");\n\t\tPreconditions.containsNoNullElements(parameterTypes, \"Parameter types array must not contain null elements\");\n\t\treturn new NestedMethodSelector(null, enclosingClassNames, nestedClassName, methodName, parameterTypes);\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested {@link Class} and method name.\n\t *\n\t * @param enclosingClasses the path to the nested class to select; never {@code null} or empty\n\t * @param nestedClass the nested class to select; never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @since 1.6\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<Class<?>> enclosingClasses, Class<?> nestedClass,\n\t\t\tString methodName) {\n\n\t\treturn selectNestedMethod(enclosingClasses, nestedClass, methodName, \"\");\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied {@link Class}, method name,\n\t * and parameter type names.\n\t *\n\t * <p>The parameter type names {@code String} is typically a comma-separated\n\t * list of atomic types, fully qualified class names, or array types; however,\n\t * the exact syntax depends on the underlying test engine.\n\t *\n\t * @param enclosingClasses the path to the nested class to select; never {@code null} or empty\n\t * @param nestedClass the nested class to select; never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypeNames the parameter type names as a single string; never\n\t * {@code null} though potentially an empty string if the method does not declare\n\t * parameters\n\t * @since 1.6\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<Class<?>> enclosingClasses, Class<?> nestedClass,\n\t\t\tString methodName, String parameterTypeNames) {\n\n\t\tPreconditions.notEmpty(enclosingClasses, \"Enclosing classes must not be null or empty\");\n\t\tPreconditions.notNull(nestedClass, \"Nested class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypeNames, \"Parameter types must not be null\");\n\t\treturn new NestedMethodSelector(enclosingClasses, nestedClass, methodName, parameterTypeNames.strip());\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied enclosing classes,\n\t * nested class, method name, and parameter types.\n\t *\n\t * @param enclosingClasses the path to the nested class to select; never {@code null}\n\t * or empty\n\t * @param nestedClass the nested class to select; never {@code null}\n\t * @param methodName the name of the method to select; never {@code null} or blank\n\t * @param parameterTypes the formal parameter types of the method; never {@code null}\n\t * though potentially empty if the method does not declare parameters\n\t * @since 1.10\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<Class<?>> enclosingClasses, Class<?> nestedClass,\n\t\t\tString methodName, Class<?>... parameterTypes) {\n\n\t\tPreconditions.notEmpty(enclosingClasses, \"Enclosing classes must not be null or empty\");\n\t\tPreconditions.notNull(nestedClass, \"Nested class must not be null\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tPreconditions.notNull(parameterTypes, \"Parameter types array must not be null\");\n\t\tPreconditions.containsNoNullElements(parameterTypes, \"Parameter types array must not contain null elements\");\n\t\treturn new NestedMethodSelector(enclosingClasses, nestedClass, methodName, parameterTypes);\n\t}\n\n\t/**\n\t * Create a {@code NestedMethodSelector} for the supplied nested {@link Class} and {@link Method}.\n\t *\n\t * @param enclosingClasses the path to the nested class to select; never {@code null} or empty\n\t * @param nestedClass the nested class to select; never {@code null}\n\t * @param method the method to select; never {@code null}\n\t * @since 1.6\n\t * @see NestedMethodSelector\n\t */\n\t@API(status = STABLE, since = \"1.6\")\n\tpublic static NestedMethodSelector selectNestedMethod(List<Class<?>> enclosingClasses, Class<?> nestedClass,\n\t\t\tMethod method) {\n\n\t\tPreconditions.notEmpty(enclosingClasses, \"Enclosing classes must not be null or empty\");\n\t\tPreconditions.notNull(nestedClass, \"Nested class must not be null\");\n\t\tPreconditions.notNull(method, \"Method must not be null\");\n\t\treturn new NestedMethodSelector(enclosingClasses, nestedClass, method);\n\t}\n\n\t/**\n\t * Create a {@code UniqueIdSelector} for the supplied {@link UniqueId}.\n\t *\n\t * @param uniqueId the {@code UniqueId} to select; never {@code null}\n\t * @see UniqueIdSelector\n\t */\n\tpublic static UniqueIdSelector selectUniqueId(UniqueId uniqueId) {\n\t\tPreconditions.notNull(uniqueId, \"UniqueId must not be null\");\n\t\treturn new UniqueIdSelector(uniqueId);\n\t}\n\n\t/**\n\t * Create a {@code UniqueIdSelector} for the supplied unique ID.\n\t *\n\t * @param uniqueId the unique ID to select; never {@code null} or blank\n\t * @see UniqueIdSelector\n\t */\n\tpublic static UniqueIdSelector selectUniqueId(String uniqueId) {\n\t\tPreconditions.notBlank(uniqueId, \"Unique ID must not be null or blank\");\n\t\treturn new UniqueIdSelector(UniqueId.parse(uniqueId));\n\t}\n\n\t/**\n\t * Create an {@code IterationSelector} for the supplied parent selector and\n\t * iteration indices.\n\t *\n\t * @param parentSelector the parent selector to select iterations for; never\n\t * {@code null}\n\t * @param iterationIndices the iteration indices to select; never {@code null}\n\t * or empty\n\t * @since 1.9\n\t * @see IterationSelector\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static IterationSelector selectIteration(DiscoverySelector parentSelector, int... iterationIndices) {\n\t\tPreconditions.notNull(parentSelector, \"Parent selector must not be null\");\n\t\tPreconditions.notEmpty(iterationIndices, \"iteration indices must not be empty\");\n\t\treturn new IterationSelector(parentSelector, iterationIndices);\n\t}\n\n\t/**\n\t * Parse the supplied string representation of a {@link DiscoverySelectorIdentifier}.\n\t *\n\t * @param identifier the string representation of a {@code DiscoverySelectorIdentifier};\n\t * never {@code null} or blank\n\t * @return an {@link Optional} containing the corresponding {@link DiscoverySelector};\n\t * never {@code null} but potentially empty\n\t * @since 1.11\n\t * @see DiscoverySelectorIdentifierParser\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Optional<? extends DiscoverySelector> parse(String identifier) {\n\t\treturn DiscoverySelectorIdentifierParsers.parse(identifier);\n\t}\n\n\t/**\n\t * Parse the supplied {@link DiscoverySelectorIdentifier}.\n\t *\n\t * @param identifier the {@code DiscoverySelectorIdentifier} to parse;\n\t * never {@code null}\n\t * @return an {@link Optional} containing the corresponding {@link DiscoverySelector};\n\t * never {@code null} but potentially empty\n\t * @since 1.11\n\t * @see DiscoverySelectorIdentifierParser\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Optional<? extends DiscoverySelector> parse(DiscoverySelectorIdentifier identifier) {\n\t\treturn DiscoverySelectorIdentifierParsers.parse(identifier);\n\t}\n\n\t/**\n\t * Parse the supplied string representations of\n\t * {@link DiscoverySelectorIdentifier DiscoverySelectorIdentifiers}.\n\t *\n\t * @param identifiers the string representations of\n\t * {@code DiscoverySelectorIdentifiers} to parse; never {@code null}\n\t * @return a stream of the corresponding {@link DiscoverySelector DiscoverySelectors};\n\t * never {@code null} but potentially empty\n\t * @since 1.11\n\t * @see DiscoverySelectorIdentifierParser\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Stream<? extends DiscoverySelector> parseAll(String... identifiers) {\n\t\treturn DiscoverySelectorIdentifierParsers.parseAll(identifiers);\n\t}\n\n\t/**\n\t * Parse the supplied {@link DiscoverySelectorIdentifier\n\t * DiscoverySelectorIdentifiers}.\n\t *\n\t * @param identifiers the {@code DiscoverySelectorIdentifiers} to parse;\n\t * never {@code null}\n\t * @return a stream of the corresponding {@link DiscoverySelector DiscoverySelectors};\n\t * never {@code null} but potentially empty\n\t * @since 1.11\n\t * @see DiscoverySelectorIdentifierParser\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Stream<? extends DiscoverySelector> parseAll(Collection<DiscoverySelectorIdentifier> identifiers) {\n\t\treturn DiscoverySelectorIdentifierParsers.parseAll(identifiers);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludeClassNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * {@link ClassNameFilter} that matches fully qualified class names against\n * patterns in the form of regular expressions.\n *\n * <p>If the fully qualified name of a class matches against at least one\n * pattern, the class will be excluded.\n *\n * @since 1.0\n */\nclass ExcludeClassNameFilter extends AbstractClassNameFilter {\n\n\tExcludeClassNameFilter(String... patterns) {\n\t\tsuper(patterns);\n\t}\n\n\t@Override\n\tpublic FilterResult apply(String className) {\n\t\treturn findMatchingPattern(className) //\n\t\t\t\t.map(pattern -> excluded(formatExclusionReason(className, pattern))) //\n\t\t\t\t.orElseGet(() -> included(formatInclusionReason(className)));\n\t}\n\n\tprivate String formatInclusionReason(String className) {\n\t\treturn \"Class name [%s] does not match any excluded pattern: %s\".formatted(className, patternDescription);\n\t}\n\n\tprivate String formatExclusionReason(String className, Pattern pattern) {\n\t\treturn \"Class name [%s] matches excluded pattern: '%s'\".formatted(className, pattern);\n\t}\n\n\t@Override\n\tpublic Predicate<String> toPredicate() {\n\t\treturn className -> findMatchingPattern(className).isEmpty();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that excludes class names that match one of the following regular expressions: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), this.patternDescription);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ExcludePackageNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * {@link PackageNameFilter} that matches fully qualified package names that\n * are <em>not</em> prefixed by one of the package names provided to the filter.\n *\n * <p>If the fully qualified name of a package starts with at least one of the\n * packages names of the filter, the package will be excluded.\n *\n * @since 1.0\n */\nclass ExcludePackageNameFilter implements PackageNameFilter {\n\n\tprivate final List<String> packageNames;\n\tprivate final String patternDescription;\n\n\tExcludePackageNameFilter(String... packageNames) {\n\t\tPreconditions.notEmpty(packageNames, \"packageNames must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(packageNames, \"packageNames must not contain null elements\");\n\t\tthis.packageNames = Arrays.asList(packageNames);\n\t\tthis.patternDescription = Arrays.stream(packageNames).collect(joining(\"' OR '\", \"'\", \"'\"));\n\t}\n\n\t@Override\n\tpublic FilterResult apply(String packageName) {\n\t\treturn findMatchingName(packageName) //\n\t\t\t\t.map(matchedName -> excluded(formatExclusionReason(packageName, matchedName))) //\n\t\t\t\t.orElseGet(() -> included(formatInclusionReason(packageName)));\n\t}\n\n\tprivate String formatInclusionReason(String packageName) {\n\t\treturn \"Package name [%s] does not match any excluded names: %s\".formatted(packageName,\n\t\t\tthis.patternDescription);\n\t}\n\n\tprivate String formatExclusionReason(String packageName, String matchedName) {\n\t\treturn \"Package name [%s] matches excluded name: '%s'\".formatted(packageName, matchedName);\n\t}\n\n\t@Override\n\tpublic Predicate<String> toPredicate() {\n\t\treturn packageName -> findMatchingName(packageName).isEmpty();\n\t}\n\n\tprivate Optional<String> findMatchingName(String packageName) {\n\t\treturn this.packageNames.stream().filter(\n\t\t\tname -> name.equals(packageName) || packageName.startsWith(name + \".\")).findAny();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that excludes packages whose names are either equal to or start with one of the following: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), this.patternDescription);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FilePosition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Position inside a file represented by {@linkplain #getLine line} and\n * {@linkplain #getColumn column} numbers.\n *\n * @implNote This class is a copy of\n * {@link org.junit.platform.engine.support.descriptor.FilePosition FilePosition},\n * which is not accessible from this package. The decision to duplicate it is\n * motivated by an eventual divergence between the two classes in the future.\n *\n * @since 1.7\n */\n@API(status = STABLE, since = \"1.7\")\npublic final class FilePosition implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(FilePosition.class);\n\n\t/**\n\t * Create a new {@code FilePosition} using the supplied {@code line} number\n\t * and an undefined column number.\n\t *\n\t * @param line the line number; must be greater than zero\n\t * @return a {@link FilePosition} with the given line number\n\t */\n\tpublic static FilePosition from(int line) {\n\t\treturn new FilePosition(line);\n\t}\n\n\t/**\n\t * Create a new {@code FilePosition} using the supplied {@code line} and\n\t * {@code column} numbers.\n\t *\n\t * @param line the line number; must be greater than zero\n\t * @param column the column number; must be greater than zero\n\t * @return a {@link FilePosition} with the given line and column numbers\n\t */\n\tpublic static FilePosition from(int line, int column) {\n\t\treturn new FilePosition(line, column);\n\t}\n\n\t/**\n\t * Create an optional {@code FilePosition} by parsing the supplied\n\t * {@code query} string.\n\t *\n\t * <p>Examples of valid {@code query} strings:\n\t * <ul>\n\t *     <li>{@code \"line=23\"}</li>\n\t *     <li>{@code \"line=23&column=42\"}</li>\n\t * </ul>\n\t *\n\t * @param query the query string; may be {@code null}\n\t * @return an {@link Optional} containing a {@link FilePosition} with\n\t * the parsed line and column numbers; never {@code null} but potentially\n\t * empty\n\t * @since 1.3\n\t * @see #from(int)\n\t * @see #from(int, int)\n\t */\n\tpublic static Optional<FilePosition> fromQuery(String query) {\n\t\tFilePosition result = null;\n\t\tInteger line = null;\n\t\tInteger column = null;\n\t\tif (StringUtils.isNotBlank(query)) {\n\t\t\ttry {\n\t\t\t\tfor (String pair : query.split(\"&\")) {\n\t\t\t\t\tString[] data = pair.split(\"=\");\n\t\t\t\t\tif (data.length == 2) {\n\t\t\t\t\t\tString key = data[0];\n\t\t\t\t\t\tif (line == null && \"line\".equals(key)) {\n\t\t\t\t\t\t\tline = Integer.valueOf(data[1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (column == null && \"column\".equals(key)) {\n\t\t\t\t\t\t\tcolumn = Integer.valueOf(data[1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Already found what we're looking for?\n\t\t\t\t\tif (line != null && column != null) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException ex) {\n\t\t\t\tlogger.debug(ex, () -> \"Failed to parse 'line' and/or 'column' from query string: \" + query);\n\t\t\t\t// fall-through and continue\n\t\t\t}\n\n\t\t\tif (line != null) {\n\t\t\t\tresult = column == null ? new FilePosition(line) : new FilePosition(line, column);\n\t\t\t}\n\t\t}\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\tprivate final int line;\n\n\tprivate final @Nullable Integer column;\n\n\tprivate FilePosition(int line) {\n\t\tPreconditions.condition(line > 0, \"line number must be greater than zero\");\n\t\tthis.line = line;\n\t\tthis.column = null;\n\t}\n\n\tprivate FilePosition(int line, int column) {\n\t\tPreconditions.condition(line > 0, \"line number must be greater than zero\");\n\t\tPreconditions.condition(column > 0, \"column number must be greater than zero\");\n\t\tthis.line = line;\n\t\tthis.column = column;\n\t}\n\n\t/**\n\t * Get the line number of this {@code FilePosition}.\n\t *\n\t * @return the line number\n\t */\n\tpublic int getLine() {\n\t\treturn this.line;\n\t}\n\n\t/**\n\t * Get the column number of this {@code FilePosition}, if available.\n\t *\n\t * @return an {@code Optional} containing the column number; never\n\t * {@code null} but potentially empty\n\t */\n\tpublic Optional<Integer> getColumn() {\n\t\treturn Optional.ofNullable(this.column);\n\t}\n\n\tString toQueryPart() {\n\t\tStringBuilder builder = new StringBuilder(\"line=\").append(this.line);\n\t\tif (this.column != null) {\n\t\t\tbuilder.append(\"&column=\").append(this.column);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFilePosition that = (FilePosition) o;\n\t\treturn (this.line == that.line) && Objects.equals(this.column, that.column);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.line, this.column);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"line\", this.line)\n\t\t\t\t.append(\"column\", getColumn().orElse(-1))\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/FileSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a file so that\n * {@link org.junit.platform.engine.TestEngine TestEngines}\n * can discover tests or containers based on files in the\n * file system.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectFile(String)\n * @see DiscoverySelectors#selectFile(File)\n * @see DirectorySelector\n * @see #getFile()\n * @see #getPath()\n * @see #getRawPath()\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class FileSelector implements DiscoverySelector {\n\n\tprivate final String path;\n\n\tprivate final @Nullable FilePosition position;\n\n\tFileSelector(String path, @Nullable FilePosition position) {\n\t\tthis.path = path;\n\t\tthis.position = position;\n\t}\n\n\t/**\n\t * Get the selected file as a {@link java.io.File}.\n\t *\n\t * @see #getPath()\n\t * @see #getRawPath()\n\t */\n\tpublic File getFile() {\n\t\treturn new File(this.path);\n\t}\n\n\t/**\n\t * Get the selected file as a {@link java.nio.file.Path} using the\n\t * {@linkplain FileSystems#getDefault default} {@link FileSystem}.\n\t *\n\t * @see #getFile()\n\t * @see #getRawPath()\n\t */\n\tpublic Path getPath() {\n\t\treturn Path.of(this.path);\n\t}\n\n\t/**\n\t * Get the selected file as a <em>raw path</em>.\n\t *\n\t * @see #getFile()\n\t * @see #getPath()\n\t */\n\tpublic String getRawPath() {\n\t\treturn this.path;\n\t}\n\n\t/**\n\t * Get the selected position within the file as a {@link FilePosition}.\n\t */\n\tpublic Optional<FilePosition> getPosition() {\n\t\treturn Optional.ofNullable(this.position);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFileSelector that = (FileSelector) o;\n\t\treturn Objects.equals(this.path, that.path) && Objects.equals(this.position, that.position);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.path, this.position);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"path\", this.path).append(\"position\", this.position).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\tif (this.position == null) {\n\t\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.path));\n\t\t}\n\t\telse {\n\t\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX,\n\t\t\t\t\"%s?%s\".formatted(this.path, this.position.toQueryPart())));\n\t\t}\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link FileSelector\n\t * FileSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"file\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<FileSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(StringUtils.splitIntoTwo('?', identifier.getValue()).map( //\n\t\t\t\tDiscoverySelectors::selectFile, //\n\t\t\t\t(path, query) -> {\n\t\t\t\t\tFilePosition position = FilePosition.fromQuery(query).orElse(null);\n\t\t\t\t\treturn DiscoverySelectors.selectFile(path, position);\n\t\t\t\t} //\n\t\t\t));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludeClassNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * {@link ClassNameFilter} that matches fully qualified class names against\n * patterns in the form of regular expressions.\n *\n * <p>If the fully qualified name of a class matches against at least one\n * pattern, the class will be included.\n *\n * @since 1.0\n */\nclass IncludeClassNameFilter extends AbstractClassNameFilter {\n\n\tIncludeClassNameFilter(String... patterns) {\n\t\tsuper(patterns);\n\t}\n\n\t@Override\n\tpublic FilterResult apply(String className) {\n\t\treturn findMatchingPattern(className) //\n\t\t\t\t.map(pattern -> included(formatInclusionReason(className, pattern))) //\n\t\t\t\t.orElseGet(() -> excluded(formatExclusionReason(className)));\n\t}\n\n\tprivate String formatInclusionReason(String className, Pattern pattern) {\n\t\treturn \"Class name [%s] matches included pattern: '%s'\".formatted(className, pattern);\n\t}\n\n\tprivate String formatExclusionReason(String className) {\n\t\treturn \"Class name [%s] does not match any included pattern: %s\".formatted(className, this.patternDescription);\n\t}\n\n\t@Override\n\tpublic Predicate<String> toPredicate() {\n\t\treturn className -> findMatchingPattern(className).isPresent();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that includes class names that match one of the following regular expressions: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), this.patternDescription);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IncludePackageNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * {@link PackageNameFilter} that matches fully qualified package names that\n * are prefixed by one of the package names provided to the filter.\n *\n * <p>If the fully qualified name of a package starts with at least one of the\n * packages names of the filter, the package will be included.\n *\n * @since 1.0\n */\nclass IncludePackageNameFilter implements PackageNameFilter {\n\n\tprivate final List<String> packageNames;\n\tprivate final String patternDescription;\n\n\tIncludePackageNameFilter(String... packageNames) {\n\t\tPreconditions.notEmpty(packageNames, \"packageNames array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(packageNames, \"packageNames array must not contain null elements\");\n\t\tthis.packageNames = Arrays.asList(packageNames);\n\t\tthis.patternDescription = Arrays.stream(packageNames).collect(joining(\"' OR '\", \"'\", \"'\"));\n\t}\n\n\t@Override\n\tpublic FilterResult apply(String packageName) {\n\t\treturn findMatchingName(packageName) //\n\t\t\t\t.map(matchedName -> included(formatInclusionReason(packageName, matchedName))) //\n\t\t\t\t.orElseGet(() -> excluded(formatExclusionReason(packageName)));\n\t}\n\n\tprivate String formatInclusionReason(String packageName, String matchedName) {\n\t\treturn \"Package name [%s] matches included name: '%s'\".formatted(packageName, matchedName);\n\t}\n\n\tprivate String formatExclusionReason(String packageName) {\n\t\treturn \"Package name [%s] does not match any included names: %s\".formatted(packageName,\n\t\t\tthis.patternDescription);\n\t}\n\n\t@Override\n\tpublic Predicate<String> toPredicate() {\n\t\treturn packageName -> findMatchingName(packageName).isPresent();\n\t}\n\n\tprivate Optional<String> findMatchingName(String packageName) {\n\t\treturn this.packageNames.stream().filter(\n\t\t\tname -> name.equals(packageName) || packageName.startsWith(name + \".\")).findAny();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that includes packages whose names are either equal to or start with one of the following: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), this.patternDescription);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/IterationSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.stream.Collectors.collectingAndThen;\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toCollection;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.IntStream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects the iterations of a parent\n * {@code DiscoverySelector} via their indices so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * a subset of the iterations of tests or containers.\n *\n * @since 1.9\n * @see DiscoverySelectors#selectIteration(DiscoverySelector, int...)\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic final class IterationSelector implements DiscoverySelector {\n\n\tprivate final DiscoverySelector parentSelector;\n\tprivate final SortedSet<Integer> iterationIndices;\n\n\tIterationSelector(DiscoverySelector parentSelector, int... iterationIndices) {\n\t\tthis.parentSelector = parentSelector;\n\t\tthis.iterationIndices = toSortedSet(iterationIndices);\n\t}\n\n\tprivate SortedSet<Integer> toSortedSet(int[] iterationIndices) {\n\t\treturn Arrays.stream(iterationIndices) //\n\t\t\t\t.boxed() //\n\t\t\t\t.collect(collectingAndThen(toCollection(TreeSet::new), Collections::unmodifiableSortedSet));\n\t}\n\n\t/**\n\t * Get the selected parent {@link DiscoverySelector}.\n\t */\n\tpublic DiscoverySelector getParentSelector() {\n\t\treturn this.parentSelector;\n\t}\n\n\t/**\n\t * Get the selected iteration indices.\n\t */\n\tpublic SortedSet<Integer> getIterationIndices() {\n\t\treturn this.iterationIndices;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tIterationSelector that = (IterationSelector) o;\n\t\treturn this.parentSelector.equals(that.parentSelector) && this.iterationIndices.equals(that.iterationIndices);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.parentSelector, this.iterationIndices);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"parentSelector\", this.parentSelector)\n\t\t\t\t.append(\"iterationIndices\", this.iterationIndices)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn this.parentSelector.toIdentifier().map(parentSelectorString -> DiscoverySelectorIdentifier.create( //\n\t\t\tIdentifierParser.PREFIX, //\n\t\t\t\"%s[%s]\".formatted(parentSelectorString, formatIterationIndicesAsRanges())) //\n\t\t);\n\t}\n\n\tprivate String formatIterationIndicesAsRanges() {\n\n\t\tclass Range {\n\t\t\tfinal int start;\n\t\t\tint end;\n\n\t\t\tRange(int start) {\n\t\t\t\tthis.start = start;\n\t\t\t\tthis.end = start;\n\t\t\t}\n\t\t}\n\n\t\tList<Range> ranges = new ArrayList<>();\n\t\tRange current = new Range(this.iterationIndices.first());\n\t\tranges.add(current);\n\t\tfor (int n : this.iterationIndices.tailSet(current.start + 1)) {\n\t\t\tif (n == current.end + 1) {\n\t\t\t\tcurrent.end = n;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcurrent = new Range(n);\n\t\t\t\tranges.add(current);\n\t\t\t}\n\t\t}\n\t\treturn ranges.stream() //\n\t\t\t\t.map(range -> {\n\t\t\t\t\tif (range.start == range.end) {\n\t\t\t\t\t\treturn String.valueOf(range.start);\n\t\t\t\t\t}\n\t\t\t\t\tif (range.start == range.end - 1) {\n\t\t\t\t\t\treturn range.start + \",\" + range.end;\n\t\t\t\t\t}\n\t\t\t\t\treturn range.start + \"..\" + range.end;\n\t\t\t\t}) //\n\t\t\t\t.collect(joining(\",\"));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link IterationSelector IterationSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tpublic static final String PREFIX = \"iteration\";\n\n\t\tprivate static final Pattern PATTERN = Pattern.compile(\n\t\t\t\"(?<parentIdentifier>.+)\\\\[(?<indices>(\\\\d+)(\\\\.\\\\.\\\\d+)?(\\\\s*,\\\\s*(\\\\d+)(\\\\.\\\\.\\\\d+)?)*)]\");\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<IterationSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\tMatcher matcher = PATTERN.matcher(identifier.getValue());\n\t\t\tPreconditions.condition(matcher.matches(), \"Invalid format: must be IDENTIFIER[INDEX(,INDEX)*]\");\n\t\t\treturn context.parse(matcher.group(\"parentIdentifier\")) //\n\t\t\t\t\t.map(parent -> {\n\t\t\t\t\t\tint[] iterationIndices = Arrays.stream(matcher.group(\"indices\").split(\",\")) //\n\t\t\t\t\t\t\t\t.flatMapToInt(this::parseIndexDefinition) //\n\t\t\t\t\t\t\t\t.toArray();\n\t\t\t\t\t\treturn DiscoverySelectors.selectIteration(parent, iterationIndices);\n\t\t\t\t\t});\n\t\t}\n\n\t\tprivate IntStream parseIndexDefinition(String value) {\n\t\t\treturn StringUtils.splitIntoTwo(\"..\", value).map( //\n\t\t\t\tindex -> IntStream.of(Integer.parseInt(index)), //\n\t\t\t\t(firstIndex, lastIndex) -> IntStream.rangeClosed(Integer.parseInt(firstIndex),\n\t\t\t\t\tInteger.parseInt(lastIndex))//\n\t\t\t);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/MethodSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Method;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a {@link Method} or a combination of\n * class name, method name, and parameter types so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover tests\n * or containers based on methods.\n *\n * <p>If a Java {@link Method} is provided, the selector will return that\n * {@linkplain #getJavaMethod() method} and its method name, class name, and\n * parameter types accordingly. If a {@link Class} and method name, a class name\n * and method name, or a <em>fully qualified method name</em> is provided,\n * this selector will only attempt to lazily load the class, method, or parameter\n * types if {@link #getJavaClass()}, {@link #getJavaMethod()}, or\n * {@link #getParameterTypes()} is invoked.\n *\n * <p>In this context, a Java {@code Method} means anything that can be referenced\n * as a {@link Method} on the JVM &mdash; for example, methods from Java classes\n * or methods from other JVM languages such Groovy, Scala, etc.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectMethod(String)\n * @see DiscoverySelectors#selectMethod(String, String)\n * @see DiscoverySelectors#selectMethod(String, String, String)\n * @see DiscoverySelectors#selectMethod(Class, String)\n * @see DiscoverySelectors#selectMethod(Class, String, String)\n * @see DiscoverySelectors#selectMethod(Class, Method)\n * @see org.junit.platform.engine.support.descriptor.MethodSource\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class MethodSelector implements DiscoverySelector {\n\n\tprivate final @Nullable ClassLoader classLoader;\n\tprivate final String className;\n\tprivate final String methodName;\n\tprivate final String parameterTypeNames;\n\n\tprivate volatile @Nullable Class<?> javaClass;\n\n\tprivate volatile @Nullable Method javaMethod;\n\n\tprivate volatile Class<?> @Nullable [] parameterTypes;\n\n\t/**\n\t * @since 1.10\n\t */\n\tMethodSelector(@Nullable ClassLoader classLoader, String className, String methodName, String parameterTypeNames) {\n\t\tthis.classLoader = classLoader;\n\t\tthis.className = className;\n\t\tthis.methodName = methodName;\n\t\tthis.parameterTypeNames = parameterTypeNames;\n\t}\n\n\tMethodSelector(Class<?> javaClass, String methodName, String parameterTypeNames) {\n\t\tthis(javaClass.getClassLoader(), javaClass.getName(), methodName, parameterTypeNames);\n\t\tthis.javaClass = javaClass;\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tMethodSelector(@Nullable ClassLoader classLoader, String className, String methodName, Class<?>... parameterTypes) {\n\t\tthis(classLoader, className, methodName, ClassUtils.nullSafeToString(Class::getTypeName, parameterTypes));\n\t\tthis.parameterTypes = parameterTypes.clone();\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tMethodSelector(Class<?> javaClass, String methodName, Class<?>... parameterTypes) {\n\t\tthis(javaClass.getClassLoader(), javaClass.getName(), methodName,\n\t\t\tClassUtils.nullSafeToString(Class::getTypeName, parameterTypes));\n\t\tthis.javaClass = javaClass;\n\t\tthis.parameterTypes = parameterTypes.clone();\n\t}\n\n\tMethodSelector(Class<?> javaClass, Method method) {\n\t\tthis(javaClass, method, method.getParameterTypes());\n\t}\n\n\tprivate MethodSelector(Class<?> javaClass, Method method, Class<?>... parameterTypes) {\n\t\tthis(javaClass.getClassLoader(), javaClass.getName(), method.getName(),\n\t\t\tClassUtils.nullSafeToString(Class::getTypeName, parameterTypes));\n\t\tthis.javaClass = javaClass;\n\t\tthis.javaMethod = method;\n\t\tthis.parameterTypes = parameterTypes;\n\t}\n\n\t/**\n\t * Get the {@link ClassLoader} used to load the specified class.\n\t *\n\t * @return the {@code ClassLoader}; potentially {@code null}\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic @Nullable ClassLoader getClassLoader() {\n\t\treturn this.classLoader;\n\t}\n\n\t/**\n\t * Get the selected class name.\n\t */\n\tpublic String getClassName() {\n\t\treturn this.className;\n\t}\n\n\t/**\n\t * Get the selected method name.\n\t */\n\tpublic String getMethodName() {\n\t\treturn this.methodName;\n\t}\n\n\t/**\n\t * Get the names of parameter types for the selected method as a {@link String},\n\t * typically a comma-separated list of primitive types, fully qualified class\n\t * names, or array types.\n\t *\n\t * <p>Note: the names of parameter types are provided as a single string instead\n\t * of a collection in order to allow this selector to be used in a generic\n\t * fashion by various test engines. It is therefore the responsibility of\n\t * the caller of this method to determine how to parse the returned string.\n\t *\n\t * @return the names of parameter types supplied to this {@code MethodSelector}\n\t * via a constructor or deduced from a {@code Method} or parameter types supplied\n\t * via a constructor; never {@code null} but potentially an empty string\n\t * @since 1.10\n\t * @see #getParameterTypes()\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic String getParameterTypeNames() {\n\t\treturn this.parameterTypeNames;\n\t}\n\n\t/**\n\t * Get the {@link Class} in which the selected {@linkplain #getJavaMethod\n\t * method} is declared, or a subclass thereof.\n\t *\n\t * <p>If the {@link Class} was not provided, but only the name, this method\n\t * attempts to lazily load the {@code Class} based on its name and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t *\n\t * @see #getJavaMethod()\n\t */\n\tpublic Class<?> getJavaClass() {\n\t\treturn lazyLoadJavaClass();\n\t}\n\n\t/**\n\t * Get the selected {@link Method}.\n\t *\n\t * <p>If the {@link Method} was not provided, but only the name, this method\n\t * attempts to lazily load the {@code Method} based on its name and throws a\n\t * {@link PreconditionViolationException} if the method cannot be loaded.\n\t *\n\t * @see #getJavaClass()\n\t */\n\tpublic Method getJavaMethod() {\n\t\treturn lazyLoadJavaMethod();\n\t}\n\n\t/**\n\t * Get the parameter types for the selected method.\n\t *\n\t * <p>If the parameter types were not provided as {@link Class} references\n\t * (or could not be deduced as {@code Class} references in the constructor),\n\t * this method attempts to lazily load the class reference for each parameter\n\t * type based on its name and throws a {@link JUnitException} if the class\n\t * cannot be loaded.\n\t *\n\t * @return the method's parameter types; never {@code null} but potentially\n\t * an empty array if the selected method does not declare parameters\n\t * @since 1.10\n\t * @see #getParameterTypeNames()\n\t * @see Method#getParameterTypes()\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic Class<?>[] getParameterTypes() {\n\t\treturn lazyLoadParameterTypes().clone();\n\t}\n\n\tprivate Class<?> lazyLoadJavaClass() {\n\t\tClass<?> value = this.javaClass;\n\t\tif (value == null) {\n\t\t\t// @formatter:off\n\t\t\tTry<Class<?>> tryToLoadClass = this.classLoader == null\n\t\t\t\t? ReflectionSupport.tryToLoadClass(this.className)\n\t\t\t\t: ReflectionSupport.tryToLoadClass(this.className, this.classLoader);\n\t\t\tvalue = tryToLoadClass.getNonNullOrThrow(cause ->\n\t\t\t\tnew PreconditionViolationException(\"Could not load class with name: \" + this.className, cause));\n\t\t\t// @formatter:on\n\t\t\tthis.javaClass = value;\n\t\t}\n\t\treturn value;\n\t}\n\n\tprivate Method lazyLoadJavaMethod() {\n\t\tvar value = this.javaMethod;\n\t\tif (value == null) {\n\t\t\tClass<?> javaClass = lazyLoadJavaClass();\n\t\t\tvar parameterTypes = lazyLoadParameterTypes();\n\t\t\tif (parameterTypes.length > 0) {\n\t\t\t\tvalue = ReflectionSupport.findMethod(javaClass, this.methodName, parameterTypes).orElseThrow(\n\t\t\t\t\t() -> new PreconditionViolationException(\n\t\t\t\t\t\t\"Could not find method with name [%s] and parameter types [%s] in class [%s].\".formatted(\n\t\t\t\t\t\t\tthis.methodName, this.parameterTypeNames, javaClass.getName())));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvalue = ReflectionSupport.findMethod(javaClass, this.methodName).orElseThrow(\n\t\t\t\t\t() -> new PreconditionViolationException(\n\t\t\t\t\t\t\"Could not find method with name [%s] in class [%s].\".formatted(this.methodName,\n\t\t\t\t\t\t\tjavaClass.getName())));\n\t\t\t}\n\t\t\tthis.javaMethod = value;\n\t\t}\n\t\treturn value;\n\t}\n\n\tprivate Class<?>[] lazyLoadParameterTypes() {\n\t\tvar value = this.parameterTypes;\n\t\tif (value == null) {\n\t\t\tvalue = ReflectionUtils.resolveParameterTypes(lazyLoadJavaClass(), this.methodName,\n\t\t\t\tthis.parameterTypeNames);\n\t\t\tthis.parameterTypes = value;\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodSelector that = (MethodSelector) o;\n\t\treturn Objects.equals(this.className, that.className)//\n\t\t\t\t&& Objects.equals(this.methodName, that.methodName)//\n\t\t\t\t&& Objects.equals(this.parameterTypeNames, that.parameterTypeNames);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.className, this.methodName, this.parameterTypeNames);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"className\", getClassName())\n\t\t\t\t.append(\"methodName\", getMethodName())\n\t\t\t\t.append(\"parameterTypes\", getParameterTypeNames())\n\t\t\t\t.append(\"classLoader\", getClassLoader())\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\tString fullyQualifiedMethodName = ReflectionUtils.getFullyQualifiedMethodName(this.className, this.methodName,\n\t\t\tthis.parameterTypeNames);\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, fullyQualifiedMethodName));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link MethodSelector\n\t * MethodSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"method\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<MethodSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectMethod(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/ModuleSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a module name so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * tests or containers based on modules.\n *\n * @since 1.1\n * @see DiscoverySelectors#selectModule(String)\n * @see DiscoverySelectors#selectModules(java.util.Set)\n */\n@API(status = STABLE, since = \"1.1\")\npublic final class ModuleSelector implements DiscoverySelector {\n\n\t@Nullable\n\tprivate final Module module;\n\tprivate final String moduleName;\n\n\tModuleSelector(Module module) {\n\t\tthis.module = module;\n\t\tthis.moduleName = module.getName();\n\t}\n\n\tModuleSelector(String moduleName) {\n\t\tthis.module = null;\n\t\tthis.moduleName = moduleName;\n\t}\n\n\t/**\n\t * {@return the selected {@link Module}, if available}\n\t *\n\t * @since 6.1\n\t * @see DiscoverySelectors#selectModule(Module)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic Optional<Module> getModule() {\n\t\treturn Optional.ofNullable(module);\n\t}\n\n\t/**\n\t * Get the selected module name.\n\t */\n\tpublic String getModuleName() {\n\t\treturn this.moduleName;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tModuleSelector that = (ModuleSelector) o;\n\t\treturn Objects.equals(this.moduleName, that.moduleName);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.moduleName.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"moduleName\", this.moduleName).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.moduleName));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link ModuleSelector\n\t * ModuleSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"module\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<ModuleSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectModule(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedClassSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a nested {@link Class}\n * or class name enclosed in other classes so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * tests or containers based on classes.\n *\n * <p>If Java {@link Class} references are provided for the nested class or\n * the enclosing classes, the selector will return those classes and their class\n * names accordingly. If class names are provided, the selector will only attempt\n * to lazily load classes if {@link #getEnclosingClasses()} or\n * {@link #getNestedClass()} is invoked.\n *\n * <p>In this context, Java {@link Class} means anything that can be referenced\n * as a {@link Class} on the JVM &mdash; for example, classes from other JVM\n * languages such Groovy, Scala, etc.\n *\n * @since 1.6\n * @see DiscoverySelectors#selectNestedClass(List, Class)\n * @see DiscoverySelectors#selectNestedClass(List, String)\n * @see org.junit.platform.engine.support.descriptor.ClassSource\n * @see ClassSelector\n */\n@API(status = STABLE, since = \"1.6\")\npublic final class NestedClassSelector implements DiscoverySelector {\n\n\tprivate final @Nullable ClassLoader classLoader;\n\n\tprivate final List<ClassSelector> enclosingClassSelectors;\n\tprivate final ClassSelector nestedClassSelector;\n\n\tNestedClassSelector(@Nullable ClassLoader classLoader, List<String> enclosingClassNames, String nestedClassName) {\n\t\tthis.classLoader = classLoader;\n\t\tthis.enclosingClassSelectors = enclosingClassNames.stream() //\n\t\t\t\t.map(className -> new ClassSelector(classLoader, className)) //\n\t\t\t\t.toList();\n\t\tthis.nestedClassSelector = new ClassSelector(classLoader, nestedClassName);\n\t}\n\n\tNestedClassSelector(List<Class<?>> enclosingClasses, Class<?> nestedClass) {\n\t\tthis.classLoader = nestedClass.getClassLoader();\n\t\tthis.enclosingClassSelectors = enclosingClasses.stream().map(ClassSelector::new).toList();\n\t\tthis.nestedClassSelector = new ClassSelector(nestedClass);\n\t}\n\n\t/**\n\t * Get the {@link ClassLoader} used to load the selected nested class.\n\t *\n\t * @return the {@code ClassLoader}; potentially {@code null}\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic @Nullable ClassLoader getClassLoader() {\n\t\treturn this.classLoader;\n\t}\n\n\t/**\n\t * Get the names of the classes enclosing the selected nested class.\n\t */\n\tpublic List<String> getEnclosingClassNames() {\n\t\treturn this.enclosingClassSelectors.stream().map(ClassSelector::getClassName).toList();\n\t}\n\n\t/**\n\t * Get the list of {@link Class} enclosing the selected nested\n\t * {@link Class}.\n\t *\n\t * <p>If the {@link Class} were not provided, but only the name of the\n\t * nested class and its enclosing classes, this method attempts to lazily\n\t * load the list of enclosing {@link Class} and throws a\n\t * {@link PreconditionViolationException} if the classes cannot be loaded.\n\t */\n\tpublic List<Class<?>> getEnclosingClasses() {\n\t\treturn this.enclosingClassSelectors.stream().<Class<?>> map(ClassSelector::getJavaClass).toList();\n\t}\n\n\t/**\n\t * Get the name of the selected nested class.\n\t */\n\tpublic String getNestedClassName() {\n\t\treturn this.nestedClassSelector.getClassName();\n\t}\n\n\t/**\n\t * Get the selected nested {@link Class}.\n\t *\n\t * <p>If the {@link Class} were not provided, but only the name of the\n\t * nested class and its enclosing classes, this method attempts to lazily\n\t * load the nested {@link Class} and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t */\n\tpublic Class<?> getNestedClass() {\n\t\treturn this.nestedClassSelector.getJavaClass();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tNestedClassSelector that = (NestedClassSelector) o;\n\t\treturn this.enclosingClassSelectors.equals(that.enclosingClassSelectors)\n\t\t\t\t&& this.nestedClassSelector.equals(that.nestedClassSelector);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.enclosingClassSelectors, this.nestedClassSelector);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"enclosingClassNames\", getEnclosingClassNames()) //\n\t\t\t\t.append(\"nestedClassName\", getNestedClassName()) //\n\t\t\t\t.append(\"classLoader\", getClassLoader()) //\n\t\t\t\t.toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\tString allClassNames = Stream.concat(enclosingClassSelectors.stream(), Stream.of(nestedClassSelector)) //\n\t\t\t\t.map(ClassSelector::getClassName) //\n\t\t\t\t.collect(joining(\"/\"));\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, allClassNames));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link NestedClassSelector NestedClassSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tstatic final String PREFIX = \"nested-class\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<NestedClassSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\tList<String> parts = Arrays.asList(identifier.getValue().split(\"/\"));\n\t\t\treturn Optional.of(\n\t\t\t\tDiscoverySelectors.selectNestedClass(parts.subList(0, parts.size() - 1), parts.get(parts.size() - 1)));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/NestedMethodSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a nested {@link Method} or a\n * combination of enclosing class names, class name, method name, and parameter\n * types so that {@link org.junit.platform.engine.TestEngine TestEngines} can\n * discover tests or containers based on methods.\n *\n * <p>If a Java {@link Method} is provided, the selector will return that\n * {@linkplain #getMethod() method} and its method name, class name, enclosing\n * class names, and parameter types accordingly. If class names or method names\n * are provided, this selector will only attempt to lazily load a class or method\n * if {@link #getEnclosingClasses()}, {@link #getNestedClass()},\n * {@link #getMethod()}, or {@link #getParameterTypes()} is invoked.\n *\n * <p>In this context, a Java {@code Method} means anything that can be referenced\n * as a {@link Method} on the JVM &mdash; for example, methods from Java classes\n * or methods from other JVM languages such Groovy, Scala, etc.\n *\n * @since 1.6\n * @see DiscoverySelectors#selectNestedMethod(List, String, String)\n * @see DiscoverySelectors#selectNestedMethod(List, String, String, String)\n * @see DiscoverySelectors#selectNestedMethod(List, Class, String)\n * @see DiscoverySelectors#selectNestedMethod(List, Class, String, String)\n * @see DiscoverySelectors#selectNestedMethod(List, Class, Method)\n * @see org.junit.platform.engine.support.descriptor.MethodSource\n * @see NestedClassSelector\n * @see MethodSelector\n */\n@API(status = STABLE, since = \"1.6\")\npublic final class NestedMethodSelector implements DiscoverySelector {\n\n\tprivate final NestedClassSelector nestedClassSelector;\n\tprivate final MethodSelector methodSelector;\n\n\tNestedMethodSelector(@Nullable ClassLoader classLoader, List<String> enclosingClassNames, String nestedClassName,\n\t\t\tString methodName, String parameterTypeNames) {\n\t\tthis.nestedClassSelector = new NestedClassSelector(classLoader, enclosingClassNames, nestedClassName);\n\t\tthis.methodSelector = new MethodSelector(classLoader, nestedClassName, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tNestedMethodSelector(@Nullable ClassLoader classLoader, List<String> enclosingClassNames, String nestedClassName,\n\t\t\tString methodName, Class<?>... parameterTypes) {\n\t\tthis.nestedClassSelector = new NestedClassSelector(classLoader, enclosingClassNames, nestedClassName);\n\t\tthis.methodSelector = new MethodSelector(classLoader, nestedClassName, methodName, parameterTypes);\n\t}\n\n\tNestedMethodSelector(List<Class<?>> enclosingClasses, Class<?> nestedClass, String methodName,\n\t\t\tString parameterTypeNames) {\n\t\tthis.nestedClassSelector = new NestedClassSelector(enclosingClasses, nestedClass);\n\t\tthis.methodSelector = new MethodSelector(nestedClass, methodName, parameterTypeNames);\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\tNestedMethodSelector(List<Class<?>> enclosingClasses, Class<?> nestedClass, String methodName,\n\t\t\tClass<?>... parameterTypes) {\n\t\tthis.nestedClassSelector = new NestedClassSelector(enclosingClasses, nestedClass);\n\t\tthis.methodSelector = new MethodSelector(nestedClass, methodName, parameterTypes);\n\t}\n\n\tNestedMethodSelector(List<Class<?>> enclosingClasses, Class<?> nestedClass, Method method) {\n\t\tthis.nestedClassSelector = new NestedClassSelector(enclosingClasses, nestedClass);\n\t\tthis.methodSelector = new MethodSelector(nestedClass, method);\n\t}\n\n\t/**\n\t * Get the {@link ClassLoader} used to load the nested class.\n\t *\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic @Nullable ClassLoader getClassLoader() {\n\t\treturn this.nestedClassSelector.getClassLoader();\n\t}\n\n\t/**\n\t * Get the names of the classes enclosing the nested class\n\t * containing the selected method.\n\t */\n\tpublic List<String> getEnclosingClassNames() {\n\t\treturn this.nestedClassSelector.getEnclosingClassNames();\n\t}\n\n\t/**\n\t * Get the list of {@link Class} enclosing the nested {@link Class}\n\t * containing the selected {@link Method}.\n\t *\n\t * <p>If the {@link Class} were not provided, but only the name of the\n\t * nested class and its enclosing classes, this method attempts to lazily\n\t * load the list of enclosing {@link Class} and throws a\n\t * {@link PreconditionViolationException} if the classes cannot be loaded.\n\t */\n\tpublic List<Class<?>> getEnclosingClasses() {\n\t\treturn this.nestedClassSelector.getEnclosingClasses();\n\t}\n\n\t/**\n\t * Get the name of the nested class containing the selected method.\n\t */\n\tpublic String getNestedClassName() {\n\t\treturn this.nestedClassSelector.getNestedClassName();\n\t}\n\n\t/**\n\t * Get the nested {@link Class} containing the selected {@link Method}.\n\t *\n\t * <p>If the {@link Class} were not provided, but only the name of the\n\t * nested class and its enclosing classes, this method attempts to lazily\n\t * load the nested {@link Class} and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t */\n\tpublic Class<?> getNestedClass() {\n\t\treturn this.nestedClassSelector.getNestedClass();\n\t}\n\n\t/**\n\t * Get the name of the selected method.\n\t */\n\tpublic String getMethodName() {\n\t\treturn this.methodSelector.getMethodName();\n\t}\n\n\t/**\n\t * Get the selected {@link Method}.\n\t *\n\t * <p>If the {@link Method} was not provided, but only the name, this method\n\t * attempts to lazily load the {@code Method} based on its name and throws a\n\t * {@link PreconditionViolationException} if the method cannot be loaded.\n\t */\n\tpublic Method getMethod() {\n\t\treturn this.methodSelector.getJavaMethod();\n\t}\n\n\t/**\n\t * Get the names of parameter types for the selected method as a {@link String}.\n\t *\n\t * <p>See {@link MethodSelector#getParameterTypeNames()} for details.\n\t *\n\t * @return the names of parameter types supplied to this {@code NestedMethodSelector}\n\t * via a constructor or deduced from a {@code Method} or parameter types supplied\n\t * via a constructor; never {@code null} but potentially an empty string\n\t * @since 1.10\n\t * @see MethodSelector#getParameterTypeNames()\n\t *\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic String getParameterTypeNames() {\n\t\treturn this.methodSelector.getParameterTypeNames();\n\t}\n\n\t/**\n\t * Get the parameter types for the selected method.\n\t *\n\t * <p>See {@link MethodSelector#getParameterTypes()} for details.\n\t *\n\t * @return the method's parameter types; never {@code null} but potentially\n\t * an empty array if the selected method does not declare parameters\n\t * @since 1.10\n\t * @see #getParameterTypeNames()\n\t * @see MethodSelector#getParameterTypes()\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic Class<?>[] getParameterTypes() {\n\t\treturn this.methodSelector.getParameterTypes();\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tNestedMethodSelector that = (NestedMethodSelector) o;\n\t\treturn this.nestedClassSelector.equals(that.nestedClassSelector)\n\t\t\t\t&& this.methodSelector.equals(that.methodSelector);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.nestedClassSelector, this.methodSelector);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"enclosingClassNames\", getEnclosingClassNames()) //\n\t\t\t\t.append(\"nestedClassName\", getNestedClassName()) //\n\t\t\t\t.append(\"methodName\", getMethodName()) //\n\t\t\t\t.append(\"parameterTypes\", getParameterTypeNames()) //\n\t\t\t\t.append(\"classLoader\", getClassLoader()) //\n\t\t\t\t.toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn nestedClassSelector.toIdentifier() //\n\t\t\t\t.map(parent -> {\n\t\t\t\t\tString fullyQualifiedMethodName = ReflectionUtils.getFullyQualifiedMethodName(parent.getValue(),\n\t\t\t\t\t\tmethodSelector.getMethodName(), methodSelector.getParameterTypeNames());\n\t\t\t\t\treturn DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, fullyQualifiedMethodName);\n\t\t\t\t});\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link NestedMethodSelector NestedMethodSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"nested-method\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<NestedMethodSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\tList<String> parts = Arrays.asList(identifier.getValue().split(\"/\"));\n\t\t\tList<String> enclosingClassNames = parts.subList(0, parts.size() - 1);\n\n\t\t\tString[] methodParts = ReflectionUtils.parseFullyQualifiedMethodName(parts.get(parts.size() - 1));\n\t\t\tString nestedClassName = methodParts[0];\n\t\t\tString methodName = methodParts[1];\n\t\t\tString parameterTypeNames = methodParts[2];\n\n\t\t\treturn Optional.of(DiscoverySelectors.selectNestedMethod(enclosingClassNames, nestedClassName, methodName,\n\t\t\t\tparameterTypeNames));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageNameFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.DiscoveryFilter;\n\n/**\n * {@link DiscoveryFilter} that is applied to the name of a {@link Package}.\n *\n * @since 1.0\n * @see #includePackageNames(String...)\n * @see #excludePackageNames(String...)\n * @see ClassNameFilter\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface PackageNameFilter extends DiscoveryFilter<String> {\n\n\t/**\n\t * Create a new <em>include</em> {@link PackageNameFilter} based on the\n\t * supplied package names.\n\t *\n\t * <p>The names are combined using OR semantics, i.e. if the fully\n\t * qualified name of a package starts with at least one of the names,\n\t * the package will be included in the result set.\n\t *\n\t * @param names package names that we be compared against fully qualified\n\t * package names; never {@code null}, empty, or containing {@code null}\n\t * @see Package#getName()\n\t * @see #includePackageNames(List)\n\t * @see #excludePackageNames(String...)\n\t */\n\tstatic PackageNameFilter includePackageNames(String... names) {\n\t\treturn new IncludePackageNameFilter(names);\n\t}\n\n\t/**\n\t * Create a new <em>include</em> {@link PackageNameFilter} based on the\n\t * supplied package names.\n\t *\n\t * <p>The names are combined using OR semantics, i.e. if the fully\n\t * qualified name of a package starts with at least one of the names,\n\t * the package will be included in the result set.\n\t *\n\t * @param names package names that we be compared against fully qualified\n\t * package names; never {@code null}, empty, or containing {@code null}\n\t * @see Package#getName()\n\t * @see #includePackageNames(String...)\n\t * @see #excludePackageNames(String...)\n\t */\n\tstatic PackageNameFilter includePackageNames(List<String> names) {\n\t\treturn includePackageNames(names.toArray(new String[0]));\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@link PackageNameFilter} based on the\n\t * supplied package names.\n\t *\n\t * <p>The names are combined using OR semantics, i.e. if the fully\n\t * qualified name of a package starts with at least one of the names,\n\t * the package will be excluded in the result set.\n\t *\n\t * @param names package names that we be compared against fully qualified\n\t * package names; never {@code null}, empty, or containing {@code null}\n\t * @see Package#getName()\n\t * @see #excludePackageNames(List)\n\t * @see #includePackageNames(String...)\n\t */\n\tstatic PackageNameFilter excludePackageNames(String... names) {\n\t\treturn new ExcludePackageNameFilter(names);\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@link PackageNameFilter} based on the\n\t * supplied package names.\n\t *\n\t * <p>The names are combined using OR semantics, i.e. if the fully\n\t * qualified name of a package starts with at least one of the names,\n\t * the package will be excluded in the result set.\n\t *\n\t * @param names package names that we be compared against fully qualified\n\t * package names; never {@code null}, empty, or containing {@code null}\n\t * @see Package#getName()\n\t * @see #excludePackageNames(String...)\n\t * @see #includePackageNames(String...)\n\t */\n\tstatic PackageNameFilter excludePackageNames(List<String> names) {\n\t\treturn excludePackageNames(names.toArray(new String[0]));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/PackageSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a package name so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * tests or containers based on packages.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectPackage(String)\n * @see org.junit.platform.engine.support.descriptor.PackageSource\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class PackageSelector implements DiscoverySelector {\n\n\tprivate final String packageName;\n\n\tPackageSelector(String packageName) {\n\t\tthis.packageName = packageName;\n\t}\n\n\t/**\n\t * Get the selected package name.\n\t */\n\tpublic String getPackageName() {\n\t\treturn this.packageName;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tPackageSelector that = (PackageSelector) o;\n\t\treturn Objects.equals(this.packageName, that.packageName);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.packageName.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"packageName\", this.packageName).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.packageName));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link PackageSelector\n\t * PackageSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"package\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<PackageSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectPackage(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UniqueIdSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * A {@link DiscoverySelector} that selects a {@link UniqueId} so that\n * {@link org.junit.platform.engine.TestEngine TestEngines} can discover\n * tests or containers based on unique IDs.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectUniqueId(String)\n * @see DiscoverySelectors#selectUniqueId(UniqueId)\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class UniqueIdSelector implements DiscoverySelector {\n\n\tprivate final UniqueId uniqueId;\n\n\tUniqueIdSelector(UniqueId uniqueId) {\n\t\tthis.uniqueId = uniqueId;\n\t}\n\n\t/**\n\t * Get the selected {@link UniqueId}.\n\t */\n\tpublic UniqueId getUniqueId() {\n\t\treturn this.uniqueId;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tUniqueIdSelector that = (UniqueIdSelector) o;\n\t\treturn Objects.equals(this.uniqueId, that.uniqueId);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.uniqueId.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"uniqueId\", this.uniqueId).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.uniqueId.toString()));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for\n\t * {@link UniqueIdSelector UniqueIdSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"uid\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<UniqueIdSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectUniqueId(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/UriSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.net.URI;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * A {@link DiscoverySelector} that selects a {@link URI} so that\n * {@link org.junit.platform.engine.TestEngine TestEngines}\n * can discover tests or containers based on URIs.\n *\n * @since 1.0\n * @see DiscoverySelectors#selectUri(String)\n * @see DiscoverySelectors#selectUri(URI)\n * @see FileSelector\n * @see DirectorySelector\n * @see org.junit.platform.engine.support.descriptor.UriSource\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class UriSelector implements DiscoverySelector {\n\n\tprivate final URI uri;\n\n\tUriSelector(URI uri) {\n\t\tthis.uri = uri;\n\t}\n\n\t/**\n\t * Get the selected {@link URI}.\n\t */\n\tpublic URI getUri() {\n\t\treturn this.uri;\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tUriSelector that = (UriSelector) o;\n\t\treturn Objects.equals(this.uri, that.uri);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.uri.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"uri\", this.uri).toString();\n\t}\n\n\t@Override\n\tpublic Optional<DiscoverySelectorIdentifier> toIdentifier() {\n\t\treturn Optional.of(DiscoverySelectorIdentifier.create(IdentifierParser.PREFIX, this.uri.toString()));\n\t}\n\n\t/**\n\t * The {@link DiscoverySelectorIdentifierParser} for {@link UriSelector\n\t * UriSelectors}.\n\t */\n\t@API(status = INTERNAL, since = \"1.11\")\n\tpublic static class IdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t\tprivate static final String PREFIX = \"uri\";\n\n\t\tpublic IdentifierParser() {\n\t\t}\n\n\t\t@Override\n\t\tpublic String getPrefix() {\n\t\t\treturn PREFIX;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<UriSelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\t\treturn Optional.of(DiscoverySelectors.selectUri(identifier.getValue()));\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/discovery/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Concrete {@linkplain org.junit.platform.engine.DiscoverySelector selectors} and\n * {@linkplain org.junit.platform.engine.DiscoveryFilter filters} to be used in\n * {@linkplain org.junit.platform.engine.EngineDiscoveryRequest discovery requests}.\n */\n\n@NullMarked\npackage org.junit.platform.engine.discovery;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Public API for test engines.\n */\n\n@NullMarked\npackage org.junit.platform.engine;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/FileEntry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.reporting;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.nio.file.Path;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code FileEntry} encapsulates a file or directory to be published to the\n * reporting infrastructure.\n *\n * @since 1.12\n * @see #from(Path, String)\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic final class FileEntry {\n\n\t/**\n\t * Factory for creating a new {@code FileEntry} from the supplied path and\n\t * media type.\n\t *\n\t * <p>The {@link Path} may represent a file or a directory.\n\t *\n\t * @param path the path to publish; never {@code null}\n\t * @param mediaType the media type of the path to publish; may be\n\t * {@code null} &mdash; for example, if the path represents a directory\n\t */\n\tpublic static FileEntry from(Path path, @Nullable String mediaType) {\n\t\treturn new FileEntry(path, mediaType);\n\t}\n\n\tprivate final LocalDateTime timestamp = LocalDateTime.now(ZoneId.systemDefault());\n\n\tprivate final Path path;\n\n\tprivate final @Nullable String mediaType;\n\n\tprivate FileEntry(Path path, @Nullable String mediaType) {\n\t\tthis.path = Preconditions.notNull(path, \"path must not be null\");\n\t\tthis.mediaType = mediaType;\n\t}\n\n\t/**\n\t * Get the timestamp for when this {@code FileEntry} was created.\n\t *\n\t * @return when this entry was created; never {@code null}\n\t */\n\tpublic LocalDateTime getTimestamp() {\n\t\treturn this.timestamp;\n\t}\n\n\t/**\n\t * Get the path for the file or directory to be published.\n\t *\n\t * @return the path to publish; never {@code null}\n\t */\n\tpublic Path getPath() {\n\t\treturn path;\n\t}\n\n\t/**\n\t * Get the media type of the path to be published.\n\t *\n\t * @return the media type of the path to publish; never {@code null} but\n\t * potentially empty &mdash; for example, if the path represents a directory\n\t */\n\tpublic Optional<String> getMediaType() {\n\t\treturn Optional.ofNullable(mediaType);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tToStringBuilder builder = new ToStringBuilder(this);\n\t\tbuilder.append(\"timestamp\", this.timestamp);\n\t\tbuilder.append(\"path\", this.path);\n\t\tif (this.mediaType != null) {\n\t\t\tbuilder.append(\"mediaType\", this.mediaType);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/OutputDirectoryProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.reporting;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.OutputDirectoryCreator;\n\n/**\n * Provider of output directories for test engines to write reports and other\n * output files to.\n *\n * @since 1.12\n * @see EngineDiscoveryRequest#getOutputDirectoryProvider()\n * @deprecated Please implement {@link OutputDirectoryCreator} instead\n */\n@SuppressWarnings(\"removal\")\n@Deprecated(since = \"1.14\", forRemoval = true)\n@API(status = DEPRECATED, since = \"1.14\")\npublic interface OutputDirectoryProvider extends OutputDirectoryCreator {\n\n\t/**\n\t * Cast or adapt an {@link OutputDirectoryCreator} to a\n\t * {@code OutputDirectoryProvider}.\n\t *\n\t * @since 1.14\n\t */\n\t@API(status = INTERNAL, since = \"1.14\")\n\tstatic OutputDirectoryProvider castOrAdapt(OutputDirectoryCreator outputDirectoryCreator) {\n\t\tif (outputDirectoryCreator instanceof OutputDirectoryProvider provider) {\n\t\t\treturn provider;\n\t\t}\n\t\treturn new OutputDirectoryProviderAdapter(outputDirectoryCreator);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/OutputDirectoryProviderAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.reporting;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * @since 1.14\n */\n@SuppressWarnings(\"removal\")\nclass OutputDirectoryProviderAdapter implements OutputDirectoryProvider {\n\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\n\tOutputDirectoryProviderAdapter(OutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t}\n\n\t@Override\n\tpublic Path getRootDirectory() {\n\t\treturn this.outputDirectoryCreator.getRootDirectory();\n\t}\n\n\t@Override\n\tpublic Path createOutputDirectory(TestDescriptor testDescriptor) throws IOException {\n\t\treturn this.outputDirectoryCreator.createOutputDirectory(testDescriptor);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/ReportEntry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.reporting;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code ReportEntry} encapsulates a time-stamped map of {@code String}-based\n * key-value pairs to be published to the reporting infrastructure.\n *\n * @since 1.0\n * @see #from(Map)\n * @see #from(String, String)\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ReportEntry {\n\n\tprivate final LocalDateTime timestamp = LocalDateTime.now(ZoneId.systemDefault());\n\tprivate final Map<String, String> keyValuePairs = new LinkedHashMap<>();\n\n\tprivate ReportEntry() {\n\t}\n\n\t/**\n\t * Factory for creating a new {@code ReportEntry} from a map of key-value pairs.\n\t *\n\t * @param keyValuePairs the map of key-value pairs to be published; never\n\t * {@code null}; keys and values within entries in the map also must not be\n\t * {@code null} or blank\n\t */\n\tpublic static ReportEntry from(Map<String, String> keyValuePairs) {\n\t\tPreconditions.notNull(keyValuePairs, \"keyValuePairs must not be null\");\n\n\t\tReportEntry reportEntry = new ReportEntry();\n\t\tkeyValuePairs.forEach(reportEntry::add);\n\t\treturn reportEntry;\n\t}\n\n\t/**\n\t * Factory for creating a new {@code ReportEntry} from a key-value pair.\n\t *\n\t * @param key the key under which the value should published; never\n\t * {@code null} or blank\n\t * @param value the value to publish; never {@code null} or blank\n\t */\n\tpublic static ReportEntry from(String key, String value) {\n\t\tReportEntry reportEntry = new ReportEntry();\n\t\treportEntry.add(key, value);\n\t\treturn reportEntry;\n\t}\n\n\tprivate void add(String key, String value) {\n\t\tPreconditions.notBlank(key, \"key must not be null or blank\");\n\t\tPreconditions.notBlank(value, \"value must not be null or blank\");\n\t\tthis.keyValuePairs.put(key, value);\n\t}\n\n\t/**\n\t * Get an unmodifiable copy of the map of key-value pairs to be published.\n\t *\n\t * @return a copy of the map of key-value pairs; never {@code null}\n\t */\n\tpublic Map<String, String> getKeyValuePairs() {\n\t\treturn Collections.unmodifiableMap(this.keyValuePairs);\n\t}\n\n\t/**\n\t * Get the timestamp for when this {@code ReportEntry} was created.\n\t *\n\t * <p>Can be used, for example, to order entries.\n\t *\n\t * @return when this entry was created; never {@code null}\n\t */\n\tpublic LocalDateTime getTimestamp() {\n\t\treturn this.timestamp;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tToStringBuilder builder = new ToStringBuilder(this);\n\t\tbuilder.append(\"timestamp\", this.timestamp);\n\t\tthis.keyValuePairs.forEach(builder::append);\n\t\treturn builder.toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/reporting/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Classes used by test engines to report additional data to execution\n * listeners.\n */\n\n@NullMarked\npackage org.junit.platform.engine.reporting;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/PrefixedConfigurationParameters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.config;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * View of {@link ConfigurationParameters} that applies a supplied prefix to all\n * queries.\n *\n * @since 1.3\n */\n@API(status = STABLE, since = \"1.10\")\npublic class PrefixedConfigurationParameters implements ConfigurationParameters {\n\n\tprivate final ConfigurationParameters delegate;\n\tprivate final String prefix;\n\n\t/**\n\t * Create a new view of the supplied {@link ConfigurationParameters} that\n\t * applies the supplied prefix to all queries.\n\t *\n\t * @param delegate the {@link ConfigurationParameters} to delegate to; never\n\t * {@code null}\n\t * @param prefix the prefix to apply to all queries; never {@code null} or\n\t * blank\n\t */\n\tpublic PrefixedConfigurationParameters(ConfigurationParameters delegate, String prefix) {\n\t\tthis.delegate = Preconditions.notNull(delegate, \"delegate must not be null\");\n\t\tthis.prefix = Preconditions.notBlank(prefix, \"prefix must not be null or blank\");\n\t}\n\n\t@Override\n\tpublic Optional<String> get(String key) {\n\t\treturn delegate.get(prefixed(key));\n\t}\n\n\t@Override\n\tpublic Optional<Boolean> getBoolean(String key) {\n\t\treturn delegate.getBoolean(prefixed(key));\n\t}\n\n\t@Override\n\tpublic <T> Optional<T> get(String key, Function<? super String, ? extends @Nullable T> transformer) {\n\t\treturn delegate.get(prefixed(key), transformer);\n\t}\n\n\tprivate String prefixed(String key) {\n\t\treturn prefix + key;\n\t}\n\n\t@Override\n\tpublic Set<String> keySet() {\n\t\treturn delegate.keySet();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/config/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * {@link org.junit.platform.engine.ConfigurationParameters}-related support\n * classes intended to be used by test engine implementations.\n */\n\n@NullMarked\npackage org.junit.platform.engine.support.config;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static java.util.Collections.emptySet;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.UnaryOperator;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Abstract base implementation of {@link TestDescriptor} that may be used by\n * custom {@link org.junit.platform.engine.TestEngine TestEngines}.\n *\n * <p>Subclasses should provide a {@link TestSource} in their constructor, if\n * possible, and override {@link #getTags()}, if appropriate.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic abstract class AbstractTestDescriptor implements TestDescriptor {\n\n\t/**\n\t * The Unicode replacement character, often displayed as a black diamond with\n\t * a white question mark in it: {@value}\n\t */\n\tprivate static final String UNICODE_REPLACEMENT_CHARACTER = \"\\uFFFD\";\n\n\tprivate final UniqueId uniqueId;\n\n\tprivate final String displayName;\n\n\tprivate final @Nullable TestSource source;\n\n\tprivate @Nullable TestDescriptor parent;\n\n\t/**\n\t * The synchronized set of children associated with this {@code TestDescriptor}.\n\t *\n\t * <p>This set is used in methods such as {@link #addChild(TestDescriptor)},\n\t * {@link #removeChild(TestDescriptor)}, {@link #removeFromHierarchy()}, and\n\t * {@link #findByUniqueId(UniqueId)}, and an immutable copy of this set is\n\t * returned by {@link #getChildren()}.\n\t *\n\t * <p>If a subclass overrides any of the methods related to children, this\n\t * set should be used instead of a set local to the subclass.\n\t */\n\tprotected final Set<TestDescriptor> children = Collections.synchronizedSet(new LinkedHashSet<>(16));\n\n\t/**\n\t * Create a new {@code AbstractTestDescriptor} with the supplied\n\t * {@link UniqueId} and display name.\n\t *\n\t * <p>As of JUnit 6.0, ISO control characters in the provided display name\n\t * will be replaced. See {@link #AbstractTestDescriptor(UniqueId, String, TestSource)}\n\t * for details.\n\t *\n\t * @param uniqueId the unique ID of this {@code TestDescriptor}; never\n\t * {@code null}\n\t * @param displayName the display name for this {@code TestDescriptor};\n\t * never {@code null} or blank\n\t * @see #AbstractTestDescriptor(UniqueId, String, TestSource)\n\t */\n\tprotected AbstractTestDescriptor(UniqueId uniqueId, String displayName) {\n\t\tthis(uniqueId, displayName, null);\n\t}\n\n\t/**\n\t * Create a new {@code AbstractTestDescriptor} with the supplied\n\t * {@link UniqueId}, display name, and source.\n\t *\n\t * <p>As of JUnit 6.0, ISO control characters in the provided display name\n\t * will be replaced according to the following table.\n\t *\n\t * <table class=\"plain\">\n\t * <caption>Control Character Replacement</caption>\n\t * <tr><th> Original                </th><th> Replacement  </th><th> Description                                 </th></tr>\n\t * <tr><td> {@code \\r}              </td><td> {@code <CR>} </td><td> Textual representation of a carriage return </td></tr>\n\t * <tr><td> {@code \\n}              </td><td> {@code <LF>} </td><td> Textual representation of a line feed       </td></tr>\n\t * <tr><td> Other control character </td><td> &#xFFFD;     </td><td> Unicode replacement character (U+FFFD)      </td></tr>\n\t * </table>\n\t *\n\t * @param uniqueId the unique ID of this {@code TestDescriptor}; never\n\t * {@code null}\n\t * @param displayName the display name for this {@code TestDescriptor};\n\t * never {@code null} or blank\n\t * @param source the source of the test or container described by this\n\t * {@code TestDescriptor}; can be {@code null}\n\t * @see #AbstractTestDescriptor(UniqueId, String)\n\t */\n\tprotected AbstractTestDescriptor(UniqueId uniqueId, String displayName, @Nullable TestSource source) {\n\t\tthis.uniqueId = Preconditions.notNull(uniqueId, \"UniqueId must not be null\");\n\t\tthis.displayName = replaceControlCharacters(\n\t\t\tPreconditions.notBlank(displayName, \"displayName must not be null or blank\"));\n\t\tthis.source = source;\n\t}\n\n\t@Override\n\tpublic final UniqueId getUniqueId() {\n\t\treturn this.uniqueId;\n\t}\n\n\t@Override\n\tpublic final String getDisplayName() {\n\t\treturn this.displayName;\n\t}\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\treturn emptySet();\n\t}\n\n\t@Override\n\tpublic Optional<TestSource> getSource() {\n\t\treturn Optional.ofNullable(this.source);\n\t}\n\n\t@Override\n\tpublic final Optional<TestDescriptor> getParent() {\n\t\treturn Optional.ofNullable(this.parent);\n\t}\n\n\t@Override\n\tpublic final void setParent(@Nullable TestDescriptor parent) {\n\t\tthis.parent = parent;\n\t}\n\n\t@Override\n\tpublic final Set<? extends TestDescriptor> getChildren() {\n\t\treturn Collections.unmodifiableSet(new LinkedHashSet<>(this.children));\n\t}\n\n\t@Override\n\tpublic void addChild(TestDescriptor child) {\n\t\tPreconditions.notNull(child, \"child must not be null\");\n\t\tchild.setParent(this);\n\t\tthis.children.add(child);\n\t}\n\n\t@Override\n\tpublic void removeChild(TestDescriptor child) {\n\t\tPreconditions.notNull(child, \"child must not be null\");\n\t\tthis.children.remove(child);\n\t\tchild.setParent(null);\n\t}\n\n\t@Override\n\tpublic void removeFromHierarchy() {\n\t\tvar parent = Preconditions.notNull(this.parent, \"cannot remove the root of a hierarchy\");\n\t\tparent.removeChild(this);\n\t\tthis.children.forEach(child -> child.setParent(null));\n\t\tthis.children.clear();\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void orderChildren(UnaryOperator<List<TestDescriptor>> orderer) {\n\t\tPreconditions.notNull(orderer, \"orderer must not be null\");\n\t\tList<TestDescriptor> suggestedOrder = orderer.apply(new ArrayList<>(this.children));\n\t\tPreconditions.notNull(suggestedOrder, \"orderer may not return null\");\n\n\t\tSet<? extends TestDescriptor> orderedChildren = new LinkedHashSet<>(suggestedOrder);\n\t\tboolean unmodified = this.children.equals(orderedChildren);\n\t\tPreconditions.condition(unmodified && this.children.size() == suggestedOrder.size(),\n\t\t\t\"orderer may not add or remove test descriptors\");\n\n\t\tthis.children.clear();\n\t\tthis.children.addAll(orderedChildren);\n\t}\n\n\t@Override\n\tpublic Optional<? extends TestDescriptor> findByUniqueId(UniqueId uniqueId) {\n\t\tPreconditions.notNull(uniqueId, \"UniqueId must not be null\");\n\t\tif (getUniqueId().equals(uniqueId)) {\n\t\t\treturn Optional.of(this);\n\t\t}\n\t\t// @formatter:off\n\t\treturn this.children.stream()\n\t\t\t\t.map(child -> child.findByUniqueId(uniqueId))\n\t\t\t\t.filter(Optional::isPresent)\n\t\t\t\t.findAny()\n\t\t\t\t.orElse(Optional.empty());\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic final int hashCode() {\n\t\treturn this.uniqueId.hashCode();\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"EqualsGetClass\")\n\tpublic final boolean equals(Object other) {\n\t\tif (other == null) {\n\t\t\treturn false;\n\t\t}\n\t\tif (this.getClass() != other.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tTestDescriptor that = (TestDescriptor) other;\n\t\treturn this.getUniqueId().equals(that.getUniqueId());\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn getClass().getSimpleName() + \": \" + getUniqueId();\n\t}\n\n\tprivate static String replaceControlCharacters(String text) {\n\t\tStringBuilder builder = new StringBuilder();\n\t\tfor (int i = 0; i < text.length(); i++) {\n\t\t\tbuilder.append(replaceControlCharacter(text.charAt(i)));\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n\tprivate static String replaceControlCharacter(char ch) {\n\t\treturn switch (ch) {\n\t\t\tcase '\\r' -> \"<CR>\";\n\t\t\tcase '\\n' -> \"<LF>\";\n\t\t\tdefault -> Character.isISOControl(ch) ? UNICODE_REPLACEMENT_CHARACTER : String.valueOf(ch);\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClassSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.net.URI;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * Class based {@link org.junit.platform.engine.TestSource TestSource} with\n * an optional {@linkplain FilePosition file position}.\n *\n * <p>If a Java {@link Class} reference is provided, the {@code ClassSource}\n * will contain that {@code Class} and its class name accordingly. If a class\n * name is provided, the {@code ClassSource} will contain the class name and\n * will only attempt to lazily load the {@link Class} if {@link #getJavaClass()}\n * is invoked.\n *\n * <p>In this context, Java {@link Class} means anything that can be referenced\n * as a {@link Class} on the JVM &mdash; for example, classes from other JVM\n * languages such Groovy, Scala, etc.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.ClassSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ClassSource implements TestSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * {@link URI} {@linkplain URI#getScheme() scheme} for class sources: {@value}\n\t *\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.8\")\n\tpublic static final String CLASS_SCHEME = \"class\";\n\n\t/**\n\t * Create a new {@code ClassSource} using the supplied class name.\n\t *\n\t * @param className the class name; must not be {@code null} or blank\n\t */\n\tpublic static ClassSource from(String className) {\n\t\treturn new ClassSource(className);\n\t}\n\n\t/**\n\t * Create a new {@code ClassSource} using the supplied class name and\n\t * {@linkplain FilePosition file position}.\n\t *\n\t * @param className the class name; must not be {@code null} or blank\n\t * @param filePosition the position in the source file; may be {@code null}\n\t */\n\tpublic static ClassSource from(String className, @Nullable FilePosition filePosition) {\n\t\treturn new ClassSource(className, filePosition);\n\t}\n\n\t/**\n\t * Create a new {@code ClassSource} using the supplied {@linkplain Class class}.\n\t *\n\t * @param javaClass the Java class; must not be {@code null}\n\t */\n\tpublic static ClassSource from(Class<?> javaClass) {\n\t\treturn new ClassSource(javaClass);\n\t}\n\n\t/**\n\t * Create a new {@code ClassSource} using the supplied {@linkplain Class class}\n\t * and {@linkplain FilePosition file position}.\n\t *\n\t * @param javaClass the Java class; must not be {@code null}\n\t * @param filePosition the position in the Java source file; may be {@code null}\n\t */\n\tpublic static ClassSource from(Class<?> javaClass, FilePosition filePosition) {\n\t\treturn new ClassSource(javaClass, filePosition);\n\t}\n\n\t/**\n\t * Create a new {@code ClassSource} from the supplied {@link URI}.\n\t *\n\t * <p>URIs should be formatted as {@code class:fully.qualified.class.Name}.\n\t * The {@linkplain URI#getQuery() query} component of the {@code URI}, if\n\t * present, will be used to retrieve the {@link FilePosition} via\n\t * {@link FilePosition#fromQuery(String)}. For example, line 42 and column\n\t * 13 can be referenced in class {@code org.example.MyType} via the following\n\t * URI: {@code class:com.example.MyType?line=42&column=13}. The URI fragment,\n\t * if present, will be ignored.\n\t *\n\t * @param uri the {@code URI} for the class source; never {@code null}\n\t * @return a new {@code ClassSource}; never {@code null}\n\t * @throws PreconditionViolationException if the supplied {@code URI} is\n\t * {@code null}, if the scheme of the supplied {@code URI} is not equal\n\t * to the {@link #CLASS_SCHEME}, or if the specified class name is empty\n\t * @since 1.8\n\t * @see #CLASS_SCHEME\n\t */\n\t@API(status = STABLE, since = \"1.8\")\n\tpublic static ClassSource from(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t\tPreconditions.condition(CLASS_SCHEME.equals(uri.getScheme()),\n\t\t\t() -> \"URI [\" + uri + \"] must have [\" + CLASS_SCHEME + \"] scheme\");\n\n\t\tString className = uri.getSchemeSpecificPart();\n\t\tFilePosition filePosition = null;\n\t\tint indexOfQuery = className.indexOf('?');\n\t\tif (indexOfQuery >= 0) {\n\t\t\tfilePosition = FilePosition.fromQuery(className.substring(indexOfQuery + 1)).orElse(null);\n\t\t\tclassName = className.substring(0, indexOfQuery);\n\t\t}\n\n\t\treturn ClassSource.from(className, filePosition);\n\t}\n\n\tprivate final String className;\n\n\tprivate final @Nullable FilePosition filePosition;\n\n\tprivate @Nullable Class<?> javaClass;\n\n\tprivate ClassSource(String className) {\n\t\tthis(className, null);\n\t}\n\n\tprivate ClassSource(String className, @Nullable FilePosition filePosition) {\n\t\tthis.className = Preconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\tthis.filePosition = filePosition;\n\t}\n\n\tprivate ClassSource(Class<?> javaClass) {\n\t\tthis(javaClass, null);\n\t}\n\n\tprivate ClassSource(Class<?> javaClass, @Nullable FilePosition filePosition) {\n\t\tthis.javaClass = Preconditions.notNull(javaClass, \"Class must not be null\");\n\t\tthis.className = this.javaClass.getName();\n\t\tthis.filePosition = filePosition;\n\t}\n\n\t/**\n\t * Get the class name of this source.\n\t *\n\t * @see #getJavaClass()\n\t * @see #getPosition()\n\t */\n\tpublic String getClassName() {\n\t\treturn this.className;\n\t}\n\n\t/**\n\t * Get the {@linkplain Class Java class} of this source.\n\t *\n\t * <p>If the {@link Class} was not provided, but only the name, this method\n\t * attempts to lazily load the {@link Class} based on its name and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t *\n\t * @see #getClassName()\n\t * @see #getPosition()\n\t */\n\tpublic Class<?> getJavaClass() {\n\t\tif (this.javaClass == null) {\n\t\t\t// @formatter:off\n\t\t\tthis.javaClass = ReflectionSupport.tryToLoadClass(this.className).getNonNullOrThrow(\n\t\t\t\tcause -> new PreconditionViolationException(\"Could not load class with name: \" + this.className, cause));\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn this.javaClass;\n\t}\n\n\t/**\n\t * Get the {@linkplain FilePosition position} in the source file for\n\t * the associated {@linkplain #getClassName class}, if available.\n\t *\n\t * @see #getClassName()\n\t * @see #getJavaClass()\n\t */\n\tpublic Optional<FilePosition> getPosition() {\n\t\treturn Optional.ofNullable(this.filePosition);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClassSource that = (ClassSource) o;\n\t\treturn Objects.equals(this.className, that.className) && Objects.equals(this.filePosition, that.filePosition);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.className, this.filePosition);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"className\", this.className)\n\t\t\t\t.append(\"filePosition\", this.filePosition)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.net.URI;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ResourceUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * <em>Classpath resource</em> based {@link org.junit.platform.engine.TestSource}\n * with an optional {@linkplain FilePosition position}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.ClasspathResourceSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class ClasspathResourceSource implements TestSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * {@link URI} {@linkplain URI#getScheme() scheme} for classpath\n\t * resources: {@value}\n\t *\n\t * @since 1.3\n\t */\n\tpublic static final String CLASSPATH_SCHEME = \"classpath\";\n\n\t/**\n\t * Create a new {@code ClasspathResourceSource} using the supplied classpath\n\t * resource name.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the classpath resource; never\n\t * {@code null} or blank\n\t * @see ClassLoader#getResource(String)\n\t * @see ClassLoader#getResourceAsStream(String)\n\t * @see ClassLoader#getResources(String)\n\t */\n\tpublic static ClasspathResourceSource from(String classpathResourceName) {\n\t\treturn new ClasspathResourceSource(classpathResourceName);\n\t}\n\n\t/**\n\t * Create a new {@code ClasspathResourceSource} using the supplied classpath\n\t * resource name and {@link FilePosition}.\n\t *\n\t * <p>The name of a <em>classpath resource</em> must follow the semantics\n\t * for resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * <p>If the supplied classpath resource name is prefixed with a slash\n\t * ({@code /}), the slash will be removed.\n\t *\n\t * @param classpathResourceName the name of the classpath resource; never\n\t * {@code null} or blank\n\t * @param filePosition the position in the classpath resource; may be {@code null}\n\t */\n\tpublic static ClasspathResourceSource from(String classpathResourceName, @Nullable FilePosition filePosition) {\n\t\treturn new ClasspathResourceSource(classpathResourceName, filePosition);\n\t}\n\n\t/**\n\t * Create a new {@code ClasspathResourceSource} from the supplied {@link URI}.\n\t *\n\t * <p>The {@link URI#getPath() path} component of the {@code URI} (excluding\n\t * the query) will be used as the classpath resource name. The\n\t * {@linkplain URI#getQuery() query} component of the {@code URI}, if present,\n\t * will be used to retrieve the {@link FilePosition} via\n\t * {@link FilePosition#fromQuery(String)}.\n\t *\n\t * @param uri the {@code URI} for the classpath resource; never {@code null}\n\t * @return a new {@code ClasspathResourceSource}; never {@code null}\n\t * @throws PreconditionViolationException if the supplied {@code URI} is\n\t * {@code null} or if the scheme of the supplied {@code URI} is not equal\n\t * to the {@link #CLASSPATH_SCHEME}\n\t * @since 1.3\n\t * @see #CLASSPATH_SCHEME\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\tpublic static ClasspathResourceSource from(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t\tPreconditions.condition(CLASSPATH_SCHEME.equals(uri.getScheme()),\n\t\t\t() -> \"URI [\" + uri + \"] must have [\" + CLASSPATH_SCHEME + \"] scheme\");\n\n\t\tString classpathResource = ResourceUtils.stripQueryComponent(uri).getPath();\n\t\tFilePosition filePosition = FilePosition.fromQuery(uri.getQuery()).orElse(null);\n\t\treturn ClasspathResourceSource.from(classpathResource, filePosition);\n\t}\n\n\tprivate final String classpathResourceName;\n\n\tprivate final @Nullable FilePosition filePosition;\n\n\tprivate ClasspathResourceSource(String classpathResourceName) {\n\t\tthis(classpathResourceName, null);\n\t}\n\n\tprivate ClasspathResourceSource(String classpathResourceName, @Nullable FilePosition filePosition) {\n\t\tPreconditions.notBlank(classpathResourceName, \"Classpath resource name must not be null or blank\");\n\t\tboolean startsWithSlash = classpathResourceName.startsWith(\"/\");\n\t\tthis.classpathResourceName = (startsWithSlash ? classpathResourceName.substring(1) : classpathResourceName);\n\t\tthis.filePosition = filePosition;\n\t}\n\n\t/**\n\t * Get the name of the source <em>classpath resource</em>.\n\t *\n\t * <p>The name of a <em>classpath resource</em> follows the semantics for\n\t * resource paths as defined in {@link ClassLoader#getResource(String)}.\n\t *\n\t * @see ClassLoader#getResource(String)\n\t * @see ClassLoader#getResourceAsStream(String)\n\t * @see ClassLoader#getResources(String)\n\t */\n\tpublic String getClasspathResourceName() {\n\t\treturn this.classpathResourceName;\n\t}\n\n\t/**\n\t * Get the {@link FilePosition}, if available.\n\t */\n\tpublic Optional<FilePosition> getPosition() {\n\t\treturn Optional.ofNullable(this.filePosition);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tClasspathResourceSource that = (ClasspathResourceSource) o;\n\t\treturn Objects.equals(this.classpathResourceName, that.classpathResourceName)\n\t\t\t\t&& Objects.equals(this.filePosition, that.filePosition);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.classpathResourceName, this.filePosition);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"classpathResourceName\", this.classpathResourceName)\n\t\t\t\t.append(\"filePosition\", this.filePosition)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/CompositeTestSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * A {@code CompositeTestSource} contains one or more {@link TestSource TestSources}.\n *\n * <p>{@code CompositeTestSource} and its {@link #getSources sources} are immutable.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class CompositeTestSource implements TestSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Create a new {@code CompositeTestSource} based on the supplied\n\t * collection of {@link TestSource sources}.\n\t *\n\t * <p>This constructor makes a defensive copy of the supplied collection\n\t * and stores the sources as a list in the order in which they are\n\t * returned by the collection's iterator.\n\t *\n\t * @param sources the collection of sources to store in this\n\t * {@code CompositeTestSource}; never {@code null} or empty\n\t */\n\tpublic static CompositeTestSource from(Collection<? extends TestSource> sources) {\n\t\treturn new CompositeTestSource(sources);\n\t}\n\n\t@SuppressWarnings({ \"serial\", \"RedundantSuppression\" }) // always used with serializable implementation (unmodifiableList())\n\tprivate final List<TestSource> sources;\n\n\tprivate CompositeTestSource(Collection<? extends TestSource> sources) {\n\t\tPreconditions.notEmpty(sources, \"TestSource collection must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(sources, \"individual TestSources must not be null\");\n\t\tthis.sources = List.copyOf(sources);\n\t}\n\n\t/**\n\t * Get an immutable list of the {@linkplain TestSource sources} stored in this\n\t * {@code CompositeTestSource}.\n\t *\n\t * @return the sources stored in this {@code CompositeTestSource}; never {@code null}\n\t */\n\tpublic List<TestSource> getSources() {\n\t\treturn this.sources;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null || getClass() != obj.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tCompositeTestSource that = (CompositeTestSource) obj;\n\t\treturn this.sources.equals(that.sources);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.sources.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"sources\", this.sources).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DefaultUriSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport java.io.Serial;\nimport java.net.URI;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Default implementation of {@link UriSource}.\n *\n * @since 1.3\n */\nrecord DefaultUriSource(URI uri) implements UriSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tDefaultUriSource {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\t}\n\n\t@Override\n\tpublic URI getUri() {\n\t\treturn uri;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"uri\", this.uri).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/DirectorySource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.net.URI;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Directory based {@link org.junit.platform.engine.TestSource}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.DirectorySelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class DirectorySource implements FileSystemSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Create a new {@code DirectorySource} using the supplied\n\t * {@linkplain File directory}.\n\t *\n\t * @param directory the source directory; must not be {@code null}\n\t */\n\tpublic static DirectorySource from(File directory) {\n\t\treturn new DirectorySource(directory);\n\t}\n\n\tprivate final File directory;\n\n\tprivate DirectorySource(File directory) {\n\t\tPreconditions.notNull(directory, \"directory must not be null\");\n\t\ttry {\n\t\t\tthis.directory = directory.getCanonicalFile();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new JUnitException(\"Failed to retrieve canonical path for directory: \" + directory, ex);\n\t\t}\n\t}\n\n\t/**\n\t * Get the {@link URI} for the source {@linkplain #getFile directory}.\n\t *\n\t * @return the source {@code URI}; never {@code null}\n\t */\n\t@Override\n\tpublic URI getUri() {\n\t\treturn getFile().toURI();\n\t}\n\n\t/**\n\t * Get the source {@linkplain File directory}.\n\t *\n\t * @return the source directory; never {@code null}\n\t */\n\t@Override\n\tpublic File getFile() {\n\t\treturn this.directory;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tDirectorySource that = (DirectorySource) o;\n\t\treturn this.directory.equals(that.directory);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.directory.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"directory\", this.directory).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/EngineDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * An {@code EngineDescriptor} is a {@link org.junit.platform.engine.TestDescriptor\n * TestDescriptor} for a specific {@link org.junit.platform.engine.TestEngine TestEngine}.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic class EngineDescriptor extends AbstractTestDescriptor {\n\n\t/**\n\t * Create a new {@code EngineDescriptor} with the supplied {@link UniqueId}\n\t * and display name.\n\t *\n\t * @param uniqueId the {@code UniqueId} for the described {@code TestEngine};\n\t * never {@code null}\n\t * @param displayName the display name for the described {@code TestEngine};\n\t * never {@code null} or blank\n\t * @see org.junit.platform.engine.TestEngine#getId()\n\t * @see org.junit.platform.engine.TestDescriptor#getDisplayName()\n\t */\n\tpublic EngineDescriptor(UniqueId uniqueId, String displayName) {\n\t\tsuper(uniqueId, displayName);\n\t}\n\n\t/**\n\t * Returns {@link org.junit.platform.engine.TestDescriptor.Type#CONTAINER}.\n\t *\n\t * @see org.junit.platform.engine.TestDescriptor#isContainer()\n\t * @see org.junit.platform.engine.TestDescriptor#isTest()\n\t */\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FilePosition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * Position inside a file represented by {@linkplain #getLine line} and\n * {@linkplain #getColumn column} numbers.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class FilePosition implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(FilePosition.class);\n\n\t/**\n\t * Create a new {@code FilePosition} using the supplied {@code line} number\n\t * and an undefined column number.\n\t *\n\t * @param line the line number; must be greater than zero\n\t * @return a {@link FilePosition} with the given line number\n\t */\n\tpublic static FilePosition from(int line) {\n\t\treturn new FilePosition(line);\n\t}\n\n\t/**\n\t * Create a new {@code FilePosition} using the supplied {@code line} and\n\t * {@code column} numbers.\n\t *\n\t * @param line the line number; must be greater than zero\n\t * @param column the column number; must be greater than zero\n\t * @return a {@link FilePosition} with the given line and column numbers\n\t */\n\tpublic static FilePosition from(int line, int column) {\n\t\treturn new FilePosition(line, column);\n\t}\n\n\t/**\n\t * Create an optional {@code FilePosition} by parsing the supplied\n\t * {@code query} string.\n\t *\n\t * <p>Examples of valid {@code query} strings:\n\t * <ul>\n\t *     <li>{@code \"line=23\"}</li>\n\t *     <li>{@code \"line=23&column=42\"}</li>\n\t * </ul>\n\t *\n\t * @param query the query string; may be {@code null}\n\t * @return an {@link Optional} containing a {@link FilePosition} with\n\t * the parsed line and column numbers; never {@code null} but potentially\n\t * empty\n\t * @since 1.3\n\t * @see #from(int)\n\t * @see #from(int, int)\n\t */\n\tpublic static Optional<FilePosition> fromQuery(String query) {\n\t\tFilePosition result = null;\n\t\tInteger line = null;\n\t\tInteger column = null;\n\t\tif (StringUtils.isNotBlank(query)) {\n\t\t\ttry {\n\t\t\t\tfor (String pair : query.split(\"&\")) {\n\t\t\t\t\tString[] data = pair.split(\"=\");\n\t\t\t\t\tif (data.length == 2) {\n\t\t\t\t\t\tString key = data[0];\n\t\t\t\t\t\tif (line == null && \"line\".equals(key)) {\n\t\t\t\t\t\t\tline = Integer.valueOf(data[1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse if (column == null && \"column\".equals(key)) {\n\t\t\t\t\t\t\tcolumn = Integer.valueOf(data[1]);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Already found what we're looking for?\n\t\t\t\t\tif (line != null && column != null) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (IllegalArgumentException ex) {\n\t\t\t\tlogger.debug(ex, () -> \"Failed to parse 'line' and/or 'column' from query string: \" + query);\n\t\t\t\t// fall-through and continue\n\t\t\t}\n\n\t\t\tif (line != null) {\n\t\t\t\tresult = column == null ? new FilePosition(line) : new FilePosition(line, column);\n\t\t\t}\n\t\t}\n\t\treturn Optional.ofNullable(result);\n\t}\n\n\tprivate final int line;\n\n\tprivate final @Nullable Integer column;\n\n\tprivate FilePosition(int line) {\n\t\tPreconditions.condition(line > 0, \"line number must be greater than zero\");\n\t\tthis.line = line;\n\t\tthis.column = null;\n\t}\n\n\tprivate FilePosition(int line, int column) {\n\t\tPreconditions.condition(line > 0, \"line number must be greater than zero\");\n\t\tPreconditions.condition(column > 0, \"column number must be greater than zero\");\n\t\tthis.line = line;\n\t\tthis.column = column;\n\t}\n\n\t/**\n\t * Get the line number of this {@code FilePosition}.\n\t *\n\t * @return the line number\n\t */\n\tpublic int getLine() {\n\t\treturn this.line;\n\t}\n\n\t/**\n\t * Get the column number of this {@code FilePosition}, if available.\n\t *\n\t * @return an {@code Optional} containing the column number; never\n\t * {@code null} but potentially empty\n\t */\n\tpublic Optional<Integer> getColumn() {\n\t\treturn Optional.ofNullable(this.column);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFilePosition that = (FilePosition) o;\n\t\treturn (this.line == that.line) && Objects.equals(this.column, that.column);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.line, this.column);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"line\", this.line)\n\t\t\t\t.append(\"column\", getColumn().orElse(-1))\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.net.URI;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * File based {@link org.junit.platform.engine.TestSource} with an optional\n * {@linkplain FilePosition position}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.FileSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class FileSource implements FileSystemSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Create a new {@code FileSource} using the supplied {@link File file}.\n\t *\n\t * @param file the source file; must not be {@code null}\n\t */\n\tpublic static FileSource from(File file) {\n\t\treturn new FileSource(file);\n\t}\n\n\t/**\n\t * Create a new {@code FileSource} using the supplied {@link File file} and\n\t * {@link FilePosition filePosition}.\n\t *\n\t * @param file the source file; must not be {@code null}\n\t * @param filePosition the position in the source file; may be {@code null}\n\t * @see #withPosition(FilePosition)\n\t */\n\tpublic static FileSource from(File file, @Nullable FilePosition filePosition) {\n\t\treturn new FileSource(file, filePosition);\n\t}\n\n\tprivate final File file;\n\n\tprivate final @Nullable FilePosition filePosition;\n\n\tprivate FileSource(File file) {\n\t\tthis(file, null);\n\t}\n\n\tprivate FileSource(File file, @Nullable FilePosition filePosition) {\n\t\tPreconditions.notNull(file, \"file must not be null\");\n\t\ttry {\n\t\t\tthis.file = file.getCanonicalFile();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new JUnitException(\"Failed to retrieve canonical path for file: \" + file, ex);\n\t\t}\n\t\tthis.filePosition = filePosition;\n\t}\n\n\tprivate FileSource(FileSource fileSource, @Nullable FilePosition filePosition) {\n\t\tthis.file = fileSource.file;\n\t\tthis.filePosition = filePosition;\n\t}\n\n\t/**\n\t * Get the {@link URI} for the source {@linkplain #getFile file}.\n\t *\n\t * @return the source {@code URI}; never {@code null}\n\t */\n\t@Override\n\tpublic URI getUri() {\n\t\treturn getFile().toURI();\n\t}\n\n\t/**\n\t * Get the source {@linkplain File file}.\n\t *\n\t * @return the source file; never {@code null}\n\t */\n\t@Override\n\tpublic File getFile() {\n\t\treturn this.file;\n\t}\n\n\t/**\n\t * Get the {@link FilePosition}, if available.\n\t */\n\tpublic Optional<FilePosition> getPosition() {\n\t\treturn Optional.ofNullable(this.filePosition);\n\t}\n\n\t/**\n\t * {@return a {@code FileSource} based on this instance but with the\n\t * supplied {@link FilePosition}}\n\t *\n\t * <p>If the supplied {@code FilePosition}\n\t * {@linkplain Objects#equals(Object, Object) equals} the existing one, this\n\t * method returns {@code this}. Otherwise, a new instance is created and\n\t * returned.\n\t *\n\t * <p>Calling this method rather than creating a new {@code FileSource} via\n\t * {@link #from(File, FilePosition)} avoids the overhead of redundant\n\t * canonical path resolution.\n\t *\n\t * @param filePosition the position in the source file; may be {@code null}\n\t * @since 1.14\n\t */\n\t@API(status = EXPERIMENTAL, since = \"1.14\")\n\tpublic FileSource withPosition(@Nullable FilePosition filePosition) {\n\t\tif (Objects.equals(this.filePosition, filePosition)) {\n\t\t\treturn this;\n\t\t}\n\t\treturn new FileSource(this, filePosition);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tFileSource that = (FileSource) o;\n\t\treturn Objects.equals(this.file, that.file) //\n\t\t\t\t&& Objects.equals(this.filePosition, that.filePosition);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.file, this.filePosition);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"file\", this.file)\n\t\t\t\t.append(\"filePosition\", this.filePosition)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/FileSystemSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.File;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * File system based {@link TestSource}.\n *\n * <p>This interface uses {@link File} instead of {@link java.nio.file.Path}\n * because the latter does not implement {@link java.io.Serializable}.\n *\n * @since 1.0\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface FileSystemSource extends UriSource {\n\n\t/**\n\t * Get the source file or directory.\n\t *\n\t * @return the source file or directory; never {@code null}\n\t */\n\tFile getFile();\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/MethodSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.commons.util.ClassUtils.nullSafeToString;\n\nimport java.io.Serial;\nimport java.lang.reflect.Method;\nimport java.util.Objects;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * Method based {@link org.junit.platform.engine.TestSource TestSource}.\n *\n * <p>This class stores the method name along with the names of its parameter\n * types because {@link Method} does not implement {@link java.io.Serializable}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.MethodSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class MethodSource implements TestSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Create a new {@code MethodSource} using the supplied class name and\n\t * method name.\n\t *\n\t * @param className the class name; must not be {@code null} or blank\n\t * @param methodName the method name; must not be {@code null} or blank\n\t */\n\tpublic static MethodSource from(String className, String methodName) {\n\t\treturn new MethodSource(className, methodName);\n\t}\n\n\t/**\n\t * Create a new {@code MethodSource} using the supplied class name, method\n\t * name, and method parameter types.\n\t *\n\t * @param className the class name; must not be {@code null} or blank\n\t * @param methodName the method name; must not be {@code null} or blank\n\t * @param methodParameterTypes a comma-separated list of fully qualified\n\t * class names representing the method parameter types\n\t */\n\tpublic static MethodSource from(String className, String methodName, String methodParameterTypes) {\n\t\treturn new MethodSource(className, methodName, methodParameterTypes);\n\t}\n\n\t/**\n\t * Create a new {@code MethodSource} using the supplied class name, method\n\t * name, and method parameter types.\n\t *\n\t * @param className the class name; must not be {@code null} or blank\n\t * @param methodName the method name; must not be {@code null} or blank\n\t * @param methodParameterTypes a varargs array of classes representing the\n\t * method parameter types\n\t * @since 1.5\n\t */\n\t@API(status = STABLE, since = \"1.5\")\n\tpublic static MethodSource from(String className, String methodName, Class<?>... methodParameterTypes) {\n\t\treturn new MethodSource(className, methodName, nullSafeToString(methodParameterTypes));\n\t}\n\n\t/**\n\t * Create a new {@code MethodSource} using the supplied {@link Method method}.\n\t *\n\t * @param testMethod the Java method; must not be {@code null}\n\t * @see #from(Class, Method)\n\t */\n\tpublic static MethodSource from(Method testMethod) {\n\t\treturn new MethodSource(testMethod);\n\t}\n\n\t/**\n\t * Create a new {@code MethodSource} using the supplied\n\t * {@link Class class} and {@link Method method}.\n\t *\n\t * <p>This method should be used in favor of {@link #from(Method)} if the\n\t * test method is inherited from a superclass or present as an interface\n\t * {@code default} method.\n\t *\n\t * @param testClass the Java class; must not be {@code null}\n\t * @param testMethod the Java method; must not be {@code null}\n\t * @since 1.3\n\t */\n\t@API(status = STABLE, since = \"1.3\")\n\tpublic static MethodSource from(Class<?> testClass, Method testMethod) {\n\t\treturn new MethodSource(testClass, testMethod);\n\t}\n\n\tprivate final String className;\n\tprivate final String methodName;\n\n\tprivate final @Nullable String methodParameterTypes;\n\n\tprivate @Nullable Class<?> javaClass;\n\n\tprivate transient @Nullable Method javaMethod;\n\n\tprivate MethodSource(String className, String methodName) {\n\t\tthis(className, methodName, null);\n\t}\n\n\tprivate MethodSource(String className, String methodName, @Nullable String methodParameterTypes) {\n\t\tPreconditions.notBlank(className, \"Class name must not be null or blank\");\n\t\tPreconditions.notBlank(methodName, \"Method name must not be null or blank\");\n\t\tthis.className = className;\n\t\tthis.methodName = methodName;\n\t\tthis.methodParameterTypes = methodParameterTypes;\n\t}\n\n\tprivate MethodSource(Method testMethod) {\n\t\tthis(Preconditions.notNull(testMethod, \"Method must not be null\").getDeclaringClass(), testMethod);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\tprivate MethodSource(Class<?> testClass, Method testMethod) {\n\t\tPreconditions.notNull(testClass, \"Class must not be null\");\n\t\tPreconditions.notNull(testMethod, \"Method must not be null\");\n\t\tthis.className = testClass.getName();\n\t\tthis.methodName = testMethod.getName();\n\t\tthis.methodParameterTypes = nullSafeToString(testMethod.getParameterTypes());\n\t\tthis.javaClass = testClass;\n\t\tthis.javaMethod = testMethod;\n\t}\n\n\t/**\n\t * Get the class name of this source.\n\t */\n\tpublic String getClassName() {\n\t\treturn this.className;\n\t}\n\n\t/**\n\t * Get the method name of this source.\n\t */\n\tpublic String getMethodName() {\n\t\treturn this.methodName;\n\t}\n\n\t/**\n\t * Get the method parameter types of this source.\n\t */\n\tpublic @Nullable String getMethodParameterTypes() {\n\t\treturn this.methodParameterTypes;\n\t}\n\n\t/**\n\t * Get the {@linkplain Class Java class} of this source.\n\t *\n\t * <p>If the {@link Class} was not provided, but only the name, this method\n\t * attempts to lazily load the {@link Class} based on its name and throws a\n\t * {@link PreconditionViolationException} if the class cannot be loaded.\n\t *\n\t * @since 1.7\n\t * @see #getClassName()\n\t */\n\t@API(status = STABLE, since = \"1.7\")\n\tpublic Class<?> getJavaClass() {\n\t\treturn lazyLoadJavaClass();\n\t}\n\n\t/**\n\t * Get the {@linkplain Method Java method} of this source.\n\t *\n\t * <p>If the {@link Method} was not provided, but only the name, this method\n\t * attempts to lazily load the {@code Method} based on its name and throws a\n\t * {@link PreconditionViolationException} if the method cannot be loaded.\n\t *\n\t * @since 1.7\n\t * @see #getMethodName()\n\t */\n\t@API(status = STABLE, since = \"1.7\")\n\tpublic Method getJavaMethod() {\n\t\treturn lazyLoadJavaMethod();\n\t}\n\n\tprivate Class<?> lazyLoadJavaClass() {\n\t\tif (this.javaClass == null) {\n\t\t\t// @formatter:off\n\t\t\tthis.javaClass = ReflectionSupport.tryToLoadClass(this.className).getNonNullOrThrow(\n\t\t\t\tcause -> new PreconditionViolationException(\"Could not load class with name: \" + this.className, cause));\n\t\t\t// @formatter:on\n\t\t}\n\t\treturn this.javaClass;\n\t}\n\n\tprivate Method lazyLoadJavaMethod() {\n\t\tif (this.javaMethod == null) {\n\t\t\tClass<?> javaClass = getJavaClass();\n\t\t\tif (StringUtils.isNotBlank(this.methodParameterTypes)) {\n\t\t\t\tthis.javaMethod = ReflectionSupport.findMethod(javaClass, this.methodName,\n\t\t\t\t\tthis.methodParameterTypes).orElseThrow(\n\t\t\t\t\t\t() -> new PreconditionViolationException(\n\t\t\t\t\t\t\t\"Could not find method with name [%s] and parameter types [%s] in class [%s].\".formatted(\n\t\t\t\t\t\t\t\tthis.methodName, this.methodParameterTypes, javaClass.getName())));\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.javaMethod = ReflectionSupport.findMethod(javaClass, this.methodName).orElseThrow(\n\t\t\t\t\t() -> new PreconditionViolationException(\n\t\t\t\t\t\t\"Could not find method with name [%s] in class [%s].\".formatted(this.methodName,\n\t\t\t\t\t\t\tjavaClass.getName())));\n\t\t\t}\n\t\t}\n\t\treturn this.javaMethod;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tMethodSource that = (MethodSource) o;\n\t\treturn Objects.equals(this.className, that.className)//\n\t\t\t\t&& Objects.equals(this.methodName, that.methodName)//\n\t\t\t\t&& Objects.equals(this.methodParameterTypes, that.methodParameterTypes);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn Objects.hash(this.className, this.methodName, this.methodParameterTypes);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"className\", this.className)\n\t\t\t\t.append(\"methodName\", this.methodName)\n\t\t\t\t.append(\"methodParameterTypes\", this.methodParameterTypes)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/PackageSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.Serial;\nimport java.util.Objects;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * Package based {@link org.junit.platform.engine.TestSource}.\n *\n * <p>This class stores the package name because {@link Package} does not\n * implement {@link java.io.Serializable}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.PackageSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class PackageSource implements TestSource {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\t/**\n\t * Create a new {@code PackageSource} using the supplied Java {@link Package}.\n\t *\n\t * @param javaPackage the Java package; must not be {@code null}\n\t */\n\tpublic static PackageSource from(Package javaPackage) {\n\t\treturn new PackageSource(javaPackage);\n\t}\n\n\t/**\n\t * Create a new {@code PackageSource} using the supplied {@code packageName}.\n\t *\n\t * @param packageName the package name; must not be {@code null} or blank\n\t */\n\tpublic static PackageSource from(String packageName) {\n\t\treturn new PackageSource(packageName);\n\t}\n\n\tprivate final String packageName;\n\n\tprivate PackageSource(Package javaPackage) {\n\t\tthis(Preconditions.notNull(javaPackage, \"package must not be null\").getName());\n\t}\n\n\tprivate PackageSource(String packageName) {\n\t\tPreconditions.notNull(packageName, \"package name must not be null\");\n\t\tPreconditions.condition(packageName.isEmpty() || !packageName.isBlank(),\n\t\t\t\"package name must not contain only whitespace\");\n\t\tthis.packageName = packageName;\n\t}\n\n\t/**\n\t * Get the package name of this test source.\n\t */\n\tpublic String getPackageName() {\n\t\treturn this.packageName;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tPackageSource that = (PackageSource) o;\n\t\treturn Objects.equals(this.packageName, that.packageName);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.packageName.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"packageName\", this.packageName).toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/UriSource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.net.URI;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ResourceUtils;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * A {@link TestSource} that can be represented as a {@link URI}.\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.UriSelector\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface UriSource extends TestSource {\n\n\t/**\n\t * Get the {@link URI} that represents this source.\n\t *\n\t * @return the source {@code URI}; never {@code null}\n\t */\n\tURI getUri();\n\n\t/**\n\t * Create a new {@code UriSource} using the supplied {@code URI}.\n\t *\n\t * <p>This implementation first attempts to resolve the supplied {@code URI}\n\t * to a path-based {@code UriSource} in the local filesystem. If that fails\n\t * for any reason, an instance of the default {@code UriSource}\n\t * implementation storing the supplied {@code URI} <em>as-is</em> will be\n\t * returned.\n\t *\n\t * @param uri the URI to use as the source; never {@code null}\n\t * @return an appropriate {@code UriSource} for the supplied {@code URI}\n\t * @since 1.3\n\t * @see org.junit.platform.engine.support.descriptor.FileSource\n\t * @see org.junit.platform.engine.support.descriptor.DirectorySource\n\t */\n\tstatic UriSource from(URI uri) {\n\t\tPreconditions.notNull(uri, \"URI must not be null\");\n\n\t\ttry {\n\t\t\tURI uriWithoutQuery = ResourceUtils.stripQueryComponent(uri);\n\t\t\tPath path = Path.of(uriWithoutQuery);\n\t\t\tif (Files.isRegularFile(path)) {\n\t\t\t\treturn FileSource.from(path.toFile(), FilePosition.fromQuery(uri.getQuery()).orElse(null));\n\t\t\t}\n\t\t\tif (Files.isDirectory(path)) {\n\t\t\t\treturn DirectorySource.from(path.toFile());\n\t\t\t}\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\tLoggerFactory.getLogger(UriSource.class).debug(ex,\n\t\t\t\t() -> \"The supplied URI [%s] is not path-based. Falling back to default UriSource implementation.\".formatted(\n\t\t\t\t\turi));\n\t\t}\n\n\t\t// Store supplied URI as-is\n\t\treturn new DefaultUriSource(uri);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/descriptor/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * {@link org.junit.platform.engine.TestDescriptor}-related support classes\n * intended to be used by test engine implementations and clients of\n * the launcher.\n */\n\n@NullMarked\npackage org.junit.platform.engine.support.descriptor;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ClassContainerSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.platform.commons.support.ReflectionSupport.findAllClassesInClasspathRoot;\nimport static org.junit.platform.commons.support.ReflectionSupport.findAllClassesInModule;\nimport static org.junit.platform.commons.support.ReflectionSupport.findAllClassesInPackage;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.selectors;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\n\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\n\n/**\n * @since 1.5\n */\nclass ClassContainerSelectorResolver implements SelectorResolver {\n\n\tprivate final Predicate<Class<?>> classFilter;\n\tprivate final Predicate<String> classNameFilter;\n\n\tClassContainerSelectorResolver(Predicate<Class<?>> classFilter, Predicate<String> classNameFilter) {\n\t\tthis.classFilter = Preconditions.notNull(classFilter, \"classFilter must not be null\");\n\t\tthis.classNameFilter = Preconditions.notNull(classNameFilter, \"classNameFilter must not be null\");\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ClasspathRootSelector selector, Context context) {\n\t\treturn classSelectors(findAllClassesInClasspathRoot(selector.getClasspathRoot(), classFilter, classNameFilter));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ModuleSelector selector, Context context) {\n\t\tif (selector.getModule().isPresent()) {\n\t\t\tModule module = selector.getModule().get();\n\t\t\treturn classSelectors(findAllClassesInModule(module, classFilter, classNameFilter));\n\t\t}\n\t\treturn classSelectors(findAllClassesInModule(selector.getModuleName(), classFilter, classNameFilter));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(PackageSelector selector, Context context) {\n\t\treturn classSelectors(findAllClassesInPackage(selector.getPackageName(), classFilter, classNameFilter));\n\t}\n\n\tprivate Resolution classSelectors(List<Class<?>> classes) {\n\t\tif (classes.isEmpty()) {\n\t\t\treturn unresolved();\n\t\t}\n\t\treturn selectors(classes.stream().map(DiscoverySelectors::selectClass).collect(toSet()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/DiscoveryIssueReporter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.EngineDiscoveryListener;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@code DiscoveryIssueReporter} defines the API for reporting\n * {@link DiscoveryIssue DiscoveryIssues}.\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 1.13\n * @see SelectorResolver.Context\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic interface DiscoveryIssueReporter {\n\n\t/**\n\t * Create a new {@code DiscoveryIssueReporter} that reports issues to the\n\t * supplied {@link EngineDiscoveryListener} for the specified engine.\n\t *\n\t * @param engineDiscoveryListener the listener to report issues to; never\n\t * {@code null}\n\t * @param engineId the unique identifier of the engine; never {@code null}\n\t */\n\tstatic DiscoveryIssueReporter forwarding(EngineDiscoveryListener engineDiscoveryListener, UniqueId engineId) {\n\t\tPreconditions.notNull(engineDiscoveryListener, \"engineDiscoveryListener must not be null\");\n\t\tPreconditions.notNull(engineId, \"engineId must not be null\");\n\t\treturn issue -> engineDiscoveryListener.issueEncountered(engineId, issue);\n\t}\n\n\t/**\n\t * Create a new {@code DiscoveryIssueReporter} that adds reported issues to\n\t * the supplied collection.\n\t *\n\t * @param collection the collection to add issues to; never {@code null}\n\t */\n\tstatic DiscoveryIssueReporter collecting(Collection<? super DiscoveryIssue> collection) {\n\t\tPreconditions.notNull(collection, \"collection must not be null\");\n\t\treturn consuming(collection::add);\n\t}\n\n\t/**\n\t * Create a new {@code DiscoveryIssueReporter} that adds reported issues to\n\t * the supplied consumer.\n\t *\n\t * @param consumer the consumer to report issues to; never {@code null}\n\t */\n\tstatic DiscoveryIssueReporter consuming(Consumer<? super DiscoveryIssue> consumer) {\n\t\tPreconditions.notNull(consumer, \"consumer must not be null\");\n\t\treturn consumer::accept;\n\t}\n\n\t/**\n\t * Create a new {@code DiscoveryIssueReporter} that avoids reporting\n\t * duplicate issues.\n\t *\n\t * <p>The implementation returned by this method is not thread-safe.\n\t *\n\t * @param delegate the delegate to forward issues to; never {@code null}\n\t */\n\tstatic DiscoveryIssueReporter deduplicating(DiscoveryIssueReporter delegate) {\n\t\tPreconditions.notNull(delegate, \"delegate must not be null\");\n\t\tSet<DiscoveryIssue> seen = new HashSet<>();\n\t\treturn issue -> {\n\t\t\tboolean notSeen = seen.add(issue);\n\t\t\tif (notSeen) {\n\t\t\t\tdelegate.reportIssue(issue);\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Build the supplied {@link DiscoveryIssue.Builder Builder} and report the\n\t * resulting {@link DiscoveryIssue}.\n\t */\n\tdefault void reportIssue(DiscoveryIssue.Builder builder) {\n\t\treportIssue(builder.build());\n\t}\n\n\t/**\n\t * Report the supplied {@link DiscoveryIssue}.\n\t */\n\tvoid reportIssue(DiscoveryIssue issue);\n\n\t/**\n\t * Create a {@link Condition} that reports a {@link DiscoveryIssue} when the\n\t * supplied {@link Predicate} is not met.\n\t *\n\t * @param predicate the predicate to test; never {@code null}\n\t * @param issueCreator the function to create the issue with; never {@code null}\n\t * @return a new {@code Condition}; never {@code null}\n\t */\n\tdefault <T> Condition<T> createReportingCondition(Predicate<T> predicate,\n\t\t\tFunction<T, DiscoveryIssue> issueCreator) {\n\t\tPreconditions.notNull(predicate, \"predicate must not be null\");\n\t\tPreconditions.notNull(issueCreator, \"issueCreator must not be null\");\n\t\treturn value -> {\n\t\t\tif (predicate.test(value)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\telse {\n\t\t\t\treportIssue(issueCreator.apply(value));\n\t\t\t\treturn false;\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * A {@code Condition} is a union of {@link Predicate} and {@link Consumer}.\n\t *\n\t * <p>Instances of this type may be used as {@link Predicate Predicates} or\n\t * {@link Consumer Consumers}. For example, a {@code Condition} may be\n\t * passed to {@link java.util.stream.Stream#filter(Predicate)} if it is used\n\t * for filtering, or to {@link java.util.stream.Stream#peek(Consumer)} if it\n\t * is only used for reporting or other side effects.\n\t *\n\t * <p>This interface is not intended to be implemented by clients.\n\t *\n\t * @see #createReportingCondition(Predicate, Function)\n\t */\n\tinterface Condition<T> {\n\n\t\t/**\n\t\t * Create a {@link Condition} that is always satisfied.\n\t\t */\n\t\tstatic <T> Condition<T> alwaysSatisfied() {\n\t\t\treturn __ -> true;\n\t\t}\n\n\t\t/**\n\t\t * Evaluate this condition to potentially report an issue.\n\t\t */\n\t\tboolean check(T value);\n\n\t\t/**\n\t\t * Return a composed condition that represents a logical AND of this\n\t\t * and the supplied condition.\n\t\t *\n\t\t * <p>The default implementation avoids short-circuiting so\n\t\t * <em>both</em> conditions will be evaluated even if this condition\n\t\t * returns {@code false} to ensure that all issues are reported.\n\t\t *\n\t\t * @return the composed condition; never {@code null}\n\t\t */\n\t\t@SuppressWarnings(\"ShortCircuitBoolean\")\n\t\tdefault Condition<T> and(Condition<? super T> that) {\n\t\t\tPreconditions.notNull(that, \"condition must not be null\");\n\t\t\treturn value -> this.check(value) & that.check(value);\n\t\t}\n\n\t\t/**\n\t\t * {@return this condition as a {@link Predicate}}\n\t\t */\n\t\tdefault Predicate<T> toPredicate() {\n\t\t\treturn this::check;\n\t\t}\n\n\t\t/**\n\t\t * {@return this condition as a {@link Consumer}}\n\t\t */\n\t\tdefault Consumer<T> toConsumer() {\n\t\t\treturn this::check;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolution.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static java.util.Objects.requireNonNullElse;\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.SelectorResolutionResult.failed;\nimport static org.junit.platform.engine.SelectorResolutionResult.resolved;\nimport static org.junit.platform.engine.SelectorResolutionResult.unresolved;\n\nimport java.util.ArrayDeque;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Queue;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryListener;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.NestedClassSelector;\nimport org.junit.platform.engine.discovery.NestedMethodSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\nimport org.junit.platform.engine.support.discovery.SelectorResolver.Context;\nimport org.junit.platform.engine.support.discovery.SelectorResolver.Match;\nimport org.junit.platform.engine.support.discovery.SelectorResolver.Resolution;\n\n/**\n * @since 1.5\n */\nclass EngineDiscoveryRequestResolution {\n\n\tprivate final EngineDiscoveryRequest request;\n\tprivate final Context defaultContext;\n\tprivate final List<SelectorResolver> resolvers;\n\tprivate final List<TestDescriptor.Visitor> visitors;\n\tprivate final TestDescriptor engineDescriptor;\n\tprivate final Map<DiscoverySelector, Resolution> resolvedSelectors = new LinkedHashMap<>();\n\tprivate final Map<UniqueId, Match> resolvedUniqueIds = new LinkedHashMap<>();\n\tprivate final Queue<DiscoverySelector> remainingSelectors = new ArrayDeque<>();\n\tprivate final Map<DiscoverySelector, Context> contextBySelector = new HashMap<>();\n\n\tEngineDiscoveryRequestResolution(EngineDiscoveryRequest request, TestDescriptor engineDescriptor,\n\t\t\tList<SelectorResolver> resolvers, List<TestDescriptor.Visitor> visitors) {\n\t\tthis.request = request;\n\t\tthis.engineDescriptor = engineDescriptor;\n\t\tthis.resolvers = resolvers;\n\t\tthis.visitors = visitors;\n\t\tthis.defaultContext = new DefaultContext(null);\n\t\tthis.resolvedUniqueIds.put(engineDescriptor.getUniqueId(), Match.exact(engineDescriptor));\n\t}\n\n\tvoid run() {\n\t\tremainingSelectors.addAll(request.getSelectorsByType(DiscoverySelector.class));\n\t\twhile (!remainingSelectors.isEmpty()) {\n\t\t\tresolveCompletely(remainingSelectors.poll());\n\t\t}\n\t\tvisitors.forEach(engineDescriptor::accept);\n\t}\n\n\tprivate void resolveCompletely(DiscoverySelector selector) {\n\t\tEngineDiscoveryListener discoveryListener = request.getDiscoveryListener();\n\t\tUniqueId engineId = engineDescriptor.getUniqueId();\n\t\ttry {\n\t\t\tOptional<Resolution> result = resolve(selector);\n\t\t\tif (result.isPresent()) {\n\t\t\t\tdiscoveryListener.selectorProcessed(engineId, selector, resolved());\n\t\t\t\tenqueueAdditionalSelectors(result.get());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdiscoveryListener.selectorProcessed(engineId, selector, unresolved());\n\t\t\t}\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tdiscoveryListener.selectorProcessed(engineId, selector, failed(t));\n\t\t}\n\t}\n\n\tprivate void enqueueAdditionalSelectors(Resolution resolution) {\n\t\tremainingSelectors.addAll(resolution.getSelectors());\n\t\tresolution.getMatches().stream().filter(Match::isExact).forEach(match -> {\n\t\t\tSet<? extends DiscoverySelector> childSelectors = match.expand();\n\t\t\tif (!childSelectors.isEmpty()) {\n\t\t\t\tremainingSelectors.addAll(childSelectors);\n\t\t\t\tDefaultContext context = new DefaultContext(match.getTestDescriptor());\n\t\t\t\tchildSelectors.forEach(selector -> contextBySelector.put(selector, context));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate Optional<Resolution> resolve(DiscoverySelector selector) {\n\t\tif (resolvedSelectors.containsKey(selector)) {\n\t\t\treturn Optional.of(resolvedSelectors.get(selector));\n\t\t}\n\t\tif (selector instanceof UniqueIdSelector uniqueIdSelector) {\n\t\t\treturn resolveUniqueId(uniqueIdSelector);\n\t\t}\n\t\treturn resolve(selector, resolver -> {\n\t\t\tContext context = getContext(selector);\n\t\t\tif (selector instanceof ClasspathResourceSelector classpathResourceSelector) {\n\t\t\t\treturn resolver.resolve(classpathResourceSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof ClasspathRootSelector classpathRootSelector) {\n\t\t\t\treturn resolver.resolve(classpathRootSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof ClassSelector classSelector) {\n\t\t\t\treturn resolver.resolve(classSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof IterationSelector iterationSelector) {\n\t\t\t\treturn resolver.resolve(iterationSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof NestedClassSelector nestedClassSelector) {\n\t\t\t\treturn resolver.resolve(nestedClassSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof DirectorySelector directorySelector) {\n\t\t\t\treturn resolver.resolve(directorySelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof FileSelector fileSelector) {\n\t\t\t\treturn resolver.resolve(fileSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof MethodSelector methodSelector) {\n\t\t\t\treturn resolver.resolve(methodSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof NestedMethodSelector nestedMethodSelector) {\n\t\t\t\treturn resolver.resolve(nestedMethodSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof ModuleSelector moduleSelector) {\n\t\t\t\treturn resolver.resolve(moduleSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof PackageSelector packageSelector) {\n\t\t\t\treturn resolver.resolve(packageSelector, context);\n\t\t\t}\n\t\t\tif (selector instanceof UriSelector uriSelector) {\n\t\t\t\treturn resolver.resolve(uriSelector, context);\n\t\t\t}\n\t\t\treturn resolver.resolve(selector, context);\n\t\t});\n\t}\n\n\tprivate Optional<Resolution> resolveUniqueId(UniqueIdSelector selector) {\n\t\tUniqueId uniqueId = selector.getUniqueId();\n\t\tif (resolvedUniqueIds.containsKey(uniqueId)) {\n\t\t\treturn Optional.of(Resolution.match(resolvedUniqueIds.get(uniqueId)));\n\t\t}\n\t\tif (!uniqueId.hasPrefix(engineDescriptor.getUniqueId())) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn resolve(selector, resolver -> resolver.resolve(selector, getContext(selector)));\n\t}\n\n\tprivate Context getContext(DiscoverySelector selector) {\n\t\treturn contextBySelector.getOrDefault(selector, defaultContext);\n\t}\n\n\tprivate Optional<Resolution> resolve(DiscoverySelector selector,\n\t\t\tFunction<SelectorResolver, Resolution> resolutionFunction) {\n\t\t// @formatter:off\n\t\treturn resolvers.stream()\n\t\t\t\t.map(resolutionFunction)\n\t\t\t\t.filter(Resolution::isResolved)\n\t\t\t\t.findFirst()\n\t\t\t\t.map(resolution -> {\n\t\t\t\t\tcontextBySelector.remove(selector);\n\t\t\t\t\tresolvedSelectors.put(selector, resolution);\n\t\t\t\t\tresolution.getMatches()\n\t\t\t\t\t\t\t.forEach(match -> resolvedUniqueIds.put(match.getTestDescriptor().getUniqueId(), match));\n\t\t\t\t\treturn resolution;\n\t\t\t\t});\n\t\t// @formatter:on\n\t}\n\n\tprivate class DefaultContext implements Context {\n\n\t\t@Nullable\n\t\tprivate final TestDescriptor parent;\n\n\t\tDefaultContext(@Nullable TestDescriptor parent) {\n\t\t\tthis.parent = parent;\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends TestDescriptor> Optional<T> addToParent(Function<TestDescriptor, Optional<T>> creator) {\n\t\t\treturn createAndAdd(requireNonNullElse(parent, engineDescriptor), creator);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T extends TestDescriptor> Optional<T> addToParent(Supplier<DiscoverySelector> parentSelectorSupplier,\n\t\t\t\tFunction<TestDescriptor, Optional<T>> creator) {\n\t\t\tif (parent != null) {\n\t\t\t\treturn createAndAdd(parent, creator);\n\t\t\t}\n\t\t\treturn resolve(parentSelectorSupplier.get()).flatMap(parent -> createAndAdd(parent, creator));\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<TestDescriptor> resolve(DiscoverySelector selector) {\n\t\t\t// @formatter:off\n\t\t\treturn EngineDiscoveryRequestResolution.this.resolve(selector)\n\t\t\t\t\t.map(Resolution::getMatches)\n\t\t\t\t\t.flatMap(matches -> {\n\t\t\t\t\t\tif (matches.size() > 1) {\n\t\t\t\t\t\t\tString stringRepresentation = matches.stream()\n\t\t\t\t\t\t\t\t\t.map(Match::getTestDescriptor)\n\t\t\t\t\t\t\t\t\t.map(Objects::toString)\n\t\t\t\t\t\t\t\t\t.collect(joining(\", \"));\n\t\t\t\t\t\t\tthrow new JUnitException(\n\t\t\t\t\t\t\t\t\"Selector \" + selector + \" did not yield unique test descriptor: \" + stringRepresentation);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (matches.size() == 1) {\n\t\t\t\t\t\t\treturn Optional.of(getOnlyElement(matches).getTestDescriptor());\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn Optional.empty();\n\t\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <T extends TestDescriptor> Optional<T> createAndAdd(TestDescriptor parent,\n\t\t\t\tFunction<TestDescriptor, Optional<T>> creator) {\n\t\t\tOptional<T> child = creator.apply(parent);\n\t\t\tif (child.isPresent()) {\n\t\t\t\tUniqueId uniqueId = child.get().getUniqueId();\n\t\t\t\tif (resolvedUniqueIds.containsKey(uniqueId)) {\n\t\t\t\t\treturn Optional.of((T) resolvedUniqueIds.get(uniqueId).getTestDescriptor());\n\t\t\t\t}\n\t\t\t\tparent.addChild(child.get());\n\t\t\t}\n\t\t\treturn child;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static java.util.stream.Collectors.toCollection;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageNameFilter;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.support.discovery.SelectorResolver.Match;\nimport org.junit.platform.engine.support.discovery.SelectorResolver.Resolution;\n\n/**\n * Configurable test discovery implementation based on {@link SelectorResolver}\n * and {@link TestDescriptor.Visitor} that can be reused by different\n * {@link org.junit.platform.engine.TestEngine TestEngines}.\n *\n * <p>This resolver takes care of notifying registered\n * {@link org.junit.platform.engine.EngineDiscoveryListener\n * EngineDiscoveryListeners} about the results of processed\n * {@link org.junit.platform.engine.DiscoverySelector DiscoverySelectors}.\n *\n * @param <T> the type of the engine's descriptor\n * @since 1.5\n * @see #builder()\n * @see #resolve(EngineDiscoveryRequest, TestDescriptor)\n */\n@API(status = STABLE, since = \"1.10\")\npublic class EngineDiscoveryRequestResolver<T extends TestDescriptor> {\n\n\tprivate final List<Function<InitializationContext<T>, SelectorResolver>> resolverCreators;\n\tprivate final List<Function<InitializationContext<T>, TestDescriptor.Visitor>> visitorCreators;\n\n\tprivate EngineDiscoveryRequestResolver(List<Function<InitializationContext<T>, SelectorResolver>> resolverCreators,\n\t\t\tList<Function<InitializationContext<T>, TestDescriptor.Visitor>> visitorCreators) {\n\t\tthis.resolverCreators = new ArrayList<>(resolverCreators);\n\t\tthis.visitorCreators = new ArrayList<>(visitorCreators);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link EngineDiscoveryRequest} and collect the\n\t * results into the supplied {@link TestDescriptor} while forwarding\n\t * encountered discovery issues to the {@link EngineDiscoveryRequest}'s\n\t * {@link org.junit.platform.engine.EngineDiscoveryListener}.\n\t *\n\t * <p>The algorithm works as follows:\n\t *\n\t * <ol>\n\t *     <li>Enqueue all selectors in the supplied\n\t *     {@linkplain EngineDiscoveryRequest request} to be resolved.\n\t *     </li>\n\t *     <li>\n\t *         While there are selectors to be resolved, get the next one.\n\t *         Otherwise, the resolution is finished.\n\t *         <ol>\n\t *             <li>\n\t *                 Iterate over all registered {@linkplain SelectorResolver\n\t *                 resolvers} in the order they were registered in and find the\n\t *                 first one that returns a {@linkplain Resolution resolution}\n\t *                 other than {@link Resolution#unresolved() unresolved()}.\n\t *             </li>\n\t *             <li>\n\t *                 If such a {@linkplain Resolution resolution} exists, enqueue\n\t *                 its {@linkplain Resolution#getSelectors() selectors}.\n\t *             </li>\n\t *             <li>\n\t *                 For each exact {@linkplain Match match} in the {@linkplain\n\t *                 Resolution resolution}, {@linkplain Match#expand() expand}\n\t *                 its children and enqueue them as well.\n\t *             </li>\n\t *         </ol>\n\t *     </li>\n\t *     <li>\n\t *         Iterate over all registered {@linkplain TestDescriptor.Visitor\n\t *         visitors} and let the engine test descriptor {@linkplain\n\t *         TestDescriptor#accept(TestDescriptor.Visitor) accept} them.\n\t *     </li>\n\t * </ol>\n\t *\n\t * @param request the request to be resolved; never {@code null}\n\t * @param engineDescriptor the engine's {@code TestDescriptor} to be used\n\t * for adding direct children\n\t * @see SelectorResolver\n\t * @see TestDescriptor.Visitor\n\t */\n\tpublic void resolve(EngineDiscoveryRequest request, T engineDescriptor) {\n\t\tPreconditions.notNull(request, \"request must not be null\");\n\t\tPreconditions.notNull(engineDescriptor, \"engineDescriptor must not be null\");\n\t\tDiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.forwarding(request.getDiscoveryListener(),\n\t\t\tengineDescriptor.getUniqueId());\n\t\tresolve(request, engineDescriptor, issueReporter);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link EngineDiscoveryRequest} and collect the\n\t * results into the supplied {@link TestDescriptor} using the supplied\n\t * {@link DiscoveryIssueReporter} to report issues encountered during\n\t * resolution.\n\t *\n\t * <p>The algorithm works as described in\n\t * {@link #resolve(EngineDiscoveryRequest, TestDescriptor)}.\n\t *\n\t * @param request the request to be resolved; never {@code null}\n\t * @param engineDescriptor the engine's {@code TestDescriptor} to be used\n\t * for adding direct children\n\t * @param issueReporter the {@link DiscoveryIssueReporter} to report issues\n\t * encountered during resolution\n\t * @since 1.13\n\t * @see #resolve(EngineDiscoveryRequest, TestDescriptor)\n\t * @see SelectorResolver\n\t * @see TestDescriptor.Visitor\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic void resolve(EngineDiscoveryRequest request, T engineDescriptor, DiscoveryIssueReporter issueReporter) {\n\t\tPreconditions.notNull(request, \"request must not be null\");\n\t\tPreconditions.notNull(engineDescriptor, \"engineDescriptor must not be null\");\n\t\tPreconditions.notNull(issueReporter, \"issueReporter must not be null\");\n\t\tInitializationContext<T> initializationContext = new DefaultInitializationContext<>(request, engineDescriptor,\n\t\t\tissueReporter);\n\t\tList<SelectorResolver> resolvers = instantiate(resolverCreators, initializationContext);\n\t\tList<TestDescriptor.Visitor> visitors = instantiate(visitorCreators, initializationContext);\n\t\tnew EngineDiscoveryRequestResolution(request, engineDescriptor, resolvers, visitors).run();\n\t}\n\n\tprivate <R> List<R> instantiate(List<Function<InitializationContext<T>, R>> creators,\n\t\t\tInitializationContext<T> context) {\n\t\treturn creators.stream().map(creator -> creator.apply(context)).collect(toCollection(ArrayList::new));\n\t}\n\n\t/**\n\t * Create a new {@link Builder} for creating a {@link EngineDiscoveryRequestResolver}.\n\t *\n\t * @param <T> the type of the engine's descriptor\n\t * @return a new builder; never {@code null}\n\t */\n\tpublic static <T extends TestDescriptor> Builder<T> builder() {\n\t\treturn new Builder<>();\n\t}\n\n\t/**\n\t * Builder for {@link EngineDiscoveryRequestResolver}.\n\t *\n\t * @param <T> the type of the engine's descriptor\n\t * @since 1.5\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static class Builder<T extends TestDescriptor> {\n\n\t\tprivate final List<Function<InitializationContext<T>, SelectorResolver>> resolverCreators = new ArrayList<>();\n\t\tprivate final List<Function<InitializationContext<T>, TestDescriptor.Visitor>> visitorCreators = new ArrayList<>();\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\t/**\n\t\t * Add a predefined resolver that resolves {@link ClasspathRootSelector\n\t\t * ClasspathRootSelectors}, {@link ModuleSelector ModuleSelectors}, and\n\t\t * {@link PackageSelector PackageSelectors} into {@link ClassSelector\n\t\t * ClassSelectors} by scanning for classes that satisfy the supplied\n\t\t * predicate in the respective class containers to this builder.\n\t\t *\n\t\t * @param classFilter predicate the resolved classes must satisfy; never\n\t\t * {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder<T> addClassContainerSelectorResolver(Predicate<Class<?>> classFilter) {\n\t\t\tPreconditions.notNull(classFilter, \"classFilter must not be null\");\n\t\t\treturn addClassContainerSelectorResolverWithContext(__ -> classFilter);\n\t\t}\n\n\t\t/**\n\t\t * Add a predefined resolver that resolves {@link ClasspathRootSelector\n\t\t * ClasspathRootSelectors}, {@link ModuleSelector ModuleSelectors}, and\n\t\t * {@link PackageSelector PackageSelectors} into {@link ClassSelector\n\t\t * ClassSelectors} by scanning for classes that satisfy the predicate\n\t\t * created by the supplied {@code Function} in the respective class\n\t\t * containers to this builder.\n\t\t *\n\t\t * @param classFilterCreator the function that will be called to create\n\t\t * the predicate the resolved classes must satisfy; never\n\t\t * {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tpublic Builder<T> addClassContainerSelectorResolverWithContext(\n\t\t\t\tFunction<InitializationContext<T>, Predicate<Class<?>>> classFilterCreator) {\n\t\t\tPreconditions.notNull(classFilterCreator, \"classFilterCreator must not be null\");\n\t\t\treturn addSelectorResolver(context -> new ClassContainerSelectorResolver(classFilterCreator.apply(context),\n\t\t\t\tcontext.getClassNameFilter()));\n\t\t}\n\n\t\t/**\n\t\t * Add a predefined resolver that resolves {@link ClasspathRootSelector\n\t\t * ClasspathRootSelectors}, {@link ModuleSelector ModuleSelectors}, and\n\t\t * {@link PackageSelector PackageSelectors} into {@link ClasspathResourceSelector\n\t\t * ClasspathResourceSelectors} by scanning for resources that satisfy the supplied\n\t\t * predicate in the respective class containers to this builder.\n\t\t *\n\t\t * @param resourceFilter predicate the resolved classes must satisfy; never\n\t\t * {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.12\n\t\t * @deprecated Please use {@link #addResourceContainerSelectorResolver(ResourceFilter)} instead.\n\t\t */\n\t\t@API(status = DEPRECATED, since = \"1.14\")\n\t\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t\t@SuppressWarnings(\"removal\")\n\t\tpublic Builder<T> addResourceContainerSelectorResolver(\n\t\t\t\tPredicate<org.junit.platform.commons.support.Resource> resourceFilter) {\n\t\t\tPreconditions.notNull(resourceFilter, \"resourceFilter must not be null\");\n\t\t\treturn addResourceContainerSelectorResolver(\n\t\t\t\tResourceFilter.of(r -> resourceFilter.test(org.junit.platform.commons.support.Resource.of(r))));\n\t\t}\n\n\t\t/**\n\t\t * Add a predefined resolver that resolves {@link ClasspathRootSelector\n\t\t * ClasspathRootSelectors}, {@link ModuleSelector ModuleSelectors}, and\n\t\t * {@link PackageSelector PackageSelectors} into\n\t\t * {@link ClasspathResourceSelector ClasspathResourceSelectors} by\n\t\t * scanning for resources that match the supplied resource filter in the\n\t\t * respective class containers to this builder.\n\t\t *\n\t\t * @param resourceFilter filter the resolved classes must match; never\n\t\t * {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.14\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"1.14\")\n\t\tpublic Builder<T> addResourceContainerSelectorResolver(ResourceFilter resourceFilter) {\n\t\t\tPreconditions.notNull(resourceFilter, \"resourceFilter must not be null\");\n\t\t\treturn addSelectorResolver(\n\t\t\t\tcontext -> new ResourceContainerSelectorResolver(resourceFilter, context.getPackageFilter()));\n\t\t}\n\n\t\t/**\n\t\t * Add a context insensitive {@link SelectorResolver} to this builder.\n\t\t *\n\t\t * @param resolver the resolver to add; never {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder<T> addSelectorResolver(SelectorResolver resolver) {\n\t\t\tPreconditions.notNull(resolver, \"resolver must not be null\");\n\t\t\treturn addSelectorResolver(context -> resolver);\n\t\t}\n\n\t\t/**\n\t\t * Add a context sensitive {@link SelectorResolver} to this builder.\n\t\t *\n\t\t * @param resolverCreator the function that will be called to create the\n\t\t * {@link SelectorResolver} to be added.\n\t\t * @return this builder for method chaining\n\t\t * @see InitializationContext\n\t\t */\n\t\tpublic Builder<T> addSelectorResolver(Function<InitializationContext<T>, SelectorResolver> resolverCreator) {\n\t\t\tresolverCreators.add(resolverCreator);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add a context sensitive {@link TestDescriptor.Visitor} to this\n\t\t * builder.\n\t\t *\n\t\t * <p>If multiple {@linkplain TestDescriptor.Visitor visitors} are registered,\n\t\t * they will iterate over the test tree separately. To avoid the overhead of\n\t\t * multiple iterations, consider combining multiple visitors into a single\n\t\t * visitor using\n\t\t * {@link TestDescriptor.Visitor#composite(TestDescriptor.Visitor...)}.\n\t\t *\n\t\t * @param visitorCreator the function that will be called to create the\n\t\t * {@link TestDescriptor.Visitor} to be added.\n\t\t * @return this builder for method chaining\n\t\t * @see InitializationContext\n\t\t */\n\t\tpublic Builder<T> addTestDescriptorVisitor(\n\t\t\t\tFunction<InitializationContext<T>, TestDescriptor.Visitor> visitorCreator) {\n\t\t\tvisitorCreators.add(visitorCreator);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link EngineDiscoveryRequestResolver} that has been\n\t\t * configured via this builder.\n\t\t */\n\t\tpublic EngineDiscoveryRequestResolver<T> build() {\n\t\t\treturn new EngineDiscoveryRequestResolver<>(resolverCreators, visitorCreators);\n\t\t}\n\n\t}\n\n\t/**\n\t * The initialization context for creating {@linkplain SelectorResolver\n\t * resolvers} and {@linkplain TestDescriptor.Visitor visitors} that depend\n\t * on the {@link EngineDiscoveryRequest} to be resolved or the engine\n\t * descriptor that will be used to collect the results.\n\t *\n\t * @since 1.5\n\t * @see Builder#addSelectorResolver(Function)\n\t * @see Builder#addTestDescriptorVisitor(Function)\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic interface InitializationContext<T extends TestDescriptor> {\n\n\t\t/**\n\t\t * Get the {@link EngineDiscoveryRequest} that is about to be resolved.\n\t\t *\n\t\t * @return the {@link EngineDiscoveryRequest}; never {@code null}\n\t\t */\n\t\tEngineDiscoveryRequest getDiscoveryRequest();\n\n\t\t/**\n\t\t * Get the engine's {@link TestDescriptor} that will be used to collect\n\t\t * the results.\n\t\t *\n\t\t * @return engine's {@link TestDescriptor}; never {@code null}\n\t\t */\n\t\tT getEngineDescriptor();\n\n\t\t/**\n\t\t * Get the class name filter built from the {@link ClassNameFilter\n\t\t * ClassNameFilters} and {@link PackageNameFilter PackageNameFilters}\n\t\t * in the {@link EngineDiscoveryRequest} that is about to be resolved.\n\t\t *\n\t\t * @return the predicate for filtering the resolved class names; never\n\t\t * {@code null}\n\t\t */\n\t\tPredicate<String> getClassNameFilter();\n\n\t\t/**\n\t\t * Get the package name filter built from the {@link PackageNameFilter\n\t\t * PackageNameFilters} in the {@link EngineDiscoveryRequest} that is\n\t\t * about to be resolved.\n\t\t *\n\t\t * @return the predicate for filtering the resolved resource names; never\n\t\t * {@code null}\n\t\t * @since 1.12\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"1.13.3\")\n\t\tPredicate<String> getPackageFilter();\n\n\t\t/**\n\t\t * {@return the {@link DiscoveryIssueReporter} for the current\n\t\t * resolution}\n\t\t *\n\t\t * @since 1.13\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tDiscoveryIssueReporter getIssueReporter();\n\t}\n\n\tprivate static class DefaultInitializationContext<T extends TestDescriptor> implements InitializationContext<T> {\n\n\t\tprivate final EngineDiscoveryRequest request;\n\t\tprivate final T engineDescriptor;\n\t\tprivate final Predicate<String> classNameFilter;\n\t\tprivate final Predicate<String> packageFilter;\n\t\tprivate final DiscoveryIssueReporter issueReporter;\n\n\t\tDefaultInitializationContext(EngineDiscoveryRequest request, T engineDescriptor,\n\t\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\t\tthis.request = request;\n\t\t\tthis.engineDescriptor = engineDescriptor;\n\t\t\tthis.classNameFilter = buildClassNamePredicate(request);\n\t\t\tthis.packageFilter = buildPackagePredicate(request);\n\t\t\tthis.issueReporter = issueReporter;\n\t\t}\n\n\t\t/**\n\t\t * Build a {@link Predicate} for fully qualified class names to be used for\n\t\t * classpath scanning from an {@link EngineDiscoveryRequest}.\n\t\t *\n\t\t * @param request the request to build a predicate from\n\t\t */\n\t\tprivate Predicate<String> buildClassNamePredicate(EngineDiscoveryRequest request) {\n\t\t\tList<DiscoveryFilter<String>> filters = new ArrayList<>();\n\t\t\tfilters.addAll(request.getFiltersByType(ClassNameFilter.class));\n\t\t\tfilters.addAll(request.getFiltersByType(PackageNameFilter.class));\n\t\t\treturn Filter.composeFilters(filters).toPredicate();\n\t\t}\n\n\t\tprivate Predicate<String> buildPackagePredicate(EngineDiscoveryRequest request) {\n\t\t\tList<DiscoveryFilter<String>> filters = new ArrayList<>();\n\t\t\tfilters.addAll(request.getFiltersByType(PackageNameFilter.class));\n\t\t\treturn Filter.composeFilters(filters).toPredicate();\n\t\t}\n\n\t\t@Override\n\t\tpublic EngineDiscoveryRequest getDiscoveryRequest() {\n\t\t\treturn request;\n\t\t}\n\n\t\t@Override\n\t\tpublic T getEngineDescriptor() {\n\t\t\treturn engineDescriptor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<String> getClassNameFilter() {\n\t\t\treturn classNameFilter;\n\t\t}\n\n\t\t@Override\n\t\tpublic Predicate<String> getPackageFilter() {\n\t\t\treturn packageFilter;\n\t\t}\n\n\t\t@Override\n\t\tpublic DiscoveryIssueReporter getIssueReporter() {\n\t\t\treturn issueReporter;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static java.util.stream.Collectors.groupingBy;\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInClasspathRoot;\nimport static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInModule;\nimport static org.junit.platform.commons.support.ResourceSupport.findAllResourcesInPackage;\nimport static org.junit.platform.engine.support.discovery.ResourceUtils.packageName;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.selectors;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\n\n/**\n * @since 1.12\n */\nclass ResourceContainerSelectorResolver implements SelectorResolver {\n\tprivate final ResourceFilter resourceFilter;\n\n\tResourceContainerSelectorResolver(ResourceFilter resourceFilter, Predicate<String> packageFilter) {\n\t\tthis.resourceFilter = ResourceFilter.of(packageName(packageFilter).and(resourceFilter::match));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ClasspathRootSelector selector, Context context) {\n\t\treturn resourceSelectors(findAllResourcesInClasspathRoot(selector.getClasspathRoot(), resourceFilter));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ModuleSelector selector, Context context) {\n\t\tif (selector.getModule().isPresent()) {\n\t\t\tModule module = selector.getModule().get();\n\t\t\treturn resourceSelectors(findAllResourcesInModule(module, resourceFilter));\n\t\t}\n\t\treturn resourceSelectors(findAllResourcesInModule(selector.getModuleName(), resourceFilter));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(PackageSelector selector, Context context) {\n\t\treturn resourceSelectors(findAllResourcesInPackage(selector.getPackageName(), resourceFilter));\n\t}\n\n\tprivate Resolution resourceSelectors(List<Resource> resources) {\n\t\tSet<ClasspathResourceSelector> selectors = resources.stream() //\n\t\t\t\t.collect(groupingBy(Resource::getName)) //\n\t\t\t\t.values() //\n\t\t\t\t.stream() //\n\t\t\t\t.map(LinkedHashSet::new) //\n\t\t\t\t.map(DiscoverySelectors::selectClasspathResourceByName) //\n\t\t\t\t.collect(toSet());\n\n\t\tif (selectors.isEmpty()) {\n\t\t\treturn unresolved();\n\t\t}\n\t\treturn selectors(selectors);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/ResourceUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * Resource-related utilities to be used in conjunction with {@link ReflectionSupport}.\n *\n * @since 1.12\n */\nclass ResourceUtils {\n\tpublic static final String DEFAULT_PACKAGE_NAME = \"\";\n\tprivate static final char CLASSPATH_RESOURCE_PATH_SEPARATOR = '/';\n\tprivate static final char PACKAGE_SEPARATOR_CHAR = '.';\n\n\t/**\n\t * Match resources against a package filter.\n\t *\n\t * <p>The {@code /} separated path of a resource is rewritten to a\n\t * {@code .} separated package names. The package filter is applied to that\n\t * package name.\n\t */\n\tstatic Predicate<Resource> packageName(Predicate<String> packageFilter) {\n\t\treturn resource -> packageFilter.test(packageName(resource.getName()));\n\t}\n\n\tprivate static String packageName(String classpathResourceName) {\n\t\tint lastIndexOf = classpathResourceName.lastIndexOf(CLASSPATH_RESOURCE_PATH_SEPARATOR);\n\t\tif (lastIndexOf < 0) {\n\t\t\treturn DEFAULT_PACKAGE_NAME;\n\t\t}\n\t\t// classpath resource names do not start with /\n\t\tString resourcePackagePath = classpathResourceName.substring(0, lastIndexOf);\n\t\treturn resourcePackagePath.replace(CLASSPATH_RESOURCE_PATH_SEPARATOR, PACKAGE_SEPARATOR_CHAR);\n\t}\n\n\tprivate ResourceUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/SelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static java.util.Collections.emptySet;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.NestedClassSelector;\nimport org.junit.platform.engine.discovery.NestedMethodSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\n\n/**\n * A resolver that supports resolving one or multiple types of\n * {@link DiscoverySelector DiscoverySelectors}.\n *\n * <p>An implementation of a {@code resolve()} method is typically comprised\n * of the following steps:\n *\n * <ol>\n *     <li>\n *         Check whether the selector is applicable for the current\n *         {@link org.junit.platform.engine.TestEngine} and the current\n *         {@link org.junit.platform.engine.EngineDiscoveryRequest} (e.g.\n *         for a test class: is it relevant for the current engine and does\n *         it pass all filters in the request?).\n *     </li>\n *     <li>\n *         If so, use the supplied {@link Context Context}, to add one or\n *         multiple {@link TestDescriptor TestDescriptors} to the designated\n *         parent (see {@link Context Context} for details) and return a\n *         {@linkplain Resolution#match(Match) match} or multiple {@linkplain\n *         Resolution#matches(Set) matches}. Alternatively, convert the supplied\n *         selector into one or multiple other\n *         {@linkplain Resolution#selectors(Set) selectors} (e.g. a {@link\n *         PackageSelector} into a set of {@link ClassSelector ClassSelectors}).\n *         Otherwise, return {@link Resolution#unresolved() unresolved()}.\n *     </li>\n * </ol>\n *\n * @since 1.5\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface SelectorResolver {\n\n\t/**\n\t * Resolve the supplied {@link ClasspathResourceSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(ClasspathResourceSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link ClasspathRootSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(ClasspathRootSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link ClassSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(ClassSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link NestedClassSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(NestedClassSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link DirectorySelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(DirectorySelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link FileSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(FileSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link MethodSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(MethodSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link NestedMethodSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(NestedMethodSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link ModuleSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(ModuleSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link PackageSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(PackageSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link UniqueIdSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link UriSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\tdefault Resolution resolve(UriSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link IterationSelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>The default implementation delegates to {@link\n\t * #resolve(DiscoverySelector, Context)}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see #resolve(DiscoverySelector, Context)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tdefault Resolution resolve(IterationSelector selector, Context context) {\n\t\treturn resolve((DiscoverySelector) selector, context);\n\t}\n\n\t/**\n\t * Resolve the supplied {@link DiscoverySelector} using the supplied\n\t * {@link Context Context}.\n\t *\n\t * <p>This method is only called if none of the overloaded variants match.\n\t *\n\t * <p>The default implementation returns {@link Resolution#unresolved()\n\t * unresolved()}.\n\t *\n\t * @param selector the selector to be resolved; never {@code null}\n\t * @param context the context to be used for resolving the selector; never\n\t * {@code null}\n\t * @return a {@link Resolution Resolution} of {@link Resolution#unresolved()\n\t * unresolved()}, {@link Resolution#selectors(Set) selectors()}, or {@link\n\t * Resolution#matches(Set) matches()}; never {@code null}\n\t * @see Context\n\t */\n\tdefault Resolution resolve(DiscoverySelector selector, Context context) {\n\t\treturn Resolution.unresolved();\n\t}\n\n\t/**\n\t * The context for resolving a {@link DiscoverySelector} and adding it to\n\t * the test tree.\n\t *\n\t * <p>The context is used to add resolved {@link TestDescriptor\n\t * TestDescriptors} to the test tree if and only if the parent\n\t * {@code TestDescriptor} could be found. Alternatively, a resolver may\n\t * use the context to {@linkplain #resolve(DiscoverySelector) resolve} a\n\t * certain {@code DiscoverySelector} into a {@code TestDescriptor} (e.g. for\n\t * adding a filter and returning a {@linkplain Match#partial(TestDescriptor)\n\t * partial match}).\n\t *\n\t * @since 1.5\n\t * @see SelectorResolver\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tinterface Context {\n\n\t\t/**\n\t\t * Resolve the supplied {@link TestDescriptor}, if possible.\n\t\t *\n\t\t * <p>Calling this method has the same effect as returning a {@linkplain\n\t\t * Match#partial(TestDescriptor) partial match} from a {@link\n\t\t * SelectorResolver}: the children of the resulting {@link\n\t\t * TestDescriptor} will only be resolved if a subsequent resolution\n\t\t * finds an exact match that contains a {@code TestDescriptor} with the\n\t\t * same {@linkplain TestDescriptor#getUniqueId() unique ID}.\n\t\t *\n\t\t * @param selector the selector to resolve\n\t\t * @return the resolved {@code TestDescriptor}; never {@code null} but\n\t\t * potentially empty\n\t\t */\n\t\tOptional<TestDescriptor> resolve(DiscoverySelector selector);\n\n\t\t/**\n\t\t * Add a {@link TestDescriptor} to an unspecified parent, usually the\n\t\t * engine descriptor, by applying the supplied {@code Function} to the\n\t\t * new parent.\n\t\t *\n\t\t * <p>The parent will be the engine descriptor unless another parent has\n\t\t * already been determined, i.e. if the selector that is being resolved\n\t\t * is the result of {@linkplain Match#expand() expanding} a {@link\n\t\t * Match}.\n\t\t *\n\t\t * <p>If the result of applying the {@code Function} is {@linkplain\n\t\t * Optional#isPresent() present}, it will be added as a child of the\n\t\t * parent {@code TestDescriptor} unless a descriptor with the same\n\t\t * {@linkplain TestDescriptor#getUniqueId() unique ID} was added\n\t\t * earlier.\n\t\t *\n\t\t * @param creator {@code Function} that will be called with the new\n\t\t * parent to determine the new {@code TestDescriptor} to be added; must\n\t\t * not return {@code null}\n\t\t * @param <T> the type of the new {@code TestDescriptor}\n\t\t * @return the new {@code TestDescriptor} or the previously existing one\n\t\t * with the same unique ID; never {@code null} but potentially empty\n\t\t * @throws ClassCastException if the previously existing {@code\n\t\t * TestDescriptor} is not an instance of {@code T}\n\t\t */\n\t\t<T extends TestDescriptor> Optional<T> addToParent(Function<TestDescriptor, Optional<T>> creator);\n\n\t\t/**\n\t\t * Add a {@link TestDescriptor} to a parent, specified by the {@link\n\t\t * DiscoverySelector} returned by the supplied {@code Supplier}, by\n\t\t * applying the supplied {@code Function} to the new parent.\n\t\t *\n\t\t * <p>Unless another parent has already been determined, i.e. if the\n\t\t * selector that is being resolved is the result of {@linkplain\n\t\t * Match#expand() expanding} a {@link Match}, the {@link\n\t\t * DiscoverySelector} returned by the supplied {@code Supplier} will\n\t\t * be used to determine the parent. If no parent is found, the supplied\n\t\t * {@code Function} will not be called. If there are multiple potential\n\t\t * parents, an exception will be thrown. Otherwise, the resolved\n\t\t * {@code TestDescriptor} will be used as the parent and passed to the\n\t\t * supplied {@code Function}.\n\t\t *\n\t\t * <p>If the result of applying the {@code Function} is {@linkplain\n\t\t * Optional#isPresent() present}, it will be added as a child of the\n\t\t * parent {@code TestDescriptor} unless a descriptor with the same\n\t\t * {@linkplain TestDescriptor#getUniqueId() unique ID} was added\n\t\t * earlier.\n\t\t *\n\t\t * @param creator {@code Function} that will be called with the new\n\t\t * parent to determine the new {@code TestDescriptor} to be added; must\n\t\t * not return {@code null}\n\t\t * @param <T> the type of the new {@code TestDescriptor}\n\t\t * @return the new {@code TestDescriptor} or the previously existing one\n\t\t * with the same unique ID; never {@code null} but potentially empty\n\t\t * @throws ClassCastException if the previously existing {@code\n\t\t * TestDescriptor} is not an instance of {@code T}\n\t\t */\n\t\t<T extends TestDescriptor> Optional<T> addToParent(Supplier<DiscoverySelector> parentSelectorSupplier,\n\t\t\t\tFunction<TestDescriptor, Optional<T>> creator);\n\n\t}\n\n\t/**\n\t * The result of an attempt to resolve a {@link DiscoverySelector}.\n\t *\n\t * <p>A resolution is either {@linkplain #unresolved unresolved}, contains a\n\t * {@linkplain #match match} or multiple {@linkplain #matches}, or a set of\n\t * {@linkplain #selectors selectors}.\n\t *\n\t * @since 1.5\n\t * @see SelectorResolver\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tclass Resolution {\n\n\t\tprivate static final Resolution UNRESOLVED = new Resolution(emptySet(), emptySet());\n\n\t\tprivate final Set<Match> matches;\n\t\tprivate final Set<? extends DiscoverySelector> selectors;\n\n\t\t/**\n\t\t * Factory for creating <em>unresolved</em> resolutions.\n\t\t *\n\t\t * @return an <em>unresolved</em> resolution; never {@code null}\n\t\t */\n\t\tpublic static Resolution unresolved() {\n\t\t\treturn UNRESOLVED;\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating a resolution that contains the supplied\n\t\t * {@link Match Match}.\n\t\t *\n\t\t * @param match the resolved {@code Match}; never {@code null}\n\t\t * @return a resolution that contains the supplied {@code Match}; never\n\t\t * {@code null}\n\t\t */\n\t\tpublic static Resolution match(Match match) {\n\t\t\tPreconditions.notNull(match, \"match must not be null\");\n\t\t\treturn new Resolution(Set.of(match), emptySet());\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating a resolution that contains the supplied\n\t\t * {@link Match Matches}.\n\t\t *\n\t\t * @param matches the resolved {@code Matches}; never {@code null} or\n\t\t * empty\n\t\t * @return a resolution that contains the supplied {@code Matches};\n\t\t * never {@code null}\n\t\t */\n\t\tpublic static Resolution matches(Set<Match> matches) {\n\t\t\tPreconditions.notNull(matches, \"matches must not be null\");\n\t\t\tPreconditions.notEmpty(matches, \"matches must not be empty\");\n\t\t\tPreconditions.containsNoNullElements(matches, \"matches must not contain null elements\");\n\t\t\treturn new Resolution(matches, emptySet());\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating a resolution that contains the supplied\n\t\t * {@link DiscoverySelector DiscoverySelectors}.\n\t\t *\n\t\t * @param selectors the resolved {@code DiscoverySelectors}; never\n\t\t * {@code null} or empty\n\t\t * @return a resolution that contains the supplied\n\t\t * {@code DiscoverySelectors}; never {@code null}\n\t\t */\n\t\tpublic static Resolution selectors(Set<? extends DiscoverySelector> selectors) {\n\t\t\tPreconditions.notNull(selectors, \"selectors must not be null\");\n\t\t\tPreconditions.notEmpty(selectors, \"selectors must not be empty\");\n\t\t\tPreconditions.containsNoNullElements(selectors, \"selectors must not contain null elements\");\n\t\t\treturn new Resolution(emptySet(), selectors);\n\t\t}\n\n\t\tprivate Resolution(Set<Match> matches, Set<? extends DiscoverySelector> selectors) {\n\t\t\tthis.matches = matches;\n\t\t\tthis.selectors = selectors;\n\t\t}\n\n\t\t/**\n\t\t * Whether this resolution contains matches or selectors.\n\t\t *\n\t\t * @return {@code true} if this resolution contains matches or selectors\n\t\t */\n\t\tpublic boolean isResolved() {\n\t\t\treturn this != UNRESOLVED;\n\t\t}\n\n\t\t/**\n\t\t * Returns the matches contained by this resolution.\n\t\t *\n\t\t * @return the set of matches; never {@code null} but potentially empty\n\t\t */\n\t\tpublic Set<Match> getMatches() {\n\t\t\treturn matches;\n\t\t}\n\n\t\t/**\n\t\t * Returns the selectors contained by this resolution.\n\t\t *\n\t\t * @return the set of selectors; never {@code null} but potentially empty\n\t\t */\n\t\tpublic Set<? extends DiscoverySelector> getSelectors() {\n\t\t\treturn selectors;\n\t\t}\n\n\t}\n\n\t/**\n\t * An exact or partial match for resolving a {@link DiscoverySelector} into\n\t * a {@link TestDescriptor}.\n\t *\n\t * <p>A match is <em>exact</em> if the {@link DiscoverySelector} directly\n\t * represents the resulting {@link TestDescriptor}, e.g. if a\n\t * {@link ClassSelector} was resolved into the {@link TestDescriptor} that\n\t * represents the test class. It is <em>partial</em> if the matching\n\t * {@link TestDescriptor} does not directly correspond to the resolved\n\t * {@link DiscoverySelector}, e.g. when resolving a {@link UniqueIdSelector}\n\t * that represents a dynamic child of the resolved {@link TestDescriptor}.\n\t *\n\t * <p>In addition to the {@link TestDescriptor}, a match may contain a\n\t * {@code Supplier} of {@link DiscoverySelector DiscoverySelectors} that may\n\t * be used to discover the children of the {@link TestDescriptor}. The\n\t * algorithm implemented by {@link EngineDiscoveryRequestResolver}\n\t * {@linkplain #expand() expands} all exact matches immediately, i.e. it\n\t * resolves all of their children. Partial matches will only be expanded in\n\t * case a subsequent resolution finds an exact match that contains a {@link\n\t * TestDescriptor} with the same {@linkplain TestDescriptor#getUniqueId()\n\t * unique ID}.\n\t *\n\t * @since 1.5\n\t * @see SelectorResolver\n\t * @see Resolution#match(Match)\n\t * @see Resolution#matches(Set)\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tclass Match {\n\n\t\tprivate final TestDescriptor testDescriptor;\n\t\tprivate final Supplier<Set<? extends DiscoverySelector>> childSelectorsSupplier;\n\t\tprivate final Type type;\n\n\t\t/**\n\t\t * Factory for creating an exact match without any children.\n\t\t *\n\t\t * @param testDescriptor the resolved {@code TestDescriptor}; never\n\t\t * {@code null}\n\t\t * @return a match that contains the supplied {@code TestDescriptor};\n\t\t * never {@code null}\n\t\t */\n\t\tpublic static Match exact(TestDescriptor testDescriptor) {\n\t\t\treturn exact(testDescriptor, Collections::emptySet);\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating an exact match with potential children.\n\t\t *\n\t\t * @param testDescriptor the resolved {@code TestDescriptor}; never\n\t\t * {@code null}\n\t\t * @param childSelectorsSupplier a {@code Supplier} of children\n\t\t * selectors that will be resolved when this match is expanded; never\n\t\t * {@code null}\n\t\t * @return a match that contains the supplied {@code TestDescriptor};\n\t\t * never {@code null}\n\t\t */\n\t\tpublic static Match exact(TestDescriptor testDescriptor,\n\t\t\t\tSupplier<Set<? extends DiscoverySelector>> childSelectorsSupplier) {\n\t\t\treturn new Match(testDescriptor, childSelectorsSupplier, Type.EXACT);\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating a partial match without any children.\n\t\t *\n\t\t * @param testDescriptor the resolved {@code TestDescriptor}; never\n\t\t * {@code null}\n\t\t * @return a match that contains the supplied {@code TestDescriptor};\n\t\t * never {@code null}\n\t\t */\n\t\tpublic static Match partial(TestDescriptor testDescriptor) {\n\t\t\treturn partial(testDescriptor, Collections::emptySet);\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating a partial match with potential children.\n\t\t *\n\t\t * @param testDescriptor the resolved {@code TestDescriptor}; never\n\t\t * {@code null}\n\t\t * @param childSelectorsSupplier a {@code Supplier} of children\n\t\t * selectors that will be resolved when this match is expanded; never\n\t\t * {@code null}\n\t\t * @return a match that contains the supplied {@code TestDescriptor};\n\t\t * never {@code null}\n\t\t */\n\t\tpublic static Match partial(TestDescriptor testDescriptor,\n\t\t\t\tSupplier<Set<? extends DiscoverySelector>> childSelectorsSupplier) {\n\t\t\treturn new Match(testDescriptor, childSelectorsSupplier, Type.PARTIAL);\n\t\t}\n\n\t\tprivate Match(TestDescriptor testDescriptor, Supplier<Set<? extends DiscoverySelector>> childSelectorsSupplier,\n\t\t\t\tType type) {\n\t\t\tthis.testDescriptor = Preconditions.notNull(testDescriptor, \"testDescriptor must not be null\");\n\t\t\tthis.childSelectorsSupplier = Preconditions.notNull(childSelectorsSupplier,\n\t\t\t\t\"childSelectorsSupplier must not be null\");\n\t\t\tthis.type = type;\n\t\t}\n\n\t\t/**\n\t\t * Whether this match is exact.\n\t\t *\n\t\t * @return {@code true} if this match is exact; {@code false} if it's\n\t\t * partial\n\t\t */\n\t\tpublic boolean isExact() {\n\t\t\treturn type == Type.EXACT;\n\t\t}\n\n\t\t/**\n\t\t * Get the contained {@link TestDescriptor}.\n\t\t *\n\t\t * @return the contained {@code TestDescriptor}; never {@code null}\n\t\t */\n\t\tpublic TestDescriptor getTestDescriptor() {\n\t\t\treturn testDescriptor;\n\t\t}\n\n\t\t/**\n\t\t * Expand this match in order to resolve the children of the contained\n\t\t * {@link TestDescriptor}.\n\t\t *\n\t\t * @return the set of {@code DiscoverySelectors} that represent the\n\t\t * children of the contained {@code TestDescriptor}; never {@code null}\n\t\t */\n\t\tpublic Set<? extends DiscoverySelector> expand() {\n\t\t\treturn childSelectorsSupplier.get();\n\t\t}\n\n\t\tprivate enum Type {\n\t\t\tEXACT, PARTIAL\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/discovery/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Configurable test discovery implementation that can be reused by different test engines.\n *\n * @see org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver#builder()\n * @see org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver#resolve\n * @see org.junit.platform.engine.support.discovery.SelectorResolver\n */\n\n@NullMarked\npackage org.junit.platform.engine.support.discovery;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/BlockingAwareFuture.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 6.1\n */\nabstract class BlockingAwareFuture<T extends @Nullable Object> extends DelegatingFuture<T> {\n\n\tBlockingAwareFuture(Future<T> delegate) {\n\t\tsuper(delegate);\n\t}\n\n\t@Override\n\tpublic T get() throws InterruptedException, ExecutionException {\n\t\tif (delegate.isDone()) {\n\t\t\treturn delegate.get();\n\t\t}\n\t\treturn handleSafely(delegate::get);\n\t}\n\n\t@Override\n\tpublic T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {\n\t\tif (delegate.isDone()) {\n\t\t\treturn delegate.get();\n\t\t}\n\t\treturn handleSafely(() -> delegate.get(timeout, unit));\n\t}\n\n\tprivate T handleSafely(Callable<T> callable) {\n\t\ttry {\n\t\t\treturn handle(callable);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow throwAsUncheckedException(e);\n\t\t}\n\t}\n\n\tprotected abstract T handle(Callable<T> callable) throws Exception;\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/CompositeLock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.locks.Lock;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * @since 1.3\n */\nclass CompositeLock implements ResourceLock {\n\n\tprivate final List<ExclusiveResource> resources;\n\tprivate final List<Lock> locks;\n\tprivate final boolean exclusive;\n\n\tCompositeLock(List<ExclusiveResource> resources, List<Lock> locks) {\n\t\tPreconditions.condition(resources.size() == locks.size(), \"Resources and locks must have the same size\");\n\t\tthis.resources = List.copyOf(resources);\n\t\tthis.locks = Preconditions.notEmpty(locks, \"Locks must not be empty\");\n\t\tthis.exclusive = resources.stream().anyMatch(\n\t\t\tresource -> resource.getLockMode() == ExclusiveResource.LockMode.READ_WRITE);\n\t}\n\n\t@Override\n\tpublic List<ExclusiveResource> getResources() {\n\t\treturn resources;\n\t}\n\n\t// for tests only\n\tList<Lock> getLocks() {\n\t\treturn this.locks;\n\t}\n\n\t@Override\n\tpublic boolean tryAcquire() {\n\t\tList<Lock> acquiredLocks = new ArrayList<>(this.locks.size());\n\t\tfor (Lock lock : this.locks) {\n\t\t\tif (lock.tryLock()) {\n\t\t\t\tacquiredLocks.add(lock);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (acquiredLocks.size() == this.locks.size()) {\n\t\t\treturn true;\n\t\t}\n\t\telse {\n\t\t\trelease(acquiredLocks);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic ResourceLock acquire() throws InterruptedException {\n\t\tForkJoinPool.managedBlock(new CompositeLockManagedBlocker());\n\t\treturn this;\n\t}\n\n\tprivate void acquireAllLocks() throws InterruptedException {\n\t\tList<Lock> acquiredLocks = new ArrayList<>(this.locks.size());\n\t\ttry {\n\t\t\tfor (Lock lock : this.locks) {\n\t\t\t\tlock.lockInterruptibly();\n\t\t\t\tacquiredLocks.add(lock);\n\t\t\t}\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\trelease(acquiredLocks);\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\trelease(this.locks);\n\t}\n\n\tprivate void release(List<Lock> acquiredLocks) {\n\t\tfor (int i = acquiredLocks.size() - 1; i >= 0; i--) {\n\t\t\tacquiredLocks.get(i).unlock();\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean isExclusive() {\n\t\treturn exclusive;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"resources\", resources) //\n\t\t\t\t.toString();\n\t}\n\n\tprivate class CompositeLockManagedBlocker implements ForkJoinPool.ManagedBlocker {\n\n\t\tprivate volatile boolean acquired;\n\n\t\t@Override\n\t\tpublic boolean block() throws InterruptedException {\n\t\t\tif (!this.acquired) {\n\t\t\t\tacquireAllLocks();\n\t\t\t\tthis.acquired = true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isReleasable() {\n\t\t\treturn this.acquired;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.function.Predicate;\n\n/**\n * @since 1.3\n */\nclass DefaultParallelExecutionConfiguration implements ParallelExecutionConfiguration {\n\n\tprivate final int parallelism;\n\tprivate final int minimumRunnable;\n\tprivate final int maxPoolSize;\n\tprivate final int corePoolSize;\n\tprivate final int keepAliveSeconds;\n\tprivate final Predicate<? super ForkJoinPool> saturate;\n\n\tDefaultParallelExecutionConfiguration(int parallelism, int minimumRunnable, int maxPoolSize, int corePoolSize,\n\t\t\tint keepAliveSeconds, Predicate<? super ForkJoinPool> saturate) {\n\t\tthis.parallelism = parallelism;\n\t\tthis.minimumRunnable = minimumRunnable;\n\t\tthis.maxPoolSize = maxPoolSize;\n\t\tthis.corePoolSize = corePoolSize;\n\t\tthis.keepAliveSeconds = keepAliveSeconds;\n\t\tthis.saturate = saturate;\n\t}\n\n\t@Override\n\tpublic int getParallelism() {\n\t\treturn parallelism;\n\t}\n\n\t@Override\n\tpublic int getMinimumRunnable() {\n\t\treturn minimumRunnable;\n\t}\n\n\t@Override\n\tpublic int getMaxPoolSize() {\n\t\treturn maxPoolSize;\n\t}\n\n\t@Override\n\tpublic int getCorePoolSize() {\n\t\treturn corePoolSize;\n\t}\n\n\t@Override\n\tpublic int getKeepAliveSeconds() {\n\t\treturn keepAliveSeconds;\n\t}\n\n\t@Override\n\tpublic Predicate<? super ForkJoinPool> getSaturatePredicate() {\n\t\treturn saturate;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategy.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.math.BigDecimal;\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * Default implementations of configuration strategies for parallel test\n * execution.\n *\n * @since 1.3\n */\n@API(status = STABLE, since = \"1.10\")\npublic enum DefaultParallelExecutionConfigurationStrategy implements ParallelExecutionConfigurationStrategy {\n\n\t/**\n\t * Uses the mandatory {@value #CONFIG_FIXED_PARALLELISM_PROPERTY_NAME} configuration\n\t * parameter as the desired parallelism.\n\t */\n\tFIXED {\n\t\t@Override\n\t\tpublic ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {\n\t\t\tint parallelism = configurationParameters.get(CONFIG_FIXED_PARALLELISM_PROPERTY_NAME,\n\t\t\t\tInteger::valueOf).orElseThrow(\n\t\t\t\t\t() -> new JUnitException(\n\t\t\t\t\t\t\"Configuration parameter '%s' must be set\".formatted(CONFIG_FIXED_PARALLELISM_PROPERTY_NAME)));\n\n\t\t\tint maxPoolSize = configurationParameters.get(CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME,\n\t\t\t\tInteger::valueOf).orElse(parallelism + 256);\n\n\t\t\tboolean saturate = configurationParameters.get(CONFIG_FIXED_SATURATE_PROPERTY_NAME,\n\t\t\t\tBoolean::valueOf).orElse(true);\n\n\t\t\treturn new DefaultParallelExecutionConfiguration(parallelism, parallelism, maxPoolSize, parallelism,\n\t\t\t\tKEEP_ALIVE_SECONDS, __ -> saturate);\n\t\t}\n\t},\n\n\t/**\n\t * Computes the desired parallelism based on the number of available\n\t * processors/cores multiplied by the {@value #CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME}\n\t * configuration parameter.\n\t */\n\tDYNAMIC {\n\t\t@Override\n\t\tpublic ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {\n\t\t\tBigDecimal factor = configurationParameters.get(CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME,\n\t\t\t\tBigDecimal::new).orElse(BigDecimal.ONE);\n\n\t\t\tPreconditions.condition(factor.compareTo(BigDecimal.ZERO) > 0,\n\t\t\t\t() -> \"Factor '%s' specified via configuration parameter '%s' must be greater than 0\".formatted(factor,\n\t\t\t\t\tCONFIG_DYNAMIC_FACTOR_PROPERTY_NAME));\n\n\t\t\tint parallelism = Math.max(1,\n\t\t\t\tfactor.multiply(BigDecimal.valueOf(Runtime.getRuntime().availableProcessors())).intValue());\n\n\t\t\tint maxPoolSize = configurationParameters.get(CONFIG_DYNAMIC_MAX_POOL_SIZE_FACTOR_PROPERTY_NAME,\n\t\t\t\tBigDecimal::new).map(maxPoolSizeFactor -> {\n\t\t\t\t\tPreconditions.condition(maxPoolSizeFactor.compareTo(BigDecimal.ONE) >= 0,\n\t\t\t\t\t\t() -> \"Factor '%s' specified via configuration parameter '%s' must be greater than or equal to 1\".formatted(\n\t\t\t\t\t\t\tfactor, CONFIG_DYNAMIC_MAX_POOL_SIZE_FACTOR_PROPERTY_NAME));\n\t\t\t\t\treturn maxPoolSizeFactor.multiply(BigDecimal.valueOf(parallelism)).intValue();\n\t\t\t\t}).orElseGet(() -> 256 + parallelism);\n\n\t\t\tboolean saturate = configurationParameters.get(CONFIG_DYNAMIC_SATURATE_PROPERTY_NAME,\n\t\t\t\tBoolean::valueOf).orElse(true);\n\n\t\t\treturn new DefaultParallelExecutionConfiguration(parallelism, parallelism, maxPoolSize, parallelism,\n\t\t\t\tKEEP_ALIVE_SECONDS, __ -> saturate);\n\t\t}\n\t},\n\n\t/**\n\t * Allows the specification of a custom {@link ParallelExecutionConfigurationStrategy}\n\t * implementation via the mandatory {@value #CONFIG_CUSTOM_CLASS_PROPERTY_NAME}\n\t * configuration parameter to determine the desired configuration.\n\t */\n\tCUSTOM {\n\t\t@Override\n\t\tpublic ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {\n\t\t\tString className = configurationParameters.get(CONFIG_CUSTOM_CLASS_PROPERTY_NAME).orElseThrow(\n\t\t\t\t() -> new JUnitException(CONFIG_CUSTOM_CLASS_PROPERTY_NAME + \" must be set\"));\n\t\t\treturn ReflectionSupport.tryToLoadClass(className) //\n\t\t\t\t\t.andThenTry(strategyClass -> {\n\t\t\t\t\t\tPreconditions.condition(\n\t\t\t\t\t\t\tParallelExecutionConfigurationStrategy.class.isAssignableFrom(strategyClass),\n\t\t\t\t\t\t\tCONFIG_CUSTOM_CLASS_PROPERTY_NAME + \" does not implement \"\n\t\t\t\t\t\t\t\t\t+ ParallelExecutionConfigurationStrategy.class);\n\t\t\t\t\t\treturn (ParallelExecutionConfigurationStrategy) ReflectionSupport.newInstance(strategyClass);\n\t\t\t\t\t}) //\n\t\t\t\t\t.andThenTry(strategy -> requireNonNull(strategy).createConfiguration(configurationParameters)) //\n\t\t\t\t\t.getNonNullOrThrow(cause -> new JUnitException(\n\t\t\t\t\t\t\"Could not create configuration for strategy class: \" + className, cause));\n\t\t}\n\t};\n\n\tprivate static final int KEEP_ALIVE_SECONDS = 30;\n\n\t/**\n\t * Property name used to determine the desired configuration strategy.\n\t *\n\t * <p>Value must be one of {@code dynamic}, {@code fixed}, or\n\t * {@code custom}.\n\t */\n\tpublic static final String CONFIG_STRATEGY_PROPERTY_NAME = \"strategy\";\n\n\t/**\n\t * Property name used to determine the desired parallelism for the\n\t * {@link #FIXED} configuration strategy.\n\t *\n\t * <p>No default value; must be an integer.\n\t *\n\t * @see #FIXED\n\t */\n\tpublic static final String CONFIG_FIXED_PARALLELISM_PROPERTY_NAME = \"fixed.parallelism\";\n\n\t/**\n\t * Property name used to configure the maximum pool size of the underlying\n\t * fork-join pool for the {@link #FIXED} configuration strategy.\n\t *\n\t * <p>Value must be an integer and greater than or equal to\n\t * {@value #CONFIG_FIXED_PARALLELISM_PROPERTY_NAME}; defaults to\n\t * {@code 256 + fixed.parallelism}.\n\t *\n\t * @since 1.10\n\t * @see #FIXED\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME = \"fixed.max-pool-size\";\n\n\t/**\n\t * Property name used to disable saturation of the underlying fork-join pool\n\t * for the {@link #FIXED} configuration strategy.\n\t *\n\t * <p>When set to {@code false} the underlying fork-join pool will reject\n\t * additional tasks if all available workers are busy and the maximum\n\t * pool-size would be exceeded.\n\t * <p>Value must either {@code true} or {@code false}; defaults to {@code true}.\n\t *\n\t * @since 1.10\n\t * @see #FIXED\n\t * @see #CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String CONFIG_FIXED_SATURATE_PROPERTY_NAME = \"fixed.saturate\";\n\n\t/**\n\t * Property name of the factor used to determine the desired parallelism for the\n\t * {@link #DYNAMIC} configuration strategy.\n\t *\n\t * <p>Value must be a non-negative decimal number; defaults to {@code 1}.\n\t *\n\t * @see #DYNAMIC\n\t */\n\tpublic static final String CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME = \"dynamic.factor\";\n\n\t/**\n\t * Property name of the factor used to determine the maximum pool size of\n\t * the underlying fork-join pool for the {@link #DYNAMIC} configuration\n\t * strategy.\n\t *\n\t * <p>Value must be a decimal number equal and greater than or equal to\n\t * {@code 1}. When set the maximum pool size is calculated as\n\t * {@code dynamic.max-pool-size-factor * dynamic.factor * Runtime.getRuntime().availableProcessors()}\n\t * When not set the maximum pool size is calculated as\n\t * {@code 256 + dynamic.factor * Runtime.getRuntime().availableProcessors()}\n\t * instead.\n\t *\n\t * @since 1.10\n\t * @see #DYNAMIC\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String CONFIG_DYNAMIC_MAX_POOL_SIZE_FACTOR_PROPERTY_NAME = \"dynamic.max-pool-size-factor\";\n\n\t/**\n\t * Property name used to disable saturation of the underlying fork-join pool\n\t * for the {@link #DYNAMIC} configuration strategy.\n\t *\n\t * <p>When set to {@code false} the underlying fork-join pool will reject\n\t * additional tasks if all available workers are busy and the maximum\n\t * pool-size would be exceeded.\n\t * <p>Value must either {@code true} or {@code false}; defaults to {@code true}.\n\t *\n\t * @since 1.10\n\t * @see #DYNAMIC\n\t * @see #CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String CONFIG_DYNAMIC_SATURATE_PROPERTY_NAME = \"dynamic.saturate\";\n\n\t/**\n\t * Property name used to specify the fully qualified class name of the\n\t * {@link ParallelExecutionConfigurationStrategy} to be used by the\n\t * {@link #CUSTOM} configuration strategy.\n\t *\n\t * @see #CUSTOM\n\t */\n\tpublic static final String CONFIG_CUSTOM_CLASS_PROPERTY_NAME = \"custom.class\";\n\n\tstatic ParallelExecutionConfiguration toConfiguration(ConfigurationParameters configurationParameters) {\n\t\treturn getStrategy(configurationParameters).createConfiguration(configurationParameters);\n\t}\n\n\tstatic ParallelExecutionConfigurationStrategy getStrategy(ConfigurationParameters configurationParameters) {\n\t\treturn valueOf(\n\t\t\tconfigurationParameters.get(CONFIG_STRATEGY_PROPERTY_NAME).orElse(\"dynamic\").toUpperCase(Locale.ROOT));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/DelegatingFuture.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 6.1\n */\nclass DelegatingFuture<T extends @Nullable Object> implements Future<T> {\n\n\tprotected final Future<T> delegate;\n\n\tDelegatingFuture(Future<T> delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic boolean cancel(boolean mayInterruptIfRunning) {\n\t\treturn delegate.cancel(mayInterruptIfRunning);\n\t}\n\n\t@Override\n\tpublic boolean isCancelled() {\n\t\treturn delegate.isCancelled();\n\t}\n\n\t@Override\n\tpublic boolean isDone() {\n\t\treturn delegate.isDone();\n\t}\n\n\t@Override\n\tpublic T get() throws InterruptedException, ExecutionException {\n\t\treturn delegate.get();\n\t}\n\n\t@Override\n\tpublic T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {\n\t\treturn delegate.get(timeout, unit);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/EngineExecutionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * Marker interface for an execution context used by a concrete implementation\n * of {@link HierarchicalTestEngine} and its collaborators.\n *\n * @since 1.0\n * @see HierarchicalTestEngine\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic interface EngineExecutionContext {\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ExclusiveResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.Comparator.naturalOrder;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Comparator;\nimport java.util.Objects;\nimport java.util.concurrent.locks.ReadWriteLock;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\n\n/**\n * An exclusive resource identified by a key with a lock mode that is used to\n * synchronize access to shared resources when executing nodes in parallel.\n *\n * @since 1.3\n * @see Node#getExecutionMode()\n */\n@API(status = STABLE, since = \"1.10\")\npublic class ExclusiveResource {\n\n\t/**\n\t * Key of the global resource lock that all direct children of the engine\n\t * descriptor acquire in {@linkplain LockMode#READ read mode} by default:\n\t * {@value}\n\t *\n\t * <p>If any node {@linkplain Node#getExclusiveResources() requires} an\n\t * exclusive resource with the same key in\n\t * {@linkplain LockMode#READ_WRITE read-write mode}, the lock will be\n\t * coarsened to be acquired by the node's ancestor that is a direct child of\n\t * the engine descriptor and all of the ancestor's descendants will be\n\t * forced to run in the {@linkplain ExecutionMode#SAME_THREAD same thread}.\n\t *\n\t * @since 1.7\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static final String GLOBAL_KEY = \"org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY\";\n\n\tstatic final ExclusiveResource GLOBAL_READ = new ExclusiveResource(GLOBAL_KEY, LockMode.READ);\n\tstatic final ExclusiveResource GLOBAL_READ_WRITE = new ExclusiveResource(GLOBAL_KEY, LockMode.READ_WRITE);\n\n\tstatic final Comparator<ExclusiveResource> COMPARATOR //\n\t\t= comparing(ExclusiveResource::getKey, globalKeyFirst().thenComparing(naturalOrder())) //\n\t\t\t\t.thenComparing(ExclusiveResource::getLockMode);\n\n\tprivate static Comparator<String> globalKeyFirst() {\n\t\treturn comparing(key -> !GLOBAL_KEY.equals(key));\n\t}\n\n\tprivate final String key;\n\tprivate final LockMode lockMode;\n\tprivate int hash;\n\n\t/**\n\t * Create a new {@code ExclusiveResource}.\n\t *\n\t * @param key the identifier of the resource; never {@code null} or blank\n\t * @param lockMode the lock mode to use to synchronize access to the\n\t * resource; never {@code null}\n\t */\n\tpublic ExclusiveResource(String key, LockMode lockMode) {\n\t\tthis.key = Preconditions.notBlank(key, \"key must not be blank\");\n\t\tthis.lockMode = Preconditions.notNull(lockMode, \"lockMode must not be null\");\n\t}\n\n\t/**\n\t * Get the key of this resource.\n\t */\n\tpublic String getKey() {\n\t\treturn key;\n\t}\n\n\t/**\n\t * Get the lock mode of this resource.\n\t */\n\tpublic LockMode getLockMode() {\n\t\treturn lockMode;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"EqualsGetClass\")\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tExclusiveResource that = (ExclusiveResource) o;\n\t\treturn Objects.equals(key, that.key) && lockMode == that.lockMode;\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\tint h = hash;\n\t\tif (h == 0) {\n\t\t\th = hash = Objects.hash(key, lockMode);\n\t\t}\n\t\treturn h;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).append(\"key\", key).append(\"lockMode\", lockMode).toString();\n\t}\n\n\t/**\n\t * {@code LockMode} translates to the respective {@link ReadWriteLock}\n\t * locks.\n\t *\n\t * @implNote Enum order is important, since it can be used to sort locks, so\n\t * the stronger mode has to be first.\n\t */\n\tpublic enum LockMode {\n\n\t\t/**\n\t\t * Require read and write access to the resource.\n\t\t *\n\t\t * @see ReadWriteLock#writeLock()\n\t\t */\n\t\tREAD_WRITE,\n\n\t\t/**\n\t\t * Require only read access to the resource.\n\t\t *\n\t\t * @see ReadWriteLock#readLock()\n\t\t */\n\t\tREAD\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorService.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD;\n\nimport java.io.Serial;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Deque;\nimport java.util.List;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.ForkJoinTask;\nimport java.util.concurrent.ForkJoinWorkerThread;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\n\n/**\n * A {@link ForkJoinPool}-based\n * {@linkplain HierarchicalTestExecutorService executor service} that executes\n * {@linkplain TestTask test tasks} with the configured parallelism.\n *\n * @since 1.3\n * @see ParallelHierarchicalTestExecutorServiceFactory\n * @see ParallelExecutorServiceType#FORK_JOIN_POOL\n * @see DefaultParallelExecutionConfigurationStrategy\n * @see ForkJoinPool\n */\n@API(status = MAINTAINED, since = \"1.10\")\npublic class ForkJoinPoolHierarchicalTestExecutorService implements HierarchicalTestExecutorService {\n\n\t// package-private for testing\n\tfinal ForkJoinPool forkJoinPool;\n\n\tprivate final TaskEventListener taskEventListener;\n\tprivate final int parallelism;\n\tprivate final ThreadLocal<ThreadLock> threadLocks = ThreadLocal.withInitial(ThreadLock::new);\n\n\t/**\n\t * Create a new {@code ForkJoinPoolHierarchicalTestExecutorService} based on\n\t * the supplied {@link ConfigurationParameters}.\n\t *\n\t * @see DefaultParallelExecutionConfigurationStrategy\n\t * @deprecated Please use\n\t * {@link ParallelHierarchicalTestExecutorServiceFactory#create(ConfigurationParameters)}\n\t * with configuration parameter\n\t * {@value ParallelHierarchicalTestExecutorServiceFactory#EXECUTOR_SERVICE_PROPERTY_NAME}\n\t * set to\n\t * {@link ParallelExecutorServiceType#FORK_JOIN_POOL FORK_JOIN_POOL}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(since = \"6.1\")\n\tpublic ForkJoinPoolHierarchicalTestExecutorService(ConfigurationParameters configurationParameters) {\n\t\tthis(DefaultParallelExecutionConfigurationStrategy.toConfiguration(configurationParameters));\n\t}\n\n\t/**\n\t * Create a new {@code ForkJoinPoolHierarchicalTestExecutorService} based on\n\t * the supplied {@link ParallelExecutionConfiguration}.\n\t *\n\t * @since 1.7\n\t * @deprecated Please use\n\t * {@link ParallelHierarchicalTestExecutorServiceFactory#create(ParallelExecutorServiceType, ParallelExecutionConfiguration)}\n\t * with\n\t * {@link ParallelExecutorServiceType#FORK_JOIN_POOL ParallelExecutorServiceType.FORK_JOIN_POOL}\n\t * instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(since = \"6.1\")\n\tpublic ForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration) {\n\t\tthis(configuration, TaskEventListener.NOOP);\n\t}\n\n\tForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration,\n\t\t\tTaskEventListener taskEventListener) {\n\t\tforkJoinPool = createForkJoinPool(configuration);\n\t\tthis.taskEventListener = taskEventListener;\n\t\tparallelism = forkJoinPool.getParallelism();\n\t\tLoggerFactory.getLogger(getClass()).config(() -> \"Using ForkJoinPool with parallelism of \" + parallelism);\n\t}\n\n\tprivate ForkJoinPool createForkJoinPool(ParallelExecutionConfiguration configuration) {\n\t\ttry {\n\t\t\treturn new ForkJoinPool(configuration.getParallelism(), new WorkerThreadFactory(), null, false,\n\t\t\t\tconfiguration.getCorePoolSize(), configuration.getMaxPoolSize(), configuration.getMinimumRunnable(),\n\t\t\t\tconfiguration.getSaturatePredicate(), configuration.getKeepAliveSeconds(), TimeUnit.SECONDS);\n\t\t}\n\t\tcatch (Exception cause) {\n\t\t\tthrow new JUnitException(\"Failed to create ForkJoinPool\", cause);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Future<@Nullable Void> submit(TestTask testTask) {\n\t\tExclusiveTask exclusiveTask = new ExclusiveTask(testTask);\n\t\tif (!isAlreadyRunningInForkJoinPool()) {\n\t\t\t// ensure we're running inside the ForkJoinPool so we\n\t\t\t// can use ForkJoinTask API in invokeAll etc.\n\t\t\treturn forkJoinPool.submit(exclusiveTask);\n\t\t}\n\t\t// Limit the amount of queued work so we don't consume dynamic tests too eagerly\n\t\t// by forking only if the current worker thread's queue length is below the\n\t\t// desired parallelism. This optimistically assumes that the already queued tasks\n\t\t// can be stolen by other workers and the new task requires about the same\n\t\t// execution time as the already queued tasks. If the other workers are busy,\n\t\t// the parallelism is already at its desired level. If all already queued tasks\n\t\t// can be stolen by otherwise idle workers and the new task takes significantly\n\t\t// longer, parallelism will drop. However, that only happens if the enclosing test\n\t\t// task is the only one remaining which should rarely be the case.\n\t\tif (testTask.getExecutionMode() == CONCURRENT && ForkJoinTask.getSurplusQueuedTaskCount() < parallelism) {\n\t\t\treturn exclusiveTask.fork();\n\t\t}\n\t\texclusiveTask.execSync();\n\t\treturn completedFuture(null);\n\t}\n\n\tprivate boolean isAlreadyRunningInForkJoinPool() {\n\t\treturn ForkJoinTask.getPool() == forkJoinPool;\n\t}\n\n\t@Override\n\tpublic void invokeAll(List<? extends TestTask> tasks) {\n\t\tif (tasks.size() == 1) {\n\t\t\tnew ExclusiveTask(tasks.get(0)).execSync();\n\t\t\treturn;\n\t\t}\n\t\tDeque<ExclusiveTask> isolatedTasks = new ArrayDeque<>();\n\t\tDeque<ExclusiveTask> sameThreadTasks = new ArrayDeque<>();\n\t\tDeque<ExclusiveTask> concurrentTasksInReverseOrder = new ArrayDeque<>();\n\t\tforkConcurrentTasks(tasks, isolatedTasks, sameThreadTasks, concurrentTasksInReverseOrder);\n\t\texecuteSync(sameThreadTasks);\n\t\tjoinConcurrentTasksInReverseOrderToEnableWorkStealing(concurrentTasksInReverseOrder);\n\t\texecuteSync(isolatedTasks);\n\t}\n\n\tprivate void forkConcurrentTasks(List<? extends TestTask> tasks, Deque<ExclusiveTask> isolatedTasks,\n\t\t\tDeque<ExclusiveTask> sameThreadTasks, Deque<ExclusiveTask> concurrentTasksInReverseOrder) {\n\t\tfor (TestTask testTask : tasks) {\n\t\t\tExclusiveTask exclusiveTask = new ExclusiveTask(testTask);\n\t\t\tif (requiresGlobalReadWriteLock(testTask)) {\n\t\t\t\tisolatedTasks.add(exclusiveTask);\n\t\t\t}\n\t\t\telse if (testTask.getExecutionMode() == SAME_THREAD) {\n\t\t\t\tsameThreadTasks.add(exclusiveTask);\n\t\t\t}\n\t\t\telse {\n\t\t\t\texclusiveTask.fork();\n\t\t\t\tconcurrentTasksInReverseOrder.addFirst(exclusiveTask);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean requiresGlobalReadWriteLock(TestTask testTask) {\n\t\treturn testTask.getResourceLock().getResources().contains(GLOBAL_READ_WRITE);\n\t}\n\n\tprivate void executeSync(Deque<ExclusiveTask> tasks) {\n\t\tfor (ExclusiveTask task : tasks) {\n\t\t\ttask.execSync();\n\t\t}\n\t}\n\n\tprivate void joinConcurrentTasksInReverseOrderToEnableWorkStealing(\n\t\t\tDeque<ExclusiveTask> concurrentTasksInReverseOrder) {\n\t\tfor (ExclusiveTask forkedTask : concurrentTasksInReverseOrder) {\n\t\t\tforkedTask.join();\n\t\t\tresubmitDeferredTasks();\n\t\t}\n\t}\n\n\tprivate void resubmitDeferredTasks() {\n\t\tList<ExclusiveTask> deferredTasks = threadLocks.get().deferredTasks;\n\t\tfor (ExclusiveTask deferredTask : deferredTasks) {\n\t\t\tif (!deferredTask.isDone()) {\n\t\t\t\tdeferredTask.fork();\n\t\t\t}\n\t\t}\n\t\tdeferredTasks.clear();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tforkJoinPool.shutdownNow();\n\t}\n\n\t// this class cannot not be serialized because TestTask is not Serializable\n\t@SuppressWarnings({ \"serial\", \"RedundantSuppression\" })\n\tclass ExclusiveTask extends ForkJoinTask<@Nullable Void> {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1;\n\n\t\tprivate final TestTask testTask;\n\n\t\tExclusiveTask(TestTask testTask) {\n\t\t\tthis.testTask = testTask;\n\t\t}\n\n\t\t/**\n\t\t * Always returns {@code null}.\n\t\t *\n\t\t * @return {@code null} always\n\t\t */\n\t\t@Override\n\t\tpublic final Void getRawResult() {\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * Requires null completion value.\n\t\t */\n\t\t@Override\n\t\tprotected final void setRawResult(Void mustBeNull) {\n\t\t}\n\n\t\tvoid execSync() {\n\t\t\tboolean completed = exec();\n\t\t\tif (!completed) {\n\t\t\t\tthrow new IllegalStateException(\n\t\t\t\t\t\"Task was deferred but should have been executed synchronously: \" + testTask);\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"try\")\n\t\t@Override\n\t\tpublic boolean exec() {\n\t\t\t// Check if this task is compatible with the current resource lock, if there is any.\n\t\t\t// If not, we put this task in the thread local as a deferred task\n\t\t\t// and let the worker thread fork it once it is done with the current task.\n\t\t\tResourceLock resourceLock = testTask.getResourceLock();\n\t\t\tThreadLock threadLock = threadLocks.get();\n\t\t\tif (!threadLock.areAllHeldLocksCompatibleWith(resourceLock)) {\n\t\t\t\tthreadLock.addDeferredTask(this);\n\t\t\t\ttaskEventListener.deferred(testTask);\n\t\t\t\t// Return false to indicate that this task is not done yet\n\t\t\t\t// this means that .join() will wait.\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\ttry ( //\n\t\t\t\t\tResourceLock lock = resourceLock.acquire(); //\n\t\t\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t\t\tThreadLock.NestedResourceLock nested = threadLock.withNesting(lock) //\n\t\t\t) {\n\t\t\t\ttestTask.execute();\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(e);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"ExclusiveTask [\" + testTask + \"]\";\n\t\t}\n\t}\n\n\tstatic class WorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {\n\n\t\tprivate final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n\n\t\t@Override\n\t\tpublic ForkJoinWorkerThread newThread(ForkJoinPool pool) {\n\t\t\treturn new WorkerThread(pool, contextClassLoader);\n\t\t}\n\n\t}\n\n\tstatic class WorkerThread extends ForkJoinWorkerThread {\n\n\t\tWorkerThread(ForkJoinPool pool, ClassLoader contextClassLoader) {\n\t\t\tsuper(pool);\n\t\t\tsetContextClassLoader(contextClassLoader);\n\t\t}\n\n\t}\n\n\tstatic class ThreadLock {\n\t\tprivate final Deque<ResourceLock> locks = new ArrayDeque<>(2);\n\t\tprivate final List<ExclusiveTask> deferredTasks = new ArrayList<>();\n\n\t\tvoid addDeferredTask(ExclusiveTask task) {\n\t\t\tdeferredTasks.add(task);\n\t\t}\n\n\t\tNestedResourceLock withNesting(ResourceLock lock) {\n\t\t\tlocks.push(lock);\n\t\t\treturn locks::pop;\n\t\t}\n\n\t\tboolean areAllHeldLocksCompatibleWith(ResourceLock lock) {\n\t\t\treturn locks.stream().allMatch(l -> l.isCompatible(lock));\n\t\t}\n\n\t\tinterface NestedResourceLock extends AutoCloseable {\n\t\t\t@Override\n\t\t\tvoid close();\n\t\t}\n\t}\n\n\tinterface TaskEventListener {\n\n\t\tTaskEventListener NOOP = __ -> {\n\t\t};\n\n\t\tvoid deferred(TestTask testTask);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * Abstract base class for all {@link TestEngine} implementations that wish\n * to organize test suites hierarchically based on the {@link Node} abstraction.\n *\n * @param <C> the type of {@code EngineExecutionContext} used by this engine\n * @since 1.0\n * @see Node\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic abstract class HierarchicalTestEngine<C extends EngineExecutionContext> implements TestEngine {\n\n\tpublic HierarchicalTestEngine() {\n\t}\n\n\t/**\n\t * Create an {@linkplain #createExecutorService(ExecutionRequest) executor\n\t * service}; create an initial {@linkplain #createExecutionContext execution\n\t * context}; execute the behavior of all {@linkplain Node nodes} in the\n\t * hierarchy starting with the supplied {@code request}'s\n\t * {@linkplain ExecutionRequest#getRootTestDescriptor() root} and notify\n\t * its {@linkplain ExecutionRequest#getEngineExecutionListener() execution\n\t * listener} of test execution events.\n\t *\n\t * <p>Supports cancellation via the {@link CancellationToken} passed in the\n\t * supplied {@code request}.\n\t *\n\t * @see Node\n\t * @see #createExecutorService\n\t * @see #createExecutionContext\n\t */\n\t@Override\n\tpublic final void execute(ExecutionRequest request) {\n\t\ttry (HierarchicalTestExecutorService executorService = createExecutorService(request)) {\n\t\t\tC executionContext = createExecutionContext(request);\n\t\t\tThrowableCollector.Factory throwableCollectorFactory = createThrowableCollectorFactory(request);\n\t\t\tnew HierarchicalTestExecutor<>(request, executionContext, executorService,\n\t\t\t\tthrowableCollectorFactory).execute().get();\n\t\t}\n\t\tcatch (Exception exception) {\n\t\t\tthrow new JUnitException(\"Error executing tests for engine \" + getId(), exception);\n\t\t}\n\t}\n\n\t/**\n\t * Create the {@linkplain HierarchicalTestExecutorService executor service}\n\t * to use for executing the supplied {@linkplain ExecutionRequest request}.\n\t *\n\t * <p>An engine may use the information in the supplied <em>request</em>\n\t * such as the contained\n\t * {@linkplain ExecutionRequest#getConfigurationParameters() configuration parameters}\n\t * to decide what kind of service to return or how to configure it.\n\t *\n\t * <p>By default, this method returns an instance of\n\t * {@link SameThreadHierarchicalTestExecutorService}.\n\t *\n\t * @param request the request about to be executed\n\t * @since 1.3\n\t * @see ForkJoinPoolHierarchicalTestExecutorService\n\t * @see SameThreadHierarchicalTestExecutorService\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tprotected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request) {\n\t\treturn new SameThreadHierarchicalTestExecutorService();\n\t}\n\n\t/**\n\t * Create the {@linkplain ThrowableCollector.Factory factory} for creating\n\t * {@link ThrowableCollector} instances used to handle exceptions that occur\n\t * during execution of this engine's tests.\n\t *\n\t * <p>An engine may use the information in the supplied <em>request</em>\n\t * such as the contained\n\t * {@linkplain ExecutionRequest#getConfigurationParameters() configuration parameters}\n\t * to decide what kind of factory to return or how to configure it.\n\t *\n\t * <p>By default, this method returns a factory that always creates instances of\n\t * {@link OpenTest4JAwareThrowableCollector}.\n\t *\n\t * @param request the request about to be executed\n\t * @since 1.3\n\t * @see OpenTest4JAwareThrowableCollector\n\t * @see ThrowableCollector\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tprotected ThrowableCollector.Factory createThrowableCollectorFactory(ExecutionRequest request) {\n\t\treturn OpenTest4JAwareThrowableCollector::new;\n\t}\n\n\t/**\n\t * Create the initial execution context for executing the supplied\n\t * {@linkplain ExecutionRequest request}.\n\t *\n\t * @param request the request about to be executed\n\t * @return the initial context that will be passed to nodes in the hierarchy\n\t */\n\tprotected abstract C createExecutionContext(ExecutionRequest request);\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.concurrent.Future;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * Implementation core of all {@link TestEngine TestEngines} that wish to\n * use the {@link Node} abstraction as the driving principle for structuring\n * and executing test suites.\n *\n * <p>A {@code HierarchicalTestExecutor} is instantiated by a concrete\n * implementation of {@link HierarchicalTestEngine} and takes care of\n * executing nodes in the hierarchy in the appropriate order as well as\n * firing the necessary events in the {@link EngineExecutionListener}.\n *\n * @param <C> the type of {@code EngineExecutionContext} used by the\n * {@code HierarchicalTestEngine}\n * @since 1.0\n */\nclass HierarchicalTestExecutor<C extends EngineExecutionContext> {\n\n\tprivate final ExecutionRequest request;\n\tprivate final C rootContext;\n\tprivate final HierarchicalTestExecutorService executorService;\n\tprivate final ThrowableCollector.Factory throwableCollectorFactory;\n\n\tHierarchicalTestExecutor(ExecutionRequest request, C rootContext, HierarchicalTestExecutorService executorService,\n\t\t\tThrowableCollector.Factory throwableCollectorFactory) {\n\t\tthis.request = request;\n\t\tthis.rootContext = rootContext;\n\t\tthis.executorService = executorService;\n\t\tthis.throwableCollectorFactory = throwableCollectorFactory;\n\t}\n\n\tFuture<@Nullable Void> execute() {\n\t\treturn this.executorService.submit(createRootTestTask());\n\t}\n\n\tprivate NodeTestTask<C> createRootTestTask() {\n\t\tNodeTestTaskContext taskContext = createTaskContext();\n\t\tTestDescriptor rootTestDescriptor = this.request.getRootTestDescriptor();\n\t\tNodeTestTask<C> rootTestTask = new NodeTestTask<>(taskContext, rootTestDescriptor);\n\t\trootTestTask.setParentContext(this.rootContext);\n\t\treturn rootTestTask;\n\t}\n\n\tprivate NodeTestTaskContext createTaskContext() {\n\t\tEngineExecutionListener executionListener = this.request.getEngineExecutionListener();\n\t\tNodeExecutionAdvisor executionAdvisor = new NodeTreeWalker().walk(this.request.getRootTestDescriptor());\n\t\tCancellationToken cancellationToken = this.request.getCancellationToken();\n\t\treturn new NodeTestTaskContext(executionListener, this.executorService, this.throwableCollectorFactory,\n\t\t\texecutionAdvisor, cancellationToken);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorService.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.concurrent.Future;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\n\n/**\n * A closeable service that executes {@linkplain TestTask test tasks}.\n *\n * @since 1.3\n * @see HierarchicalTestEngine#createExecutorService(ExecutionRequest)\n * @see SameThreadHierarchicalTestExecutorService\n * @see ForkJoinPoolHierarchicalTestExecutorService\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface HierarchicalTestExecutorService extends AutoCloseable {\n\n\t/**\n\t * Submit the supplied {@linkplain TestTask test task} to be executed by\n\t * this service.\n\t *\n\t * <p>Implementations may {@linkplain TestTask#execute() execute} the task\n\t * asynchronously as long as its\n\t * {@linkplain TestTask#getExecutionMode() execution mode} is\n\t * {@linkplain ExecutionMode#CONCURRENT concurrent}.\n\t *\n\t * <p>Implementations must generally acquire and release the task's\n\t * {@linkplain TestTask#getResourceLock() resource lock} before and after its\n\t * execution unless they execute all tests in the same thread which\n\t * upholds the same guarantees.\n\t *\n\t * @param testTask the test task to be executed\n\t * @return a future that the caller can use to wait for the task's execution\n\t * to be finished\n\t * @see #invokeAll(List)\n\t */\n\tFuture<@Nullable Void> submit(TestTask testTask);\n\n\t/**\n\t * Invoke all supplied {@linkplain TestTask test tasks} and block until\n\t * their execution has finished.\n\t *\n\t * <p>Implementations may {@linkplain TestTask#execute() execute} one or\n\t * multiple of the supplied tasks in parallel as long as their\n\t * {@linkplain TestTask#getExecutionMode() execution mode} is\n\t * {@linkplain ExecutionMode#CONCURRENT concurrent}.\n\t *\n\t * <p>Implementations must generally acquire and release each task's\n\t * {@linkplain TestTask#getResourceLock() resource lock} before and after its\n\t * execution unless they execute all tests in the same thread which\n\t * upholds the same guarantees.\n\t *\n\t * @param testTasks the test tasks to be executed\n\t * @see #submit(TestTask)\n\t */\n\tvoid invokeAll(List<? extends TestTask> testTasks);\n\n\t/**\n\t * Close this service and let it perform any required cleanup work.\n\t *\n\t * <p>For example, thread-based implementations should usually close their\n\t * thread pools in this method.\n\t */\n\t@Override\n\tvoid close();\n\n\t/**\n\t * An executable task that represents a single test or container.\n\t */\n\tinterface TestTask {\n\n\t\t/**\n\t\t * Get the {@linkplain ExecutionMode execution mode} of this task.\n\t\t */\n\t\tExecutionMode getExecutionMode();\n\n\t\t/**\n\t\t * Get the {@linkplain ResourceLock resource lock} of this task.\n\t\t */\n\t\tResourceLock getResourceLock();\n\n\t\t/**\n\t\t * Get the {@linkplain TestDescriptor test descriptor} of this task.\n\t\t *\n\t\t * @throws UnsupportedOperationException if not supported for this TestTask implementation\n\t\t * @since 6.0\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tdefault TestDescriptor getTestDescriptor() {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t/**\n\t\t * Execute this task.\n\t\t */\n\t\tvoid execute();\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/LockManager.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.stream.Collectors.groupingBy;\nimport static java.util.stream.Collectors.toList;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReadWriteLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\n\n/**\n * @since 1.3\n */\nclass LockManager {\n\n\tprivate final Map<String, ReadWriteLock> locksByKey = new ConcurrentHashMap<>();\n\tprivate final SingleLock globalReadLock;\n\tprivate final SingleLock globalReadWriteLock;\n\n\tLockManager() {\n\t\tglobalReadLock = new SingleLock(GLOBAL_READ, toLock(GLOBAL_READ));\n\t\tglobalReadWriteLock = new SingleLock(GLOBAL_READ_WRITE, toLock(GLOBAL_READ_WRITE));\n\t}\n\n\tResourceLock getLockForResources(Collection<ExclusiveResource> resources) {\n\t\treturn toResourceLock(toDistinctSortedResources(resources));\n\t}\n\n\tResourceLock getLockForResource(ExclusiveResource resource) {\n\t\treturn toResourceLock(List.of(resource));\n\t}\n\n\tprivate List<ExclusiveResource> toDistinctSortedResources(Collection<ExclusiveResource> resources) {\n\t\tif (resources.isEmpty()) {\n\t\t\treturn emptyList();\n\t\t}\n\t\tif (resources.size() == 1) {\n\t\t\treturn List.of(getOnlyElement(resources));\n\t\t}\n\t\t// @formatter:off\n\t\tMap<String, List<ExclusiveResource>> resourcesByKey = resources.stream()\n\t\t\t\t.sorted(ExclusiveResource.COMPARATOR)\n\t\t\t\t.distinct()\n\t\t\t\t.collect(groupingBy(ExclusiveResource::getKey, LinkedHashMap::new, toList()));\n\n\t\treturn resourcesByKey.values().stream()\n\t\t\t\t.map(resourcesWithSameKey -> resourcesWithSameKey.get(0))\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate ResourceLock toResourceLock(List<ExclusiveResource> resources) {\n\t\treturn switch (resources.size()) {\n\t\t\tcase 0 -> NopLock.INSTANCE;\n\t\t\tcase 1 -> toSingleLock(getOnlyElement(resources));\n\t\t\tdefault -> new CompositeLock(resources, toLocks(resources));\n\t\t};\n\t}\n\n\tprivate SingleLock toSingleLock(ExclusiveResource resource) {\n\t\tif (GLOBAL_READ.equals(resource)) {\n\t\t\treturn globalReadLock;\n\t\t}\n\t\tif (GLOBAL_READ_WRITE.equals(resource)) {\n\t\t\treturn globalReadWriteLock;\n\t\t}\n\t\treturn new SingleLock(resource, toLock(resource));\n\t}\n\n\tprivate List<Lock> toLocks(List<ExclusiveResource> resources) {\n\t\treturn resources.stream().map(this::toLock).toList();\n\t}\n\n\tprivate Lock toLock(ExclusiveResource resource) {\n\t\tReadWriteLock lock = this.locksByKey.computeIfAbsent(resource.getKey(), key -> new ReentrantReadWriteLock());\n\t\treturn resource.getLockMode() == READ ? lock.readLock() : lock.writeLock();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/Node.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Collections.emptySet;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.Future;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * A <em>node</em> within the execution hierarchy.\n *\n * @param <C> the type of {@code EngineExecutionContext} used by the\n * {@code HierarchicalTestEngine}\n * @since 1.0\n * @see HierarchicalTestEngine\n */\n@API(status = MAINTAINED, since = \"1.0\", consumers = \"org.junit.platform.engine.support.hierarchical\")\npublic interface Node<C extends EngineExecutionContext> {\n\n\t/**\n\t * Prepare the supplied {@code context} prior to execution.\n\t *\n\t * <p>The default implementation returns the supplied {@code context} unmodified.\n\t *\n\t * @see #cleanUp(EngineExecutionContext)\n\t */\n\tdefault C prepare(C context) throws Exception {\n\t\treturn context;\n\t}\n\n\t/**\n\t * Clean up the supplied {@code context} after execution.\n\t *\n\t * <p>The default implementation does nothing.\n\t *\n\t * @param context the context to execute in\n\t * @since 1.1\n\t * @see #prepare(EngineExecutionContext)\n\t */\n\tdefault void cleanUp(C context) throws Exception {\n\t}\n\n\t/**\n\t * Determine if the execution of the supplied {@code context} should be\n\t * <em>skipped</em>.\n\t *\n\t * <p>The default implementation returns {@link SkipResult#doNotSkip()}.\n\t */\n\tdefault SkipResult shouldBeSkipped(C context) throws Exception {\n\t\treturn SkipResult.doNotSkip();\n\t}\n\n\t/**\n\t * Execute the <em>before</em> behavior of this node.\n\t *\n\t * <p>This method will be called once <em>before</em> {@linkplain #execute\n\t * execution} of this node.\n\t *\n\t * <p>The default implementation returns the supplied {@code context} unmodified.\n\t *\n\t * @param context the context to execute in\n\t * @return the new context to be used for children of this node; never\n\t * {@code null}\n\t * @see #execute(EngineExecutionContext, DynamicTestExecutor)\n\t * @see #after(EngineExecutionContext)\n\t */\n\tdefault C before(C context) throws Exception {\n\t\treturn context;\n\t}\n\n\t/**\n\t * Execute the <em>behavior</em> of this node.\n\t *\n\t * <p>Containers typically do not implement this method since the\n\t * {@link HierarchicalTestEngine} handles execution of their children.\n\t *\n\t * <p>The supplied {@code dynamicTestExecutor} may be used to submit\n\t * additional dynamic tests for immediate execution.\n\t *\n\t * <p>The default implementation returns the supplied {@code context} unmodified.\n\t *\n\t * @param context the context to execute in\n\t * @param dynamicTestExecutor the executor to submit dynamic tests to\n\t * @return the new context to be used for children of this node and for the\n\t * <em>after</em> behavior of the parent of this node, if any\n\t * @see #before\n\t * @see #after\n\t */\n\tdefault C execute(C context, DynamicTestExecutor dynamicTestExecutor) throws Exception {\n\t\treturn context;\n\t}\n\n\t/**\n\t * Execute the <em>after</em> behavior of this node.\n\t *\n\t * <p>This method will be called once <em>after</em> {@linkplain #execute\n\t * execution} of this node.\n\t *\n\t * <p>The default implementation does nothing.\n\t *\n\t * @param context the context to execute in\n\t * @see #before\n\t * @see #execute\n\t */\n\tdefault void after(C context) throws Exception {\n\t}\n\n\t/**\n\t * Wraps around the invocation of {@link #before(EngineExecutionContext)},\n\t * {@link #execute(EngineExecutionContext, DynamicTestExecutor)}, and\n\t * {@link #after(EngineExecutionContext)}.\n\t *\n\t * @param context context the context to execute in\n\t * @param invocation the wrapped invocation (must be invoked exactly once)\n\t * @since 1.4\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tdefault void around(C context, Invocation<C> invocation) throws Exception {\n\t\tinvocation.invoke(context);\n\t}\n\n\t/**\n\t * Callback invoked when the execution of this node has been skipped.\n\t *\n\t * <p>The default implementation does nothing.\n\t *\n\t * @param context the execution context\n\t * @param testDescriptor the test descriptor that was skipped\n\t * @param result the result of skipped execution\n\t * @since 1.4\n\t */\n\t@API(status = STABLE, since = \"1.10\", consumers = \"org.junit.platform.engine.support.hierarchical\")\n\tdefault void nodeSkipped(C context, TestDescriptor testDescriptor, SkipResult result) {\n\t}\n\n\t/**\n\t * Callback invoked when the execution of this node has finished.\n\t *\n\t * <p>The default implementation does nothing.\n\t *\n\t * @param context the execution context\n\t * @param testDescriptor the test descriptor that was executed\n\t * @param result the result of the execution\n\t * @since 1.4\n\t */\n\t@API(status = STABLE, since = \"1.10\", consumers = \"org.junit.platform.engine.support.hierarchical\")\n\tdefault void nodeFinished(C context, TestDescriptor testDescriptor, TestExecutionResult result) {\n\t}\n\n\t/**\n\t * Get the set of {@linkplain ExclusiveResource exclusive resources}\n\t * required to execute this node.\n\t *\n\t * <p>The default implementation returns an empty set.\n\t *\n\t * @return the set of exclusive resources required by this node; never\n\t * {@code null} but potentially empty\n\t * @since 1.3\n\t * @see ExclusiveResource\n\t */\n\t@API(status = STABLE, since = \"1.10\", consumers = \"org.junit.platform.engine.support.hierarchical\")\n\tdefault Set<ExclusiveResource> getExclusiveResources() {\n\t\treturn emptySet();\n\t}\n\n\t/**\n\t * {@return whether this node requires the global read-write lock}\n\t *\n\t * <p>Engines should return {@code true} for all nodes that have behavior,\n\t * including tests but also containers with before/after lifecycle hooks.\n\t *\n\t * <p>The default implementation returns {@code true}. The value returned by\n\t * engine-level nodes is ignored. Otherwise, if a container node returns\n\t * {@code true}, the value returned by its descendants is ignored.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tdefault boolean isGlobalReadLockRequired() {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Get the preferred of {@linkplain ExecutionMode execution mode} for\n\t * parallel execution of this node.\n\t *\n\t * <p>The default implementation returns {@link ExecutionMode#CONCURRENT}.\n\t *\n\t * @return the preferred execution mode of this node; never {@code null}\n\t * @since 1.3\n\t * @see ExecutionMode\n\t */\n\t@API(status = STABLE, since = \"1.10\", consumers = \"org.junit.platform.engine.support.hierarchical\")\n\tdefault ExecutionMode getExecutionMode() {\n\t\treturn ExecutionMode.CONCURRENT;\n\t}\n\n\t/**\n\t * The result of determining whether the execution of a given {@code context}\n\t * should be <em>skipped</em>.\n\t *\n\t * @see Node#shouldBeSkipped(EngineExecutionContext)\n\t */\n\tclass SkipResult {\n\n\t\tprivate static final SkipResult alwaysExecuteSkipResult = new SkipResult(false, null);\n\n\t\tprivate final boolean skipped;\n\t\tprivate final Optional<String> reason;\n\n\t\t/**\n\t\t * Factory for creating <em>skipped</em> results.\n\t\t *\n\t\t * <p>A context that is skipped will be not be executed.\n\t\t *\n\t\t * @param reason the reason that the context should be skipped,\n\t\t * may be {@code null}\n\t\t * @return a skipped {@code SkipResult} with the given reason\n\t\t */\n\t\tpublic static SkipResult skip(@Nullable String reason) {\n\t\t\treturn new SkipResult(true, reason);\n\t\t}\n\n\t\t/**\n\t\t * Factory for creating <em>do not skip</em> results.\n\t\t *\n\t\t * <p>A context that is not skipped will be executed as normal.\n\t\t *\n\t\t * @return a <em>do not skip</em> {@code SkipResult}\n\t\t */\n\t\tpublic static SkipResult doNotSkip() {\n\t\t\treturn alwaysExecuteSkipResult;\n\t\t}\n\n\t\tprivate SkipResult(boolean skipped, @Nullable String reason) {\n\t\t\tthis.skipped = skipped;\n\t\t\tthis.reason = Optional.ofNullable(reason);\n\t\t}\n\n\t\t/**\n\t\t * Whether execution of the context should be skipped.\n\t\t *\n\t\t * @return {@code true} if the execution should be skipped\n\t\t */\n\t\tpublic boolean isSkipped() {\n\t\t\treturn this.skipped;\n\t\t}\n\n\t\t/**\n\t\t * Get the reason that execution of the context should be skipped,\n\t\t * if available.\n\t\t */\n\t\tpublic Optional<String> getReason() {\n\t\t\treturn this.reason;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\t// @formatter:off\n\t\t\treturn new ToStringBuilder(this)\n\t\t\t\t\t.append(\"skipped\", this.skipped)\n\t\t\t\t\t.append(\"reason\", this.reason.orElse(\"<unknown>\"))\n\t\t\t\t\t.toString();\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t/**\n\t * Executor for additional, dynamic test descriptors discovered during\n\t * execution of a {@link Node}.\n\t *\n\t * <p>The test descriptors will be executed by the same\n\t * {@link HierarchicalTestExecutor} that executes the submitting node.\n\t *\n\t * <p>This interface is not intended to be implemented by clients.\n\t *\n\t * @see Node#execute(EngineExecutionContext, DynamicTestExecutor)\n\t * @see HierarchicalTestExecutor\n\t */\n\tinterface DynamicTestExecutor {\n\n\t\t/**\n\t\t * Submit a dynamic test descriptor for immediate execution.\n\t\t *\n\t\t * @param testDescriptor the test descriptor to be executed; never\n\t\t * {@code null}\n\t\t */\n\t\tvoid execute(TestDescriptor testDescriptor);\n\n\t\t/**\n\t\t * Submit a dynamic test descriptor for immediate execution with a\n\t\t * custom, potentially no-op, execution listener.\n\t\t *\n\t\t * @param testDescriptor the test descriptor to be executed; never\n\t\t * {@code null}\n\t\t * @param executionListener the executionListener to be notified; never\n\t\t * {@code null}\n\t\t * @return a future to cancel or wait for the execution\n\t\t * @since 1.7\n\t\t * @see EngineExecutionListener#NOOP\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tFuture<?> execute(TestDescriptor testDescriptor, EngineExecutionListener executionListener);\n\n\t\t/**\n\t\t * Block until all dynamic test descriptors submitted to this executor\n\t\t * are finished.\n\t\t *\n\t\t * <p>This method is useful if the node needs to perform actions in its\n\t\t * {@link #execute(EngineExecutionContext, DynamicTestExecutor)} method\n\t\t * after all its dynamic children have finished.\n\t\t *\n\t\t * @throws InterruptedException if interrupted while waiting\n\t\t */\n\t\tvoid awaitFinished() throws InterruptedException;\n\t}\n\n\t/**\n\t * Supported execution modes for parallel execution.\n\t *\n\t * @since 1.3\n\t * @see #SAME_THREAD\n\t * @see #CONCURRENT\n\t * @see Node#getExecutionMode()\n\t */\n\t@API(status = STABLE, since = \"1.10\", consumers = \"org.junit.platform.engine.support.hierarchical\")\n\tenum ExecutionMode {\n\n\t\t/**\n\t\t * Force execution in same thread as the parent node.\n\t\t *\n\t\t * @see #CONCURRENT\n\t\t */\n\t\tSAME_THREAD,\n\n\t\t/**\n\t\t * Allow concurrent execution with any other node.\n\t\t *\n\t\t * @see #SAME_THREAD\n\t\t */\n\t\tCONCURRENT\n\t}\n\n\t/**\n\t * Represents an invocation that runs with the supplied context.\n\t *\n\t * @param <C> the type of {@code EngineExecutionContext} used by the {@code HierarchicalTestEngine}\n\t * @since 1.4\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tinterface Invocation<C extends EngineExecutionContext> {\n\n\t\t/**\n\t\t * Invoke this invocation with the supplied context.\n\t\t *\n\t\t * @param context the context to invoke in\n\t\t */\n\t\tvoid invoke(C context) throws Exception;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeExecutionAdvisor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\n\n/**\n * @since 1.3.1\n */\nclass NodeExecutionAdvisor {\n\n\tprivate final Map<TestDescriptor, ExecutionMode> forcedDescendantExecutionModeByTestDescriptor = new HashMap<>();\n\tprivate final Map<TestDescriptor, ResourceLock> resourceLocksByTestDescriptor = new HashMap<>();\n\n\tvoid forceDescendantExecutionMode(TestDescriptor testDescriptor, ExecutionMode executionMode) {\n\t\tforcedDescendantExecutionModeByTestDescriptor.put(testDescriptor, executionMode);\n\t}\n\n\tvoid useResourceLock(TestDescriptor testDescriptor, ResourceLock resourceLock) {\n\t\tresourceLocksByTestDescriptor.put(testDescriptor, resourceLock);\n\t}\n\n\tvoid removeResourceLock(TestDescriptor testDescriptor) {\n\t\tresourceLocksByTestDescriptor.remove(testDescriptor);\n\t}\n\n\tOptional<ExecutionMode> getForcedExecutionMode(TestDescriptor testDescriptor) {\n\t\treturn testDescriptor.getParent().flatMap(this::lookupExecutionModeForcedByAncestor);\n\t}\n\n\tprivate Optional<ExecutionMode> lookupExecutionModeForcedByAncestor(TestDescriptor testDescriptor) {\n\t\tExecutionMode value = forcedDescendantExecutionModeByTestDescriptor.get(testDescriptor);\n\t\tif (value != null) {\n\t\t\treturn Optional.of(value);\n\t\t}\n\t\treturn testDescriptor.getParent().flatMap(this::lookupExecutionModeForcedByAncestor);\n\t}\n\n\tResourceLock getResourceLock(TestDescriptor testDescriptor) {\n\t\treturn resourceLocksByTestDescriptor.getOrDefault(testDescriptor, NopLock.INSTANCE);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTask.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.Objects.requireNonNullElse;\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static java.util.stream.Collectors.toCollection;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService.TestTask;\nimport org.junit.platform.engine.support.hierarchical.Node.DynamicTestExecutor;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\nimport org.junit.platform.engine.support.hierarchical.Node.SkipResult;\n\n/**\n * @since 1.3\n */\nclass NodeTestTask<C extends EngineExecutionContext> implements TestTask {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(NodeTestTask.class);\n\tprivate static final Runnable NOOP = () -> {\n\t};\n\n\tstatic final SkipResult CANCELLED_SKIP_RESULT = SkipResult.skip(\"Execution cancelled\");\n\n\tprivate final NodeTestTaskContext taskContext;\n\tprivate final TestDescriptor testDescriptor;\n\tprivate final Node<C> node;\n\tprivate final Runnable finalizer;\n\n\tprivate volatile @Nullable C parentContext;\n\n\tprivate @Nullable C context;\n\n\tprivate @Nullable SkipResult skipResult;\n\n\tprivate boolean started;\n\n\tprivate @Nullable ThrowableCollector throwableCollector;\n\n\tNodeTestTask(NodeTestTaskContext taskContext, TestDescriptor testDescriptor) {\n\t\tthis(taskContext, testDescriptor, NOOP);\n\t}\n\n\tNodeTestTask(NodeTestTaskContext taskContext, TestDescriptor testDescriptor, Runnable finalizer) {\n\t\tthis.taskContext = taskContext;\n\t\tthis.testDescriptor = testDescriptor;\n\t\tthis.node = NodeUtils.asNode(testDescriptor);\n\t\tthis.finalizer = finalizer;\n\t}\n\n\t@Override\n\tpublic ResourceLock getResourceLock() {\n\t\treturn taskContext.executionAdvisor().getResourceLock(testDescriptor);\n\t}\n\n\t@Override\n\tpublic ExecutionMode getExecutionMode() {\n\t\treturn taskContext.executionAdvisor().getForcedExecutionMode(testDescriptor) //\n\t\t\t\t.orElseGet(node::getExecutionMode);\n\t}\n\n\t@Override\n\tpublic TestDescriptor getTestDescriptor() {\n\t\treturn testDescriptor;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"NodeTestTask [\" + testDescriptor + \"]\";\n\t}\n\n\tvoid setParentContext(@Nullable C parentContext) {\n\t\tthis.parentContext = parentContext;\n\t}\n\n\t@Override\n\tpublic void execute() {\n\t\ttry {\n\t\t\tthrowableCollector = taskContext.throwableCollectorFactory().create();\n\t\t\tif (!taskContext.cancellationToken().isCancellationRequested()) {\n\t\t\t\tprepare();\n\t\t\t}\n\t\t\tif (throwableCollector.isEmpty()) {\n\t\t\t\tthrowableCollector.execute(() -> skipResult = checkWhetherSkipped());\n\t\t\t}\n\t\t\tif (throwableCollector.isEmpty() && !requiredSkipResult().isSkipped()) {\n\t\t\t\texecuteRecursively();\n\t\t\t}\n\t\t\tif (context != null) {\n\t\t\t\tcleanUp();\n\t\t\t}\n\t\t\treportCompletion();\n\t\t}\n\t\tfinally {\n\t\t\t// Ensure that the 'interrupted status' flag for the current thread\n\t\t\t// is cleared for reuse of the thread in subsequent task executions.\n\t\t\t// See https://github.com/junit-team/junit-framework/issues/1688\n\t\t\tif (Thread.interrupted()) {\n\t\t\t\tlogger.debug(() -> \"\"\"\n\t\t\t\t\t\tExecution of TestDescriptor with display name [%s] \\\n\t\t\t\t\t\tand unique ID [%s] failed to clear the 'interrupted status' flag for the \\\n\t\t\t\t\t\tcurrent thread. JUnit has cleared the flag, but you may wish to investigate \\\n\t\t\t\t\t\twhy the flag was not cleared by user code.\"\"\".formatted(this.testDescriptor.getDisplayName(),\n\t\t\t\t\tthis.testDescriptor.getUniqueId()));\n\t\t\t}\n\t\t\tfinalizer.run();\n\t\t}\n\n\t\t// Clear reference to context to allow it to be garbage collected.\n\t\t// See https://github.com/junit-team/junit-framework/issues/1578\n\t\tcontext = null;\n\t}\n\n\tprivate void prepare() {\n\t\trequiredThrowableCollector().execute(() -> context = node.prepare(requireNonNull(parentContext)));\n\n\t\t// Clear reference to parent context to allow it to be garbage collected.\n\t\t// See https://github.com/junit-team/junit-framework/issues/1578\n\t\tparentContext = null;\n\t}\n\n\tprivate SkipResult checkWhetherSkipped() throws Exception {\n\t\treturn taskContext.cancellationToken().isCancellationRequested() //\n\t\t\t\t? CANCELLED_SKIP_RESULT //\n\t\t\t\t: node.shouldBeSkipped(requiredContext());\n\t}\n\n\tprivate void executeRecursively() {\n\t\ttaskContext.listener().executionStarted(testDescriptor);\n\t\tstarted = true;\n\n\t\tvar throwableCollector = requiredThrowableCollector();\n\n\t\tthrowableCollector.execute(() -> {\n\t\t\tnode.around(requiredContext(), ctx -> {\n\t\t\t\tcontext = ctx;\n\t\t\t\tthrowableCollector.execute(() -> {\n\t\t\t\t\t// @formatter:off\n\t\t\t\t\tList<NodeTestTask<C>> children = testDescriptor.getChildren().stream()\n\t\t\t\t\t\t\t.map(descriptor -> new NodeTestTask<C>(taskContext, descriptor))\n\t\t\t\t\t\t\t.collect(toCollection(ArrayList::new));\n\t\t\t\t\t// @formatter:on\n\n\t\t\t\t\tcontext = node.before(requiredContext());\n\n\t\t\t\t\tfinal DynamicTestExecutor dynamicTestExecutor = new DefaultDynamicTestExecutor();\n\t\t\t\t\tcontext = node.execute(requiredContext(), dynamicTestExecutor);\n\n\t\t\t\t\tif (!children.isEmpty()) {\n\t\t\t\t\t\tchildren.forEach(child -> child.setParentContext(context));\n\t\t\t\t\t\ttaskContext.executorService().invokeAll(children);\n\t\t\t\t\t}\n\n\t\t\t\t\tthrowableCollector.execute(dynamicTestExecutor::awaitFinished);\n\t\t\t\t});\n\n\t\t\t\tthrowableCollector.execute(() -> node.after(requiredContext()));\n\t\t\t});\n\t\t});\n\t}\n\n\tprivate void cleanUp() {\n\t\trequiredThrowableCollector().execute(() -> node.cleanUp(requiredContext()));\n\t}\n\n\tprivate void reportCompletion() {\n\n\t\tvar throwableCollector = requiredThrowableCollector();\n\n\t\tif (throwableCollector.isEmpty() && requiredSkipResult().isSkipped()) {\n\t\t\tvar skipResult = requiredSkipResult();\n\t\t\ttry {\n\t\t\t\tnode.nodeSkipped(requireNonNullElse(context, parentContext), testDescriptor, skipResult);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\t\tlogger.debug(throwable,\n\t\t\t\t\t() -> \"Failed to invoke nodeSkipped() on Node %s\".formatted(testDescriptor.getUniqueId()));\n\t\t\t}\n\t\t\ttaskContext.listener().executionSkipped(testDescriptor, skipResult.getReason().orElse(\"<unknown>\"));\n\t\t\treturn;\n\t\t}\n\t\tif (!started) {\n\t\t\t// Call executionStarted first to comply with the contract of EngineExecutionListener.\n\t\t\ttaskContext.listener().executionStarted(testDescriptor);\n\t\t}\n\t\ttry {\n\t\t\tnode.nodeFinished(requiredContext(), testDescriptor, throwableCollector.toTestExecutionResult());\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\tlogger.debug(throwable,\n\t\t\t\t() -> \"Failed to invoke nodeFinished() on Node %s\".formatted(testDescriptor.getUniqueId()));\n\t\t}\n\t\ttaskContext.listener().executionFinished(testDescriptor, throwableCollector.toTestExecutionResult());\n\t\tthis.throwableCollector = null;\n\t}\n\n\tprivate C requiredContext() {\n\t\treturn requireNonNull(context);\n\t}\n\n\tprivate SkipResult requiredSkipResult() {\n\t\treturn requireNonNull(skipResult);\n\t}\n\n\tprivate ThrowableCollector requiredThrowableCollector() {\n\t\treturn requireNonNull(throwableCollector);\n\t}\n\n\tprivate class DefaultDynamicTestExecutor implements DynamicTestExecutor {\n\t\tprivate final Map<UniqueId, DynamicTaskState> unfinishedTasks = new ConcurrentHashMap<>();\n\n\t\t@Override\n\t\t@SuppressWarnings(\"FutureReturnValueIgnored\")\n\t\tpublic void execute(TestDescriptor testDescriptor) {\n\t\t\texecute(testDescriptor, taskContext.listener());\n\t\t}\n\n\t\t@Override\n\t\tpublic Future<?> execute(TestDescriptor testDescriptor, EngineExecutionListener executionListener) {\n\t\t\tPreconditions.notNull(testDescriptor, \"testDescriptor must not be null\");\n\t\t\tPreconditions.notNull(executionListener, \"executionListener must not be null\");\n\n\t\t\texecutionListener.dynamicTestRegistered(testDescriptor);\n\t\t\tSet<ExclusiveResource> exclusiveResources = NodeUtils.asNode(testDescriptor).getExclusiveResources();\n\t\t\tif (!exclusiveResources.isEmpty()) {\n\t\t\t\texecutionListener.executionStarted(testDescriptor);\n\t\t\t\tString message = \"Dynamic test descriptors must not declare exclusive resources: \" + exclusiveResources;\n\t\t\t\texecutionListener.executionFinished(testDescriptor, failed(new JUnitException(message)));\n\t\t\t\treturn completedFuture(null);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tUniqueId uniqueId = testDescriptor.getUniqueId();\n\t\t\t\tNodeTestTask<C> nodeTestTask = new NodeTestTask<>(taskContext.withListener(executionListener),\n\t\t\t\t\ttestDescriptor, () -> unfinishedTasks.remove(uniqueId));\n\t\t\t\tnodeTestTask.setParentContext(context);\n\t\t\t\tunfinishedTasks.put(uniqueId, DynamicTaskState.unscheduled());\n\t\t\t\tvar future = taskContext.executorService().submit(nodeTestTask);\n\t\t\t\tunfinishedTasks.computeIfPresent(uniqueId, (__, state) -> DynamicTaskState.scheduled(future));\n\t\t\t\treturn future;\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void awaitFinished() throws InterruptedException {\n\t\t\tfor (DynamicTaskState state : unfinishedTasks.values()) {\n\t\t\t\ttry {\n\t\t\t\t\tstate.awaitFinished();\n\t\t\t\t}\n\t\t\t\tcatch (CancellationException ignore) {\n\t\t\t\t\t// Futures returned by execute() may have been cancelled\n\t\t\t\t}\n\t\t\t\tcatch (ExecutionException e) {\n\t\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(requireNonNullElse(e.getCause(), e));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@FunctionalInterface\n\tprivate interface DynamicTaskState {\n\n\t\tDynamicTaskState UNSCHEDULED = () -> {\n\t\t};\n\n\t\tstatic DynamicTaskState unscheduled() {\n\t\t\treturn UNSCHEDULED;\n\t\t}\n\n\t\tstatic DynamicTaskState scheduled(Future<@Nullable Void> future) {\n\t\t\treturn future::get;\n\t\t}\n\n\t\tvoid awaitFinished() throws CancellationException, ExecutionException, InterruptedException;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTestTaskContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\n\n/**\n * @since 1.3.1\n */\nrecord NodeTestTaskContext(EngineExecutionListener listener, HierarchicalTestExecutorService executorService,\n\t\tThrowableCollector.Factory throwableCollectorFactory, NodeExecutionAdvisor executionAdvisor,\n\t\tCancellationToken cancellationToken) {\n\n\tNodeTestTaskContext withListener(EngineExecutionListener listener) {\n\t\tif (this.listener == listener) {\n\t\t\treturn this;\n\t\t}\n\t\treturn new NodeTestTaskContext(listener, executorService, throwableCollectorFactory, executionAdvisor,\n\t\t\tcancellationToken);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD;\nimport static org.junit.platform.engine.support.hierarchical.NodeUtils.asNode;\n\nimport java.util.HashSet;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * @since 1.3\n */\nclass NodeTreeWalker {\n\n\tprivate final LockManager lockManager;\n\tprivate final ResourceLock globalReadLock;\n\tprivate final ResourceLock globalReadWriteLock;\n\n\tNodeTreeWalker() {\n\t\tthis(new LockManager());\n\t}\n\n\tNodeTreeWalker(LockManager lockManager) {\n\t\tthis.lockManager = lockManager;\n\t\tthis.globalReadLock = lockManager.getLockForResource(GLOBAL_READ);\n\t\tthis.globalReadWriteLock = lockManager.getLockForResource(GLOBAL_READ_WRITE);\n\t}\n\n\tNodeExecutionAdvisor walk(TestDescriptor rootDescriptor) {\n\t\tPreconditions.condition(getExclusiveResources(rootDescriptor).isEmpty(),\n\t\t\t\"Engine descriptor must not declare exclusive resources\");\n\t\tNodeExecutionAdvisor advisor = new NodeExecutionAdvisor();\n\t\trootDescriptor.getChildren().forEach(child -> walk(nullUnlessRequiresGlobalReadLock(child), child, advisor));\n\t\treturn advisor;\n\t}\n\n\tprivate void walk(@Nullable TestDescriptor globalLockDescriptor, TestDescriptor testDescriptor,\n\t\t\tNodeExecutionAdvisor advisor) {\n\n\t\tif (globalLockDescriptor != null && advisor.getResourceLock(globalLockDescriptor) == globalReadWriteLock) {\n\t\t\t// Global read-write lock is already being enforced, so no additional locks are needed\n\t\t\treturn;\n\t\t}\n\n\t\tSet<ExclusiveResource> exclusiveResources = getExclusiveResources(testDescriptor);\n\t\tif (exclusiveResources.isEmpty()) {\n\t\t\tif (globalLockDescriptor != null && globalLockDescriptor.equals(testDescriptor)) {\n\t\t\t\tadvisor.useResourceLock(globalLockDescriptor, globalReadLock);\n\t\t\t}\n\t\t\ttestDescriptor.getChildren().forEach(child -> {\n\t\t\t\tvar newGlobalLockDescriptor = globalLockDescriptor == null //\n\t\t\t\t\t\t? nullUnlessRequiresGlobalReadLock(child) //\n\t\t\t\t\t\t: globalLockDescriptor;\n\t\t\t\twalk(newGlobalLockDescriptor, child, advisor);\n\t\t\t});\n\t\t}\n\t\telse {\n\t\t\tPreconditions.notNull(globalLockDescriptor,\n\t\t\t\t() -> \"Node requiring exclusive resources must also require global read lock: \" + testDescriptor);\n\n\t\t\tSet<ExclusiveResource> allResources = new HashSet<>(exclusiveResources);\n\t\t\tif (isReadOnly(allResources)) {\n\t\t\t\tdoForChildrenRecursively(testDescriptor, child -> allResources.addAll(getExclusiveResources(child)));\n\t\t\t\tif (!isReadOnly(allResources)) {\n\t\t\t\t\tforceDescendantExecutionModeRecursively(advisor, testDescriptor);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tadvisor.forceDescendantExecutionMode(testDescriptor, SAME_THREAD);\n\t\t\t\tdoForChildrenRecursively(testDescriptor, child -> {\n\t\t\t\t\tallResources.addAll(getExclusiveResources(child));\n\t\t\t\t\tadvisor.forceDescendantExecutionMode(child, SAME_THREAD);\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (allResources.contains(GLOBAL_READ_WRITE)) {\n\t\t\t\tadvisor.forceDescendantExecutionMode(globalLockDescriptor, SAME_THREAD);\n\t\t\t\tdoForChildrenRecursively(globalLockDescriptor, child -> {\n\t\t\t\t\tadvisor.forceDescendantExecutionMode(child, SAME_THREAD);\n\t\t\t\t\t// Remove any locks that may have been set for siblings or their descendants\n\t\t\t\t\tadvisor.removeResourceLock(child);\n\t\t\t\t});\n\t\t\t\tadvisor.useResourceLock(globalLockDescriptor, globalReadWriteLock);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (globalLockDescriptor.equals(testDescriptor)) {\n\t\t\t\t\tallResources.add(GLOBAL_READ);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tallResources.remove(GLOBAL_READ);\n\t\t\t\t}\n\t\t\t\tadvisor.useResourceLock(testDescriptor, lockManager.getLockForResources(allResources));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void forceDescendantExecutionModeRecursively(NodeExecutionAdvisor advisor, TestDescriptor testDescriptor) {\n\t\tadvisor.forceDescendantExecutionMode(testDescriptor, SAME_THREAD);\n\t\tdoForChildrenRecursively(testDescriptor, child -> advisor.forceDescendantExecutionMode(child, SAME_THREAD));\n\t}\n\n\tprivate boolean isReadOnly(Set<ExclusiveResource> exclusiveResources) {\n\t\treturn exclusiveResources.stream().allMatch(it -> it.getLockMode() == ExclusiveResource.LockMode.READ);\n\t}\n\n\tprivate Set<ExclusiveResource> getExclusiveResources(TestDescriptor testDescriptor) {\n\t\treturn asNode(testDescriptor).getExclusiveResources();\n\t}\n\n\tprivate static @Nullable TestDescriptor nullUnlessRequiresGlobalReadLock(TestDescriptor testDescriptor) {\n\t\treturn asNode(testDescriptor).isGlobalReadLockRequired() ? testDescriptor : null;\n\t}\n\n\tprivate void doForChildrenRecursively(TestDescriptor parent, Consumer<TestDescriptor> consumer) {\n\t\tparent.getChildren().forEach(child -> {\n\t\t\tconsumer.accept(child);\n\t\t\tdoForChildrenRecursively(child, consumer);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NodeUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * @since 1.3.1\n */\nfinal class NodeUtils {\n\n\tprivate NodeUtils() {\n\t\t/* no-op */\n\t}\n\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n\tstatic <C extends EngineExecutionContext> Node<C> asNode(TestDescriptor testDescriptor) {\n\t\treturn (testDescriptor instanceof Node node ? node : noOpNode);\n\t}\n\n\t@SuppressWarnings(\"rawtypes\")\n\tprivate static final Node noOpNode = new Node() {\n\t};\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/NopLock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Collections.emptyList;\n\nimport java.util.List;\n\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * No-op {@link ResourceLock} implementation.\n *\n * @since 1.3\n */\nclass NopLock implements ResourceLock {\n\n\tstatic final ResourceLock INSTANCE = new NopLock();\n\n\tprivate NopLock() {\n\t}\n\n\t@Override\n\tpublic List<ExclusiveResource> getResources() {\n\t\treturn emptyList();\n\t}\n\n\t@Override\n\tpublic boolean tryAcquire() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic ResourceLock acquire() {\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\t// nothing to do\n\t}\n\n\t@Override\n\tpublic boolean isExclusive() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this).toString();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/OpenTest4JAwareThrowableCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Specialization of {@link ThrowableCollector} that treats instances of\n * {@link TestAbortedException} as <em>aborting</em>.\n *\n * @since 1.3\n * @see ThrowableCollector\n */\n@API(status = MAINTAINED, since = \"1.3\")\npublic class OpenTest4JAwareThrowableCollector extends ThrowableCollector {\n\n\tpublic OpenTest4JAwareThrowableCollector() {\n\t\tsuper(TestAbortedException.class::isInstance);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfiguration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Configuration to use for parallel test execution.\n *\n * <p>Instances of this class are intended to be used to configure\n * implementations of {@link HierarchicalTestExecutorService}. Such\n * implementations may use all of the properties in this class or\n * only a subset.\n *\n * @since 1.3\n * @see ForkJoinPoolHierarchicalTestExecutorService\n * @see ParallelExecutionConfigurationStrategy\n * @see DefaultParallelExecutionConfigurationStrategy\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface ParallelExecutionConfiguration {\n\n\t/**\n\t * Get the parallelism to be used.\n\t *\n\t * @see ForkJoinPool#getParallelism()\n\t */\n\tint getParallelism();\n\n\t/**\n\t * Get the minimum number of runnable threads to be used.\n\t */\n\tint getMinimumRunnable();\n\n\t/**\n\t * Get the maximum thread pool size to be used.\n\t */\n\tint getMaxPoolSize();\n\n\t/**\n\t * Get the core thread pool size to be used.\n\t */\n\tint getCorePoolSize();\n\n\t/**\n\t * Get the number of seconds for which inactive threads should be kept alive\n\t * before terminating them and shrinking the thread pool.\n\t */\n\tint getKeepAliveSeconds();\n\n\t/**\n\t * Get the saturate predicate to be used for the execution's {@link ForkJoinPool}.\n\t * @return the saturate predicate to be passed to the {@code ForkJoinPool} constructor; may be {@code null}\n\t * @since 1.9\n\t * @see ForkJoinPool#ForkJoinPool(int, ForkJoinPool.ForkJoinWorkerThreadFactory, Thread.UncaughtExceptionHandler,\n\t * boolean, int, int, int, Predicate, long, java.util.concurrent.TimeUnit)\n\t */\n\t@API(status = STABLE, since = \"1.11\")\n\tdefault @Nullable Predicate<? super ForkJoinPool> getSaturatePredicate() {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionConfigurationStrategy.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * A strategy to use for configuring parallel test execution.\n *\n * @since 1.3\n * @see DefaultParallelExecutionConfigurationStrategy\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface ParallelExecutionConfigurationStrategy {\n\n\t/**\n\t * Create a configuration for parallel test execution based on the supplied\n\t * {@link ConfigurationParameters}.\n\t */\n\tParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters);\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ParallelHierarchicalTestExecutorServiceFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Locale;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.support.config.PrefixedConfigurationParameters;\nimport org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.TaskEventListener;\n\n/**\n * Factory for {@link HierarchicalTestExecutorService} instances that support\n * parallel execution.\n *\n * @since 6.1\n * @see ParallelExecutorServiceType\n * @see ForkJoinPoolHierarchicalTestExecutorService\n * @see WorkerThreadPoolHierarchicalTestExecutorService\n */\n@API(status = MAINTAINED, since = \"6.1\")\npublic final class ParallelHierarchicalTestExecutorServiceFactory {\n\n\t/**\n\t * Property name used to determine the desired\n\t * {@link ParallelExecutorServiceType ParallelExecutorServiceType}.\n\t *\n\t * <p>Value must be\n\t * {@link ParallelExecutorServiceType#FORK_JOIN_POOL FORK_JOIN_POOL} or\n\t * {@link ParallelExecutorServiceType#WORKER_THREAD_POOL WORKER_THREAD_POOL},\n\t * ignoring case.\n\t */\n\tpublic static final String EXECUTOR_SERVICE_PROPERTY_NAME = \"executor-service\";\n\n\t/**\n\t * Create a new {@link HierarchicalTestExecutorService} based on the\n\t * supplied {@link ConfigurationParameters}.\n\t *\n\t * <p>This method is typically invoked with an instance of\n\t * {@link PrefixedConfigurationParameters} that was created with an\n\t * engine-specific prefix.\n\t *\n\t * <p>The {@value #EXECUTOR_SERVICE_PROPERTY_NAME} key is used to determine\n\t * which service implementation is to be used. Which other parameters are\n\t * read depends on the configured\n\t * {@link ParallelExecutionConfigurationStrategy} which is determined by the\n\t * {@value DefaultParallelExecutionConfigurationStrategy#CONFIG_STRATEGY_PROPERTY_NAME}\n\t * key.\n\t *\n\t * @see #EXECUTOR_SERVICE_PROPERTY_NAME\n\t * @see ParallelExecutorServiceType\n\t * @see ParallelExecutionConfigurationStrategy\n\t * @see PrefixedConfigurationParameters\n\t */\n\tpublic static HierarchicalTestExecutorService create(ConfigurationParameters configurationParameters) {\n\t\tvar type = configurationParameters.get(EXECUTOR_SERVICE_PROPERTY_NAME, ParallelExecutorServiceType::parse) //\n\t\t\t\t.orElse(ParallelExecutorServiceType.FORK_JOIN_POOL);\n\t\tvar configuration = DefaultParallelExecutionConfigurationStrategy.toConfiguration(configurationParameters);\n\t\treturn create(type, configuration);\n\t}\n\n\t/**\n\t * Create a new {@link HierarchicalTestExecutorService} based on the\n\t * supplied {@link ConfigurationParameters}.\n\t *\n\t * <p>The {@value #EXECUTOR_SERVICE_PROPERTY_NAME} key is ignored in favor\n\t * of the supplied {@link ParallelExecutorServiceType} parameter when\n\t * invoking this method.\n\t *\n\t * @see ParallelExecutorServiceType\n\t * @see ParallelExecutionConfigurationStrategy\n\t */\n\tpublic static HierarchicalTestExecutorService create(ParallelExecutorServiceType executorServiceType,\n\t\t\tParallelExecutionConfiguration configuration) {\n\t\treturn switch (executorServiceType) {\n\t\t\tcase FORK_JOIN_POOL -> new ForkJoinPoolHierarchicalTestExecutorService(configuration,\n\t\t\t\tTaskEventListener.NOOP);\n\t\t\tcase WORKER_THREAD_POOL -> new WorkerThreadPoolHierarchicalTestExecutorService(configuration);\n\t\t};\n\t}\n\n\tprivate ParallelHierarchicalTestExecutorServiceFactory() {\n\t}\n\n\t/**\n\t * Type of {@link HierarchicalTestExecutorService} that supports parallel\n\t * execution.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = MAINTAINED, since = \"6.1\")\n\tpublic enum ParallelExecutorServiceType {\n\n\t\t/**\n\t\t * Indicates that {@link ForkJoinPoolHierarchicalTestExecutorService}\n\t\t * should be used.\n\t\t */\n\t\tFORK_JOIN_POOL,\n\n\t\t/**\n\t\t * Indicates that {@link WorkerThreadPoolHierarchicalTestExecutorService}\n\t\t * should be used.\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\t\tWORKER_THREAD_POOL;\n\n\t\tprivate static ParallelExecutorServiceType parse(String value) {\n\t\t\treturn valueOf(value.toUpperCase(Locale.ROOT));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ResourceLock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\n\n/**\n * A lock for a one or more resources.\n *\n * @since 1.3\n * @see HierarchicalTestExecutorService.TestTask#getResourceLock()\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface ResourceLock extends AutoCloseable {\n\n\t/**\n\t * Try to acquire this resource lock, without blocking.\n\t *\n\t * @return {@code true} if the lock was acquired and {@code false} otherwise\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tdefault boolean tryAcquire() {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Acquire this resource lock, potentially blocking.\n\t *\n\t * @return this lock so it can easily be used in a try-with-resources\n\t * statement.\n\t * @throws InterruptedException if the calling thread is interrupted\n\t * while waiting to acquire this lock\n\t */\n\tResourceLock acquire() throws InterruptedException;\n\n\t/**\n\t * Release this resource lock.\n\t */\n\tvoid release();\n\n\t@Override\n\tdefault void close() {\n\t\trelease();\n\t}\n\n\t/**\n\t * {@return the exclusive resources this lock represents}\n\t */\n\tList<ExclusiveResource> getResources();\n\n\t/**\n\t * {@return whether this lock requires exclusiveness}\n\t */\n\tboolean isExclusive();\n\n\t/**\n\t * {@return whether the given lock is compatible with this lock}\n\t * @param other the other lock to check for compatibility\n\t */\n\tdefault boolean isCompatible(ResourceLock other) {\n\n\t\tList<ExclusiveResource> ownResources = this.getResources();\n\t\tList<ExclusiveResource> otherResources = other.getResources();\n\n\t\tif (ownResources.isEmpty() || otherResources.isEmpty()) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Whenever there's a READ_WRITE lock, it's incompatible with any other lock\n\t\t// because we guarantee that all children will have exclusive access to the\n\t\t// resource in question. In practice, whenever a READ_WRITE lock is present,\n\t\t// NodeTreeWalker will force all children to run in the same thread so that\n\t\t// it should never attempt to steal work from another thread, and we shouldn't\n\t\t// actually reach this point.\n\t\t// The global read lock (which is always on direct children of the engine node)\n\t\t// needs special treatment so that it is compatible with the first write lock\n\t\t// (which may be on a test method).\n\t\tboolean isGlobalReadLock = ownResources.size() == 1\n\t\t\t\t&& ExclusiveResource.GLOBAL_READ.equals(ownResources.get(0));\n\t\tif ((!isGlobalReadLock && other.isExclusive()) || this.isExclusive()) {\n\t\t\treturn false;\n\t\t}\n\n\t\tOptional<ExclusiveResource> potentiallyDeadlockCausingAdditionalResource = otherResources.stream() //\n\t\t\t\t.filter(resource -> !ownResources.contains(resource)) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.filter(resource -> ExclusiveResource.COMPARATOR.compare(resource,\n\t\t\t\t\townResources.get(ownResources.size() - 1)) < 0);\n\n\t\treturn potentiallyDeadlockCausingAdditionalResource.isEmpty();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SameThreadHierarchicalTestExecutorService.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.concurrent.Future;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * A simple {@linkplain HierarchicalTestExecutorService executor service} that\n * executes all {@linkplain TestTask test tasks} in the caller's thread.\n *\n * @since 1.3\n */\n@API(status = STABLE, since = \"1.10\")\npublic class SameThreadHierarchicalTestExecutorService implements HierarchicalTestExecutorService {\n\n\tpublic SameThreadHierarchicalTestExecutorService() {\n\t}\n\n\t@Override\n\tpublic Future<@Nullable Void> submit(TestTask testTask) {\n\t\ttestTask.execute();\n\t\treturn completedFuture(null);\n\t}\n\n\t@Override\n\tpublic void invokeAll(List<? extends TestTask> tasks) {\n\t\ttasks.forEach(TestTask::execute);\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\t// nothing to do\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/SingleLock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.util.List;\nimport java.util.concurrent.ForkJoinPool;\nimport java.util.concurrent.locks.Lock;\n\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * @since 1.3\n */\nclass SingleLock implements ResourceLock {\n\n\tprivate final List<ExclusiveResource> resources;\n\tprivate final Lock lock;\n\n\tSingleLock(ExclusiveResource resource, Lock lock) {\n\t\tthis.resources = List.of(resource);\n\t\tthis.lock = lock;\n\t}\n\n\t@Override\n\tpublic List<ExclusiveResource> getResources() {\n\t\treturn resources;\n\t}\n\n\t// for tests only\n\tLock getLock() {\n\t\treturn this.lock;\n\t}\n\n\t@Override\n\tpublic boolean tryAcquire() {\n\t\treturn this.lock.tryLock();\n\t}\n\n\t@Override\n\tpublic ResourceLock acquire() throws InterruptedException {\n\t\tForkJoinPool.managedBlock(new SingleLockManagedBlocker());\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic void release() {\n\t\tthis.lock.unlock();\n\t}\n\n\t@Override\n\tpublic boolean isExclusive() {\n\t\treturn resources.get(0).getLockMode() == ExclusiveResource.LockMode.READ_WRITE;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"resource\", getOnlyElement(resources)) //\n\t\t\t\t.toString();\n\t}\n\n\tprivate class SingleLockManagedBlocker implements ForkJoinPool.ManagedBlocker {\n\n\t\tprivate volatile boolean acquired;\n\n\t\t@Override\n\t\tpublic boolean block() throws InterruptedException {\n\t\t\tif (!this.acquired) {\n\t\t\t\tSingleLock.this.lock.lockInterruptibly();\n\t\t\t\tthis.acquired = true;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isReleasable() {\n\t\t\treturn this.acquired || (this.acquired = SingleLock.this.lock.tryLock());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/ThrowableCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.engine.TestExecutionResult.aborted;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\n\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * Simple component that can be used to collect one or more instances of\n * {@link Throwable}.\n *\n * <p>This class distinguishes between {@code Throwables} that <em>abort</em>\n * and those that <em>fail</em> test execution. The latter take precedence over\n * the former, i.e. if both types of {@code Throwables} were collected, the ones\n * that abort execution are reported as\n * {@linkplain Throwable#addSuppressed(Throwable) suppressed} {@code Throwables}\n * of the first {@code Throwable} that failed execution.\n *\n * @since 1.3\n * @see OpenTest4JAwareThrowableCollector\n */\n@API(status = MAINTAINED, since = \"1.3\")\npublic class ThrowableCollector {\n\n\tprivate final Predicate<? super Throwable> abortedExecutionPredicate;\n\n\tprivate @Nullable Throwable throwable;\n\n\t/**\n\t * Create a new {@code ThrowableCollector} that uses the supplied\n\t * {@link Predicate} to determine whether a {@link Throwable}\n\t * <em>aborted</em> or <em>failed</em> execution.\n\t *\n\t * @param abortedExecutionPredicate the predicate used to decide whether a\n\t * {@code Throwable} aborted execution; never {@code null}.\n\t */\n\tpublic ThrowableCollector(Predicate<? super Throwable> abortedExecutionPredicate) {\n\t\tthis.abortedExecutionPredicate = Preconditions.notNull(abortedExecutionPredicate,\n\t\t\t\"abortedExecutionPredicate must not be null\");\n\t}\n\n\t/**\n\t * Execute the supplied {@link Executable} and collect any {@link Throwable}\n\t * thrown during the execution.\n\t *\n\t * <p>If the {@code Executable} throws an <em>unrecoverable</em> exception\n\t * &mdash; for example, an {@link OutOfMemoryError} &mdash; this method will\n\t * rethrow it.\n\t *\n\t * @param executable the {@code Executable} to execute\n\t * @see #assertEmpty()\n\t */\n\tpublic void execute(Executable executable) {\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tadd(t);\n\t\t}\n\t}\n\n\t/**\n\t * Add the supplied {@link Throwable} to this {@code ThrowableCollector}.\n\t *\n\t * @param t the {@code Throwable} to add\n\t * @see #execute(Executable)\n\t * @see #assertEmpty()\n\t */\n\tprivate void add(Throwable t) {\n\t\tPreconditions.notNull(t, \"Throwable must not be null\");\n\n\t\tif (this.throwable == null) {\n\t\t\tthis.throwable = t;\n\t\t}\n\t\telse if (hasAbortedExecution(this.throwable) && !hasAbortedExecution(t)) {\n\t\t\tt.addSuppressed(this.throwable);\n\t\t\tthis.throwable = t;\n\t\t}\n\t\telse if (throwable != t) {\n\t\t\t// Jupiter does not throw the same Throwable from Node.after() anymore but other engines might\n\t\t\tthis.throwable.addSuppressed(t);\n\t\t}\n\t}\n\n\t/**\n\t * Get the first {@link Throwable} collected by this\n\t * {@code ThrowableCollector}.\n\t *\n\t * <p>If this collector is not empty, the first collected {@code Throwable}\n\t * will be returned with any additional {@code Throwables}\n\t * {@linkplain Throwable#addSuppressed(Throwable) suppressed} in the\n\t * first {@code Throwable}.\n\t *\n\t * <p>If the first collected {@code Throwable} <em>aborted</em> execution\n\t * and at least one later collected {@code Throwable} <em>failed</em>\n\t * execution, the first <em>failing</em> {@code Throwable} will be returned\n\t * with the previous <em>aborting</em> and any additional {@code Throwables}\n\t * {@linkplain Throwable#addSuppressed(Throwable) suppressed} inside.\n\t *\n\t * @return the first collected {@code Throwable} or {@code null} if this\n\t * {@code ThrowableCollector} is empty\n\t * @see #isEmpty()\n\t * @see #assertEmpty()\n\t */\n\tpublic @Nullable Throwable getThrowable() {\n\t\treturn this.throwable;\n\t}\n\n\t/**\n\t * Determine if this {@code ThrowableCollector} is <em>empty</em> (i.e.,\n\t * has not collected any {@code Throwables}).\n\t */\n\tpublic boolean isEmpty() {\n\t\treturn (this.throwable == null);\n\t}\n\n\t/**\n\t * Determine if this {@code ThrowableCollector} is <em>not empty</em> (i.e.,\n\t * has collected at least one {@code Throwable}).\n\t */\n\tpublic boolean isNotEmpty() {\n\t\treturn (this.throwable != null);\n\t}\n\n\t/**\n\t * Assert that this {@code ThrowableCollector} is <em>empty</em> (i.e.,\n\t * has not collected any {@code Throwables}).\n\t *\n\t * <p>If this collector is not empty, the first collected {@code Throwable}\n\t * will be thrown with any additional {@code Throwables}\n\t * {@linkplain Throwable#addSuppressed(Throwable) suppressed} in the\n\t * first {@code Throwable}. Note, however, that the {@code Throwable}\n\t * will not be wrapped. Rather, it will be\n\t * {@linkplain ExceptionUtils#throwAsUncheckedException masked}\n\t * as an unchecked exception.\n\t *\n\t * @see #getThrowable()\n\t * @see ExceptionUtils#throwAsUncheckedException(Throwable)\n\t */\n\tpublic void assertEmpty() {\n\t\tif (this.throwable != null) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(this.throwable);\n\t\t}\n\t}\n\n\t/**\n\t * Convert the collected {@link Throwable Throwables} into a {@link TestExecutionResult}.\n\t *\n\t * @return {@linkplain TestExecutionResult#aborted aborted} if the collected\n\t * {@code Throwable} <em>aborted</em> execution;\n\t * {@linkplain TestExecutionResult#failed failed} if it <em>failed</em>\n\t * execution; and {@linkplain TestExecutionResult#successful successful}\n\t * otherwise\n\t * @since 1.6\n\t */\n\t@API(status = MAINTAINED, since = \"1.6\")\n\tpublic TestExecutionResult toTestExecutionResult() {\n\t\tif (this.throwable == null) {\n\t\t\treturn successful();\n\t\t}\n\t\tif (hasAbortedExecution(this.throwable)) {\n\t\t\treturn aborted(this.throwable);\n\t\t}\n\t\treturn failed(this.throwable);\n\t}\n\n\tprivate boolean hasAbortedExecution(Throwable t) {\n\t\treturn this.abortedExecutionPredicate.test(t);\n\t}\n\n\t/**\n\t * Functional interface for an executable block of code that may throw a\n\t * {@link Throwable}.\n\t */\n\t@FunctionalInterface\n\tpublic interface Executable {\n\n\t\t/**\n\t\t * Execute this executable, potentially throwing a {@link Throwable}\n\t\t * that signals abortion or failure.\n\t\t */\n\t\tvoid execute() throws Throwable;\n\n\t}\n\n\t/**\n\t * Factory for {@code ThrowableCollector} instances.\n\t */\n\tpublic interface Factory {\n\n\t\t/**\n\t\t * Create a new instance of a {@code ThrowableCollector}.\n\t\t */\n\t\tThrowableCollector create();\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/WorkerThreadPoolHierarchicalTestExecutorService.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.concurrent.CompletableFuture.completedFuture;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Deque;\nimport java.util.EnumMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentSkipListSet;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.Semaphore;\nimport java.util.concurrent.SynchronousQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.BooleanSupplier;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\n\n/**\n * An {@linkplain HierarchicalTestExecutorService executor service} based on a\n * regular thread pool that executes {@linkplain TestTask test tasks} with the\n * configured parallelism.\n *\n * @since 6.1\n * @see ParallelHierarchicalTestExecutorServiceFactory\n * @see ParallelExecutorServiceType#WORKER_THREAD_POOL\n * @see DefaultParallelExecutionConfigurationStrategy\n */\n@API(status = EXPERIMENTAL, since = \"6.1\")\npublic final class WorkerThreadPoolHierarchicalTestExecutorService implements HierarchicalTestExecutorService {\n\n\t/*\n\t\tThis implementation is based on a regular thread pool and a work queue shared among all worker threads.\n\n\t\tEach worker thread scans the shared work queue for tasks to run. Since the tasks represent hierarchically\n\t\tstructured tests, container tasks will call `submit(TestTask)` or `invokeAll(List<TestTask>)` for their\n\t\tchildren, recursively.\n\n\t\tTo maintain the desired parallelism -- regardless whether the user code performs any blocking operations --\n\t\ta fixed number of worker leases is configured. Whenever a task is submitted to the work queue to be executed\n\t\tconcurrently, an attempt is made to acquire a worker lease. If a worker lease was acquired, a worker thread is\n\t\tstarted. Each worker thread attempts to \"steal\" queue entries for its children and execute them itself prior to\n\t\twaiting for its children to finish.\n\n\t\tTo optimize CPU utilization, whenever a worker thread does need to block, it temporarily gives up its worker\n\t\tlease and attempts to start another worker thread to compensate for the reduced `parallelism`. If the max pool\n\t    size does not permit starting another thread, the attempt is ignored in case there are still other active worker\n\t    threads.\n\n\t\tThe same happens in case a resource lock needs to be acquired.\n\n\t\tTo minimize the number of idle workers, worker threads will prefer to steal top level tasks, while working\n\t\tthrough their own task hierarchy in a depth first fashion. Furthermore, child tasks with execution mode\n\t\t`CONCURRENT` are submitted to the shared queue prior to executing those with execution mode `SAME_THREAD`\n\t\tdirectly.\n\t*/\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(WorkerThreadPoolHierarchicalTestExecutorService.class);\n\n\tprivate final WorkQueue workQueue = new WorkQueue();\n\tprivate final ExecutorService executor;\n\tprivate final int parallelism;\n\tprivate final WorkerLeaseManager workerLeaseManager;\n\n\t/**\n\t * Create a new {@code WorkerThreadPoolHierarchicalTestExecutorService}\n\t * based on the supplied {@link ParallelExecutionConfiguration}.\n\t *\n\t * <p>The following attributes of the supplied configuration are applied to\n\t * the thread pool used by this executor service:\n\t *\n\t * <ul>\n\t *     <li>{@link ParallelExecutionConfiguration#getParallelism()}</li>\n\t *     <li>{@link ParallelExecutionConfiguration#getCorePoolSize()}</li>\n\t *     <li>{@link ParallelExecutionConfiguration#getMaxPoolSize()}</li>\n\t *     <li>{@link ParallelExecutionConfiguration#getKeepAliveSeconds()}</li>\n\t * </ul>\n\t *\n\t * <p>The remaining attributes, such as\n\t * {@link ParallelExecutionConfiguration#getMinimumRunnable()} and\n\t * {@link ParallelExecutionConfiguration#getSaturatePredicate()}, are\n\t * ignored.\n\t *\n\t * @see ParallelHierarchicalTestExecutorServiceFactory#create(ConfigurationParameters)\n\t */\n\tWorkerThreadPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration) {\n\t\tthis(configuration, ClassLoaderUtils.getDefaultClassLoader());\n\t}\n\n\t// package-private for testing\n\tWorkerThreadPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration,\n\t\t\tClassLoader classLoader) {\n\t\tThreadFactory threadFactory = new WorkerThreadFactory(classLoader);\n\t\tparallelism = configuration.getParallelism();\n\t\tworkerLeaseManager = new WorkerLeaseManager(parallelism, this::maybeStartWorker);\n\t\tvar rejectedExecutionHandler = new LeaseAwareRejectedExecutionHandler(workerLeaseManager);\n\t\texecutor = new ThreadPoolExecutor(configuration.getCorePoolSize(), configuration.getMaxPoolSize(),\n\t\t\tconfiguration.getKeepAliveSeconds(), SECONDS, new SynchronousQueue<>(), threadFactory,\n\t\t\trejectedExecutionHandler);\n\t\tlogger.trace(() -> \"initialized thread pool for parallelism of \" + configuration.getParallelism());\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tlogger.trace(() -> \"shutting down thread pool\");\n\t\texecutor.shutdownNow();\n\t}\n\n\t@Override\n\tpublic Future<@Nullable Void> submit(TestTask testTask) {\n\t\tlogger.trace(() -> \"submit: \" + testTask);\n\n\t\tvar workerThread = WorkerThread.get();\n\t\tif (workerThread == null) {\n\t\t\treturn enqueue(testTask, 0).future();\n\t\t}\n\n\t\tif (testTask.getExecutionMode() == SAME_THREAD) {\n\t\t\tworkerThread.executeTask(testTask);\n\t\t\treturn completedFuture(null);\n\t\t}\n\n\t\tvar entry = enqueue(testTask, workerThread.nextChildIndex());\n\t\tworkerThread.trackSubmittedChild(entry);\n\t\treturn new WorkStealingFuture(entry);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t *\n\t * @implNote This method must be called from within a worker thread that\n\t * belongs to this executor.\n\t */\n\t@Override\n\tpublic void invokeAll(List<? extends TestTask> testTasks) {\n\t\tlogger.trace(() -> \"invokeAll: \" + testTasks);\n\n\t\tvar workerThread = WorkerThread.get();\n\t\tPreconditions.condition(workerThread != null && workerThread.executor() == this,\n\t\t\t\"invokeAll() must be called from a worker thread that belongs to this executor\");\n\n\t\tworkerThread.invokeAll(testTasks);\n\t}\n\n\tprivate WorkQueue.Entry enqueue(TestTask testTask, int index) {\n\t\tvar entry = workQueue.add(testTask, index);\n\t\tmaybeStartWorker();\n\t\treturn entry;\n\t}\n\n\tprivate void forkAll(Collection<WorkQueue.Entry> entries) {\n\t\tif (entries.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tworkQueue.addAll(entries);\n\t\t// start at most (parallelism - 1) new workers as this method is called from a worker thread holding a lease\n\t\tfor (int i = 0; i < Math.min(parallelism - 1, entries.size()); i++) {\n\t\t\tmaybeStartWorker();\n\t\t}\n\t}\n\n\tprivate void maybeStartWorker() {\n\t\tmaybeStartWorker(() -> false);\n\t}\n\n\tprivate void maybeStartWorker(BooleanSupplier doneCondition) {\n\t\tif (executor.isShutdown() || workQueue.isEmpty() || doneCondition.getAsBoolean()) {\n\t\t\treturn;\n\t\t}\n\t\tvar workerLease = workerLeaseManager.tryAcquire();\n\t\tif (workerLease == null) {\n\t\t\treturn;\n\t\t}\n\t\texecutor.execute(new RunLeaseAwareWorker(workerLease, doneCondition));\n\t}\n\n\tprivate record RunLeaseAwareWorker(WorkerLease workerLease, BooleanSupplier parentDoneCondition)\n\t\t\timplements Runnable {\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\tlogger.trace(() -> \"starting worker\");\n\t\t\ttry {\n\t\t\t\tWorkerThread.getOrThrow().processQueueEntries(workerLease, parentDoneCondition);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tworkerLease.release(parentDoneCondition);\n\t\t\t\tlogger.trace(() -> \"stopping worker\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class WorkerThreadFactory implements ThreadFactory {\n\n\t\tprivate static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);\n\n\t\tprivate final AtomicInteger threadNumber = new AtomicInteger(1);\n\t\tprivate final int poolNumber;\n\t\tprivate final ClassLoader classLoader;\n\n\t\tWorkerThreadFactory(ClassLoader classLoader) {\n\t\t\tthis.classLoader = classLoader;\n\t\t\tthis.poolNumber = POOL_NUMBER.getAndIncrement();\n\t\t}\n\n\t\t@Override\n\t\tpublic Thread newThread(Runnable runnable) {\n\t\t\tvar thread = new WorkerThread(runnable,\n\t\t\t\t\"junit-%d-worker-%d\".formatted(poolNumber, threadNumber.getAndIncrement()));\n\t\t\tthread.setContextClassLoader(classLoader);\n\t\t\treturn thread;\n\t\t}\n\t}\n\n\tprivate class WorkerThread extends Thread {\n\n\t\tprivate final Deque<State> stateStack = new ArrayDeque<>();\n\n\t\t@Nullable\n\t\tWorkerLease workerLease;\n\n\t\tWorkerThread(Runnable runnable, String name) {\n\t\t\tsuper(runnable, name);\n\t\t}\n\n\t\tstatic @Nullable WorkerThread get() {\n\t\t\tif (Thread.currentThread() instanceof WorkerThread workerThread) {\n\t\t\t\treturn workerThread;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tstatic WorkerThread getOrThrow() {\n\t\t\tvar workerThread = get();\n\t\t\tif (workerThread == null) {\n\t\t\t\tthrow new IllegalStateException(\"Not on a worker thread\");\n\t\t\t}\n\t\t\treturn workerThread;\n\t\t}\n\n\t\tWorkerThreadPoolHierarchicalTestExecutorService executor() {\n\t\t\treturn WorkerThreadPoolHierarchicalTestExecutorService.this;\n\t\t}\n\n\t\tvoid processQueueEntries(WorkerLease workerLease, BooleanSupplier doneCondition) {\n\t\t\tthis.workerLease = workerLease;\n\t\t\twhile (!executor.isShutdown()) {\n\t\t\t\tif (doneCondition.getAsBoolean()) {\n\t\t\t\t\tlogger.trace(() -> \"yielding resource lock\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (workQueue.isEmpty()) {\n\t\t\t\t\tlogger.trace(() -> \"no queue entries available\");\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tprocessQueueEntries();\n\t\t\t}\n\t\t}\n\n\t\tprivate void processQueueEntries() {\n\t\t\tvar entriesRequiringResourceLocks = new ArrayList<WorkQueue.Entry>();\n\n\t\t\tfor (var entry : workQueue) {\n\t\t\t\tvar result = tryToStealWork(entry, BlockingMode.NON_BLOCKING);\n\t\t\t\tif (result == WorkStealResult.EXECUTED_BY_THIS_WORKER) {\n\t\t\t\t\t// After executing a test a significant amount of time has passed.\n\t\t\t\t\t// Process the queue from the beginning\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (result == WorkStealResult.RESOURCE_LOCK_UNAVAILABLE) {\n\t\t\t\t\tentriesRequiringResourceLocks.add(entry);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (var entry : entriesRequiringResourceLocks) {\n\t\t\t\tvar result = tryToStealWork(entry, BlockingMode.BLOCKING);\n\t\t\t\tif (result == WorkStealResult.EXECUTED_BY_THIS_WORKER) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t<T> T runBlocking(BooleanSupplier doneCondition, BlockingAction<T> blockingAction) throws InterruptedException {\n\t\t\tvar workerLease = requireNonNull(this.workerLease);\n\t\t\tworkerLease.release(doneCondition);\n\t\t\ttry {\n\t\t\t\treturn blockingAction.run();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\ttry {\n\t\t\t\t\tworkerLease.reacquire();\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tinterrupt();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvoid invokeAll(List<? extends TestTask> testTasks) {\n\n\t\t\tif (testTasks.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (testTasks.size() == 1) {\n\t\t\t\texecuteTask(testTasks.get(0));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tList<TestTask> isolatedTasks = new ArrayList<>(testTasks.size());\n\t\t\tList<TestTask> sameThreadTasks = new ArrayList<>(testTasks.size());\n\t\t\tvar queueEntries = forkConcurrentChildren(testTasks, isolatedTasks::add, sameThreadTasks);\n\t\t\texecuteAll(sameThreadTasks);\n\t\t\tvar queueEntriesByResult = tryToStealWorkWithoutBlocking(queueEntries);\n\t\t\ttryToStealWorkWithBlocking(queueEntriesByResult);\n\t\t\twaitFor(queueEntriesByResult);\n\t\t\texecuteAll(isolatedTasks);\n\t\t}\n\n\t\tprivate List<WorkQueue.Entry> forkConcurrentChildren(List<? extends TestTask> children,\n\t\t\t\tConsumer<TestTask> isolatedTaskCollector, List<TestTask> sameThreadTasks) {\n\n\t\t\tList<WorkQueue.Entry> queueEntries = new ArrayList<>(children.size());\n\t\t\tfor (TestTask child : children) {\n\t\t\t\tif (requiresGlobalReadWriteLock(child)) {\n\t\t\t\t\tisolatedTaskCollector.accept(child);\n\t\t\t\t}\n\t\t\t\telse if (child.getExecutionMode() == SAME_THREAD) {\n\t\t\t\t\tsameThreadTasks.add(child);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tqueueEntries.add(new WorkQueue.Entry(child, nextChildIndex()));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!queueEntries.isEmpty()) {\n\t\t\t\tqueueEntries.sort(WorkQueue.Entry.CHILD_COMPARATOR);\n\t\t\t\tif (sameThreadTasks.isEmpty()) {\n\t\t\t\t\t// hold back one task for this thread\n\t\t\t\t\tvar firstEntry = queueEntries.remove(0);\n\t\t\t\t\tsameThreadTasks.add(firstEntry.task);\n\t\t\t\t}\n\t\t\t\tforkAll(queueEntries);\n\t\t\t}\n\t\t\treturn queueEntries;\n\t\t}\n\n\t\tprivate Map<WorkStealResult, List<WorkQueue.Entry>> tryToStealWorkWithoutBlocking(\n\t\t\t\tIterable<WorkQueue.Entry> queueEntries) {\n\n\t\t\tMap<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult = new EnumMap<>(WorkStealResult.class);\n\t\t\ttryToStealWork(queueEntries, BlockingMode.NON_BLOCKING, queueEntriesByResult);\n\t\t\treturn queueEntriesByResult;\n\t\t}\n\n\t\tprivate void tryToStealWorkWithBlocking(Map<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult) {\n\t\t\tvar entriesRequiringResourceLocks = queueEntriesByResult.remove(WorkStealResult.RESOURCE_LOCK_UNAVAILABLE);\n\t\t\tif (entriesRequiringResourceLocks == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttryToStealWork(entriesRequiringResourceLocks, BlockingMode.BLOCKING, queueEntriesByResult);\n\t\t}\n\n\t\tprivate void tryToStealWork(Iterable<WorkQueue.Entry> entries, BlockingMode blocking,\n\t\t\t\tMap<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult) {\n\t\t\tfor (var entry : entries) {\n\t\t\t\tvar result = tryToStealWork(entry, blocking);\n\t\t\t\tqueueEntriesByResult.computeIfAbsent(result, __ -> new ArrayList<>()).add(entry);\n\t\t\t}\n\t\t}\n\n\t\tprivate WorkStealResult tryToStealWork(WorkQueue.Entry entry, BlockingMode blockingMode) {\n\t\t\tif (entry.future.isDone()) {\n\t\t\t\treturn WorkStealResult.EXECUTED_BY_DIFFERENT_WORKER;\n\t\t\t}\n\t\t\tvar claimed = workQueue.remove(entry);\n\t\t\tif (claimed) {\n\t\t\t\tlogger.trace(() -> \"stole work: \" + entry.task);\n\t\t\t\tvar executed = executeStolenWork(entry, blockingMode);\n\t\t\t\tif (executed) {\n\t\t\t\t\treturn WorkStealResult.EXECUTED_BY_THIS_WORKER;\n\t\t\t\t}\n\t\t\t\tworkQueue.reAdd(entry);\n\t\t\t\treturn WorkStealResult.RESOURCE_LOCK_UNAVAILABLE;\n\t\t\t}\n\t\t\treturn WorkStealResult.EXECUTED_BY_DIFFERENT_WORKER;\n\t\t}\n\n\t\tprivate void waitFor(Map<WorkStealResult, List<WorkQueue.Entry>> queueEntriesByResult) {\n\t\t\tvar children = queueEntriesByResult.get(WorkStealResult.EXECUTED_BY_DIFFERENT_WORKER);\n\t\t\tif (children == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvar future = toCombinedFuture(children);\n\t\t\ttry {\n\t\t\t\tif (future.isDone()) {\n\t\t\t\t\t// no need to release worker lease\n\t\t\t\t\tfuture.join();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trunBlocking(future::isDone, () -> {\n\t\t\t\t\t\tlogger.trace(() -> \"blocking for forked children : %s\".formatted(children));\n\t\t\t\t\t\treturn future.join();\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tcurrentThread().interrupt();\n\t\t\t}\n\t\t}\n\n\t\tprivate static boolean requiresGlobalReadWriteLock(TestTask testTask) {\n\t\t\treturn testTask.getResourceLock().getResources().contains(GLOBAL_READ_WRITE);\n\t\t}\n\n\t\tprivate void executeAll(List<? extends TestTask> children) {\n\t\t\tif (children.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlogger.trace(() -> \"running %d children directly\".formatted(children.size()));\n\t\t\tif (children.size() == 1) {\n\t\t\t\texecuteTask(children.get(0));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfor (var testTask : children) {\n\t\t\t\texecuteTask(testTask);\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean executeStolenWork(WorkQueue.Entry entry, BlockingMode blockingMode) {\n\t\t\treturn switch (blockingMode) {\n\t\t\t\tcase NON_BLOCKING -> tryExecute(entry);\n\t\t\t\tcase BLOCKING -> {\n\t\t\t\t\texecute(entry);\n\t\t\t\t\tyield true;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate boolean tryExecute(WorkQueue.Entry entry) {\n\t\t\ttry {\n\t\t\t\tvar executed = tryExecuteTask(entry.task);\n\t\t\t\tif (executed) {\n\t\t\t\t\tentry.future.complete(null);\n\t\t\t\t}\n\t\t\t\treturn executed;\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tentry.future.completeExceptionally(t);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tprivate void execute(WorkQueue.Entry entry) {\n\t\t\ttry {\n\t\t\t\texecuteTask(entry.task);\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tentry.future.completeExceptionally(t);\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tentry.future.complete(null);\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"try\")\n\t\tprivate void executeTask(TestTask testTask) {\n\t\t\tvar executed = tryExecuteTask(testTask);\n\t\t\tif (!executed) {\n\t\t\t\tvar resourceLock = testTask.getResourceLock();\n\t\t\t\ttry (var ignored = runBlocking(() -> false, () -> {\n\t\t\t\t\tlogger.trace(() -> \"blocking for resource lock: \" + resourceLock);\n\t\t\t\t\treturn resourceLock.acquire();\n\t\t\t\t})) {\n\t\t\t\t\tlogger.trace(() -> \"acquired resource lock: \" + resourceLock);\n\t\t\t\t\tdoExecute(testTask);\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException ex) {\n\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tlogger.trace(() -> \"released resource lock: \" + resourceLock);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate boolean tryExecuteTask(TestTask testTask) {\n\t\t\tvar resourceLock = testTask.getResourceLock();\n\t\t\tif (resourceLock.tryAcquire()) {\n\t\t\t\tlogger.trace(() -> \"acquired resource lock: \" + resourceLock);\n\t\t\t\ttry (resourceLock) {\n\t\t\t\t\tdoExecute(testTask);\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tlogger.trace(() -> \"released resource lock: \" + resourceLock);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlogger.trace(() -> \"failed to acquire resource lock: \" + resourceLock);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tprivate void doExecute(TestTask testTask) {\n\t\t\tlogger.trace(() -> \"executing: \" + testTask);\n\t\t\tstateStack.push(new State());\n\t\t\ttry {\n\t\t\t\ttestTask.execute();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tstateStack.pop();\n\t\t\t\tlogger.trace(() -> \"finished executing: \" + testTask);\n\t\t\t}\n\t\t}\n\n\t\tprivate static CompletableFuture<?> toCombinedFuture(List<WorkQueue.Entry> entries) {\n\t\t\tif (entries.size() == 1) {\n\t\t\t\treturn entries.get(0).future();\n\t\t\t}\n\t\t\tvar futures = entries.stream().map(WorkQueue.Entry::future).toArray(CompletableFuture<?>[]::new);\n\t\t\treturn CompletableFuture.allOf(futures);\n\t\t}\n\n\t\tprivate int nextChildIndex() {\n\t\t\treturn stateStack.element().nextChildIndex();\n\t\t}\n\n\t\tprivate void trackSubmittedChild(WorkQueue.Entry entry) {\n\t\t\tstateStack.element().trackSubmittedChild(entry);\n\t\t}\n\n\t\tprivate void tryToStealWorkFromSubmittedChildren() {\n\t\t\tvar currentState = stateStack.element();\n\t\t\tvar currentSubmittedChildren = currentState.submittedChildren;\n\t\t\tif (currentSubmittedChildren == null || currentSubmittedChildren.isEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcurrentSubmittedChildren.sort(WorkQueue.Entry.CHILD_COMPARATOR);\n\t\t\tvar iterator = currentSubmittedChildren.iterator();\n\t\t\twhile (iterator.hasNext()) {\n\t\t\t\tWorkQueue.Entry entry = iterator.next();\n\t\t\t\tvar result = tryToStealWork(entry, BlockingMode.NON_BLOCKING);\n\t\t\t\tif (result.isExecuted()) {\n\t\t\t\t\titerator.remove();\n\t\t\t\t}\n\t\t\t}\n\t\t\tcurrentState.clearIfEmpty();\n\t\t}\n\n\t\tprivate static class State {\n\n\t\t\tprivate int nextChildIndex = 0;\n\n\t\t\t@Nullable\n\t\t\tprivate List<WorkQueue.Entry> submittedChildren;\n\n\t\t\tprivate void trackSubmittedChild(WorkQueue.Entry entry) {\n\t\t\t\tif (submittedChildren == null) {\n\t\t\t\t\tsubmittedChildren = new ArrayList<>();\n\t\t\t\t}\n\t\t\t\tsubmittedChildren.add(entry);\n\t\t\t}\n\n\t\t\tprivate void clearIfEmpty() {\n\t\t\t\tif (submittedChildren != null && submittedChildren.isEmpty()) {\n\t\t\t\t\tsubmittedChildren = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tprivate int nextChildIndex() {\n\t\t\t\treturn nextChildIndex++;\n\t\t\t}\n\t\t}\n\n\t\tprivate enum WorkStealResult {\n\t\t\tEXECUTED_BY_DIFFERENT_WORKER, RESOURCE_LOCK_UNAVAILABLE, EXECUTED_BY_THIS_WORKER;\n\n\t\t\tprivate boolean isExecuted() {\n\t\t\t\treturn this != RESOURCE_LOCK_UNAVAILABLE;\n\t\t\t}\n\t\t}\n\n\t\tprivate interface BlockingAction<T> {\n\t\t\tT run() throws InterruptedException;\n\t\t}\n\n\t}\n\n\tprivate static class WorkStealingFuture extends BlockingAwareFuture<@Nullable Void> {\n\n\t\tprivate final WorkQueue.Entry entry;\n\n\t\tWorkStealingFuture(WorkQueue.Entry entry) {\n\t\t\tsuper(entry.future);\n\t\t\tthis.entry = entry;\n\t\t}\n\n\t\t@Override\n\t\tprotected @Nullable Void handle(Callable<@Nullable Void> callable) throws Exception {\n\t\t\tvar workerThread = WorkerThread.get();\n\t\t\tif (workerThread == null || entry.future.isDone()) {\n\t\t\t\treturn callable.call();\n\t\t\t}\n\t\t\tworkerThread.tryToStealWork(entry, BlockingMode.BLOCKING);\n\t\t\tif (entry.future.isDone()) {\n\t\t\t\treturn callable.call();\n\t\t\t}\n\t\t\tworkerThread.tryToStealWorkFromSubmittedChildren();\n\t\t\tif (entry.future.isDone()) {\n\t\t\t\treturn callable.call();\n\t\t\t}\n\t\t\tlogger.trace(() -> \"blocking for child task: \" + entry.task);\n\t\t\treturn workerThread.runBlocking(entry.future::isDone, () -> {\n\t\t\t\ttry {\n\t\t\t\t\treturn callable.call();\n\t\t\t\t}\n\t\t\t\tcatch (Exception ex) {\n\t\t\t\t\tthrow throwAsUncheckedException(ex);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t}\n\n\tprivate enum BlockingMode {\n\t\tNON_BLOCKING, BLOCKING\n\t}\n\n\tprivate static class WorkQueue implements Iterable<WorkQueue.Entry> {\n\n\t\tprivate final Set<Entry> queue = new ConcurrentSkipListSet<>(Entry.QUEUE_COMPARATOR);\n\n\t\tEntry add(TestTask task, int index) {\n\t\t\tEntry entry = new Entry(task, index);\n\t\t\tlogger.trace(() -> \"forking: \" + entry.task);\n\t\t\treturn doAdd(entry);\n\t\t}\n\n\t\tvoid addAll(Collection<Entry> entries) {\n\t\t\tentries.forEach(this::doAdd);\n\t\t}\n\n\t\tvoid reAdd(Entry entry) {\n\t\t\tlogger.trace(() -> \"re-enqueuing: \" + entry.task);\n\t\t\tdoAdd(entry);\n\t\t}\n\n\t\tprivate Entry doAdd(Entry entry) {\n\t\t\tvar added = queue.add(entry);\n\t\t\tif (!added) {\n\t\t\t\tthrow new IllegalStateException(\"Could not add entry to the queue for task: \" + entry.task);\n\t\t\t}\n\t\t\treturn entry;\n\t\t}\n\n\t\tboolean remove(Entry entry) {\n\t\t\treturn queue.remove(entry);\n\t\t}\n\n\t\tboolean isEmpty() {\n\t\t\treturn queue.isEmpty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Iterator<Entry> iterator() {\n\t\t\treturn queue.iterator();\n\t\t}\n\n\t\tprivate static final class Entry {\n\n\t\t\tprivate static final Comparator<Entry> QUEUE_COMPARATOR = comparing(Entry::level).reversed() //\n\t\t\t\t\t.thenComparing(Entry::isContainer) // tests before containers\n\t\t\t\t\t.thenComparing(Entry::index) //\n\t\t\t\t\t.thenComparing(Entry::uniqueId, new SameLengthUniqueIdComparator());\n\n\t\t\tprivate static final Comparator<Entry> CHILD_COMPARATOR = comparing(Entry::isContainer).reversed() // containers before tests\n\t\t\t\t\t.thenComparing(Entry::index);\n\n\t\t\tprivate final TestTask task;\n\t\t\tprivate final CompletableFuture<@Nullable Void> future;\n\t\t\tprivate final int index;\n\n\t\t\t@SuppressWarnings(\"FutureReturnValueIgnored\")\n\t\t\tEntry(TestTask task, int index) {\n\t\t\t\tthis.future = new CompletableFuture<>();\n\t\t\t\tthis.future.whenComplete((__, t) -> {\n\t\t\t\t\tif (t == null) {\n\t\t\t\t\t\tlogger.trace(() -> \"completed normally: \" + task);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlogger.trace(t, () -> \"completed exceptionally: \" + task);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.task = task;\n\t\t\t\tthis.index = index;\n\t\t\t}\n\n\t\t\tprivate int index() {\n\t\t\t\treturn this.index;\n\t\t\t}\n\n\t\t\tprivate int level() {\n\t\t\t\treturn uniqueId().getSegments().size();\n\t\t\t}\n\n\t\t\tprivate boolean isContainer() {\n\t\t\t\treturn task.getTestDescriptor().isContainer();\n\t\t\t}\n\n\t\t\tprivate UniqueId uniqueId() {\n\t\t\t\treturn task.getTestDescriptor().getUniqueId();\n\t\t\t}\n\n\t\t\tCompletableFuture<@Nullable Void> future() {\n\t\t\t\treturn future;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean equals(Object obj) {\n\t\t\t\tif (obj == this) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tif (obj == null || obj.getClass() != this.getClass()) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tvar that = (Entry) obj;\n\t\t\t\treturn Objects.equals(this.uniqueId(), that.uniqueId()) && this.index == that.index;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int hashCode() {\n\t\t\t\treturn Objects.hash(uniqueId(), index);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn new ToStringBuilder(this) //\n\t\t\t\t\t\t.append(\"task\", task) //\n\t\t\t\t\t\t.append(\"index\", index) //\n\t\t\t\t\t\t.toString();\n\t\t\t}\n\n\t\t\tprivate static class SameLengthUniqueIdComparator implements Comparator<UniqueId> {\n\n\t\t\t\t@Override\n\t\t\t\tpublic int compare(UniqueId a, UniqueId b) {\n\t\t\t\t\tvar aIterator = a.getSegments().iterator();\n\t\t\t\t\tvar bIterator = b.getSegments().iterator();\n\n\t\t\t\t\t// ids have the same length\n\t\t\t\t\twhile (aIterator.hasNext()) {\n\t\t\t\t\t\tvar aCurrent = aIterator.next();\n\t\t\t\t\t\tvar bCurrent = bIterator.next();\n\t\t\t\t\t\tint result = compareBy(aCurrent, bCurrent);\n\t\t\t\t\t\tif (result != 0) {\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn 0;\n\t\t\t\t}\n\n\t\t\t\tprivate static int compareBy(UniqueId.Segment a, UniqueId.Segment b) {\n\t\t\t\t\tint result = a.getType().compareTo(b.getType());\n\t\t\t\t\tif (result != 0) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t}\n\t\t\t\t\treturn a.getValue().compareTo(b.getValue());\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\tstatic class WorkerLeaseManager {\n\n\t\tprivate final int parallelism;\n\t\tprivate final Semaphore semaphore;\n\t\tprivate final Consumer<BooleanSupplier> compensation;\n\n\t\tWorkerLeaseManager(int parallelism, Consumer<BooleanSupplier> onRelease) {\n\t\t\tthis.parallelism = parallelism;\n\t\t\tthis.semaphore = new Semaphore(parallelism);\n\t\t\tthis.compensation = onRelease;\n\t\t}\n\n\t\t@Nullable\n\t\tWorkerLease tryAcquire() {\n\t\t\tboolean acquired = semaphore.tryAcquire();\n\t\t\tif (acquired) {\n\t\t\t\tlogger.trace(() -> \"acquired worker lease for new worker (available: %d)\".formatted(\n\t\t\t\t\tsemaphore.availablePermits()));\n\t\t\t\treturn new WorkerLease(this::release);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate ReacquisitionToken release(BooleanSupplier doneCondition) {\n\t\t\tsemaphore.release();\n\t\t\tlogger.trace(() -> \"release worker lease (available: %d)\".formatted(semaphore.availablePermits()));\n\t\t\tcompensation.accept(doneCondition);\n\t\t\treturn new ReacquisitionToken();\n\t\t}\n\n\t\tprivate class ReacquisitionToken {\n\n\t\t\tprivate boolean used = false;\n\n\t\t\tvoid reacquire() throws InterruptedException {\n\t\t\t\tPreconditions.condition(!used, \"Lease was already reacquired\");\n\t\t\t\tused = true;\n\t\t\t\tsemaphore.acquire();\n\t\t\t\tlogger.trace(() -> \"reacquired worker lease (available: %d)\".formatted(semaphore.availablePermits()));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn new ToStringBuilder(this) //\n\t\t\t\t\t.append(\"parallelism\", parallelism) //\n\t\t\t\t\t.append(\"semaphore\", semaphore) //\n\t\t\t\t\t.toString();\n\t\t}\n\t}\n\n\tstatic class WorkerLease {\n\n\t\tprivate final Function<BooleanSupplier, WorkerLeaseManager.ReacquisitionToken> releaseAction;\n\t\tprivate WorkerLeaseManager.@Nullable ReacquisitionToken reacquisitionToken;\n\n\t\tWorkerLease(Function<BooleanSupplier, WorkerLeaseManager.ReacquisitionToken> releaseAction) {\n\t\t\tthis.releaseAction = releaseAction;\n\t\t}\n\n\t\tpublic void release() {\n\t\t\trelease(() -> true);\n\t\t}\n\n\t\tpublic void release(BooleanSupplier doneCondition) {\n\t\t\tif (reacquisitionToken == null) {\n\t\t\t\treacquisitionToken = releaseAction.apply(doneCondition);\n\t\t\t}\n\t\t}\n\n\t\tvoid reacquire() throws InterruptedException {\n\t\t\tPreconditions.notNull(reacquisitionToken, \"Cannot reacquire an unreleased WorkerLease\");\n\t\t\treacquisitionToken.reacquire();\n\t\t\treacquisitionToken = null;\n\t\t}\n\t}\n\n\tprivate record LeaseAwareRejectedExecutionHandler(WorkerLeaseManager workerLeaseManager)\n\t\t\timplements RejectedExecutionHandler {\n\t\t@Override\n\t\tpublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {\n\t\t\tif (r instanceof RunLeaseAwareWorker worker) {\n\t\t\t\tworker.workerLease.release();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/hierarchical/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support classes and base implementation for any\n * {@link org.junit.platform.engine.TestEngine} that wishes to organize test suites\n * hierarchically based on the\n * {@link org.junit.platform.engine.support.hierarchical.Node} abstraction.\n */\n\n@NullMarked\npackage org.junit.platform.engine.support.hierarchical;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/Namespace.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.store;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * A {@code Namespace} is used to provide a <em>scope</em> for data saved by\n * extensions within a {@link NamespacedHierarchicalStore}.\n *\n * <p>Storing data in custom namespaces allows extensions to avoid accidentally\n * mixing data between extensions or across different invocations within the\n * lifecycle of a single extension.\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic final class Namespace {\n\n\t/**\n\t * The default, global namespace which allows access to stored data from\n\t * all extensions.\n\t */\n\tpublic static final Namespace GLOBAL = Namespace.create(new Object());\n\n\t/**\n\t * Create a namespace which restricts access to data to all extensions\n\t * which use the same sequence of {@code parts} for creating a namespace.\n\t *\n\t * <p>The order of the {@code parts} is significant.\n\t *\n\t * <p>Internally the {@code parts} are compared using {@link Object#equals(Object)}.\n\t */\n\tpublic static Namespace create(Object... parts) {\n\t\tPreconditions.notEmpty(parts, \"parts array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(parts, \"individual parts must not be null\");\n\t\treturn new Namespace(List.of(parts));\n\t}\n\n\t/**\n\t * Create a namespace which restricts access to data to all extensions\n\t * which use the same sequence of {@code objects} for creating a namespace.\n\t *\n\t * <p>The order of the {@code objects} is significant.\n\t *\n\t * <p>Internally the {@code objects} are compared using {@link Object#equals(Object)}.\n\t */\n\tpublic static Namespace create(List<Object> objects) {\n\t\tPreconditions.notEmpty(objects, \"objects list must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(objects, \"individual objects must not be null\");\n\t\treturn new Namespace(objects);\n\t}\n\n\tprivate final List<Object> parts;\n\n\tprivate Namespace(List<Object> parts) {\n\t\tthis.parts = List.copyOf(parts);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tif (this == o) {\n\t\t\treturn true;\n\t\t}\n\t\tif (o == null || getClass() != o.getClass()) {\n\t\t\treturn false;\n\t\t}\n\t\tNamespace that = (Namespace) o;\n\t\treturn this.parts.equals(that.parts);\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.parts.hashCode();\n\t}\n\n\t/**\n\t * Create a new namespace by appending the supplied {@code parts} to the\n\t * existing sequence of parts in this namespace.\n\t *\n\t * @return new namespace; never {@code null}\n\t */\n\tpublic Namespace append(Object... parts) {\n\t\tPreconditions.notEmpty(parts, \"parts array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(parts, \"individual parts must not be null\");\n\t\tArrayList<Object> newParts = new ArrayList<>(this.parts.size() + parts.length);\n\t\tnewParts.addAll(this.parts);\n\t\tCollections.addAll(newParts, parts);\n\t\treturn new Namespace(newParts);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStore.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.store;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\nimport static org.junit.platform.commons.util.ReflectionUtils.getWrapperType;\nimport static org.junit.platform.commons.util.ReflectionUtils.isAssignableTo;\n\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * {@code NamespacedHierarchicalStore} is a hierarchical, namespaced key-value store.\n *\n * <p>Its {@linkplain #close() closing} behavior can be customized by passing a\n * {@link CloseAction} to the\n * {@link #NamespacedHierarchicalStore(NamespacedHierarchicalStore, CloseAction)}\n * constructor.\n *\n * <p>This class is thread-safe. Please note, however, that thread safety is\n * not guaranteed while the {@link #close()} method is being invoked.\n *\n * @param <N> Namespace type\n * @since 1.10\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic final class NamespacedHierarchicalStore<N> implements AutoCloseable {\n\n\tprivate final AtomicInteger insertOrderSequence = new AtomicInteger();\n\n\tprivate final ConcurrentMap<CompositeKey<N>, StoredValue> storedValues = new ConcurrentHashMap<>(4);\n\n\tprivate final @Nullable NamespacedHierarchicalStore<N> parentStore;\n\n\tprivate final @Nullable CloseAction<N> closeAction;\n\n\tprivate volatile boolean closed = false;\n\n\t/**\n\t * Create a new store with the supplied parent.\n\t *\n\t * @param parentStore the parent store to use for lookups; may be {@code null}\n\t */\n\tpublic NamespacedHierarchicalStore(@Nullable NamespacedHierarchicalStore<N> parentStore) {\n\t\tthis(parentStore, null);\n\t}\n\n\t/**\n\t * Create a new store with the supplied parent and close action.\n\t *\n\t * @param parentStore the parent store to use for lookups; may be {@code null}\n\t * @param closeAction the action to be called for each stored value when this\n\t * store is closed; may be {@code null}\n\t */\n\tpublic NamespacedHierarchicalStore(@Nullable NamespacedHierarchicalStore<N> parentStore,\n\t\t\t@Nullable CloseAction<N> closeAction) {\n\t\tthis.parentStore = parentStore;\n\t\tthis.closeAction = closeAction;\n\t}\n\n\t/**\n\t * Create a child store with this store as its parent and this store's close\n\t * action.\n\t */\n\tpublic NamespacedHierarchicalStore<N> newChild() {\n\t\treturn new NamespacedHierarchicalStore<>(this, this.closeAction);\n\t}\n\n\t/**\n\t * Returns the parent store of this {@code NamespacedHierarchicalStore}.\n\t *\n\t * <p>If this store does not have a parent, an empty {@code Optional} is returned.\n\t *\n\t * @return an {@code Optional} containing the parent store, or an empty {@code Optional} if there is no parent\n\t * @since 1.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic Optional<NamespacedHierarchicalStore<N>> getParent() {\n\t\treturn Optional.ofNullable(this.parentStore);\n\t}\n\n\t/**\n\t * Determine if this store has been {@linkplain #close() closed}.\n\t *\n\t * @return {@code true} if this store has been closed\n\t * @since 1.11\n\t * @see #close()\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic boolean isClosed() {\n\t\treturn this.closed;\n\t}\n\n\t/**\n\t * If a {@link CloseAction} is configured, it will be called with all successfully\n\t * stored values in reverse insertion order.\n\t *\n\t * <p>Closing a store does not close its parent or any of its children.\n\t *\n\t * <p>Invocations of this method after the store has already been closed will\n\t * be ignored.\n\t *\n\t * @see #isClosed()\n\t */\n\t@Override\n\tpublic void close() {\n\t\tif (!this.closed) {\n\t\t\ttry {\n\t\t\t\tif (this.closeAction != null) {\n\t\t\t\t\tList<Throwable> failures = new ArrayList<>();\n\t\t\t\t\tthis.storedValues.entrySet().stream() //\n\t\t\t\t\t\t\t.map(e -> EvaluatedValue.createSafely(e.getKey(), e.getValue())) //\n\t\t\t\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t\t\t\t.sorted(EvaluatedValue.REVERSE_INSERT_ORDER) //\n\t\t\t\t\t\t\t.forEach(it -> {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tit.close(this.closeAction);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tcatch (Throwable t) {\n\t\t\t\t\t\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\t\t\t\t\t\tfailures.add(t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\tif (!failures.isEmpty()) {\n\t\t\t\t\t\tvar iterator = failures.iterator();\n\t\t\t\t\t\tvar throwable = iterator.next();\n\t\t\t\t\t\titerator.forEachRemaining(throwable::addSuppressed);\n\t\t\t\t\t\tthrow throwAsUncheckedException(throwable);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tthis.closed = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the value stored for the supplied namespace and key in this store or\n\t * the parent store, if present.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @return the stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if this store has already been\n\t * closed\n\t */\n\tpublic @Nullable Object get(N namespace, Object key) {\n\t\tStoredValue storedValue = getStoredValue(new CompositeKey<>(namespace, key));\n\t\treturn StoredValue.evaluateIfNotNull(storedValue);\n\t}\n\n\t/**\n\t * Get the value stored for the supplied namespace and key in this store or\n\t * the parent store, if present, and cast it to the supplied required type.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param requiredType the required type of the value; never {@code null}\n\t * @return the stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if the stored value cannot\n\t * be cast to the required type, or if this store has already been closed\n\t */\n\tpublic <T> @Nullable T get(N namespace, Object key, Class<T> requiredType)\n\t\t\tthrows NamespacedHierarchicalStoreException {\n\t\tObject value = get(namespace, key);\n\t\treturn castToRequiredType(key, value, requiredType);\n\t}\n\n\t/**\n\t * Get the value stored for the supplied namespace and key in this store or\n\t * the parent store, if present, or call the supplied function to compute it.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param defaultCreator the function called with the supplied {@code key}\n\t * to create a new value; never {@code null} but may return {@code null}\n\t * @return the stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if this store has already been\n\t * closed\n\t * @deprecated Please use {@link #computeIfAbsent(Object, Object, Function)} instead.\n\t */\n\t@Deprecated(since = \"6.0\")\n\t@API(status = DEPRECATED, since = \"6.0\")\n\tpublic <K, V extends @Nullable Object> @Nullable Object getOrComputeIfAbsent(N namespace, K key,\n\t\t\tFunction<? super K, ? extends V> defaultCreator) {\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator must not be null\");\n\t\tvar compositeKey = new CompositeKey<>(namespace, key);\n\t\tvar currentStoredValue = getStoredValue(compositeKey);\n\t\tif (currentStoredValue != null) {\n\t\t\treturn currentStoredValue.evaluate();\n\t\t}\n\t\tvar candidateStoredValue = newStoredSuppliedNullableValue(() -> {\n\t\t\trejectIfClosed();\n\t\t\treturn defaultCreator.apply(key);\n\t\t});\n\n\t\tfor (;;) {\n\t\t\tvar storedValue = storedValues.compute(compositeKey, //\n\t\t\t\t(__, oldStoredValue) -> {\n\t\t\t\t\t// The old stored value remains if a) there is an old stored value and\n\t\t\t\t\t// b) the old stored value has not yet been evaluated or c) the old\n\t\t\t\t\t// stored value was evaluated to a present value.\n\t\t\t\t\t//\n\t\t\t\t\t// Condition b ensures that we do not evaluate or await the evaluation\n\t\t\t\t\t// inside `compute`, this would lead to recursive updates or deadlocks\n\t\t\t\t\t// respectively.\n\t\t\t\t\t// Condition c guards against race conditions (repeated from\n\t\t\t\t\t// getStoredValue) this filters out failures inserted by\n\t\t\t\t\t// computeIfAbsent.\n\t\t\t\t\tif (oldStoredValue != null && (!oldStoredValue.isDone() || oldStoredValue.isPresent())) {\n\t\t\t\t\t\treturn oldStoredValue;\n\t\t\t\t\t}\n\t\t\t\t\trejectIfClosed();\n\t\t\t\t\treturn candidateStoredValue;\n\t\t\t\t});\n\n\t\t\t// Only the caller that created the candidateStoredValue may run it\n\t\t\tif (candidateStoredValue.equals(storedValue)) {\n\t\t\t\treturn candidateStoredValue.execute();\n\t\t\t}\n\t\t\t// Implicitly awaits evaluation and uses the result if it was present.\n\t\t\t// this filters out failures inserted by computeIfAbsent\n\t\t\t// Otherwise, another thread won the race but failed to insert a\n\t\t\t// present value so we try again.\n\t\t\tif (storedValue.isPresent()) {\n\t\t\t\treturn storedValue.evaluate();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Return the value stored for the supplied namespace and key in this store\n\t * or the parent store, if present and not {@code null}, or call the\n\t * supplied function to compute it.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param defaultCreator the function called with the supplied {@code key}\n\t * to create a new value; never {@code null} and must not return\n\t * {@code null}\n\t * @return the stored value; never {@code null}\n\t * @throws NamespacedHierarchicalStoreException if this store has already been\n\t * closed\n\t * @since 6.0\n\t */\n\t@API(status = MAINTAINED, since = \"6.0\")\n\tpublic <K, V> Object computeIfAbsent(N namespace, K key, Function<? super K, ? extends V> defaultCreator) {\n\t\tPreconditions.notNull(defaultCreator, \"defaultCreator must not be null\");\n\t\tvar compositeKey = new CompositeKey<>(namespace, key);\n\t\tvar currentStoredValue = getStoredValue(compositeKey);\n\t\tvar result = StoredValue.evaluateIfNotNull(currentStoredValue);\n\t\tif (result != null) {\n\t\t\treturn result;\n\t\t}\n\t\tvar candidateStoredValue = newStoredSuppliedValue(() -> {\n\t\t\trejectIfClosed();\n\t\t\treturn Preconditions.notNull(defaultCreator.apply(key), \"defaultCreator must not return null\");\n\t\t});\n\n\t\tfor (;;) {\n\t\t\tvar storedValue = storedValues.compute(compositeKey, (__, oldStoredValue) -> {\n\t\t\t\t// The old stored value remains if a) there is an old stored value and\n\t\t\t\t// b) the old stored value has not yet been evaluated or c) the old\n\t\t\t\t// stored value evaluated to null.\n\t\t\t\t//\n\t\t\t\t// Condition b ensures that we do not evaluate or await the evaluation\n\t\t\t\t// inside `compute`, this would lead to recursive updates or deadlocks\n\t\t\t\t// respectively.\n\t\t\t\t// Condition c ensures we replace both null and absent values.\n\t\t\t\tif (oldStoredValue != null && (!oldStoredValue.isDone() || oldStoredValue.evaluate() != null)) {\n\t\t\t\t\treturn oldStoredValue;\n\t\t\t\t}\n\t\t\t\trejectIfClosed();\n\t\t\t\treturn candidateStoredValue;\n\t\t\t});\n\n\t\t\t// Only the caller that created the candidateStoredValue may run it\n\t\t\t// and see the exception.\n\t\t\tif (candidateStoredValue.equals(storedValue)) {\n\t\t\t\tObject newResult = candidateStoredValue.execute();\n\t\t\t\t// DeferredOptionalValue is quite heavy, replace with lighter container\n\t\t\t\tif (candidateStoredValue.isPresent()) {\n\t\t\t\t\tstoredValues.computeIfPresent(compositeKey, compareAndPut(storedValue, newStoredValue(newResult)));\n\t\t\t\t}\n\t\t\t\treturn newResult;\n\t\t\t}\n\n\t\t\t// Awaits evaluation and uses the stored value if it was not null\n\t\t\t// this ensures we replace both null and absent values.\n\t\t\t// Otherwise, another thread won the race but failed to insert a\n\t\t\t// non-null value so we try again.\n\t\t\tObject storedResult = storedValue.evaluate();\n\t\t\tif (storedResult != null) {\n\t\t\t\treturn storedResult;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static <N> BiFunction<CompositeKey<N>, StoredValue, StoredValue> compareAndPut(StoredValue expectedValue,\n\t\t\tStoredValue newValue) {\n\t\treturn (__, storedValue) -> {\n\t\t\tif (expectedValue.equals(storedValue)) {\n\t\t\t\treturn newValue;\n\t\t\t}\n\t\t\treturn storedValue;\n\t\t};\n\t}\n\n\t/**\n\t * Get the value stored for the supplied namespace and key in this store or\n\t * the parent store, if present, or call the supplied function to compute it\n\t * and, finally, cast it to the supplied required type.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param defaultCreator the function called with the supplied {@code key}\n\t * to create a new value; never {@code null} but may return {@code null}\n\t * @param requiredType the required type of the value; never {@code null}\n\t * @return the stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if the stored value cannot\n\t * be cast to the required type, or if this store has already been closed\n\t * @deprecated Please use {@link #computeIfAbsent(Object, Object, Function, Class)} instead.\n\t */\n\t@Deprecated(since = \"6.0\")\n\t@API(status = DEPRECATED, since = \"6.0\")\n\tpublic <K, V extends @Nullable Object> @Nullable V getOrComputeIfAbsent(N namespace, K key,\n\t\t\tFunction<? super K, ? extends V> defaultCreator, Class<V> requiredType)\n\t\t\tthrows NamespacedHierarchicalStoreException {\n\n\t\tObject value = getOrComputeIfAbsent(namespace, key, defaultCreator);\n\t\treturn castToRequiredType(key, value, requiredType);\n\t}\n\n\t/**\n\t * Return the value stored for the supplied namespace and key in this store\n\t * or the parent store, if present and not {@code null}, or call the\n\t * supplied function to compute it and, finally, cast it to the supplied\n\t * required type.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param defaultCreator the function called with the supplied {@code key}\n\t * to create a new value; never {@code null} and must not return\n\t * {@code null}\n\t * @param requiredType the required type of the value; never {@code null}\n\t * @return the stored value; never {@code null}\n\t * @throws NamespacedHierarchicalStoreException if the stored value cannot\n\t * be cast to the required type, or if this store has already been closed\n\t * @since 6.0\n\t */\n\t@API(status = MAINTAINED, since = \"6.0\")\n\tpublic <K, V> V computeIfAbsent(N namespace, K key, Function<? super K, ? extends V> defaultCreator,\n\t\t\tClass<V> requiredType) throws NamespacedHierarchicalStoreException {\n\n\t\tObject value = computeIfAbsent(namespace, key, defaultCreator);\n\t\treturn castNonNullToRequiredType(key, value, requiredType);\n\t}\n\n\t/**\n\t * Put the supplied value for the supplied namespace and key into this\n\t * store and return the previously associated value in this store.\n\t *\n\t * <p>The {@link CloseAction} will <em>not</em> be called for the previously\n\t * stored value, if any.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param value the value to store; may be {@code null}\n\t * @return the previously stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if an error occurs while\n\t * storing the value, or if this store has already been closed\n\t */\n\tpublic @Nullable Object put(N namespace, Object key, @Nullable Object value)\n\t\t\tthrows NamespacedHierarchicalStoreException {\n\t\trejectIfClosed();\n\t\tStoredValue oldValue = this.storedValues.put(new CompositeKey<>(namespace, key), newStoredValue(value));\n\t\treturn StoredValue.evaluateIfNotNull(oldValue);\n\t}\n\n\t/**\n\t * Remove the value stored for the supplied namespace and key from this\n\t * store.\n\t *\n\t * <p>The {@link CloseAction} will <em>not</em> be called for the removed\n\t * value.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @return the previously stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if this store has already been\n\t * closed\n\t */\n\tpublic @Nullable Object remove(N namespace, Object key) {\n\t\trejectIfClosed();\n\t\tStoredValue previous = this.storedValues.remove(new CompositeKey<>(namespace, key));\n\t\treturn StoredValue.evaluateIfNotNull(previous);\n\t}\n\n\t/**\n\t * Remove the value stored for the supplied namespace and key from this\n\t * store and cast it to the supplied required type.\n\t *\n\t * <p>The {@link CloseAction} will <em>not</em> be called for the removed\n\t * value.\n\t *\n\t * @param namespace the namespace; never {@code null}\n\t * @param key the key; never {@code null}\n\t * @param requiredType the required type of the value; never {@code null}\n\t * @return the previously stored value; may be {@code null}\n\t * @throws NamespacedHierarchicalStoreException if the stored value cannot\n\t * be cast to the required type, or if this store has already been closed\n\t */\n\tpublic <T> @Nullable T remove(N namespace, Object key, Class<T> requiredType)\n\t\t\tthrows NamespacedHierarchicalStoreException {\n\t\trejectIfClosed();\n\t\tObject value = remove(namespace, key);\n\t\treturn castToRequiredType(key, value, requiredType);\n\t}\n\n\tprivate StoredValue.Value newStoredValue(@Nullable Object value) {\n\t\tvar sequenceNumber = insertOrderSequence.getAndIncrement();\n\t\treturn new StoredValue.Value(sequenceNumber, value);\n\t}\n\n\tprivate StoredValue.DeferredValue newStoredSuppliedNullableValue(Supplier<@Nullable Object> supplier) {\n\t\tvar sequenceNumber = insertOrderSequence.getAndIncrement();\n\t\treturn new StoredValue.DeferredValue(sequenceNumber, supplier);\n\t}\n\n\tprivate StoredValue.DeferredOptionalValue newStoredSuppliedValue(Supplier<Object> supplier) {\n\t\tvar sequenceNumber = insertOrderSequence.getAndIncrement();\n\t\treturn new StoredValue.DeferredOptionalValue(sequenceNumber, supplier);\n\t}\n\n\tprivate @Nullable StoredValue getStoredValue(CompositeKey<N> compositeKey) {\n\t\tStoredValue storedValue = this.storedValues.get(compositeKey);\n\t\tif (StoredValue.isNonNullAndPresent(storedValue)) {\n\t\t\treturn storedValue;\n\t\t}\n\t\tif (this.parentStore != null) {\n\t\t\treturn this.parentStore.getStoredValue(compositeKey);\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate <T> @Nullable T castToRequiredType(Object key, @Nullable Object value, Class<T> requiredType) {\n\t\tPreconditions.notNull(requiredType, \"requiredType must not be null\");\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\treturn castNonNullToRequiredType(key, value, requiredType);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T, V> T castNonNullToRequiredType(Object key, V value, Class<T> requiredType) {\n\t\tif (isAssignableTo(value, requiredType)) {\n\t\t\tif (requiredType.isPrimitive()) {\n\t\t\t\treturn (T) requireNonNull(getWrapperType(requiredType)).cast(value);\n\t\t\t}\n\t\t\treturn requiredType.cast(value);\n\t\t}\n\t\t// else\n\t\tthrow new NamespacedHierarchicalStoreException(\n\t\t\t\"Object stored under key [%s] is not of required type [%s], but was [%s]: %s\".formatted(key,\n\t\t\t\trequiredType.getName(), value.getClass().getName(), value));\n\t}\n\n\tprivate void rejectIfClosed() {\n\t\tif (this.closed) {\n\t\t\tthrow new NamespacedHierarchicalStoreException(\n\t\t\t\t\"A NamespacedHierarchicalStore cannot be modified or queried after it has been closed\");\n\t\t}\n\t}\n\n\tprivate record CompositeKey<N>(N namespace, Object key) {\n\n\t\tCompositeKey {\n\t\t\tPreconditions.notNull(namespace, \"namespace must not be null\");\n\t\t\tPreconditions.notNull(key, \"key must not be null\");\n\t\t}\n\n\t}\n\n\tprivate interface StoredValue {\n\n\t\tint order();\n\n\t\t@Nullable\n\t\tObject evaluate();\n\n\t\tboolean isPresent();\n\n\t\tboolean isDone();\n\n\t\tstatic @Nullable Object evaluateIfNotNull(@Nullable StoredValue value) {\n\t\t\treturn value != null ? value.evaluate() : null;\n\t\t}\n\n\t\tstatic boolean isNonNullAndPresent(@Nullable StoredValue value) {\n\t\t\treturn value != null && value.isPresent();\n\t\t}\n\n\t\t/**\n\t\t * May contain {@code null} or a value, never an exception.\n\t\t */\n\t\tfinal class Value implements StoredValue {\n\t\t\tprivate final int order;\n\t\t\tprivate final @Nullable Object value;\n\n\t\t\tValue(int order, @Nullable Object value) {\n\t\t\t\tthis.order = order;\n\t\t\t\tthis.value = value;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object evaluate() {\n\t\t\t\treturn value;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isPresent() {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isDone() {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int order() {\n\t\t\t\treturn order;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * May eventually contain {@code null} or a value or an exception.\n\t\t */\n\t\tfinal class DeferredValue implements StoredValue {\n\t\t\tprivate final int order;\n\t\t\tprivate final DeferredSupplier delegate;\n\n\t\t\tDeferredValue(int order, Supplier<@Nullable Object> delegate) {\n\t\t\t\tthis.order = order;\n\t\t\t\tthis.delegate = new DeferredSupplier(delegate);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object evaluate() {\n\t\t\t\treturn delegate.getOrThrow();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isPresent() {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isDone() {\n\t\t\t\treturn delegate.isDone();\n\t\t\t}\n\n\t\t\t@Nullable\n\t\t\tObject execute() {\n\t\t\t\tdelegate.run();\n\t\t\t\treturn delegate.getOrThrow();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int order() {\n\t\t\t\treturn order;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * May eventually contain a value or an exception, never {@code null}.\n\t\t */\n\t\tfinal class DeferredOptionalValue implements StoredValue {\n\t\t\tprivate final int order;\n\t\t\tprivate final DeferredSupplier delegate;\n\n\t\t\tDeferredOptionalValue(int order, Supplier<Object> delegate) {\n\t\t\t\tthis.order = order;\n\t\t\t\tthis.delegate = new DeferredSupplier(delegate);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object evaluate() {\n\t\t\t\treturn delegate.get();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isPresent() {\n\t\t\t\treturn evaluate() != null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean isDone() {\n\t\t\t\treturn delegate.isDone();\n\t\t\t}\n\n\t\t\tObject execute() {\n\t\t\t\tdelegate.run();\n\t\t\t\t// Delegate does not produce null\n\t\t\t\treturn requireNonNull(delegate.getOrThrow());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int order() {\n\t\t\t\treturn order;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate record EvaluatedValue<N>(CompositeKey<N> compositeKey, int order, Object value) {\n\n\t\tprivate static <N> @Nullable EvaluatedValue<N> createSafely(CompositeKey<N> compositeKey, StoredValue value) {\n\t\t\ttry {\n\t\t\t\tvar evaluatedValue = value.evaluate();\n\t\t\t\tif (evaluatedValue == null) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\treturn new EvaluatedValue<>(compositeKey, value.order(), evaluatedValue);\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\tprivate static final Comparator<EvaluatedValue<?>> REVERSE_INSERT_ORDER = comparing(\n\t\t\t(EvaluatedValue<?> it) -> it.order).reversed();\n\n\t\tprivate void close(CloseAction<N> closeAction) throws Throwable {\n\t\t\tcloseAction.close(this.compositeKey.namespace, this.compositeKey.key, this.value);\n\t\t}\n\n\t}\n\n\t/**\n\t * Deferred computation that can be added to the store.\n\t * <p>\n\t * This allows values to be computed outside the\n\t * {@link ConcurrentHashMap#compute(Object, BiFunction)} calls and\n\t * prevents recursive updates.\n\t */\n\tstatic final class DeferredSupplier {\n\n\t\tprivate final FutureTask<@Nullable Object> task;\n\n\t\tDeferredSupplier(Supplier<?> delegate) {\n\t\t\tthis.task = new FutureTask<>(delegate::get);\n\t\t}\n\n\t\tvoid run() {\n\t\t\ttask.run();\n\t\t}\n\n\t\t@Nullable\n\t\tObject get() {\n\t\t\ttry {\n\t\t\t\treturn task.get();\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\tthrow throwAsUncheckedException(e);\n\t\t\t}\n\t\t\tcatch (ExecutionException e) {\n\t\t\t\t// non-null guaranteed by FutureTask\n\t\t\t\tvar cause = requireNonNull(e.getCause());\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(cause);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Nullable\n\t\tObject getOrThrow() {\n\t\t\ttry {\n\t\t\t\treturn task.get();\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\tthrow throwAsUncheckedException(e);\n\t\t\t}\n\t\t\tcatch (ExecutionException e) {\n\t\t\t\t// non-null guaranteed by FutureTask\n\t\t\t\tvar cause = requireNonNull(e.getCause());\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(cause);\n\t\t\t\tthrow throwAsUncheckedException(cause);\n\t\t\t}\n\t\t}\n\n\t\tboolean isDone() {\n\t\t\treturn task.isDone();\n\t\t}\n\t}\n\n\t/**\n\t * Called for each successfully stored non-null value in the store when a\n\t * {@link NamespacedHierarchicalStore} is\n\t * {@linkplain NamespacedHierarchicalStore#close() closed}.\n\t */\n\t@FunctionalInterface\n\tpublic interface CloseAction<N> {\n\n\t\t/**\n\t\t * Static factory method for creating a {@link CloseAction} which\n\t\t * {@linkplain #close(Object, Object, Object) closes} any value that\n\t\t * implements {@link AutoCloseable}.\n\t\t *\n\t\t * @since 6.0\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tstatic <N> CloseAction<N> closeAutoCloseables() {\n\t\t\treturn (__, ___, value) -> {\n\t\t\t\tif (value instanceof AutoCloseable closeable) {\n\t\t\t\t\tcloseable.close();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Close the supplied {@code value}.\n\t\t *\n\t\t * @param namespace the namespace; never {@code null}\n\t\t * @param key the key; never {@code null}\n\t\t * @param value the value; never {@code null}\n\t\t */\n\t\tvoid close(N namespace, Object key, Object value) throws Throwable;\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.store;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Exception thrown by failed {@link NamespacedHierarchicalStore} operations.\n *\n * @since 1.10\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic class NamespacedHierarchicalStoreException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic NamespacedHierarchicalStoreException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic NamespacedHierarchicalStoreException(String message, Throwable cause) {\n\t\tsuper(message, cause);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/main/java/org/junit/platform/engine/support/store/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Reusable data structures for test engines and their extensions.\n */\n\n@NullMarked\npackage org.junit.platform.engine.support.store;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-engine/src/main/resources/META-INF/services/org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser",
    "content": "org.junit.platform.engine.discovery.ClasspathResourceSelector$IdentifierParser\norg.junit.platform.engine.discovery.ClasspathRootSelector$IdentifierParser\norg.junit.platform.engine.discovery.ClassSelector$IdentifierParser\norg.junit.platform.engine.discovery.DirectorySelector$IdentifierParser\norg.junit.platform.engine.discovery.FileSelector$IdentifierParser\norg.junit.platform.engine.discovery.IterationSelector$IdentifierParser\norg.junit.platform.engine.discovery.MethodSelector$IdentifierParser\norg.junit.platform.engine.discovery.ModuleSelector$IdentifierParser\norg.junit.platform.engine.discovery.NestedClassSelector$IdentifierParser\norg.junit.platform.engine.discovery.NestedMethodSelector$IdentifierParser\norg.junit.platform.engine.discovery.PackageSelector$IdentifierParser\norg.junit.platform.engine.discovery.UniqueIdSelector$IdentifierParser\norg.junit.platform.engine.discovery.UriSelector$IdentifierParser\n"
  },
  {
    "path": "junit-platform-engine/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoEngineExecutionContext.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport org.junit.platform.engine.ExecutionRequest;\n\n/**\n * @since 1.0\n */\npublic record DemoEngineExecutionContext(ExecutionRequest request) implements EngineExecutionContext {\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalContainerDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.engine.support.hierarchical.Node.SkipResult.doNotSkip;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\n\n/**\n * @since 1.0\n */\npublic class DemoHierarchicalContainerDescriptor extends AbstractTestDescriptor\n\t\timplements Node<DemoEngineExecutionContext> {\n\n\tprivate final @Nullable Runnable beforeBlock;\n\n\tpublic DemoHierarchicalContainerDescriptor(UniqueId uniqueId, String displayName, @Nullable TestSource source,\n\t\t\t@Nullable Runnable beforeBlock) {\n\t\tsuper(uniqueId, displayName, source);\n\t\tthis.beforeBlock = beforeBlock;\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\t@Override\n\tpublic boolean mayRegisterTests() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(DemoEngineExecutionContext context) {\n\t\treturn doNotSkip();\n\t}\n\n\t@Override\n\tpublic DemoEngineExecutionContext before(DemoEngineExecutionContext context) {\n\t\tif (this.beforeBlock != null) {\n\t\t\tthis.beforeBlock.run();\n\t\t}\n\t\treturn context;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalEngineDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.engine.support.hierarchical.Node.SkipResult.doNotSkip;\nimport static org.junit.platform.engine.support.hierarchical.Node.SkipResult.skip;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * @since 1.0\n */\npublic class DemoHierarchicalEngineDescriptor extends EngineDescriptor implements Node<DemoEngineExecutionContext> {\n\n\tprivate @Nullable String skippedReason;\n\n\tprivate boolean skipped;\n\tprivate Runnable beforeAllBehavior = () -> {\n\t};\n\n\tpublic DemoHierarchicalEngineDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, uniqueId.getEngineId().orElseThrow());\n\t}\n\n\tpublic void markSkipped(String reason) {\n\t\tthis.skipped = true;\n\t\tthis.skippedReason = reason;\n\t}\n\n\tpublic void setBeforeAllBehavior(Runnable beforeAllBehavior) {\n\t\tthis.beforeAllBehavior = beforeAllBehavior;\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(DemoEngineExecutionContext context) {\n\t\treturn skipped ? skip(skippedReason) : doNotSkip();\n\t}\n\n\t@Override\n\tpublic DemoEngineExecutionContext before(DemoEngineExecutionContext context) {\n\t\tbeforeAllBehavior.run();\n\t\treturn context;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.platform.engine.support.hierarchical.Node.SkipResult.doNotSkip;\nimport static org.junit.platform.engine.support.hierarchical.Node.SkipResult.skip;\n\nimport java.util.function.BiConsumer;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\n\n/**\n * @since 1.0\n */\npublic class DemoHierarchicalTestDescriptor extends AbstractTestDescriptor implements Node<DemoEngineExecutionContext> {\n\n\tprivate final @Nullable BiConsumer<DemoEngineExecutionContext, TestDescriptor> executeBlock;\n\n\tprivate @Nullable String skippedReason;\n\n\tprivate boolean skipped;\n\n\tpublic DemoHierarchicalTestDescriptor(UniqueId uniqueId, String displayName,\n\t\t\t@Nullable BiConsumer<DemoEngineExecutionContext, TestDescriptor> executeBlock) {\n\t\tthis(uniqueId, displayName, null, executeBlock);\n\t}\n\n\tpublic DemoHierarchicalTestDescriptor(UniqueId uniqueId, String displayName, @Nullable TestSource source,\n\t\t\t@Nullable BiConsumer<DemoEngineExecutionContext, TestDescriptor> executeBlock) {\n\t\tsuper(uniqueId, displayName, source);\n\t\tthis.executeBlock = executeBlock;\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn this.executeBlock != null ? Type.TEST : Type.CONTAINER;\n\t}\n\n\tpublic void markSkipped(String reason) {\n\t\tthis.skipped = true;\n\t\tthis.skippedReason = reason;\n\t}\n\n\t@Override\n\tpublic SkipResult shouldBeSkipped(DemoEngineExecutionContext context) {\n\t\treturn skipped ? skip(skippedReason) : doNotSkip();\n\t}\n\n\t@Override\n\tpublic DemoEngineExecutionContext execute(DemoEngineExecutionContext context,\n\t\t\tDynamicTestExecutor dynamicTestExecutor) {\n\t\tif (this.executeBlock != null) {\n\t\t\tthis.executeBlock.accept(context, this);\n\t\t}\n\t\treturn context;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/engine/support/hierarchical/DemoHierarchicalTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.function.BiConsumer;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 1.0\n */\npublic final class DemoHierarchicalTestEngine extends HierarchicalTestEngine<DemoEngineExecutionContext> {\n\n\tprivate final String engineId;\n\tprivate final DemoHierarchicalEngineDescriptor engineDescriptor;\n\n\tpublic DemoHierarchicalTestEngine() {\n\t\tthis(\"dummy\");\n\t}\n\n\tpublic DemoHierarchicalTestEngine(String engineId) {\n\t\tthis.engineId = engineId;\n\t\tthis.engineDescriptor = new DemoHierarchicalEngineDescriptor(UniqueId.forEngine(getId()));\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn engineId;\n\t}\n\n\tpublic DemoHierarchicalEngineDescriptor getEngineDescriptor() {\n\t\treturn engineDescriptor;\n\t}\n\n\tpublic DemoHierarchicalTestDescriptor addTest(String uniqueName, Runnable executeBlock) {\n\t\treturn addTest(uniqueName, uniqueName, executeBlock);\n\t}\n\n\tpublic DemoHierarchicalTestDescriptor addTest(String uniqueName, String displayName, Runnable executeBlock) {\n\t\treturn addChild(uniqueName,\n\t\t\tuniqueId -> new DemoHierarchicalTestDescriptor(uniqueId, displayName, (c, t) -> executeBlock.run()),\n\t\t\t\"test\");\n\t}\n\n\tpublic DemoHierarchicalTestDescriptor addTest(String uniqueName, String displayName,\n\t\t\tBiConsumer<DemoEngineExecutionContext, TestDescriptor> executeBlock) {\n\t\treturn addChild(uniqueName, uniqueId -> new DemoHierarchicalTestDescriptor(uniqueId, displayName, executeBlock),\n\t\t\t\"test\");\n\t}\n\n\tpublic DemoHierarchicalContainerDescriptor addContainer(String uniqueName, String displayName,\n\t\t\t@Nullable TestSource source) {\n\t\treturn addContainer(uniqueName, displayName, source, null);\n\t}\n\n\tpublic DemoHierarchicalContainerDescriptor addContainer(String uniqueName, Runnable beforeBlock) {\n\t\treturn addContainer(uniqueName, uniqueName, null, beforeBlock);\n\t}\n\n\tpublic DemoHierarchicalContainerDescriptor addContainer(String uniqueName, String displayName,\n\t\t\t@Nullable TestSource source, @Nullable Runnable beforeBlock) {\n\n\t\treturn addChild(uniqueName,\n\t\t\tuniqueId -> new DemoHierarchicalContainerDescriptor(uniqueId, displayName, source, beforeBlock),\n\t\t\t\"container\");\n\t}\n\n\tpublic <T extends TestDescriptor & Node<DemoEngineExecutionContext>> T addChild(String uniqueName,\n\t\t\tFunction<UniqueId, T> creator, String segmentType) {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(segmentType, uniqueName);\n\t\tvar child = creator.apply(uniqueId);\n\t\tengineDescriptor.addChild(child);\n\t\treturn child;\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\treturn engineDescriptor;\n\t}\n\n\t@Override\n\tprotected DemoEngineExecutionContext createExecutionContext(ExecutionRequest request) {\n\t\treturn new DemoEngineExecutionContext(request);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/FaultyTestEngines.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.fakes;\n\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\npublic class FaultyTestEngines {\n\n\tpublic static TestEngineStub createEngineThatCannotResolveAnything(String engineId) {\n\t\treturn new TestEngineStub(engineId) {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tdiscoveryRequest.getSelectorsByType(DiscoverySelector.class) //\n\t\t\t\t\t\t.forEach(selector -> discoveryRequest.getDiscoveryListener().selectorProcessed(uniqueId,\n\t\t\t\t\t\t\tselector, SelectorResolutionResult.unresolved()));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Some Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar listener = request.getEngineExecutionListener();\n\t\t\t\tvar rootTestDescriptor = request.getRootTestDescriptor();\n\t\t\t\tlistener.executionStarted(rootTestDescriptor);\n\t\t\t\tlistener.executionFinished(rootTestDescriptor, TestExecutionResult.successful());\n\t\t\t}\n\t\t};\n\t}\n\n\tpublic static TestEngineStub createEngineThatFailsToResolveAnything(String engineId, Throwable rootCause) {\n\t\treturn new TestEngineStub(engineId) {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tdiscoveryRequest.getSelectorsByType(DiscoverySelector.class) //\n\t\t\t\t\t\t.forEach(selector -> discoveryRequest.getDiscoveryListener().selectorProcessed(uniqueId,\n\t\t\t\t\t\t\tselector, SelectorResolutionResult.failed(rootCause)));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Some Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar listener = request.getEngineExecutionListener();\n\t\t\t\tvar rootTestDescriptor = request.getRootTestDescriptor();\n\t\t\t\tlistener.executionStarted(rootTestDescriptor);\n\t\t\t\tlistener.executionFinished(rootTestDescriptor, TestExecutionResult.successful());\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate FaultyTestEngines() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestDescriptorStub.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.fakes;\n\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\n\n/**\n * @since 1.4\n */\npublic class TestDescriptorStub extends AbstractTestDescriptor {\n\n\tpublic TestDescriptorStub(UniqueId uniqueId, String displayName) {\n\t\tsuper(uniqueId, displayName);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn getChildren().isEmpty() ? Type.TEST : Type.CONTAINER;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineSpy.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.fakes;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 1.4\n */\npublic class TestEngineSpy implements TestEngine {\n\n\tprivate final String id;\n\n\tpublic @Nullable ExecutionRequest requestForExecution;\n\n\tpublic TestEngineSpy() {\n\t\tthis(TestEngineSpy.class.getSimpleName());\n\t}\n\n\tpublic TestEngineSpy(String id) {\n\t\tthis.id = id;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn id;\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tvar engineUniqueId = UniqueId.forEngine(id);\n\t\tvar engineDescriptor = new TestDescriptorStub(engineUniqueId, id);\n\t\tvar testDescriptor = new TestDescriptorStub(engineUniqueId.append(\"test\", \"test\"), \"test\");\n\t\tengineDescriptor.addChild(testDescriptor);\n\t\treturn engineDescriptor;\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\tthis.requestForExecution = request;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/TestEngineStub.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.fakes;\n\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 1.4\n */\npublic class TestEngineStub implements TestEngine {\n\n\tprivate final String id;\n\n\tpublic TestEngineStub() {\n\t\tthis(TestEngineStub.class.getSimpleName());\n\t}\n\n\tpublic TestEngineStub(String id) {\n\t\tthis.id = id;\n\t}\n\n\t@Override\n\tpublic String getId() {\n\t\treturn this.id;\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\treturn new TestDescriptorStub(uniqueId.appendEngine(getId()), getId());\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-engine/src/testFixtures/java/org/junit/platform/fakes/package-info.java",
    "content": "\n@NullMarked\npackage org.junit.platform.fakes;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/junit-platform-launcher.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Platform Launcher\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformEngine)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tosgiVerification(projects.junitJupiterEngine)\n}\n\njavadocConventions {\n\taddExtraModuleReferences(projects.junitPlatformReporting)\n}\n\ntasks {\n\tjar {\n\t\tbundle {\n\t\t\tval importAPIGuardian: String by extra\n\t\t\tval importJSpecify: String by extra\n\t\t\tval importCommonsLogging: String by extra\n\t\t\tval version = project.version\n\t\t\tbnd(\"\"\"\n\t\t\t\tImport-Package: \\\n\t\t\t\t\t${importAPIGuardian},\\\n\t\t\t\t\t${importJSpecify},\\\n\t\t\t\t\t${importCommonsLogging},\\\n\t\t\t\t\tjdk.jfr;resolution:=\"optional\",\\\n\t\t\t\t\t*\n\t\t\t\tProvide-Capability:\\\n\t\t\t\t\torg.junit.platform.launcher;\\\n\t\t\t\t\t\torg.junit.platform.launcher='junit-platform-launcher';\\\n\t\t\t\t\t\tversion:Version=\"${'$'}{version_cleanup;${version}}\"\n\t\t\t\"\"\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Public API for configuring and launching test plans.\n *\n * <p>This API is typically used by IDEs and build tools.\n *\n * @since 1.0\n * @uses org.junit.platform.engine.TestEngine\n * @uses org.junit.platform.launcher.LauncherDiscoveryListener\n * @uses org.junit.platform.launcher.LauncherInterceptor\n * @uses org.junit.platform.launcher.LauncherSessionListener\n * @uses org.junit.platform.launcher.PostDiscoveryFilter\n * @uses org.junit.platform.launcher.TestExecutionListener\n */\nmodule org.junit.platform.launcher {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\trequires static jdk.jfr;\n\n\trequires transitive java.logging;\n\trequires transitive org.junit.platform.commons;\n\trequires transitive org.junit.platform.engine;\n\n\texports org.junit.platform.launcher;\n\texports org.junit.platform.launcher.core;\n\texports org.junit.platform.launcher.listeners;\n\texports org.junit.platform.launcher.listeners.discovery;\n\n\tuses org.junit.platform.engine.TestEngine;\n\tuses org.junit.platform.launcher.LauncherDiscoveryListener;\n\tuses org.junit.platform.launcher.LauncherInterceptor;\n\tuses org.junit.platform.launcher.LauncherSessionListener;\n\tuses org.junit.platform.launcher.PostDiscoveryFilter;\n\tuses org.junit.platform.launcher.TestExecutionListener;\n\n\tprovides org.junit.platform.launcher.TestExecutionListener\n\t\t\twith org.junit.platform.launcher.listeners.UniqueIdTrackingListener;\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/AbstractMethodFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * Abstract {@link MethodFilter} that servers as a superclass\n * for filters including or excluding fully qualified method names\n * without parameters based on pattern-matching.\n *\n * @since 1.12\n */\nabstract class AbstractMethodFilter implements MethodFilter {\n\n\tprotected final List<Pattern> patterns;\n\tprotected final String patternDescription;\n\n\tAbstractMethodFilter(String... patterns) {\n\t\tPreconditions.notEmpty(patterns, \"patterns array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(patterns, \"patterns array must not contain null elements\");\n\t\tthis.patterns = Arrays.stream(patterns).map(Pattern::compile).toList();\n\t\tthis.patternDescription = Arrays.stream(patterns).collect(joining(\"' OR '\", \"'\", \"'\"));\n\t}\n\n\tprotected Optional<Pattern> findMatchingPattern(@Nullable String methodName) {\n\t\tif (methodName == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn this.patterns.stream().filter(pattern -> pattern.matcher(methodName).matches()).findAny();\n\t}\n\n\tprotected @Nullable String getFullyQualifiedMethodNameFromDescriptor(TestDescriptor descriptor) {\n\t\treturn descriptor.getSource() //\n\t\t\t\t.filter(MethodSource.class::isInstance) //\n\t\t\t\t.map(methodSource -> getFullyQualifiedMethodNameWithoutParameters(((MethodSource) methodSource))) //\n\t\t\t\t.orElse(null);\n\t}\n\n\tprivate String getFullyQualifiedMethodNameWithoutParameters(MethodSource methodSource) {\n\t\tString methodNameWithParentheses = ReflectionUtils.getFullyQualifiedMethodName(methodSource.getJavaClass(),\n\t\t\tmethodSource.getMethodName(), (Class<?>[]) null);\n\t\treturn methodNameWithParentheses.substring(0, methodNameWithParentheses.length() - 2);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineDiscoveryResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ToStringBuilder;\n\n/**\n * {@code EngineDiscoveryResult} encapsulates the result of test discovery by a\n * {@link org.junit.platform.engine.TestEngine}.\n *\n * <p>A {@code EngineDiscoveryResult} consists of a mandatory\n * {@link #getStatus() Status} and an optional {@link #getThrowable() Throwable}.\n *\n * @since 1.6\n */\n@API(status = STABLE, since = \"1.10\")\npublic class EngineDiscoveryResult {\n\n\t/**\n\t * Status of test discovery by a\n\t * {@link org.junit.platform.engine.TestEngine}.\n\t */\n\tpublic enum Status {\n\n\t\t/**\n\t\t * Indicates that test discovery was <em>successful</em>.\n\t\t */\n\t\tSUCCESSFUL,\n\n\t\t/**\n\t\t * Indicates that test discovery has <em>failed</em>.\n\t\t */\n\t\tFAILED\n\n\t}\n\n\tprivate static final EngineDiscoveryResult SUCCESSFUL_RESULT = new EngineDiscoveryResult(Status.SUCCESSFUL, null);\n\n\t/**\n\t * Create a {@code EngineDiscoveryResult} for a <em>successful</em> test\n\t * discovery.\n\t * @return the {@code EngineDiscoveryResult}; never {@code null}\n\t */\n\tpublic static EngineDiscoveryResult successful() {\n\t\treturn SUCCESSFUL_RESULT;\n\t}\n\n\t/**\n\t * Create a {@code EngineDiscoveryResult} for a <em>failed</em> test\n\t * discovery.\n\t *\n\t * @param throwable the throwable that caused the failed discovery; may be\n\t * {@code null}\n\t * @return the {@code EngineDiscoveryResult}; never {@code null}\n\t */\n\tpublic static EngineDiscoveryResult failed(@Nullable Throwable throwable) {\n\t\treturn new EngineDiscoveryResult(Status.FAILED, throwable);\n\t}\n\n\tprivate final Status status;\n\n\tprivate final @Nullable Throwable throwable;\n\n\tprivate EngineDiscoveryResult(Status status, @Nullable Throwable throwable) {\n\t\tthis.status = status;\n\t\tthis.throwable = throwable;\n\t}\n\n\t/**\n\t * Get the {@linkplain Status status} of this result.\n\t *\n\t * @return the status; never {@code null}\n\t */\n\tpublic Status getStatus() {\n\t\treturn status;\n\t}\n\n\t/**\n\t * Get the throwable that caused this result, if available.\n\t *\n\t * @return an {@code Optional} containing the throwable; never {@code null}\n\t * but potentially empty\n\t */\n\tpublic Optional<Throwable> getThrowable() {\n\t\treturn Optional.ofNullable(throwable);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"status\", status)\n\t\t\t\t.append(\"throwable\", throwable)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/EngineFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.engine.FilterResult.includedIf;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * An {@code EngineFilter} is applied to all {@link TestEngine TestEngines}\n * before they are used.\n *\n * <p><strong>Warning</strong>: be cautious when registering multiple competing\n * {@link #includeEngines include} {@code EngineFilters} or multiple competing\n * {@link #excludeEngines exclude} {@code EngineFilters} for the same discovery\n * request since doing so will likely lead to undesirable results (i.e., zero\n * engines being active).\n *\n * @since 1.0\n * @see #includeEngines(String...)\n * @see #excludeEngines(String...)\n * @see LauncherDiscoveryRequest\n */\n@API(status = STABLE, since = \"1.0\")\npublic class EngineFilter implements Filter<TestEngine> {\n\n\t/**\n\t * Create a new <em>include</em> {@code EngineFilter} based on the\n\t * supplied engine IDs.\n\t *\n\t * <p>Only {@code TestEngines} with matching engine IDs will be\n\t * <em>included</em> within the test discovery and execution.\n\t *\n\t * @param engineIds the list of engine IDs to match against; never {@code null}\n\t * or empty; individual IDs must also not be null or blank\n\t * @see #includeEngines(String...)\n\t */\n\tpublic static EngineFilter includeEngines(String... engineIds) {\n\t\treturn includeEngines(Arrays.asList(engineIds));\n\t}\n\n\t/**\n\t * Create a new <em>include</em> {@code EngineFilter} based on the\n\t * supplied engine IDs.\n\t *\n\t * <p>Only {@code TestEngines} with matching engine IDs will be\n\t * <em>included</em> within the test discovery and execution.\n\t *\n\t * @param engineIds the list of engine IDs to match against; never {@code null}\n\t * or empty; individual IDs must also not be null or blank\n\t * @see #includeEngines(String...)\n\t */\n\tpublic static EngineFilter includeEngines(List<String> engineIds) {\n\t\treturn new EngineFilter(engineIds, Type.INCLUDE);\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@code EngineFilter} based on the\n\t * supplied engine IDs.\n\t *\n\t * <p>{@code TestEngines} with matching engine IDs will be\n\t * <em>excluded</em> from test discovery and execution.\n\t *\n\t * @param engineIds the list of engine IDs to match against; never {@code null}\n\t * or empty; individual IDs must also not be null or blank\n\t * @see #excludeEngines(List)\n\t */\n\tpublic static EngineFilter excludeEngines(String... engineIds) {\n\t\treturn excludeEngines(Arrays.asList(engineIds));\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@code EngineFilter} based on the\n\t * supplied engine IDs.\n\t *\n\t * <p>{@code TestEngines} with matching engine IDs will be\n\t * <em>excluded</em> from test discovery and execution.\n\t *\n\t * @param engineIds the list of engine IDs to match against; never {@code null}\n\t * or empty; individual IDs must also not be null or blank\n\t * @see #includeEngines(String...)\n\t */\n\tpublic static EngineFilter excludeEngines(List<String> engineIds) {\n\t\treturn new EngineFilter(engineIds, Type.EXCLUDE);\n\t}\n\n\tprivate final List<String> engineIds;\n\tprivate final Type type;\n\n\tprivate EngineFilter(List<String> engineIds, Type type) {\n\t\tthis.engineIds = validateAndTrim(engineIds);\n\t\tthis.type = type;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.9\")\n\tpublic List<String> getEngineIds() {\n\t\treturn engineIds;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.9\")\n\tpublic boolean isIncludeFilter() {\n\t\treturn type == Type.INCLUDE;\n\t}\n\n\t@Override\n\tpublic FilterResult apply(TestEngine testEngine) {\n\t\tPreconditions.notNull(testEngine, \"TestEngine must not be null\");\n\t\tString engineId = testEngine.getId();\n\t\tPreconditions.notBlank(engineId, \"TestEngine ID must not be null or blank\");\n\n\t\tif (this.type == Type.INCLUDE) {\n\t\t\treturn includedIf(this.engineIds.stream().anyMatch(engineId::equals), //\n\t\t\t\t() -> \"Engine ID [%s] is in included list [%s]\".formatted(engineId, this.engineIds), //\n\t\t\t\t() -> \"Engine ID [%s] is not in included list [%s]\".formatted(engineId, this.engineIds));\n\t\t}\n\t\telse {\n\t\t\treturn includedIf(this.engineIds.stream().noneMatch(engineId::equals), //\n\t\t\t\t() -> \"Engine ID [%s] is not in excluded list [%s]\".formatted(engineId, this.engineIds), //\n\t\t\t\t() -> \"Engine ID [%s] is in excluded list [%s]\".formatted(engineId, this.engineIds));\n\t\t}\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that %s engines with IDs %s\".formatted(getClass().getSimpleName(), this.type.verb, this.engineIds);\n\t}\n\n\tprivate static List<String> validateAndTrim(List<String> engineIds) {\n\t\tPreconditions.notEmpty(engineIds, \"engine ID list must not be null or empty\");\n\n\t\t// @formatter:off\n\t\treturn engineIds.stream()\n\t\t\t\t.map(id -> Preconditions.notBlank(id, \"engine ID must not be null or blank\").strip())\n\t\t\t\t.distinct()\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate enum Type {\n\n\t\tINCLUDE(\"includes\"),\n\n\t\tEXCLUDE(\"excludes\");\n\n\t\tprivate final String verb;\n\n\t\tType(String verb) {\n\t\t\tthis.verb = verb;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/ExcludeMethodFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * {@link MethodFilter} that matches fully qualified method names against\n * patterns in the form of regular expressions.\n *\n * <p>If the fully qualified name of a method matches against at least one\n * pattern, the class will be excluded.\n *\n * @since 1.12\n */\nclass ExcludeMethodFilter extends AbstractMethodFilter {\n\n\tExcludeMethodFilter(String... patterns) {\n\t\tsuper(patterns);\n\t}\n\n\t@Override\n\tpublic FilterResult apply(TestDescriptor descriptor) {\n\t\tString methodName = getFullyQualifiedMethodNameFromDescriptor(descriptor);\n\t\treturn findMatchingPattern(methodName) //\n\t\t\t\t.map(pattern -> excluded(formatExclusionReason(methodName, pattern))) //\n\t\t\t\t.orElseGet(() -> included(formatInclusionReason(methodName)));\n\t}\n\n\tprivate String formatInclusionReason(@Nullable String methodName) {\n\t\treturn \"Method name [%s] does not match any excluded pattern: %s\".formatted(methodName, patternDescription);\n\t}\n\n\tprivate String formatExclusionReason(@Nullable String methodName, Pattern pattern) {\n\t\treturn \"Method name [%s] matches excluded pattern: '%s'\".formatted(methodName, pattern);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that excludes method names that match one of the following regular expressions: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), patternDescription);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/IncludeMethodFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * {@link MethodFilter} that matches fully qualified method names against\n * patterns in the form of regular expressions.\n *\n * <p>If the fully qualified name of a method matches against at least one\n * pattern, the method will be included.\n *\n * @since 1.12\n */\nclass IncludeMethodFilter extends AbstractMethodFilter {\n\n\tIncludeMethodFilter(String... patterns) {\n\t\tsuper(patterns);\n\t}\n\n\t@Override\n\tpublic FilterResult apply(TestDescriptor descriptor) {\n\t\tString methodName = getFullyQualifiedMethodNameFromDescriptor(descriptor);\n\t\treturn findMatchingPattern(methodName) //\n\t\t\t\t.map(pattern -> included(formatInclusionReason(methodName, pattern))) //\n\t\t\t\t.orElseGet(() -> excluded(formatExclusionReason(methodName)));\n\t}\n\n\tprivate String formatInclusionReason(@Nullable String methodName, Pattern pattern) {\n\t\treturn \"Method name [%s] matches included pattern: '%s'\".formatted(methodName, pattern);\n\t}\n\n\tprivate String formatExclusionReason(@Nullable String methodName) {\n\t\treturn \"Method name [%s] does not match any included pattern: %s\".formatted(methodName, patternDescription);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"%s that includes method names that match one of the following regular expressions: %s\".formatted(\n\t\t\tgetClass().getSimpleName(), patternDescription);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/Launcher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\n\n/**\n * The {@code Launcher} API is the main entry point for client code that\n * wishes to <em>discover</em> and <em>execute</em> tests using one or more\n * {@linkplain org.junit.platform.engine.TestEngine test engines}.\n *\n * <p>Implementations of this interface are responsible for determining\n * the set of test engines to delegate to at runtime and for ensuring that\n * each test engine has an\n * {@linkplain org.junit.platform.engine.TestEngine#getId ID} that is unique\n * among the registered test engines. For example, the default implementation\n * returned by {@link org.junit.platform.launcher.core.LauncherFactory#create}\n * dynamically discovers test engines via Java's {@link java.util.ServiceLoader\n * ServiceLoader} mechanism.\n *\n * <p>Test discovery and execution require a {@link LauncherDiscoveryRequest}\n * that is passed to all registered engines. Each engine decides which tests it\n * can discover and execute according to the supplied request.\n *\n * <p>Prior to executing tests, clients of this interface should\n * {@linkplain #registerTestExecutionListeners register} one or more\n * {@link TestExecutionListener} instances in order to get feedback about the\n * progress and results of test execution. Listeners will be notified of events\n * in the order in which they were registered. The default implementation\n * returned by {@link org.junit.platform.launcher.core.LauncherFactory#create}\n * dynamically discovers test execution listeners via Java's\n * {@link java.util.ServiceLoader ServiceLoader} mechanism.\n *\n * @since 1.0\n * @see LauncherDiscoveryRequest\n * @see TestPlan\n * @see TestExecutionListener\n * @see org.junit.platform.launcher.core.LauncherFactory\n * @see org.junit.platform.engine.TestEngine\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface Launcher {\n\n\t/**\n\t * Register one or more listeners for test discovery.\n\t *\n\t * @param listeners the listeners to be notified of test discovery events;\n\t * never {@code null} or empty\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tvoid registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners);\n\n\t/**\n\t * Register one or more listeners for test execution.\n\t *\n\t * @param listeners the listeners to be notified of test execution events;\n\t * never {@code null} or empty\n\t */\n\tvoid registerTestExecutionListeners(TestExecutionListener... listeners);\n\n\t/**\n\t * Discover tests and build a {@link TestPlan} according to the supplied\n\t * {@link LauncherDiscoveryRequest} by querying all registered engines and\n\t * collecting their results.\n\t *\n\t * @apiNote This method may be called to generate a preview of the test\n\t * tree. The resulting {@link TestPlan} is unmodifiable and may be passed to\n\t * {@link #execute(TestPlan, TestExecutionListener...)} for execution at\n\t * most once.\n\t *\n\t * @param discoveryRequest the launcher discovery request; never {@code null}\n\t * @return an unmodifiable {@code TestPlan} that contains all resolved\n\t * {@linkplain TestIdentifier identifiers} from all registered engines\n\t */\n\tTestPlan discover(LauncherDiscoveryRequest discoveryRequest);\n\n\t/**\n\t * Execute a {@link TestPlan} which is built according to the supplied\n\t * {@link LauncherDiscoveryRequest} by querying all registered engines and\n\t * collecting their results, and notify\n\t * {@linkplain #registerTestExecutionListeners registered listeners} about\n\t * the progress and results of the execution.\n\t *\n\t * <p>Supplied test execution listeners are registered in addition to already\n\t * registered listeners but only for the supplied launcher discovery request.\n\t *\n\t * @apiNote Calling this method will cause test discovery to be executed for\n\t * all registered engines. If the same {@link LauncherDiscoveryRequest} was\n\t * previously passed to {@link #discover(LauncherDiscoveryRequest)}, you\n\t * should instead call {@link #execute(TestPlan, TestExecutionListener...)}\n\t * and pass the already acquired {@link TestPlan} to avoid the potential\n\t * performance degradation (e.g., classpath scanning) of running test\n\t * discovery twice.\n\t *\n\t * @param discoveryRequest the launcher discovery request; never {@code null}\n\t * @param listeners additional test execution listeners; never {@code null}\n\t * @see #execute(TestPlan, TestExecutionListener...)\n\t * @see #execute(LauncherExecutionRequest)\n\t */\n\tvoid execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners);\n\n\t/**\n\t * Execute the supplied {@link TestPlan} and notify\n\t * {@linkplain #registerTestExecutionListeners registered listeners} about\n\t * the progress and results of the execution.\n\t *\n\t * <p>Supplied test execution listeners are registered in addition to\n\t * already registered listeners but only for the execution of the supplied\n\t * test plan.\n\t *\n\t * @apiNote The supplied {@link TestPlan} must not have been executed\n\t * previously.\n\t *\n\t * @param testPlan the test plan to execute; never {@code null}\n\t * @param listeners additional test execution listeners; never {@code null}\n\t * @since 1.4\n\t * @see #execute(LauncherDiscoveryRequest, TestExecutionListener...)\n\t * @see #execute(LauncherExecutionRequest)\n\t */\n\t@API(status = STABLE, since = \"1.4\")\n\tvoid execute(TestPlan testPlan, TestExecutionListener... listeners);\n\n\t/**\n\t * Execute tests according to the supplied {@link LauncherExecutionRequest} and\n\t * notify {@linkplain #registerTestExecutionListeners registered listeners} about\n\t * the progress and results of the execution.\n\t *\n\t * <p>Test execution listeners supplied\n\t * {@linkplain LauncherExecutionRequest#getAdditionalTestExecutionListeners()\n\t * as part of the request} are registered in addition to already registered\n\t * listeners but only for the supplied execution request.\n\t *\n\t * @apiNote If the execution request contains a {@link TestPlan} rather than\n\t * a {@link LauncherDiscoveryRequest}, it must not have been executed\n\t * previously.\n\t *\n\t * <p>If the execution request contains a {@link LauncherDiscoveryRequest},\n\t * calling this method will cause test discovery to be executed for all\n\t * registered engines. If the same {@link LauncherDiscoveryRequest} was\n\t * previously passed to {@link #discover(LauncherDiscoveryRequest)}, you\n\t * should instead provide the resulting {@link TestPlan} as part of the\n\t * supplied execution request to avoid the potential performance degradation\n\t * (e.g., classpath scanning) of running test discovery twice.\n\t *\n\t * @param executionRequest the launcher execution request; never {@code null}\n\t * @since 6.0\n\t * @see #execute(LauncherDiscoveryRequest, TestExecutionListener...)\n\t * @see #execute(TestPlan, TestExecutionListener...)\n\t */\n\t@API(status = MAINTAINED, since = \"6.0\")\n\tvoid execute(LauncherExecutionRequest executionRequest);\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherConstants.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.ClassNamePatternFilterUtils;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * Collection of constants related to {@link Launcher}.\n *\n * @since 1.3\n * @see org.junit.platform.engine.ConfigurationParameters\n */\n@API(status = STABLE, since = \"1.7\")\npublic class LauncherConstants {\n\n\t/**\n\t * Property name used to enable capturing output to {@link System#out}:\n\t * {@value}\n\t *\n\t * <p>By default, output to {@link System#out} is not captured.\n\t *\n\t * <p>If enabled, the JUnit Platform captures the corresponding output and\n\t * publishes it as a {@link ReportEntry} using the\n\t * {@value #STDOUT_REPORT_ENTRY_KEY} key immediately before reporting the\n\t * test identifier as finished.\n\t *\n\t * @see #STDOUT_REPORT_ENTRY_KEY\n\t * @see ReportEntry\n\t * @see TestExecutionListener#reportingEntryPublished(TestIdentifier, ReportEntry)\n\t */\n\tpublic static final String CAPTURE_STDOUT_PROPERTY_NAME = \"junit.platform.output.capture.stdout\";\n\n\t/**\n\t * Property name used to enable capturing output to {@link System#err}:\n\t * {@value}\n\t *\n\t * <p>By default, output to {@link System#err} is not captured.\n\t *\n\t * <p>If enabled, the JUnit Platform captures the corresponding output and\n\t * publishes it as a {@link ReportEntry} using the\n\t * {@value #STDERR_REPORT_ENTRY_KEY} key immediately before reporting the\n\t * test identifier as finished.\n\t *\n\t * @see #STDERR_REPORT_ENTRY_KEY\n\t * @see ReportEntry\n\t * @see TestExecutionListener#reportingEntryPublished(TestIdentifier, ReportEntry)\n\t */\n\tpublic static final String CAPTURE_STDERR_PROPERTY_NAME = \"junit.platform.output.capture.stderr\";\n\n\t/**\n\t * Property name used to configure the maximum number of bytes for buffering\n\t * to use per thread and output type if output capturing is enabled:\n\t * {@value}\n\t *\n\t * <p>Value must be an integer; defaults to {@value #CAPTURE_MAX_BUFFER_DEFAULT}.\n\t *\n\t * @see #CAPTURE_MAX_BUFFER_DEFAULT\n\t */\n\tpublic static final String CAPTURE_MAX_BUFFER_PROPERTY_NAME = \"junit.platform.output.capture.maxBuffer\";\n\n\t/**\n\t * Default maximum number of bytes for buffering to use per thread and\n\t * output type if output capturing is enabled.\n\t *\n\t * @see #CAPTURE_MAX_BUFFER_PROPERTY_NAME\n\t */\n\tpublic static final int CAPTURE_MAX_BUFFER_DEFAULT = 4 * 1024 * 1024;\n\n\t/**\n\t * Key used to publish captured output to {@link System#out} as part of a\n\t * {@link ReportEntry}: {@value}\n\t */\n\tpublic static final String STDOUT_REPORT_ENTRY_KEY = \"stdout\";\n\n\t/**\n\t * Key used to publish captured output to {@link System#err} as part of a\n\t * {@link ReportEntry}: {@value}\n\t */\n\tpublic static final String STDERR_REPORT_ENTRY_KEY = \"stderr\";\n\n\t/**\n\t * Property name used to provide patterns for deactivating\n\t * {@linkplain TestExecutionListener listeners} registered via the\n\t * {@link java.util.ServiceLoader ServiceLoader} mechanism: {@value}\n\t *\n\t * <h4>Pattern Matching Syntax</h4>\n\t *\n\t * <p>If the property value consists solely of an asterisk ({@code *}), all\n\t * listeners will be deactivated. Otherwise, the property value will be treated\n\t * as a comma-separated list of patterns where each individual pattern will be\n\t * matched against the fully qualified class name (<em>FQCN</em>) of each registered\n\t * listener. Any dot ({@code .}) in a pattern will match against a dot ({@code .})\n\t * or a dollar sign ({@code $}) in a FQCN. Any asterisk ({@code *}) will match\n\t * against one or more characters in a FQCN. All other characters in a pattern\n\t * will be matched one-to-one against a FQCN.\n\t *\n\t * <h4>Examples</h4>\n\t *\n\t * <ul>\n\t * <li>{@code *}: deactivates all listeners.\n\t * <li>{@code org.junit.*}: deactivates every listener under the {@code org.junit}\n\t * base package and any of its subpackages.\n\t * <li>{@code *.MyListener}: deactivates every listener whose simple class name is\n\t * exactly {@code MyListener}.\n\t * <li>{@code *System*, *Dev*}: deactivates every listener whose FQCN contains\n\t * {@code System} or {@code Dev}.\n\t * <li>{@code org.example.MyListener, org.example.TheirListener}: deactivates\n\t * listeners whose FQCN is exactly {@code org.example.MyListener} or\n\t * {@code org.example.TheirListener}.\n\t * </ul>\n\t *\n\t * <p>Only listeners registered via the {@code ServiceLoader} mechanism can\n\t * be deactivated. In other words, any listener registered explicitly via the\n\t * {@link LauncherDiscoveryRequest} cannot be deactivated via this\n\t * configuration parameter.\n\t *\n\t * <p>In addition, since execution listeners are registered before the test\n\t * run starts, this configuration parameter can only be supplied as a JVM\n\t * system property or via the JUnit Platform configuration file but cannot\n\t * be supplied in the {@link LauncherDiscoveryRequest}} that is passed to\n\t * the {@link Launcher}.\n\t *\n\t * @see #DEACTIVATE_ALL_LISTENERS_PATTERN\n\t * @see org.junit.platform.launcher.TestExecutionListener\n\t */\n\tpublic static final String DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME = \"junit.platform.execution.listeners.deactivate\";\n\n\t/**\n\t * Wildcard pattern which signals that all listeners registered via the\n\t * {@link java.util.ServiceLoader ServiceLoader} mechanism should be deactivated:\n\t * {@value}\n\t *\n\t * @see #DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME\n\t * @see org.junit.platform.launcher.TestExecutionListener\n\t */\n\tpublic static final String DEACTIVATE_ALL_LISTENERS_PATTERN = ClassNamePatternFilterUtils.ALL_PATTERN;\n\n\t/**\n\t * Property name used to enable support for\n\t * {@link LauncherInterceptor} instances to be registered via the\n\t * {@link java.util.ServiceLoader ServiceLoader} mechanism: {@value}\n\t *\n\t * <p>By default, interceptor registration is disabled.\n\t *\n\t * <p>Since interceptors are registered before the test run starts, this\n\t * configuration parameter can only be supplied as a JVM system property or\n\t * via the JUnit Platform configuration file but cannot be supplied in the\n\t * {@link LauncherDiscoveryRequest}} that is passed to the {@link Launcher}.\n\t *\n\t * @see LauncherInterceptor\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String ENABLE_LAUNCHER_INTERCEPTORS = \"junit.platform.launcher.interceptors.enabled\";\n\n\t/**\n\t * Property name used to enable dry-run mode for test execution.\n\t *\n\t * <p>When dry-run mode is enabled, no tests will be executed. Instead, all\n\t * registered {@link TestExecutionListener TestExecutionListeners} will\n\t * receive events for all test descriptors that are part of the discovered\n\t * {@link TestPlan}. All containers will be reported as successful and all\n\t * tests as skipped. This can be useful to test changes in the configuration\n\t * of a build or to verify a listener is called as expected without having\n\t * to wait for all tests to be executed.\n\t *\n\t * <p>Value must be either {@code true} or {@code false}; defaults to {@code false}.\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String DRY_RUN_PROPERTY_NAME = \"junit.platform.execution.dryRun.enabled\";\n\n\t/**\n\t * Property name used to enable or disable stack trace pruning.\n\t *\n\t * <p>By default, stack trace pruning is enabled.\n\t *\n\t * @see org.junit.platform.launcher.core.EngineExecutionOrchestrator\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME = \"junit.platform.stacktrace.pruning.enabled\";\n\n\t/**\n\t * Property name used to configure the output directory for reporting.\n\t *\n\t * <p>If set, value must be a valid path that will be created if it doesn't\n\t * exist. If not set, the default output directory will be determined by the\n\t * reporting engine based on the current working directory.\n\t *\n\t * @since 1.12\n\t * @see #OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER\n\t * @see org.junit.platform.engine.OutputDirectoryCreator\n\t * @see EngineDiscoveryRequest#getOutputDirectoryCreator()\n\t * @see TestPlan#getOutputDirectoryCreator()\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String OUTPUT_DIR_PROPERTY_NAME = \"junit.platform.reporting.output.dir\";\n\n\t/**\n\t * Placeholder for use in {@link #OUTPUT_DIR_PROPERTY_NAME} that will be\n\t * replaced with a unique number.\n\t *\n\t * <p>This can be used to create a unique output directory for each test\n\t * run. For example, if multiple forks are used, each fork can be configured\n\t * to write its output to a separate directory.\n\t *\n\t * @since 1.12\n\t * @see #OUTPUT_DIR_PROPERTY_NAME\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static final String OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER = \"{uniqueNumber}\";\n\n\t/**\n\t * Property name used to configure the critical severity of issues\n\t * encountered during test discovery.\n\t *\n\t * <p>If an engine reports an issue with a severity equal to or higher than\n\t * the configured critical severity, its tests will not be executed.\n\t * Depending on {@link #DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME}, a\n\t * {@link org.junit.platform.launcher.core.DiscoveryIssueException} listing\n\t * all critical issues will be thrown during discovery or be reported as\n\t * engine-level failure during execution.\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values include names of enum constants defined in\n\t * {@link org.junit.platform.engine.DiscoveryIssue.Severity Severity},\n\t * ignoring case.\n\t *\n\t * <p>If not specified, the default is \"error\" which corresponds to\n\t * {@code Severity.ERROR)}.\n\t *\n\t * @since 1.13\n\t * @see org.junit.platform.engine.DiscoveryIssue.Severity\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static final String CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME = \"junit.platform.discovery.issue.severity.critical\";\n\n\t/**\n\t * Property name used to configure the phase that critical discovery issues\n\t * should cause a failure\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values are \"discovery\" or \"execution\".\n\t *\n\t * <p>If not specified, the {@code Launcher} will report discovery issues\n\t * during the discovery phase if\n\t * {@link Launcher#discover(LauncherDiscoveryRequest)} is called, and during\n\t * the execution phase if {@link Launcher#execute(LauncherExecutionRequest)}\n\t * is called.\n\t *\n\t * @since 1.13\n\t * @see #CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static final String DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME = \"junit.platform.discovery.issue.failure.phase\";\n\n\t/**\n\t * Property name used to enable the <em>experimental</em> memory cleanup\n\t * mode.\n\t *\n\t * <p>Supported values are {@code true} or {@code false}; defaults to\n\t * {@code false}.\n\t *\n\t * <p>If enabled, the {@link Launcher} removes finished or skipped tests and\n\t * their children from the {@link TestPlan} and engine-internal data\n\t * structures right away to reduce memory consumption, particularly in the\n\t * presence of many dynamically reported tests.\n\t *\n\t * <p>This is an <em>experimental</em> feature since it breaks some existing\n\t * {@link TestExecutionListener} implementations, in particular if they rely\n\t * on the test plan to provide information about <em>all</em> executed tests\n\t * and containers after they have been executed. Not all\n\t * {@link org.junit.platform.engine.TestEngine TestEngine} implementations\n\t * may be compatible, either. In both cases, please report issues to the\n\t * maintainers of the affected listeners or engines.\n\t *\n\t * @since 6.1\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.1\")\n\tpublic static final String MEMORY_CLEANUP_ENABLED_PROPERTY_NAME = \"junit.platform.execution.memory.cleanup.enabled\";\n\n\tprivate LauncherConstants() {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.EngineDiscoveryListener;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Register a concrete implementation of this interface with a\n * {@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder} or\n * {@link Launcher} to be notified of events that occur during test discovery.\n *\n * <p>All methods in this interface have empty <em>default</em> implementations.\n * Concrete implementations may therefore override one or more of these methods\n * to be notified of the selected events.\n *\n * <p>JUnit provides default implementations that are created via the factory\n * methods in\n * {@link org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners}.\n *\n * <p>The methods declared in this interface are called by the {@link Launcher}\n * created via the {@link org.junit.platform.launcher.core.LauncherFactory}\n * during test discovery.\n *\n * @since 1.6\n * @see org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners\n * @see LauncherDiscoveryRequest#getDiscoveryListener()\n * @see org.junit.platform.launcher.core.LauncherConfig.Builder#addLauncherDiscoveryListeners\n */\n@API(status = STABLE, since = \"1.11\")\npublic interface LauncherDiscoveryListener extends EngineDiscoveryListener {\n\n\t/**\n\t * No-op implementation of {@code LauncherDiscoveryListener}\n\t */\n\tLauncherDiscoveryListener NOOP = new LauncherDiscoveryListener() {\n\t};\n\n\t/**\n\t * Called when test discovery is about to be started.\n\t *\n\t * @param request the request for which discovery is being started\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tdefault void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t}\n\n\t/**\n\t * Called when test discovery has finished.\n\t *\n\t * @param request the request for which discovery has finished\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tdefault void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {\n\t}\n\n\t/**\n\t * Called when test discovery is about to be started for an engine.\n\t *\n\t * @param engineId the unique ID of the engine descriptor\n\t */\n\tdefault void engineDiscoveryStarted(UniqueId engineId) {\n\t}\n\n\t/**\n\t * Called when test discovery has finished for an engine.\n\t *\n\t * <p>Exceptions thrown by implementations of this method will cause the\n\t * complete test discovery to be aborted.\n\t *\n\t * @param engineId the unique ID of the engine descriptor\n\t * @param result the discovery result of the supplied engine\n\t * @see EngineDiscoveryResult\n\t */\n\tdefault void engineDiscoveryFinished(UniqueId engineId, EngineDiscoveryResult result) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherDiscoveryRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\n\n/**\n * {@code LauncherDiscoveryRequest} extends the {@link EngineDiscoveryRequest} API\n * with additional filters that are applied by the {@link Launcher} itself.\n *\n * <p>Specifically, a {@code LauncherDiscoveryRequest} contains the following.\n *\n * <ul>\n * <li>{@linkplain EngineFilter Engine Filters}: filters that are applied before\n * each {@code TestEngine} is executed. All of them have to include an engine for it\n * to contribute to the test plan.</li>\n * <li>{@linkplain ConfigurationParameters Configuration Parameters}: configuration\n * parameters that can be used to influence the discovery process</li>\n * <li>{@linkplain DiscoverySelector Discovery Selectors}: components that select\n * resources that a {@code TestEngine} can use to discover tests</li>\n * <li>{@linkplain DiscoveryFilter Discovery Filters}: filters that should be applied\n * by {@code TestEngines} during test discovery. All of them have to include a\n * resource for it to end up in the test plan.</li>\n * <li>{@linkplain PostDiscoveryFilter Post-Discovery Filters}: filters that will be\n * applied by the {@code Launcher} after {@code TestEngines} have performed test\n * discovery. All of them have to include a {@code TestDescriptor} for it to end up\n * in the test plan.</li>\n * </ul>\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 1.0\n * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder\n * @see EngineDiscoveryRequest\n * @see EngineFilter\n * @see ConfigurationParameters\n * @see DiscoverySelector\n * @see DiscoveryFilter\n * @see PostDiscoveryFilter\n * @see #getEngineFilters()\n * @see #getPostDiscoveryFilters()\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface LauncherDiscoveryRequest extends EngineDiscoveryRequest {\n\n\t/**\n\t * Get the {@code EngineFilters} for this request.\n\t *\n\t * <p>The returned filters are to be combined using AND semantics, i.e. all\n\t * of them have to include an engine for it to contribute to the test plan.\n\t *\n\t * @return the list of {@code EngineFilters} for this request; never\n\t * {@code null} but potentially empty\n\t */\n\tList<EngineFilter> getEngineFilters();\n\n\t/**\n\t * Get the {@code PostDiscoveryFilters} for this request.\n\t *\n\t * <p>The returned filters are to be combined using AND semantics, i.e. all\n\t * of them have to include a {@code TestDescriptor} for it to end up in the\n\t * test plan.\n\t *\n\t * @return the list of {@code PostDiscoveryFilters} for this request; never\n\t * {@code null} but potentially empty\n\t */\n\tList<PostDiscoveryFilter> getPostDiscoveryFilters();\n\n\t/**\n\t * Get the {@link LauncherDiscoveryListener} for this request.\n\t *\n\t * <p>The default implementation returns a no-op listener that ignores all\n\t * calls so that engines that call this methods can be used with an earlier\n\t * version of the JUnit Platform that did not yet include it.\n\t *\n\t * @return the discovery listener; never {@code null}\n\t * @since 1.6\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\t@Override\n\tdefault LauncherDiscoveryListener getDiscoveryListener() {\n\t\treturn LauncherDiscoveryListener.NOOP;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherExecutionRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.Collection;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.CancellationToken;\n\n/**\n * {@code LauncherExecutionRequest} encapsulates a request for test execution\n * passed to the {@link Launcher}.\n *\n * <p>Most importantly, a {@code LauncherExecutionRequest} contains either a\n * {@link LauncherDiscoveryRequest} for on-the-fly test discovery or a\n * {@link TestPlan} that has previously been discovered.\n *\n * <p>Moreover, a {@code LauncherExecutionRequest} may contain the following:\n *\n * <ul>\n * <li>Additional {@linkplain TestExecutionListener Test Execution Listeners}\n * that should be notified of events pertaining to this execution request.</li>\n * </ul>\n *\n * <p>This interface is not intended to be implemented by clients.\n *\n * @since 6.0\n * @see org.junit.platform.launcher.core.LauncherExecutionRequestBuilder\n * @see Launcher#execute(LauncherExecutionRequest)\n */\n@API(status = MAINTAINED, since = \"6.0\")\npublic interface LauncherExecutionRequest {\n\n\t/**\n\t * {@return the test plan for this execution request}\n\t *\n\t * <p>If absent, a {@link TestPlan} will be present.\n\t */\n\tOptional<TestPlan> getTestPlan();\n\n\t/**\n\t * {@return the discovery request for this execution request}\n\t *\n\t * <p>If absent, a {@link TestPlan} will be present.\n\t */\n\tOptional<LauncherDiscoveryRequest> getDiscoveryRequest();\n\n\t/**\n\t * {@return the collection of additional test execution listeners that\n\t * should be notified about events pertaining to this execution request}\n\t */\n\tCollection<? extends TestExecutionListener> getAdditionalTestExecutionListeners();\n\n\t/**\n\t * {@return the cancellation token for this execution request}\n\t */\n\tCancellationToken getCancellationToken();\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Interceptor for test discovery and execution by a {@link Launcher} in the\n * context of a {@link LauncherSession}.\n *\n * <p>Interceptors are instantiated once per {@link LauncherSession} and closed\n * after the session is closed. They can\n * {@linkplain #intercept(Invocation) intercept} the following invocations:\n * <ul>\n *     <li>\n *         creation of {@link LauncherSessionListener} instances registered via the\n *         {@link java.util.ServiceLoader ServiceLoader} mechanism\n *     </li>\n *     <li>\n *         creation of {@link Launcher} instances\n *     </li>\n *     <li>\n *         calls to {@link Launcher#discover(LauncherDiscoveryRequest)},\n *         {@link Launcher#execute(TestPlan, TestExecutionListener...)},\n *         {@link Launcher#execute(LauncherDiscoveryRequest, TestExecutionListener...)},\n *         and {@link Launcher#execute(LauncherExecutionRequest)},\n *     </li>\n * </ul>\n *\n * <p>Implementations of this interface can be registered via the\n * {@link java.util.ServiceLoader ServiceLoader} mechanism by additionally\n * setting the {@value LauncherConstants#ENABLE_LAUNCHER_INTERCEPTORS}\n * configuration parameter to {@code true}.\n *\n * <p>A typical use case is to create a custom {@link ClassLoader} in the\n * constructor of the implementing class, replace the\n * {@link Thread#setContextClassLoader(ClassLoader) contextClassLoader} of the\n * current thread while {@link #intercept(Invocation) intercepting} invocations,\n * and close the custom {@code ClassLoader} in {@link #close()}\n *\n * @since 1.10\n * @see Launcher\n * @see LauncherSession\n * @see LauncherConstants#ENABLE_LAUNCHER_INTERCEPTORS\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic interface LauncherInterceptor {\n\n\t/**\n\t * Intercept the supplied invocation.\n\t *\n\t * <p>Implementations must call {@link Invocation#proceed()} exactly once.\n\t *\n\t * @param invocation the intercepted invocation; never {@code null}\n\t * @return the result of the invocation\n\t */\n\t<T extends @Nullable Object> T intercept(Invocation<T> invocation);\n\n\t/**\n\t * Closes this interceptor.\n\t *\n\t * <p>Any resources held by this interceptor should be released by this\n\t * method.\n\t */\n\tvoid close();\n\n\t/**\n\t * An invocation that can be intercepted.\n\t *\n\t * <p>This interface is not intended to be implemented by clients.\n\t */\n\tinterface Invocation<T extends @Nullable Object> {\n\t\tT proceed();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSession.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.core.LauncherFactory;\n\n/**\n * The {@code LauncherSession} API is the main entry point for client code that\n * wishes to repeatedly <em>discover</em> and <em>execute</em> tests using one\n * or more {@linkplain org.junit.platform.engine.TestEngine test engines}.\n *\n * @since 1.8\n * @see Launcher\n * @see LauncherSessionListener\n * @see LauncherFactory\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface LauncherSession extends AutoCloseable {\n\n\t/**\n\t * Get the {@link Launcher} associated with this session.\n\t *\n\t * <p>Any call to the launcher returned by this method after the session has\n\t * been closed will throw an exception.\n\t */\n\tLauncher getLauncher();\n\n\t/**\n\t * Close this session and notify all registered\n\t * {@link LauncherSessionListener LauncherSessionListeners}.\n\t *\n\t * @apiNote The behavior of calling this method concurrently with any call\n\t * to the {@link Launcher} returned by {@link #getLauncher()} is currently\n\t * undefined.\n\t */\n\t@Override\n\tvoid close();\n\n\t/**\n\t * Get the {@link NamespacedHierarchicalStore} associated with this session.\n\t *\n\t * <p>All stored values that implement {@link AutoCloseable} are notified by\n\t * invoking their {@code close()} methods when this session is closed.\n\t *\n\t * <p>Any call to the store returned by this method after the session has\n\t * been closed will throw an exception.\n\t *\n\t * @since 1.13\n\t * @see NamespacedHierarchicalStore\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tNamespacedHierarchicalStore<Namespace> getStore();\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/LauncherSessionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.launcher.core.LauncherConfig;\nimport org.junit.platform.launcher.core.LauncherFactory;\n\n/**\n * Register an implementation of this interface to be notified when a\n * {@link LauncherSession} is opened and closed.\n *\n * <p>A {@code LauncherSessionListener} can be registered programmatically with\n * the {@link LauncherConfig.Builder#addLauncherSessionListeners LauncherConfig}\n * passed to the\n * {@link LauncherFactory#openSession(LauncherConfig) LauncherFactory} or\n * automatically via Java's {@link java.util.ServiceLoader ServiceLoader}\n * mechanism.\n *\n * <p>All methods in this class have empty <em>default</em> implementations.\n * Subclasses may therefore override one or more of these methods to be notified\n * of the selected events.\n *\n * <p>The methods declared in this interface are called by the {@link Launcher}\n * or {@link LauncherSession} created via the {@link LauncherFactory}.\n *\n * @since 1.8\n * @see LauncherSession\n * @see LauncherConfig.Builder#addLauncherSessionListeners\n * @see LauncherFactory\n */\n@API(status = STABLE, since = \"1.10\")\npublic interface LauncherSessionListener {\n\n\t/**\n\t * No-op implementation of {@code LauncherSessionListener}\n\t */\n\tLauncherSessionListener NOOP = new LauncherSessionListener() {\n\t};\n\n\t/**\n\t * Called when a launcher session was opened.\n\t *\n\t * @param session the opened session\n\t */\n\tdefault void launcherSessionOpened(LauncherSession session) {\n\t}\n\n\t/**\n\t * Called when a launcher session was closed.\n\t *\n\t * @param session the closed session\n\t */\n\tdefault void launcherSessionClosed(LauncherSession session) {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/MethodFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@link PostDiscoveryFilter} that is applied to the fully qualified\n * {@link Method} name without parameters.\n *\n * @since 1.12\n * @see #includeMethodNamePatterns(String...)\n * @see #excludeMethodNamePatterns(String...)\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic interface MethodFilter extends PostDiscoveryFilter {\n\n\t/**\n\t * Create a new <em>include</em> {@link MethodFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a method matches against at least one of the patterns,\n\t * the method will be included in the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * method names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see Method#getName()\n\t * @see #includeMethodNamePatterns(List)\n\t * @see #excludeMethodNamePatterns(String...)\n\t */\n\tstatic MethodFilter includeMethodNamePatterns(String... patterns) {\n\t\treturn new IncludeMethodFilter(patterns);\n\t}\n\n\t/**\n\t * Create a new <em>include</em> {@link MethodFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a method matches against at least one of the patterns,\n\t * the method will be included in the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * method names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see Method#getName()\n\t * @see #includeMethodNamePatterns(String...)\n\t * @see #excludeMethodNamePatterns(String...)\n\t */\n\tstatic MethodFilter includeMethodNamePatterns(List<String> patterns) {\n\t\treturn includeMethodNamePatterns(patterns.toArray(new String[0]));\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@link MethodFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a method matches against at least one of the patterns,\n\t * the method will be excluded from the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * method names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see Method#getName()\n\t * @see #excludeMethodNamePatterns(List)\n\t * @see #includeMethodNamePatterns(String...)\n\t */\n\tstatic MethodFilter excludeMethodNamePatterns(String... patterns) {\n\t\treturn new ExcludeMethodFilter(patterns);\n\t}\n\n\t/**\n\t * Create a new <em>exclude</em> {@link MethodFilter} based on the\n\t * supplied patterns.\n\t *\n\t * <p>The patterns are combined using OR semantics, i.e. if the fully\n\t * qualified name of a method matches against at least one of the patterns,\n\t * the method will be excluded from the result set.\n\t *\n\t * @param patterns regular expressions to match against fully qualified\n\t * method names; never {@code null}, empty, or containing {@code null}\n\t * @see Class#getName()\n\t * @see Method#getName()\n\t * @see #excludeMethodNamePatterns(String...)\n\t * @see #includeMethodNamePatterns(String...)\n\t */\n\tstatic MethodFilter excludeMethodNamePatterns(List<String> patterns) {\n\t\treturn excludeMethodNamePatterns(patterns.toArray(new String[0]));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/PostDiscoveryFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * A {@code PostDiscoveryFilter} is applied to {@link TestDescriptor TestDescriptors}\n * after test discovery.\n *\n * <p>A {@code PostDiscoveryFilter} must <strong>not</strong> modify the\n * {@link TestDescriptor TestDescriptors} it is applied to in any way.\n *\n * <p>{@link TestEngine TestEngines} must <strong>not</strong> apply\n * {@code PostDiscoveryFilters} during the test discovery phase.\n *\n * @since 1.0\n * @see LauncherDiscoveryRequest\n * @see TestEngine\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface PostDiscoveryFilter extends Filter<TestDescriptor> {\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/TagFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static java.util.Arrays.asList;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.launcher.tagexpression.TagExpression;\n\n/**\n * Factory methods for creating {@link PostDiscoveryFilter PostDiscoveryFilters}\n * based on <em>included</em> and <em>excluded</em> tags or tag expressions.\n *\n * <p>Tag expressions are boolean expressions with the following allowed\n * operators: {@code !} (not), {@code &} (and), and {@code |} (or). Parentheses\n * can be used to adjust for operator precedence. Please refer to the\n * <a href=\"https://docs.junit.org/current/running-tests/tags.html#expressions\">JUnit User Guide</a>\n * for usage examples.\n *\n * <p>Please note that a tag name is a valid tag expression. Thus, wherever a tag\n * expression can be used, a single tag name can also be used.\n *\n * @since 1.0\n * @see #includeTags(String...)\n * @see #excludeTags(String...)\n * @see TestTag\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class TagFilter {\n\n\tprivate TagFilter() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Create an <em>include</em> filter based on the supplied tag expressions.\n\t *\n\t * <p>Containers and tests will only be executed if their tags match at\n\t * least one of the supplied <em>included</em> tag expressions.\n\t *\n\t * @param tagExpressions the included tag expressions; never {@code null} or\n\t * empty\n\t * @throws PreconditionViolationException if the supplied tag expressions\n\t * array is {@code null} or empty, or if any individual tag expression is\n\t * not syntactically valid\n\t * @see #includeTags(List)\n\t * @see TestTag#isValid(String)\n\t */\n\tpublic static PostDiscoveryFilter includeTags(String... tagExpressions) throws PreconditionViolationException {\n\t\tPreconditions.notNull(tagExpressions, \"array of tag expressions must not be null\");\n\t\treturn includeTags(asList(tagExpressions));\n\t}\n\n\t/**\n\t * Create an <em>include</em> filter based on the supplied tag expressions.\n\t *\n\t * <p>Containers and tests will only be executed if their tags match at\n\t * least one of the supplied <em>included</em> tag expressions.\n\t *\n\t * @param tagExpressions the included tag expressions; never {@code null} or\n\t * empty\n\t * @throws PreconditionViolationException if the supplied tag expressions\n\t * array is {@code null} or empty, or if any individual tag expression is\n\t * not syntactically valid\n\t * @see #includeTags(String...)\n\t * @see TestTag#isValid(String)\n\t */\n\tpublic static PostDiscoveryFilter includeTags(List<String> tagExpressions) throws PreconditionViolationException {\n\t\tPreconditions.notEmpty(tagExpressions, \"list of tag expressions must not be null or empty\");\n\t\treturn includeMatching(tagExpressions);\n\t}\n\n\t/**\n\t * Create an <em>exclude</em> filter based on the supplied tag expressions.\n\t *\n\t * <p>Containers and tests will only be executed if their tags do\n\t * <em>not</em> match any of the supplied <em>excluded</em> tag expressions.\n\t *\n\t * @param tagExpressions the excluded tag expressions; never {@code null} or\n\t * empty\n\t * @throws PreconditionViolationException if the supplied tag expressions\n\t * array is {@code null} or empty, or if any individual tag expression is\n\t * not syntactically valid\n\t * @see #excludeTags(List)\n\t * @see TestTag#isValid(String)\n\t */\n\tpublic static PostDiscoveryFilter excludeTags(String... tagExpressions) throws PreconditionViolationException {\n\t\tPreconditions.notNull(tagExpressions, \"array of tag expressions must not be null\");\n\t\treturn excludeTags(asList(tagExpressions));\n\t}\n\n\t/**\n\t * Create an <em>exclude</em> filter based on the supplied tag expressions.\n\t *\n\t * <p>Containers and tests will only be executed if their tags do\n\t * <em>not</em> match any of the supplied <em>excluded</em> tag expressions.\n\t *\n\t * @param tagExpressions the excluded tag expressions; never {@code null} or\n\t * empty\n\t * @throws PreconditionViolationException if the supplied tag expressions\n\t * array is {@code null} or empty, or if any individual tag expression is\n\t * not syntactically valid\n\t * @see #excludeTags(String...)\n\t * @see TestTag#isValid(String)\n\t */\n\tpublic static PostDiscoveryFilter excludeTags(List<String> tagExpressions) throws PreconditionViolationException {\n\t\tPreconditions.notEmpty(tagExpressions, \"list of tag expressions must not be null or empty\");\n\t\treturn excludeMatching(tagExpressions);\n\t}\n\n\tprivate static PostDiscoveryFilter includeMatching(List<String> tagExpressions) {\n\t\tSupplier<@Nullable String> inclusionReason = () -> inclusionReasonExpressionSatisfy(tagExpressions);\n\t\tSupplier<@Nullable String> exclusionReason = () -> exclusionReasonExpressionNotSatisfy(tagExpressions);\n\t\tList<TagExpression> parsedTagExpressions = parseAll(tagExpressions);\n\t\treturn descriptor -> {\n\t\t\tSet<TestTag> tags = descriptor.getTags();\n\t\t\tboolean included = parsedTagExpressions.stream().anyMatch(expression -> expression.evaluate(tags));\n\n\t\t\treturn FilterResult.includedIf(included, inclusionReason, exclusionReason);\n\t\t};\n\t}\n\n\tprivate static String inclusionReasonExpressionSatisfy(List<String> tagExpressions) {\n\t\treturn \"included because tags match expression(s): [%s]\".formatted(formatToString(tagExpressions));\n\t}\n\n\tprivate static String exclusionReasonExpressionNotSatisfy(List<String> tagExpressions) {\n\t\treturn \"excluded because tags do not match tag expression(s): [%s]\".formatted(formatToString(tagExpressions));\n\t}\n\n\tprivate static PostDiscoveryFilter excludeMatching(List<String> tagExpressions) {\n\t\tSupplier<@Nullable String> inclusionReason = () -> inclusionReasonExpressionNotSatisfy(tagExpressions);\n\t\tSupplier<@Nullable String> exclusionReason = () -> exclusionReasonExpressionSatisfy(tagExpressions);\n\t\tList<TagExpression> parsedTagExpressions = parseAll(tagExpressions);\n\t\treturn descriptor -> {\n\t\t\tSet<TestTag> tags = descriptor.getTags();\n\t\t\tboolean included = parsedTagExpressions.stream().noneMatch(expression -> expression.evaluate(tags));\n\n\t\t\treturn FilterResult.includedIf(included, inclusionReason, exclusionReason);\n\t\t};\n\t}\n\n\tprivate static String inclusionReasonExpressionNotSatisfy(List<String> tagExpressions) {\n\t\treturn \"included because tags do not match expression(s): [%s]\".formatted(formatToString(tagExpressions));\n\t}\n\n\tprivate static String exclusionReasonExpressionSatisfy(List<String> tagExpressions) {\n\t\treturn \"excluded because tags match tag expression(s): [%s]\".formatted(formatToString(tagExpressions));\n\t}\n\n\tprivate static String formatToString(List<String> tagExpressions) {\n\t\treturn tagExpressions.stream().map(String::strip).sorted().collect(Collectors.joining(\",\"));\n\t}\n\n\tprivate static List<TagExpression> parseAll(List<String> tagExpressions) {\n\t\treturn tagExpressions.stream().map(TagFilter::parse).toList();\n\t}\n\n\tprivate static TagExpression parse(@Nullable String tagExpression) {\n\t\treturn TagExpression.parseFrom(tagExpression).tagExpressionOrThrow(\n\t\t\tmessage -> new PreconditionViolationException(\n\t\t\t\t\"Unable to parse tag expression \\\"\" + tagExpression + \"\\\": \" + message));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * Register a concrete implementation of this interface with a {@link Launcher}\n * or {@link LauncherExecutionRequest} to be notified of events that occur\n * during test execution.\n *\n * <p>All methods in this interface have empty <em>default</em> implementations.\n * Concrete implementations may therefore override one or more of these methods\n * to be notified of the selected events.\n *\n * <p>All {@code TestExecutionListener} methods are called sequentially. Methods\n * for start events are called in registration order while methods for finish\n * events are called in reverse order. Test case execution won't start before\n * all {@link #executionStarted(TestIdentifier)} calls have returned.\n *\n * <p>If an exception is thrown by an implementation of a method of this\n * interface, the exception will be caught and logged unless it is deemed\n * {@linkplain UnrecoverableExceptions unrecoverable}. In consequence, a\n * {@code TestExecutionListener} cannot cause test execution to fail or abort it\n * early by throwing an exception.\n *\n * <p>JUnit provides two example implementations.\n *\n * <ul>\n * <li>{@link org.junit.platform.launcher.listeners.LoggingListener}</li>\n * <li>{@link org.junit.platform.launcher.listeners.SummaryGeneratingListener}</li>\n * </ul>\n *\n * <p>Contrary to JUnit 4, {@linkplain org.junit.platform.engine.TestEngine test engines}\n * are supposed to report events not only for {@linkplain TestIdentifier identifiers}\n * that represent executable leaves in the {@linkplain TestPlan test plan} but also\n * for all intermediate containers. However, while both the JUnit Vintage and JUnit\n * Jupiter engines comply with this contract, there is no way to guarantee this for\n * third-party engines.\n *\n * <p>A {@code TestExecutionListener} can access\n * {@linkplain org.junit.platform.engine.ConfigurationParameters configuration\n * parameters} via the {@link TestPlan#getConfigurationParameters()\n * getConfigurationParameters()} method in the {@code TestPlan} supplied to\n * {@link #testPlanExecutionStarted(TestPlan)} and\n * {@link #testPlanExecutionFinished(TestPlan)}.\n *\n * <p>Note on concurrency: {@link #testPlanExecutionStarted(TestPlan)} and\n * {@link #testPlanExecutionFinished(TestPlan)} are always called from the same\n * thread. It is safe to assume that there is at most one {@code TestPlan}\n * instance at a time. All other methods could be called from different threads\n * concurrently in case one or multiple test engines execute tests in parallel.\n *\n * @since 1.0\n * @see Launcher\n * @see TestPlan\n * @see TestIdentifier\n */\n@API(status = STABLE, since = \"1.0\")\npublic interface TestExecutionListener {\n\n\t/**\n\t * Called when the execution of the {@link TestPlan} has started,\n\t * <em>before</em> any test has been executed.\n\t *\n\t * <p>Called from the same thread as {@link #testPlanExecutionFinished(TestPlan)}.\n\t *\n\t * @param testPlan describes the tree of tests about to be executed\n\t */\n\tdefault void testPlanExecutionStarted(TestPlan testPlan) {\n\t}\n\n\t/**\n\t * Called when the execution of the {@link TestPlan} has finished,\n\t * <em>after</em> all tests have been executed.\n\t *\n\t * <p>Called from the same thread as {@link #testPlanExecutionStarted(TestPlan)}.\n\t *\n\t * @param testPlan describes the tree of tests that have been executed\n\t */\n\tdefault void testPlanExecutionFinished(TestPlan testPlan) {\n\t}\n\n\t/**\n\t * Called when a new, dynamic {@link TestIdentifier} has been registered.\n\t *\n\t * <p>A <em>dynamic test</em> is a test that is not known a-priori and\n\t * therefore not contained in the original {@link TestPlan}.\n\t *\n\t * @param testIdentifier the identifier of the newly registered test\n\t * or container\n\t */\n\tdefault void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t}\n\n\t/**\n\t * Called when the execution of a leaf or subtree of the {@link TestPlan}\n\t * has been skipped.\n\t *\n\t * <p>The {@link TestIdentifier} may represent a test or a container. In\n\t * the case of a container, no listener methods will be called for any of\n\t * its descendants.\n\t *\n\t * <p>A skipped test or subtree of tests will never be reported as\n\t * {@linkplain #executionStarted started} or\n\t * {@linkplain #executionFinished finished}.\n\t *\n\t * @param testIdentifier the identifier of the skipped test or container\n\t * @param reason a human-readable message describing why the execution\n\t * has been skipped\n\t */\n\tdefault void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t}\n\n\t/**\n\t * Called when the execution of a leaf or subtree of the {@link TestPlan}\n\t * is about to be started.\n\t *\n\t * <p>The {@link TestIdentifier} may represent a test or a container.\n\t *\n\t * <p>This method will only be called if the test or container has not\n\t * been {@linkplain #executionSkipped skipped}.\n\t *\n\t * <p>This method will be called for a container {@code TestIdentifier}\n\t * <em>before</em> {@linkplain #executionStarted starting} or\n\t * {@linkplain #executionSkipped skipping} any of its children.\n\t *\n\t * @param testIdentifier the identifier of the started test or container\n\t */\n\tdefault void executionStarted(TestIdentifier testIdentifier) {\n\t}\n\n\t/**\n\t * Called when the execution of a leaf or subtree of the {@link TestPlan}\n\t * has finished, regardless of the outcome.\n\t *\n\t * <p>The {@link TestIdentifier} may represent a test or a container.\n\t *\n\t * <p>This method will only be called if the test or container has not\n\t * been {@linkplain #executionSkipped skipped}.\n\t *\n\t * <p>This method will be called for a container {@code TestIdentifier}\n\t * <em>after</em> all of its children have been\n\t * {@linkplain #executionSkipped skipped} or have\n\t * {@linkplain #executionFinished finished}.\n\t *\n\t * <p>The {@link TestExecutionResult} describes the result of the execution\n\t * for the supplied {@code TestIdentifier}. The result does not include or\n\t * aggregate the results of its children. For example, a container with a\n\t * failing test will be reported as {@link Status#SUCCESSFUL SUCCESSFUL} even\n\t * if one or more of its children are reported as {@link Status#FAILED FAILED}.\n\t *\n\t * @param testIdentifier the identifier of the finished test or container\n\t * @param testExecutionResult the (unaggregated) result of the execution for\n\t * the supplied {@code TestIdentifier}\n\t *\n\t * @see TestExecutionResult\n\t */\n\tdefault void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t}\n\n\t/**\n\t * Called when additional test reporting data has been published for\n\t * the supplied {@link TestIdentifier}.\n\t *\n\t * <p>Can be called at any time during the execution of a test plan.\n\t *\n\t * @param testIdentifier describes the test or container to which the entry pertains\n\t * @param entry the published {@code ReportEntry}\n\t */\n\tdefault void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t}\n\n\t/**\n\t * Called when a file or directory has been published for the supplied\n\t * {@link TestIdentifier}.\n\t *\n\t * <p>Can be called at any time during the execution of a test plan.\n\t *\n\t * @param testIdentifier describes the test or container to which the entry pertains\n\t * @param file the published {@code FileEntry}\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tdefault void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Collections.unmodifiableSet;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.LinkedHashSet;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestDescriptor.Type;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Immutable data transfer object that represents a test or container which is\n * usually part of a {@link TestPlan}.\n *\n * @since 1.0\n * @see TestPlan\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class TestIdentifier implements Serializable {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 2L;\n\n\tprivate final UniqueId uniqueId;\n\n\tprivate final @Nullable UniqueId parentId;\n\n\tprivate final String displayName;\n\tprivate final String legacyReportingName;\n\n\tprivate final @Nullable TestSource source;\n\n\t@SuppressWarnings(\"serial\") // Declared type is Set (not Serializable); actual instances are Serializable.\n\tprivate final Set<TestTag> tags;\n\n\tprivate final Type type;\n\n\t/**\n\t * Factory for creating a new {@link TestIdentifier} from a {@link TestDescriptor}.\n\t */\n\t@API(status = INTERNAL, since = \"1.0\")\n\tpublic static TestIdentifier from(TestDescriptor testDescriptor) {\n\t\tPreconditions.notNull(testDescriptor, \"TestDescriptor must not be null\");\n\t\tUniqueId uniqueId = testDescriptor.getUniqueId();\n\t\tString displayName = testDescriptor.getDisplayName();\n\t\tTestSource source = testDescriptor.getSource().orElse(null);\n\t\tSet<TestTag> tags = testDescriptor.getTags();\n\t\tType type = testDescriptor.getType();\n\t\tUniqueId parentId = testDescriptor.getParent().map(TestDescriptor::getUniqueId).orElse(null);\n\t\tString legacyReportingName = testDescriptor.getLegacyReportingName();\n\t\treturn new TestIdentifier(uniqueId, displayName, source, tags, type, parentId, legacyReportingName);\n\t}\n\n\tprivate TestIdentifier(UniqueId uniqueId, String displayName, @Nullable TestSource source, Set<TestTag> tags,\n\t\t\tType type, @Nullable UniqueId parentId, String legacyReportingName) {\n\t\tPreconditions.notNull(type, \"TestDescriptor.Type must not be null\");\n\t\tthis.uniqueId = uniqueId;\n\t\tthis.parentId = parentId;\n\t\tthis.displayName = displayName;\n\t\tthis.source = source;\n\t\tthis.tags = copyOf(tags);\n\t\tthis.type = type;\n\t\tthis.legacyReportingName = legacyReportingName;\n\t}\n\n\tprivate Set<TestTag> copyOf(Set<TestTag> tags) {\n\t\treturn switch (tags.size()) {\n\t\t\tcase 0 -> emptySet();\n\t\t\tcase 1 -> Set.of(getOnlyElement(tags));\n\t\t\tdefault -> new LinkedHashSet<>(tags);\n\t\t};\n\t}\n\n\t/**\n\t * Get the unique ID of the represented test or container as a\n\t * {@code String}.\n\t *\n\t * <p>Uniqueness must be guaranteed across an entire\n\t * {@linkplain TestPlan test plan}, regardless of how many engines are used\n\t * behind the scenes.\n\t *\n\t * @return the unique ID for this identifier; never {@code null}\n\t */\n\tpublic String getUniqueId() {\n\t\treturn this.uniqueId.toString();\n\t}\n\n\t/**\n\t * Get the unique ID of the represented test or container as a\n\t * {@code UniqueId}.\n\t *\n\t * <p>Uniqueness must be guaranteed across an entire\n\t * {@linkplain TestPlan test plan}, regardless of how many engines are used\n\t * behind the scenes.\n\t *\n\t * @return the unique ID for this identifier; never {@code null}\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.8\")\n\tpublic UniqueId getUniqueIdObject() {\n\t\treturn this.uniqueId;\n\t}\n\n\t/**\n\t * Get the unique ID of this identifier's parent as a {@code String}, if\n\t * available.\n\t *\n\t * <p>An identifier without a parent is called a <em>root</em>.\n\t *\n\t * @return a container for the unique ID for this identifier's parent;\n\t * never {@code null} though potentially <em>empty</em>\n\t */\n\tpublic Optional<String> getParentId() {\n\t\treturn getParentIdObject().map(UniqueId::toString);\n\t}\n\n\t/**\n\t * Get the unique ID of this identifier's parent as a {@code UniqueId}, if\n\t * available.\n\t *\n\t * <p>An identifier without a parent is called a <em>root</em>.\n\t *\n\t * @return a container for the unique ID for this identifier's parent;\n\t * never {@code null} though potentially <em>empty</em>\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.8\")\n\tpublic Optional<UniqueId> getParentIdObject() {\n\t\treturn Optional.ofNullable(this.parentId);\n\t}\n\n\t/**\n\t * Get the display name of the represented test or container.\n\t *\n\t * <p>A <em>display name</em> is a human-readable name for a test or\n\t * container that is typically used for test reporting in IDEs and build\n\t * tools. Display names may contain spaces, special characters, and emoji,\n\t * and the format may be customized by {@link org.junit.platform.engine.TestEngine\n\t * TestEngines} or potentially by end users as well. Consequently, display\n\t * names should never be parsed; rather, they should be used for display\n\t * purposes only.\n\t *\n\t * @return the display name for this identifier; never {@code null} or blank\n\t * @see #getSource()\n\t * @see org.junit.platform.engine.TestDescriptor#getDisplayName()\n\t */\n\tpublic String getDisplayName() {\n\t\treturn this.displayName;\n\t}\n\n\t/**\n\t * Get the name of this identifier in a format that is suitable for legacy\n\t * reporting infrastructure &mdash; for example, for reporting systems built\n\t * on the Ant-based XML reporting format for JUnit 4.\n\t *\n\t * <p>The default implementation delegates to {@link #getDisplayName()}.\n\t *\n\t * @return the legacy reporting name; never {@code null} or blank\n\t * @see org.junit.platform.engine.TestDescriptor#getLegacyReportingName()\n\t * @see org.junit.platform.reporting.legacy.LegacyReportingUtils\n\t */\n\t@SuppressWarnings(\"JavadocReference\")\n\tpublic String getLegacyReportingName() {\n\t\treturn this.legacyReportingName;\n\t}\n\n\t/**\n\t * Get the underlying descriptor type.\n\t *\n\t * @return the underlying descriptor type; never {@code null}\n\t */\n\tpublic Type getType() {\n\t\treturn type;\n\t}\n\n\t/**\n\t * Determine if this identifier represents a test.\n\t *\n\t * @return {@code true} if the underlying descriptor type represents a test,\n\t * {@code false} otherwise\n\t * @see Type#isTest()\n\t */\n\tpublic boolean isTest() {\n\t\treturn getType().isTest();\n\t}\n\n\t/**\n\t * Determine if this identifier represents a container.\n\t *\n\t * @return {@code true} if the underlying descriptor type represents a container,\n\t * {@code false} otherwise\n\t * @see Type#isContainer()\n\t */\n\tpublic boolean isContainer() {\n\t\treturn getType().isContainer();\n\t}\n\n\t/**\n\t * Get the {@linkplain TestSource source} of the represented test\n\t * or container, if available.\n\t *\n\t * @see TestSource\n\t */\n\tpublic Optional<TestSource> getSource() {\n\t\treturn Optional.ofNullable(this.source);\n\t}\n\n\t/**\n\t * Get the set of {@linkplain TestTag tags} associated with the represented\n\t * test or container.\n\t *\n\t * @see TestTag\n\t */\n\tpublic Set<TestTag> getTags() {\n\t\treturn unmodifiableSet(this.tags);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\treturn (obj instanceof TestIdentifier that && Objects.equals(this.uniqueId, that.uniqueId));\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn this.uniqueId.hashCode();\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"uniqueId\", this.uniqueId)\n\t\t\t\t.append(\"parentId\", this.parentId)\n\t\t\t\t.append(\"displayName\", this.displayName)\n\t\t\t\t.append(\"legacyReportingName\", this.legacyReportingName)\n\t\t\t\t.append(\"source\", this.source)\n\t\t\t\t.append(\"tags\", this.tags)\n\t\t\t\t.append(\"type\", this.type)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Collections.synchronizedSet;\nimport static java.util.Collections.unmodifiableSet;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * {@code TestPlan} describes the tree of tests and containers as discovered\n * by a {@link Launcher}.\n *\n * <p>Tests and containers are represented by {@link TestIdentifier} instances.\n * The complete set of identifiers comprises a tree-like structure. However,\n * each identifier only stores the unique ID of its parent. This class provides\n * a number of helpful methods to retrieve the\n * {@linkplain #getParent(TestIdentifier) parent},\n * {@linkplain #getChildren(TestIdentifier) children}, and\n * {@linkplain #getDescendants(TestIdentifier) descendants} of an identifier.\n *\n * <p>While the contained instances of {@link TestIdentifier} are immutable,\n * instances of this class contain mutable state. For example, when a dynamic\n * test is registered at runtime, it is added to the original test plan and\n * reported to {@link TestExecutionListener} implementations.\n *\n * <p>This class is not intended to be extended by clients.\n *\n * @since 1.0\n * @see Launcher\n * @see TestExecutionListener\n */\n@API(status = STABLE, since = \"1.0\")\npublic class TestPlan {\n\n\tprivate final Set<TestIdentifier> roots = synchronizedSet(new LinkedHashSet<>(4));\n\tprivate final Map<UniqueId, Set<TestIdentifier>> children = new ConcurrentHashMap<>(32);\n\tprivate final Map<UniqueId, TestIdentifier> allIdentifiers = new ConcurrentHashMap<>(32);\n\n\tprivate final boolean containsTests;\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\n\t/**\n\t * Construct a new {@code TestPlan} from the supplied collection of\n\t * {@link TestDescriptor TestDescriptors}.\n\t *\n\t * <p>Each supplied {@code TestDescriptor} is expected to be a descriptor\n\t * for a {@link org.junit.platform.engine.TestEngine TestEngine}.\n\t *\n\t * @param containsTests whether the test plan contains tests\n\t * @param engineDescriptors the engine test descriptors from which the test\n\t * plan should be created; never {@code null}\n\t * @param configurationParameters the {@code ConfigurationParameters} for\n\t * this test plan; never {@code null}\n\t * @param outputDirectoryCreator the {@code OutputDirectoryProvider} for\n\t * this test plan; never {@code null}\n\t * @return a new test plan\n\t */\n\t@API(status = INTERNAL, since = \"1.14\")\n\tpublic static TestPlan from(boolean containsTests, Collection<TestDescriptor> engineDescriptors,\n\t\t\tConfigurationParameters configurationParameters, OutputDirectoryCreator outputDirectoryCreator) {\n\t\tPreconditions.notNull(engineDescriptors, \"Cannot create TestPlan from a null collection of TestDescriptors\");\n\t\tPreconditions.notNull(configurationParameters, \"Cannot create TestPlan from null ConfigurationParameters\");\n\t\tTestPlan testPlan = new TestPlan(containsTests, configurationParameters, outputDirectoryCreator);\n\t\tTestDescriptor.Visitor visitor = descriptor -> testPlan.addInternal(TestIdentifier.from(descriptor));\n\t\tengineDescriptors.forEach(engineDescriptor -> engineDescriptor.accept(visitor));\n\t\treturn testPlan;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.4\")\n\tprotected TestPlan(boolean containsTests, ConfigurationParameters configurationParameters,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.containsTests = containsTests;\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t}\n\n\t@API(status = INTERNAL, since = \"1.8\")\n\tpublic void addInternal(TestIdentifier testIdentifier) {\n\t\tPreconditions.notNull(testIdentifier, \"testIdentifier must not be null\");\n\t\tallIdentifiers.put(testIdentifier.getUniqueIdObject(), testIdentifier);\n\n\t\t// Root identifiers. Typically, a test engine.\n\t\tif (testIdentifier.getParentIdObject().isEmpty()) {\n\t\t\troots.add(testIdentifier);\n\t\t\treturn;\n\t\t}\n\n\t\t// Identifiers without a parent in this test plan. Could be a test\n\t\t// engine that is used in a suite.\n\t\tUniqueId parentId = testIdentifier.getParentIdObject().get();\n\t\tif (!allIdentifiers.containsKey(parentId)) {\n\t\t\troots.add(testIdentifier);\n\t\t\treturn;\n\t\t}\n\n\t\tSet<TestIdentifier> directChildren = children.computeIfAbsent(parentId,\n\t\t\tkey -> synchronizedSet(new LinkedHashSet<>(16)));\n\t\tdirectChildren.add(testIdentifier);\n\t}\n\n\t@API(status = INTERNAL, since = \"6.1\")\n\tpublic void removeInternal(UniqueId uniqueId) {\n\t\tPreconditions.notNull(uniqueId, \"uniqueId must not be null\");\n\t\tvar removedTestIdentifier = removeSubtree(uniqueId);\n\t\tif (removedTestIdentifier != null) {\n\t\t\troots.removeIf(root -> root.getUniqueIdObject().equals(uniqueId));\n\t\t\tremovedTestIdentifier.getParentIdObject().ifPresent(\n\t\t\t\tparentId -> children.getOrDefault(parentId, Set.of()).remove(removedTestIdentifier));\n\t\t}\n\t}\n\n\tprivate @Nullable TestIdentifier removeSubtree(UniqueId uniqueId) {\n\t\tvar testIdentifier = allIdentifiers.remove(uniqueId);\n\t\tvar removedChildren = children.remove(uniqueId);\n\t\tif (removedChildren != null && !removedChildren.isEmpty()) {\n\t\t\tfor (var child : removedChildren) {\n\t\t\t\tremoveSubtree(child.getUniqueIdObject());\n\t\t\t}\n\t\t}\n\t\treturn testIdentifier;\n\t}\n\n\t/**\n\t * Get the root {@link TestIdentifier TestIdentifiers} for this test plan.\n\t *\n\t * @return an unmodifiable set of the root identifiers\n\t */\n\tpublic Set<TestIdentifier> getRoots() {\n\t\treturn unmodifiableSet(roots);\n\t}\n\n\t/**\n\t * Get the parent of the supplied {@link TestIdentifier}.\n\t *\n\t * @param child the identifier to look up the parent for; never {@code null}\n\t * @return an {@code Optional} containing the parent, if present\n\t */\n\tpublic Optional<TestIdentifier> getParent(TestIdentifier child) {\n\t\tPreconditions.notNull(child, \"child must not be null\");\n\t\treturn child.getParentIdObject().map(this::getTestIdentifier);\n\t}\n\n\t/**\n\t * Get the children of the supplied {@link TestIdentifier}.\n\t *\n\t * @param parent the identifier to look up the children for; never {@code null}\n\t * @return an unmodifiable set of the parent's children, potentially empty\n\t * @see #getChildren(UniqueId)\n\t */\n\tpublic Set<TestIdentifier> getChildren(TestIdentifier parent) {\n\t\tPreconditions.notNull(parent, \"parent must not be null\");\n\t\treturn getChildren(parent.getUniqueIdObject());\n\t}\n\n\t/**\n\t * Get the children of the supplied unique ID.\n\t *\n\t * @param parentId the unique ID to look up the children for; never\n\t * {@code null}\n\t * @return an unmodifiable set of the parent's children, potentially empty\n\t * @see #getChildren(TestIdentifier)\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic Set<TestIdentifier> getChildren(UniqueId parentId) {\n\t\treturn children.containsKey(parentId) ? unmodifiableSet(children.get(parentId)) : emptySet();\n\t}\n\n\t/**\n\t * Get the {@link TestIdentifier} with the supplied unique ID.\n\t *\n\t * @param uniqueId the unique ID to look up the identifier for; never\n\t * {@code null} or blank\n\t * @return the identifier with the supplied unique ID; never {@code null}\n\t * @throws PreconditionViolationException if no {@code TestIdentifier}\n\t * with the supplied unique ID is present in this test plan\n\t * @deprecated Use {@link #getTestIdentifier(UniqueId)} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"1.10\", consumers = \"Gradle\")\n\t@Deprecated(since = \"1.10\")\n\tpublic TestIdentifier getTestIdentifier(String uniqueId) throws PreconditionViolationException {\n\t\tPreconditions.notBlank(uniqueId, \"unique ID must not be null or blank\");\n\t\treturn getTestIdentifier(UniqueId.parse(uniqueId));\n\t}\n\n\t/**\n\t * Get the {@link TestIdentifier} with the supplied unique ID.\n\t *\n\t * @param uniqueId the unique ID to look up the identifier for; never\n\t * {@code null}\n\t * @return the identifier with the supplied unique ID; never {@code null}\n\t * @throws PreconditionViolationException if no {@code TestIdentifier}\n\t * with the supplied unique ID is present in this test plan\n\t */\n\t@API(status = MAINTAINED, since = \"1.10\")\n\tpublic TestIdentifier getTestIdentifier(UniqueId uniqueId) {\n\t\tPreconditions.notNull(uniqueId, () -> \"uniqueId must not be null\");\n\t\treturn Preconditions.notNull(allIdentifiers.get(uniqueId),\n\t\t\t() -> \"No TestIdentifier with unique ID [\" + uniqueId + \"] has been added to this TestPlan.\");\n\t}\n\n\t/**\n\t * Count all {@link TestIdentifier TestIdentifiers} that satisfy the\n\t * given {@linkplain Predicate predicate}.\n\t *\n\t * @param predicate a predicate which returns {@code true} for identifiers\n\t * to be counted; never {@code null}\n\t * @return the number of identifiers that satisfy the supplied predicate\n\t */\n\tpublic long countTestIdentifiers(Predicate<? super TestIdentifier> predicate) {\n\t\tPreconditions.notNull(predicate, \"Predicate must not be null\");\n\t\treturn allIdentifiers.values().stream().filter(predicate).count();\n\t}\n\n\t/**\n\t * Get all descendants of the supplied {@link TestIdentifier} (i.e.,\n\t * all of its children and their children, recursively).\n\t *\n\t * @param parent the identifier to look up the descendants for; never {@code null}\n\t * @return an unmodifiable set of the parent's descendants, potentially empty\n\t */\n\tpublic Set<TestIdentifier> getDescendants(TestIdentifier parent) {\n\t\tPreconditions.notNull(parent, \"parent must not be null\");\n\t\tSet<TestIdentifier> result = new LinkedHashSet<>(16);\n\t\tSet<TestIdentifier> children = getChildren(parent);\n\t\tresult.addAll(children);\n\t\tfor (TestIdentifier child : children) {\n\t\t\tresult.addAll(getDescendants(child));\n\t\t}\n\t\treturn unmodifiableSet(result);\n\t}\n\n\t/**\n\t * Return whether this test plan contains any tests.\n\t *\n\t * <p>A test plan contains tests, if at least one of the contained engine\n\t * descriptors {@linkplain TestDescriptor#containsTests(TestDescriptor)\n\t * contains tests}.\n\t *\n\t * @return {@code true} if this test plan contains tests\n\t * @see TestDescriptor#containsTests(TestDescriptor)\n\t */\n\tpublic boolean containsTests() {\n\t\treturn containsTests;\n\t}\n\n\t/**\n\t * Get the {@link ConfigurationParameters} for this test plan.\n\t *\n\t * @return the configuration parameters; never {@code null}\n\t * @since 1.8\n\t */\n\t@API(status = MAINTAINED, since = \"1.8\")\n\tpublic ConfigurationParameters getConfigurationParameters() {\n\t\treturn this.configurationParameters;\n\t}\n\n\t/**\n\t * Get the\n\t * {@link org.junit.platform.engine.reporting.OutputDirectoryProvider} for\n\t * this test plan.\n\t *\n\t * @return the output directory provider; never {@code null}\n\t * @since 1.12\n\t * @deprecated Please use {@link #getOutputDirectoryCreator()} instead\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\tpublic org.junit.platform.engine.reporting.OutputDirectoryProvider getOutputDirectoryProvider() {\n\t\treturn org.junit.platform.engine.reporting.OutputDirectoryProvider.castOrAdapt(getOutputDirectoryCreator());\n\t}\n\n\t/**\n\t * Get the {@link OutputDirectoryCreator} for this test plan.\n\t *\n\t * @return the output directory creator; never {@code null}\n\t * @since 1.14\n\t */\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn outputDirectoryCreator;\n\t}\n\n\t/**\n\t * Accept the supplied {@link Visitor} for a depth-first traversal of the\n\t * test plan.\n\t *\n\t * @param visitor the visitor to accept; never {@code null}\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic void accept(Visitor visitor) {\n\t\tgetRoots().forEach(it -> accept(visitor, it));\n\t}\n\n\tprivate void accept(Visitor visitor, TestIdentifier testIdentifier) {\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tvisitor.preVisitContainer(testIdentifier);\n\t\t}\n\t\tvisitor.visit(testIdentifier);\n\t\tgetChildren(testIdentifier).forEach(it -> accept(visitor, it));\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tvisitor.postVisitContainer(testIdentifier);\n\t\t}\n\t}\n\n\t/**\n\t * Visitor for {@link TestIdentifier TestIdentifiers} in a {@link TestPlan}.\n\t *\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic interface Visitor {\n\n\t\t/**\n\t\t * Called before visiting a container.\n\t\t *\n\t\t * @see TestIdentifier#isContainer()\n\t\t */\n\t\tdefault void preVisitContainer(TestIdentifier testIdentifier) {\n\t\t}\n\n\t\t/**\n\t\t * Called for all test identifiers regardless of their type.\n\t\t */\n\t\tdefault void visit(TestIdentifier testIdentifier) {\n\t\t}\n\n\t\t/**\n\t\t * Called after visiting a container.\n\t\t *\n\t\t * @see TestIdentifier#isContainer()\n\t\t */\n\t\tdefault void postVisitContainer(TestIdentifier testIdentifier) {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ClasspathAlignmentChecker.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Comparator.comparing;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\n\n/**\n * @since 1.12\n */\nclass ClasspathAlignmentChecker {\n\n\t// VisibleForTesting\n\tstatic final List<String> WELL_KNOWN_PACKAGES = List.of( //\n\t\t\"org.junit.jupiter.api\", //\n\t\t\"org.junit.jupiter.engine\", //\n\t\t\"org.junit.jupiter.migrationsupport\", //\n\t\t\"org.junit.jupiter.params\", //\n\t\t\"org.junit.platform.commons\", //\n\t\t\"org.junit.platform.console\", //\n\t\t\"org.junit.platform.engine\", //\n\t\t\"org.junit.platform.launcher\", //\n\t\t\"org.junit.platform.reporting\", //\n\t\t\"org.junit.platform.suite.api\", //\n\t\t\"org.junit.platform.suite.engine\", //\n\t\t\"org.junit.platform.testkit\", //\n\t\t\"org.junit.vintage.engine\" //\n\t);\n\n\tstatic Optional<JUnitException> check(LinkageError error) {\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(ClasspathAlignmentChecker.class);\n\t\treturn check(error, classLoader::getDefinedPackage);\n\t}\n\n\t// VisibleForTesting\n\tstatic Optional<JUnitException> check(LinkageError error, Function<String, @Nullable Package> packageLookup) {\n\t\tMap<String, List<Package>> packagesByVersions = new HashMap<>();\n\t\tWELL_KNOWN_PACKAGES.stream() //\n\t\t\t\t.map(packageLookup) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.forEach(pkg -> {\n\t\t\t\t\tString version = pkg.getImplementationVersion();\n\t\t\t\t\tif (version != null) {\n\t\t\t\t\t\tpackagesByVersions.computeIfAbsent(version, __ -> new ArrayList<>()).add(pkg);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\tif (packagesByVersions.size() > 1) {\n\t\t\tStringBuilder message = new StringBuilder();\n\t\t\tString lineBreak = System.lineSeparator();\n\t\t\tmessage.append(\"The wrapped \").append(error.getClass().getSimpleName()) //\n\t\t\t\t\t.append(\" is likely caused by the versions of JUnit jars on the classpath/module path \") //\n\t\t\t\t\t.append(\"not being properly aligned. \") //\n\t\t\t\t\t.append(lineBreak) //\n\t\t\t\t\t.append(\"Please ensure consistent versions are used (see https://docs.junit.org/\") //\n\t\t\t\t\t.append(ClasspathAlignmentChecker.class.getPackage().getImplementationVersion()) //\n\t\t\t\t\t.append(\"/appendix.html#dependency-metadata).\") //\n\t\t\t\t\t.append(lineBreak) //\n\t\t\t\t\t.append(\"The following conflicting versions were detected:\").append(lineBreak);\n\t\t\tpackagesByVersions.values().stream() //\n\t\t\t\t\t.flatMap(List::stream) //\n\t\t\t\t\t.sorted(comparing(Package::getName)) //\n\t\t\t\t\t.map(pkg -> \"- %s: %s%n\".formatted(pkg.getName(), pkg.getImplementationVersion())) //\n\t\t\t\t\t.forEach(message::append);\n\t\t\treturn Optional.of(new JUnitException(message.toString(), error));\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate ClasspathAlignmentChecker() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ClasspathAlignmentCheckingLauncherInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.Optional;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.launcher.LauncherInterceptor;\n\nclass ClasspathAlignmentCheckingLauncherInterceptor implements LauncherInterceptor {\n\n\tstatic final LauncherInterceptor INSTANCE = new ClasspathAlignmentCheckingLauncherInterceptor();\n\n\t@Override\n\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\ttry {\n\t\t\treturn invocation.proceed();\n\t\t}\n\t\tcatch (LinkageError e) {\n\t\t\tOptional<JUnitException> exception = ClasspathAlignmentChecker.check(e);\n\t\t\tif (exception.isPresent()) {\n\t\t\t\tthrow exception.get();\n\t\t\t}\n\t\t\tthrow e;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\t// do nothing\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeEngineExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\nclass CompositeEngineExecutionListener implements EngineExecutionListener {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(CompositeEngineExecutionListener.class);\n\n\tprivate final List<EngineExecutionListener> engineExecutionListeners;\n\n\tCompositeEngineExecutionListener(List<EngineExecutionListener> engineExecutionListeners) {\n\t\tthis.engineExecutionListeners = new ArrayList<>(engineExecutionListeners);\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.dynamicTestRegistered(testDescriptor),\n\t\t\t() -> \"dynamicTestRegistered(\" + testDescriptor + \")\");\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.executionSkipped(testDescriptor, reason),\n\t\t\t() -> \"executionSkipped(\" + testDescriptor + \", \" + reason + \")\");\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.executionStarted(testDescriptor), () -> \"executionStarted(\" + testDescriptor + \")\");\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.REVERSED,\n\t\t\tlistener -> listener.executionFinished(testDescriptor, testExecutionResult),\n\t\t\t() -> \"executionFinished(\" + testDescriptor + \", \" + testExecutionResult + \")\");\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.reportingEntryPublished(testDescriptor, entry),\n\t\t\t() -> \"reportingEntryPublished(\" + testDescriptor + \", \" + entry + \")\");\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\tnotifyEach(engineExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.fileEntryPublished(testDescriptor, file),\n\t\t\t() -> \"fileEntryPublished(\" + testDescriptor + \", \" + file + \")\");\n\t}\n\n\tprivate static <T extends EngineExecutionListener> void notifyEach(List<T> listeners, IterationOrder iterationOrder,\n\t\t\tConsumer<T> consumer, Supplier<String> description) {\n\t\titerationOrder.forEach(listeners, listener -> {\n\t\t\ttry {\n\t\t\t\tconsumer.accept(listener);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\t\tlogger.warn(throwable, () -> \"EngineExecutionListener [%s] threw exception for method: %s\".formatted(\n\t\t\t\t\tlistener.getClass().getName(), description.get()));\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/CompositeTestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\nclass CompositeTestExecutionListener implements TestExecutionListener {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(CompositeTestExecutionListener.class);\n\n\tprivate final List<TestExecutionListener> testExecutionListeners;\n\tprivate final List<EagerTestExecutionListener> eagerTestExecutionListeners;\n\n\tCompositeTestExecutionListener(List<TestExecutionListener> testExecutionListeners) {\n\t\tthis.testExecutionListeners = new ArrayList<>(testExecutionListeners);\n\t\tthis.eagerTestExecutionListeners = this.testExecutionListeners.stream() //\n\t\t\t\t.filter(EagerTestExecutionListener.class::isInstance) //\n\t\t\t\t.map(EagerTestExecutionListener.class::cast) //\n\t\t\t\t.toList();\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.dynamicTestRegistered(testIdentifier),\n\t\t\t() -> \"dynamicTestRegistered(\" + testIdentifier + \")\");\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.executionSkipped(testIdentifier, reason),\n\t\t\t() -> \"executionSkipped(\" + testIdentifier + \", \" + reason + \")\");\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tnotifyEach(eagerTestExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.executionJustStarted(testIdentifier),\n\t\t\t() -> \"executionJustStarted(\" + testIdentifier + \")\");\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.executionStarted(testIdentifier), () -> \"executionStarted(\" + testIdentifier + \")\");\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tnotifyEach(eagerTestExecutionListeners, IterationOrder.REVERSED,\n\t\t\tlistener -> listener.executionJustFinished(testIdentifier, testExecutionResult),\n\t\t\t() -> \"executionJustFinished(\" + testIdentifier + \", \" + testExecutionResult + \")\");\n\t\tnotifyEach(testExecutionListeners, IterationOrder.REVERSED,\n\t\t\tlistener -> listener.executionFinished(testIdentifier, testExecutionResult),\n\t\t\t() -> \"executionFinished(\" + testIdentifier + \", \" + testExecutionResult + \")\");\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.testPlanExecutionStarted(testPlan),\n\t\t\t() -> \"testPlanExecutionStarted(\" + testPlan + \")\");\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.REVERSED,\n\t\t\tlistener -> listener.testPlanExecutionFinished(testPlan),\n\t\t\t() -> \"testPlanExecutionFinished(\" + testPlan + \")\");\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.reportingEntryPublished(testIdentifier, entry),\n\t\t\t() -> \"reportingEntryPublished(\" + testIdentifier + \", \" + entry + \")\");\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t\tnotifyEach(testExecutionListeners, IterationOrder.ORIGINAL,\n\t\t\tlistener -> listener.fileEntryPublished(testIdentifier, file),\n\t\t\t() -> \"fileEntryPublished(\" + testIdentifier + \", \" + file + \")\");\n\t}\n\n\tprivate static <T extends TestExecutionListener> void notifyEach(List<T> listeners, IterationOrder iterationOrder,\n\t\t\tConsumer<T> consumer, Supplier<String> description) {\n\t\titerationOrder.forEach(listeners, listener -> {\n\t\t\ttry {\n\t\t\t\tconsumer.accept(listener);\n\t\t\t}\n\t\t\tcatch (Throwable throwable) {\n\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\t\tlogger.warn(throwable, () -> \"TestExecutionListener [%s] threw exception for method: %s\".formatted(\n\t\t\t\t\tlistener.getClass().getName(), description.get()));\n\t\t\t}\n\t\t});\n\t}\n\n\tinterface EagerTestExecutionListener extends TestExecutionListener {\n\t\tdefault void executionJustStarted(TestIdentifier testIdentifier) {\n\t\t}\n\n\t\tdefault void executionJustFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultDiscoveryRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.List;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.launcher.EngineFilter;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\n\n/**\n * {@code DefaultDiscoveryRequest} is the default implementation of the\n * {@link EngineDiscoveryRequest} and {@link LauncherDiscoveryRequest} APIs.\n *\n * @since 1.0\n */\nfinal class DefaultDiscoveryRequest implements LauncherDiscoveryRequest {\n\n\t// Selectors provided to the engines to be used for discovering tests\n\tprivate final List<DiscoverySelector> selectors;\n\n\t// Filters based on engines\n\tprivate final List<EngineFilter> engineFilters;\n\n\t// Discovery filters are handed through to all engines to be applied during discovery.\n\tprivate final List<DiscoveryFilter<?>> discoveryFilters;\n\n\t// Descriptor filters are applied by the launcher itself after engines have performed discovery.\n\tprivate final List<PostDiscoveryFilter> postDiscoveryFilters;\n\n\t// Configuration parameters can be used to provide custom configuration to engines, e.g. for extensions\n\tprivate final LauncherConfigurationParameters configurationParameters;\n\n\t// Listener for test discovery that may abort on errors.\n\tprivate final LauncherDiscoveryListener discoveryListener;\n\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\n\tDefaultDiscoveryRequest(List<DiscoverySelector> selectors, List<EngineFilter> engineFilters,\n\t\t\tList<DiscoveryFilter<?>> discoveryFilters, List<PostDiscoveryFilter> postDiscoveryFilters,\n\t\t\tLauncherConfigurationParameters configurationParameters, LauncherDiscoveryListener discoveryListener,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.selectors = List.copyOf(selectors);\n\t\tthis.engineFilters = List.copyOf(engineFilters);\n\t\tthis.discoveryFilters = List.copyOf(discoveryFilters);\n\t\tthis.postDiscoveryFilters = List.copyOf(postDiscoveryFilters);\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.discoveryListener = discoveryListener;\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t}\n\n\t@Override\n\tpublic <T extends DiscoverySelector> List<T> getSelectorsByType(Class<T> selectorType) {\n\t\tPreconditions.notNull(selectorType, \"selectorType must not be null\");\n\t\treturn this.selectors.stream().filter(selectorType::isInstance).map(selectorType::cast).toList();\n\t}\n\n\t@Override\n\tpublic List<EngineFilter> getEngineFilters() {\n\t\treturn this.engineFilters;\n\t}\n\n\t@Override\n\tpublic <T extends DiscoveryFilter<?>> List<T> getFiltersByType(Class<T> filterType) {\n\t\tPreconditions.notNull(filterType, \"filterType must not be null\");\n\t\treturn this.discoveryFilters.stream().filter(filterType::isInstance).map(filterType::cast).toList();\n\t}\n\n\t@Override\n\tpublic List<PostDiscoveryFilter> getPostDiscoveryFilters() {\n\t\treturn this.postDiscoveryFilters;\n\t}\n\n\t@Override\n\tpublic ConfigurationParameters getConfigurationParameters() {\n\t\treturn this.configurationParameters;\n\t}\n\n\t@Override\n\tpublic LauncherDiscoveryListener getDiscoveryListener() {\n\t\treturn this.discoveryListener;\n\t}\n\n\t@Override\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn this.outputDirectoryCreator;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Collections.unmodifiableCollection;\nimport static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;\nimport static org.junit.platform.launcher.core.LauncherPhase.DISCOVERY;\nimport static org.junit.platform.launcher.core.LauncherPhase.EXECUTION;\n\nimport java.util.Collection;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Default implementation of the {@link Launcher} API.\n *\n * <p>External clients can obtain an instance by invoking\n * {@link LauncherFactory#create()}.\n *\n * @since 1.0\n * @see Launcher\n * @see LauncherFactory\n */\nclass DefaultLauncher implements Launcher {\n\n\tprivate final LauncherListenerRegistry listenerRegistry = new LauncherListenerRegistry();\n\tprivate final EngineExecutionOrchestrator executionOrchestrator = new EngineExecutionOrchestrator(\n\t\tlistenerRegistry.testExecutionListeners);\n\tprivate final EngineDiscoveryOrchestrator discoveryOrchestrator;\n\tprivate final NamespacedHierarchicalStore<Namespace> sessionLevelStore;\n\n\t/**\n\t * Construct a new {@code DefaultLauncher} with the supplied test engines.\n\t *\n\t * @param testEngines the test engines to delegate to; never {@code null} or\n\t * empty\n\t * @param postDiscoveryFilters the additional post discovery filters for\n\t * discovery requests; never {@code null}\n\t */\n\tDefaultLauncher(Iterable<TestEngine> testEngines, Collection<PostDiscoveryFilter> postDiscoveryFilters,\n\t\t\tNamespacedHierarchicalStore<Namespace> sessionLevelStore) {\n\t\tPreconditions.condition(testEngines.iterator().hasNext(),\n\t\t\t() -> \"Cannot create Launcher without at least one TestEngine; \"\n\t\t\t\t\t+ \"consider adding an engine implementation JAR to the classpath\");\n\t\tPreconditions.notNull(postDiscoveryFilters, \"postDiscoveryFilter array must not be null\");\n\t\tPreconditions.containsNoNullElements(postDiscoveryFilters,\n\t\t\t\"postDiscoveryFilter array must not contain null elements\");\n\t\tthis.discoveryOrchestrator = new EngineDiscoveryOrchestrator(testEngines,\n\t\t\tunmodifiableCollection(postDiscoveryFilters), listenerRegistry.launcherDiscoveryListeners);\n\t\tthis.sessionLevelStore = sessionLevelStore;\n\t}\n\n\t@Override\n\tpublic void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tthis.listenerRegistry.launcherDiscoveryListeners.addAll(listeners);\n\t}\n\n\t@Override\n\tpublic void registerTestExecutionListeners(TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tthis.listenerRegistry.testExecutionListeners.addAll(listeners);\n\t}\n\n\t@Override\n\tpublic TestPlan discover(LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\treturn InternalTestPlan.from(discover(discoveryRequest, DISCOVERY));\n\t}\n\n\t@Override\n\tpublic void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tvar executionRequest = LauncherExecutionRequestBuilder.request(discoveryRequest) //\n\t\t\t\t.listeners(listeners) //\n\t\t\t\t.build();\n\t\texecute(executionRequest);\n\t}\n\n\t@Override\n\tpublic void execute(TestPlan testPlan, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(testPlan, \"testPlan must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tvar executionRequest = LauncherExecutionRequestBuilder.request(testPlan) //\n\t\t\t\t.listeners(listeners) //\n\t\t\t\t.build();\n\t\texecute(executionRequest);\n\t}\n\n\t@Override\n\tpublic void execute(LauncherExecutionRequest executionRequest) {\n\t\tPreconditions.notNull(executionRequest, \"executionRequest must not be null\");\n\t\tvar testPlan = executionRequest.getTestPlan().map(it -> {\n\t\t\tPreconditions.condition(it instanceof InternalTestPlan,\n\t\t\t\t\"The TestPlan in executionRequest was not created by this Launcher\");\n\t\t\treturn ((InternalTestPlan) it);\n\t\t}).orElseGet(() -> {\n\t\t\tPreconditions.condition(executionRequest.getDiscoveryRequest().isPresent(),\n\t\t\t\t\"Either a TestPlan or LauncherDiscoveryRequest must be present in the LauncherExecutionRequest\");\n\t\t\treturn InternalTestPlan.from(discover(executionRequest.getDiscoveryRequest().get(), EXECUTION));\n\t\t});\n\t\texecute(testPlan, executionRequest.getAdditionalTestExecutionListeners(),\n\t\t\texecutionRequest.getCancellationToken());\n\t}\n\n\tprivate LauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest, LauncherPhase phase) {\n\t\treturn discoveryOrchestrator.discover(discoveryRequest, phase);\n\t}\n\n\tprivate void execute(InternalTestPlan internalTestPlan, Collection<? extends TestExecutionListener> listeners,\n\t\t\tCancellationToken cancellationToken) {\n\t\ttry (NamespacedHierarchicalStore<Namespace> requestLevelStore = createRequestLevelStore()) {\n\t\t\texecutionOrchestrator.execute(internalTestPlan, requestLevelStore, listeners, cancellationToken);\n\t\t}\n\t}\n\n\tprivate NamespacedHierarchicalStore<Namespace> createRequestLevelStore() {\n\t\treturn new NamespacedHierarchicalStore<>(sessionLevelStore, closeAutoCloseables());\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherConfig.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Collections.unmodifiableCollection;\n\nimport java.util.Collection;\n\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * Default implementation of the {@link LauncherConfig} API.\n *\n * @since 1.3\n */\nclass DefaultLauncherConfig implements LauncherConfig {\n\n\tprivate final boolean testEngineAutoRegistrationEnabled;\n\tprivate final boolean launcherSessionListenerAutoRegistrationEnabled;\n\tprivate final boolean launcherDiscoveryListenerAutoRegistrationEnabled;\n\tprivate final boolean testExecutionListenerAutoRegistrationEnabled;\n\tprivate final boolean postDiscoveryFilterAutoRegistrationEnabled;\n\tprivate final Collection<TestEngine> additionalTestEngines;\n\tprivate final Collection<LauncherSessionListener> additionalLauncherSessionListeners;\n\tprivate final Collection<LauncherDiscoveryListener> additionalLauncherDiscoveryListeners;\n\tprivate final Collection<TestExecutionListener> additionalTestExecutionListeners;\n\tprivate final Collection<PostDiscoveryFilter> additionalPostDiscoveryFilters;\n\n\tDefaultLauncherConfig(boolean testEngineAutoRegistrationEnabled,\n\t\t\tboolean launcherSessionListenerAutoRegistrationEnabled,\n\t\t\tboolean launcherDiscoveryListenerAutoRegistrationEnabled,\n\t\t\tboolean testExecutionListenerAutoRegistrationEnabled, boolean postDiscoveryFilterAutoRegistrationEnabled,\n\t\t\tCollection<TestEngine> additionalTestEngines,\n\t\t\tCollection<LauncherSessionListener> additionalLauncherSessionListeners,\n\t\t\tCollection<LauncherDiscoveryListener> additionalLauncherDiscoveryListeners,\n\t\t\tCollection<TestExecutionListener> additionalTestExecutionListeners,\n\t\t\tCollection<PostDiscoveryFilter> additionalPostDiscoveryFilters) {\n\t\tthis.launcherSessionListenerAutoRegistrationEnabled = launcherSessionListenerAutoRegistrationEnabled;\n\t\tthis.launcherDiscoveryListenerAutoRegistrationEnabled = launcherDiscoveryListenerAutoRegistrationEnabled;\n\t\tthis.testExecutionListenerAutoRegistrationEnabled = testExecutionListenerAutoRegistrationEnabled;\n\t\tthis.testEngineAutoRegistrationEnabled = testEngineAutoRegistrationEnabled;\n\t\tthis.postDiscoveryFilterAutoRegistrationEnabled = postDiscoveryFilterAutoRegistrationEnabled;\n\t\tthis.additionalTestEngines = unmodifiableCollection(additionalTestEngines);\n\t\tthis.additionalLauncherSessionListeners = unmodifiableCollection(additionalLauncherSessionListeners);\n\t\tthis.additionalLauncherDiscoveryListeners = unmodifiableCollection(additionalLauncherDiscoveryListeners);\n\t\tthis.additionalTestExecutionListeners = unmodifiableCollection(additionalTestExecutionListeners);\n\t\tthis.additionalPostDiscoveryFilters = unmodifiableCollection(additionalPostDiscoveryFilters);\n\t}\n\n\t@Override\n\tpublic boolean isTestEngineAutoRegistrationEnabled() {\n\t\treturn this.testEngineAutoRegistrationEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isLauncherSessionListenerAutoRegistrationEnabled() {\n\t\treturn launcherSessionListenerAutoRegistrationEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isLauncherDiscoveryListenerAutoRegistrationEnabled() {\n\t\treturn launcherDiscoveryListenerAutoRegistrationEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isTestExecutionListenerAutoRegistrationEnabled() {\n\t\treturn this.testExecutionListenerAutoRegistrationEnabled;\n\t}\n\n\t@Override\n\tpublic boolean isPostDiscoveryFilterAutoRegistrationEnabled() {\n\t\treturn this.postDiscoveryFilterAutoRegistrationEnabled;\n\t}\n\n\t@Override\n\tpublic Collection<TestEngine> getAdditionalTestEngines() {\n\t\treturn this.additionalTestEngines;\n\t}\n\n\t@Override\n\tpublic Collection<LauncherSessionListener> getAdditionalLauncherSessionListeners() {\n\t\treturn additionalLauncherSessionListeners;\n\t}\n\n\t@Override\n\tpublic Collection<LauncherDiscoveryListener> getAdditionalLauncherDiscoveryListeners() {\n\t\treturn additionalLauncherDiscoveryListeners;\n\t}\n\n\t@Override\n\tpublic Collection<TestExecutionListener> getAdditionalTestExecutionListeners() {\n\t\treturn this.additionalTestExecutionListeners;\n\t}\n\n\t@Override\n\tpublic Collection<PostDiscoveryFilter> getAdditionalPostDiscoveryFilters() {\n\t\treturn this.additionalPostDiscoveryFilters;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherExecutionRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 6.0\n */\nfinal class DefaultLauncherExecutionRequest implements LauncherExecutionRequest {\n\n\tprivate final @Nullable LauncherDiscoveryRequest discoveryRequest;\n\tprivate final @Nullable TestPlan testPlan;\n\tprivate final List<? extends TestExecutionListener> executionListeners;\n\tprivate final CancellationToken cancellationToken;\n\n\tDefaultLauncherExecutionRequest(@Nullable LauncherDiscoveryRequest discoveryRequest, @Nullable TestPlan testPlan,\n\t\t\tCollection<? extends TestExecutionListener> executionListeners, CancellationToken cancellationToken) {\n\t\tthis.discoveryRequest = discoveryRequest;\n\t\tthis.testPlan = testPlan;\n\t\tthis.executionListeners = List.copyOf(executionListeners);\n\t\tthis.cancellationToken = cancellationToken;\n\t}\n\n\t@Override\n\tpublic Optional<LauncherDiscoveryRequest> getDiscoveryRequest() {\n\t\treturn Optional.ofNullable(discoveryRequest);\n\t}\n\n\t@Override\n\tpublic Optional<TestPlan> getTestPlan() {\n\t\treturn Optional.ofNullable(testPlan);\n\t}\n\n\t@Override\n\tpublic Collection<? extends TestExecutionListener> getAdditionalTestExecutionListeners() {\n\t\treturn executionListeners;\n\t}\n\n\t@Override\n\tpublic CancellationToken getCancellationToken() {\n\t\treturn cancellationToken;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DefaultLauncherSession.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.LauncherInterceptor;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.8\n */\nclass DefaultLauncherSession implements LauncherSession {\n\n\tprivate static final LauncherInterceptor NOOP_INTERCEPTOR = new LauncherInterceptor() {\n\t\t@Override\n\t\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\t\treturn invocation.proceed();\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\t// do nothing\n\t\t}\n\t};\n\n\tprivate final NamespacedHierarchicalStore<Namespace> store = new NamespacedHierarchicalStore<>(null,\n\t\tcloseAutoCloseables());\n\tprivate final LauncherInterceptor interceptor;\n\tprivate final LauncherSessionListener listener;\n\tprivate final DelegatingLauncher launcher;\n\n\tDefaultLauncherSession(List<LauncherInterceptor> interceptors, //\n\t\t\tSupplier<LauncherSessionListener> listenerSupplier, //\n\t\t\tFunction<NamespacedHierarchicalStore<Namespace>, Launcher> launcherFactory //\n\t) {\n\t\tinterceptor = composite(interceptors);\n\t\tLauncher launcher;\n\t\tif (interceptor == NOOP_INTERCEPTOR) {\n\t\t\tthis.listener = listenerSupplier.get();\n\t\t\tlauncher = launcherFactory.apply(this.store);\n\t\t}\n\t\telse {\n\t\t\tthis.listener = interceptor.intercept(listenerSupplier::get);\n\t\t\tlauncher = new InterceptingLauncher(interceptor.intercept(() -> launcherFactory.apply(this.store)),\n\t\t\t\tinterceptor);\n\t\t}\n\t\tthis.launcher = new DelegatingLauncher(launcher);\n\t\tlistener.launcherSessionOpened(this);\n\t}\n\n\t@Override\n\tpublic Launcher getLauncher() {\n\t\treturn launcher;\n\t}\n\n\tLauncherSessionListener getListener() {\n\t\treturn listener;\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\tif (launcher.delegate != ClosedLauncher.INSTANCE) {\n\t\t\tlauncher.delegate = ClosedLauncher.INSTANCE;\n\t\t\tlistener.launcherSessionClosed(this);\n\t\t\tstore.close();\n\t\t\tinterceptor.close();\n\t\t}\n\t}\n\n\t@Override\n\tpublic NamespacedHierarchicalStore<Namespace> getStore() {\n\t\treturn store;\n\t}\n\n\tprivate static class ClosedLauncher implements Launcher {\n\n\t\tstatic final ClosedLauncher INSTANCE = new ClosedLauncher();\n\n\t\tprivate ClosedLauncher() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void registerTestExecutionListeners(TestExecutionListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic TestPlan discover(LauncherDiscoveryRequest launcherDiscoveryRequest) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(LauncherDiscoveryRequest launcherDiscoveryRequest, TestExecutionListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(TestPlan testPlan, TestExecutionListener... listeners) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(LauncherExecutionRequest launcherExecutionRequest) {\n\t\t\tthrow new PreconditionViolationException(\"Launcher session has already been closed\");\n\t\t}\n\t}\n\n\tprivate static LauncherInterceptor composite(List<LauncherInterceptor> interceptors) {\n\t\tif (interceptors.isEmpty()) {\n\t\t\treturn NOOP_INTERCEPTOR;\n\t\t}\n\t\treturn interceptors.stream() //\n\t\t\t\t.skip(1) //\n\t\t\t\t.reduce(interceptors.get(0), (a, b) -> new LauncherInterceptor() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void close() {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\ta.close();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tb.close();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\t\t\t\t\treturn a.intercept(() -> b.intercept(invocation));\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingEngineExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * @since 1.6\n */\nclass DelegatingEngineExecutionListener implements EngineExecutionListener {\n\n\tprivate final EngineExecutionListener delegate;\n\n\tDelegatingEngineExecutionListener(EngineExecutionListener delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\tdelegate.dynamicTestRegistered(testDescriptor);\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tdelegate.executionSkipped(testDescriptor, reason);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\tdelegate.executionStarted(testDescriptor);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tdelegate.executionFinished(testDescriptor, testExecutionResult);\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\tdelegate.reportingEntryPublished(testDescriptor, entry);\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\tdelegate.fileEntryPublished(testDescriptor, file);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.10\n */\nclass DelegatingLauncher implements Launcher {\n\n\tprotected Launcher delegate;\n\n\tDelegatingLauncher(Launcher delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tdelegate.registerLauncherDiscoveryListeners(listeners);\n\t}\n\n\t@Override\n\tpublic void registerTestExecutionListeners(TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tdelegate.registerTestExecutionListeners(listeners);\n\t}\n\n\t@Override\n\tpublic TestPlan discover(LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\treturn delegate.discover(discoveryRequest);\n\t}\n\n\t@Override\n\tpublic void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tdelegate.execute(discoveryRequest, listeners);\n\t}\n\n\t@Override\n\tpublic void execute(TestPlan testPlan, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(testPlan, \"testPlan must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tdelegate.execute(testPlan, listeners);\n\t}\n\n\t@Override\n\tpublic void execute(LauncherExecutionRequest executionRequest) {\n\t\tPreconditions.notNull(executionRequest, \"executionRequest must not be null\");\n\t\tdelegate.execute(executionRequest);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * @since 6.1\n */\nclass DelegatingLauncherDiscoveryListener implements LauncherDiscoveryListener {\n\n\tprivate final LauncherDiscoveryListener delegate;\n\n\tDelegatingLauncherDiscoveryListener(LauncherDiscoveryListener delegate) {\n\t\tthis.delegate = delegate;\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t\tdelegate.launcherDiscoveryStarted(request);\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {\n\t\tdelegate.launcherDiscoveryFinished(request);\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryStarted(UniqueId engineId) {\n\t\tdelegate.engineDiscoveryStarted(engineId);\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryFinished(UniqueId engineId, EngineDiscoveryResult result) {\n\t\tdelegate.engineDiscoveryFinished(engineId, result);\n\t}\n\n\t@Override\n\tpublic void selectorProcessed(UniqueId engineId, DiscoverySelector selector, SelectorResolutionResult result) {\n\t\tdelegate.selectorProcessed(engineId, selector, result);\n\t}\n\n\t@Override\n\tpublic void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t\tdelegate.issueEncountered(engineId, issue);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DelegatingLauncherDiscoveryRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.List;\n\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.launcher.EngineFilter;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\n\n/**\n * @since 1.13\n */\nclass DelegatingLauncherDiscoveryRequest implements LauncherDiscoveryRequest {\n\n\tprivate final LauncherDiscoveryRequest request;\n\n\tDelegatingLauncherDiscoveryRequest(LauncherDiscoveryRequest request) {\n\t\tthis.request = request;\n\t}\n\n\t@Override\n\tpublic List<EngineFilter> getEngineFilters() {\n\t\treturn this.request.getEngineFilters();\n\t}\n\n\t@Override\n\tpublic List<PostDiscoveryFilter> getPostDiscoveryFilters() {\n\t\treturn this.request.getPostDiscoveryFilters();\n\t}\n\n\t@Override\n\tpublic LauncherDiscoveryListener getDiscoveryListener() {\n\t\treturn this.request.getDiscoveryListener();\n\t}\n\n\t@Override\n\tpublic <T extends DiscoverySelector> List<T> getSelectorsByType(Class<T> selectorType) {\n\t\treturn this.request.getSelectorsByType(selectorType);\n\t}\n\n\t@Override\n\tpublic <T extends DiscoveryFilter<?>> List<T> getFiltersByType(Class<T> filterType) {\n\t\treturn this.request.getFiltersByType(filterType);\n\t}\n\n\t@Override\n\tpublic ConfigurationParameters getConfigurationParameters() {\n\t\treturn this.request.getConfigurationParameters();\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\t@Override\n\tpublic org.junit.platform.engine.reporting.OutputDirectoryProvider getOutputDirectoryProvider() {\n\t\treturn this.request.getOutputDirectoryProvider();\n\t}\n\n\t@Override\n\tpublic OutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn this.request.getOutputDirectoryCreator();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DiscoveryIssueCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\nclass DiscoveryIssueCollector implements LauncherDiscoveryListener {\n\n\tfinal List<DiscoveryIssue> issues = new ArrayList<>();\n\tprivate final Severity criticalSeverity;\n\n\tDiscoveryIssueCollector(ConfigurationParameters configurationParameters) {\n\t\tthis.criticalSeverity = getCriticalSeverity(configurationParameters);\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryStarted(UniqueId engineId) {\n\t\tthis.issues.clear();\n\t}\n\n\t@Override\n\tpublic void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t\tthis.issues.add(issue);\n\t}\n\n\tDiscoveryIssueNotifier toNotifier() {\n\t\tif (this.issues.isEmpty()) {\n\t\t\treturn DiscoveryIssueNotifier.NO_ISSUES;\n\t\t}\n\t\treturn DiscoveryIssueNotifier.from(criticalSeverity, this.issues);\n\t}\n\n\tprivate static Severity getCriticalSeverity(ConfigurationParameters configurationParameters) {\n\t\treturn configurationParameters //\n\t\t\t\t.get(LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, value -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn Severity.valueOf(value.toUpperCase(Locale.ROOT));\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tthrow new JUnitException(\n\t\t\t\t\t\t\t\"Invalid DiscoveryIssue.Severity '%s' set via the '%s' configuration parameter.\".formatted(\n\t\t\t\t\t\t\t\tvalue, LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME));\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.orElse(Severity.ERROR);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DiscoveryIssueException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.io.Serial;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * {@code DiscoveryIssueException} is an exception that is thrown if an engine\n * reports critical issues during test discovery.\n *\n * @since 1.13\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic class DiscoveryIssueException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tDiscoveryIssueException(String message) {\n\t\tsuper(message, null, false, false);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DiscoveryIssueNotifier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.stream.Collectors.partitioningBy;\nimport static java.util.stream.Collectors.toUnmodifiableList;\nimport static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * @since 1.13\n */\nclass DiscoveryIssueNotifier {\n\n\tstatic final DiscoveryIssueNotifier NO_ISSUES = new DiscoveryIssueNotifier(List.of(), List.of(), List.of());\n\tprivate static final Logger logger = LoggerFactory.getLogger(DiscoveryIssueNotifier.class);\n\n\tprivate final List<DiscoveryIssue> allIssues;\n\tprivate final List<DiscoveryIssue> criticalIssues;\n\tprivate final List<DiscoveryIssue> nonCriticalIssues;\n\n\t@SuppressWarnings(\"NullAway\")\n\tstatic DiscoveryIssueNotifier from(Severity criticalSeverity, List<DiscoveryIssue> issues) {\n\t\tvar issuesByCriticality = partitionByCriticality(criticalSeverity, issues);\n\t\tList<DiscoveryIssue> criticalIssues = issuesByCriticality.get(true);\n\t\tList<DiscoveryIssue> nonCriticalIssues = issuesByCriticality.get(false);\n\t\treturn new DiscoveryIssueNotifier(issues, criticalIssues, nonCriticalIssues);\n\t}\n\n\tprivate static Map<Boolean, List<DiscoveryIssue>> partitionByCriticality(Severity criticalSeverity,\n\t\t\tList<DiscoveryIssue> issues) {\n\t\treturn issues.stream() //\n\t\t\t\t.sorted(comparing(DiscoveryIssue::severity).reversed()) //\n\t\t\t\t.collect(\n\t\t\t\t\tpartitioningBy(issue -> issue.severity().compareTo(criticalSeverity) >= 0, toUnmodifiableList()));\n\t}\n\n\tprivate DiscoveryIssueNotifier(List<DiscoveryIssue> allIssues, List<DiscoveryIssue> criticalIssues,\n\t\t\tList<DiscoveryIssue> nonCriticalIssues) {\n\t\tthis.allIssues = List.copyOf(allIssues);\n\t\tthis.criticalIssues = List.copyOf(criticalIssues);\n\t\tthis.nonCriticalIssues = List.copyOf(nonCriticalIssues);\n\t}\n\n\tList<DiscoveryIssue> getAllIssues() {\n\t\treturn allIssues;\n\t}\n\n\tboolean hasCriticalIssues() {\n\t\treturn !criticalIssues.isEmpty();\n\t}\n\n\tvoid logCriticalIssues(TestEngine testEngine) {\n\t\tlogIssues(testEngine, criticalIssues, \"critical\");\n\t}\n\n\tvoid logNonCriticalIssues(TestEngine testEngine) {\n\t\tlogIssues(testEngine, nonCriticalIssues, \"non-critical\");\n\t}\n\n\t@Nullable\n\tDiscoveryIssueException createExceptionForCriticalIssues(TestEngine testEngine) {\n\t\tif (criticalIssues.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tString message = formatMessage(testEngine, criticalIssues, \"critical\");\n\t\treturn new DiscoveryIssueException(message);\n\t}\n\n\tprivate void logIssues(TestEngine testEngine, List<DiscoveryIssue> issues, String adjective) {\n\t\tif (!issues.isEmpty()) {\n\t\t\tSeverity maxSeverity = issues.get(0).severity();\n\t\t\tlogger(maxSeverity).accept(() -> formatMessage(testEngine, issues, adjective));\n\t\t}\n\t}\n\n\tprivate static Consumer<Supplier<String>> logger(Severity severity) {\n\t\treturn switch (severity) {\n\t\t\tcase INFO -> logger::info;\n\t\t\tcase WARNING -> logger::warn;\n\t\t\tcase ERROR -> logger::error;\n\t\t};\n\t}\n\n\tprivate static String formatMessage(TestEngine testEngine, List<DiscoveryIssue> issues, String adjective) {\n\t\tPreconditions.notNull(testEngine, \"testEngine must not be null\");\n\t\tPreconditions.notNull(issues, \"issues must not be null\");\n\t\tPreconditions.notEmpty(issues, \"issues must not be empty\");\n\t\tString engineId = testEngine.getId();\n\t\tStringBuilder message = new StringBuilder();\n\t\tmessage.append(\"TestEngine with ID '\").append(engineId).append(\"' encountered \");\n\t\tif (issues.size() == 1) {\n\t\t\tmessage.append(\"a \").append(adjective).append(\" issue\");\n\t\t}\n\t\telse {\n\t\t\tmessage.append(issues.size()).append(' ').append(adjective).append(\" issues\");\n\t\t}\n\t\tmessage.append(\" during test discovery:\");\n\t\tfor (int i = 0; i < issues.size(); i++) {\n\t\t\tDiscoveryIssue issue = issues.get(i);\n\t\t\tmessage.append(\"\\n\\n(\").append(i + 1).append(\") [\").append(issue.severity()).append(\"] \").append(\n\t\t\t\tissue.message());\n\t\t\tissue.source().ifPresent(source -> {\n\t\t\t\tmessage.append(\"\\n    Source: \").append(source);\n\t\t\t\tif (source instanceof MethodSource methodSource) {\n\t\t\t\t\tappendIdeCompatibleLink(message, methodSource.getClassName(), methodSource.getMethodName());\n\t\t\t\t}\n\t\t\t\telse if (source instanceof ClassSource classSource) {\n\t\t\t\t\tappendIdeCompatibleLink(message, classSource.getClassName(), \"<no-method>\");\n\t\t\t\t}\n\t\t\t});\n\t\t\tissue.cause().ifPresent(t -> message.append(\"\\n    Cause: \").append(readStackTrace(t)));\n\t\t}\n\t\treturn message.toString();\n\t}\n\n\tprivate static void appendIdeCompatibleLink(StringBuilder message, String className, String methodName) {\n\t\tmessage.append(\"\\n            at \").append(className).append(\".\").append(methodName).append(\"(SourceFile:0)\");\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DiscoveryIssueReportingDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.commons.util.UnrecoverableExceptions.rethrowIfUnrecoverable;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.FAILED;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.UNRESOLVED;\n\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.ClasspathResourceSource;\nimport org.junit.platform.engine.support.descriptor.DirectorySource;\nimport org.junit.platform.engine.support.descriptor.FilePosition;\nimport org.junit.platform.engine.support.descriptor.FileSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.descriptor.PackageSource;\nimport org.junit.platform.engine.support.descriptor.UriSource;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\n/**\n * @since 6.1\n */\nclass DiscoveryIssueReportingDiscoveryListener extends DelegatingLauncherDiscoveryListener {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DiscoveryIssueReportingDiscoveryListener.class);\n\n\tDiscoveryIssueReportingDiscoveryListener(LauncherDiscoveryListener delegate) {\n\t\tsuper(delegate);\n\t}\n\n\t@Override\n\tpublic void selectorProcessed(UniqueId engineId, DiscoverySelector selector, SelectorResolutionResult result) {\n\t\tsuper.selectorProcessed(engineId, selector, result);\n\t\treportIssue(engineId, selector, result) //\n\t\t\t\t.ifPresent(issue -> issueEncountered(engineId, issue));\n\t}\n\n\tprivate Optional<DiscoveryIssue> reportIssue(UniqueId engineId, DiscoverySelector selector,\n\t\t\tSelectorResolutionResult result) {\n\t\tif (result.getStatus() == FAILED) {\n\t\t\tDiscoveryIssue issue = DiscoveryIssue.builder(Severity.ERROR, selector + \" resolution failed\") //\n\t\t\t\t\t.cause(result.getThrowable()) //\n\t\t\t\t\t.source(toSource(selector)) //\n\t\t\t\t\t.build();\n\t\t\treturn Optional.of(issue);\n\t\t}\n\t\telse if (result.getStatus() == UNRESOLVED && selector instanceof UniqueIdSelector uniqueIdSelector) {\n\t\t\tUniqueId uniqueId = uniqueIdSelector.getUniqueId();\n\t\t\tif (uniqueId.hasPrefix(engineId)) {\n\t\t\t\tDiscoveryIssue issue = DiscoveryIssue.create(Severity.ERROR, selector + \" could not be resolved\");\n\t\t\t\treturn Optional.of(issue);\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate static @Nullable TestSource toSource(DiscoverySelector selector) {\n\t\tif (selector instanceof ClassSelector classSelector) {\n\t\t\treturn ClassSource.from(classSelector.getClassName());\n\t\t}\n\t\tif (selector instanceof MethodSelector methodSelector) {\n\t\t\treturn MethodSource.from(methodSelector.getClassName(), methodSelector.getMethodName(),\n\t\t\t\tmethodSelector.getParameterTypeNames());\n\t\t}\n\t\tif (selector instanceof ClasspathResourceSelector resourceSelector) {\n\t\t\tString resourceName = resourceSelector.getClasspathResourceName();\n\t\t\treturn resourceSelector.getPosition() //\n\t\t\t\t\t.map(DiscoveryIssueReportingDiscoveryListener::convert) //\n\t\t\t\t\t.map(position -> ClasspathResourceSource.from(resourceName, position)) //\n\t\t\t\t\t.orElseGet(() -> ClasspathResourceSource.from(resourceName));\n\t\t}\n\t\tif (selector instanceof PackageSelector packageSelector) {\n\t\t\treturn PackageSource.from(packageSelector.getPackageName());\n\t\t}\n\t\ttry {\n\t\t\t// Both FileSource and DirectorySource call File.getCanonicalFile() to normalize the reported file which\n\t\t\t// can throw an exception for certain file names on certain file systems. UriSource.from(...) is affected\n\t\t\t// as well because it may return a FileSource or DirectorySource\n\t\t\tif (selector instanceof FileSelector fileSelector) {\n\t\t\t\treturn fileSelector.getPosition() //\n\t\t\t\t\t\t.map(DiscoveryIssueReportingDiscoveryListener::convert) //\n\t\t\t\t\t\t.map(position -> FileSource.from(fileSelector.getFile(), position)) //\n\t\t\t\t\t\t.orElseGet(() -> FileSource.from(fileSelector.getFile()));\n\t\t\t}\n\t\t\tif (selector instanceof DirectorySelector directorySelector) {\n\t\t\t\treturn DirectorySource.from(directorySelector.getDirectory());\n\t\t\t}\n\t\t\tif (selector instanceof UriSelector uriSelector) {\n\t\t\t\treturn UriSource.from(uriSelector.getUri());\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\trethrowIfUnrecoverable(ex);\n\t\t\tlogger.warn(ex, () -> \"Failed to convert DiscoverySelector [%s] into TestSource\".formatted(selector));\n\t\t}\n\t\treturn null;\n\t}\n\n\tprivate static FilePosition convert(org.junit.platform.engine.discovery.FilePosition position) {\n\t\treturn position.getColumn() //\n\t\t\t\t.map(column -> FilePosition.from(position.getLine(), column)) //\n\t\t\t\t.orElseGet(() -> FilePosition.from(position.getLine()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryOrchestrator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.engine.Filter.composeFilters;\nimport static org.junit.platform.launcher.core.LauncherPhase.getDiscoveryIssueFailurePhase;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;\n\n/**\n * Orchestrates test discovery using the configured test engines.\n *\n * @since 1.7\n */\n@API(status = INTERNAL, since = \"1.7\", consumers = { \"org.junit.platform.testkit\", \"org.junit.platform.suite.engine\" })\npublic class EngineDiscoveryOrchestrator {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(EngineDiscoveryOrchestrator.class);\n\n\tprivate final EngineDiscoveryResultValidator discoveryResultValidator = new EngineDiscoveryResultValidator();\n\tprivate final Iterable<TestEngine> testEngines;\n\tprivate final Collection<PostDiscoveryFilter> postDiscoveryFilters;\n\tprivate final ListenerRegistry<LauncherDiscoveryListener> launcherDiscoveryListenerRegistry;\n\n\tpublic EngineDiscoveryOrchestrator(Iterable<TestEngine> testEngines,\n\t\t\tCollection<PostDiscoveryFilter> postDiscoveryFilters) {\n\t\tthis(testEngines, postDiscoveryFilters, ListenerRegistry.forLauncherDiscoveryListeners());\n\t}\n\n\tEngineDiscoveryOrchestrator(Iterable<TestEngine> testEngines, Collection<PostDiscoveryFilter> postDiscoveryFilters,\n\t\t\tListenerRegistry<LauncherDiscoveryListener> launcherDiscoveryListenerRegistry) {\n\t\tthis.testEngines = EngineIdValidator.validate(testEngines);\n\t\tthis.postDiscoveryFilters = postDiscoveryFilters;\n\t\tthis.launcherDiscoveryListenerRegistry = launcherDiscoveryListenerRegistry;\n\t}\n\n\t/**\n\t * Discovers tests for the supplied request using the configured test\n\t * engines.\n\t *\n\t * <p>Applies {@linkplain org.junit.platform.launcher.EngineFilter engine\n\t * filters} and {@linkplain PostDiscoveryFilter post-discovery filters} and\n\t * {@linkplain TestDescriptor#prune() prunes} the resulting test tree.\n\t */\n\tpublic LauncherDiscoveryResult discover(LauncherDiscoveryRequest request) {\n\t\treturn discover(request, Optional.empty(), UniqueId::forEngine);\n\t}\n\n\tLauncherDiscoveryResult discover(LauncherDiscoveryRequest request, LauncherPhase phase) {\n\t\treturn discover(request, Optional.of(phase), UniqueId::forEngine);\n\t}\n\n\t/**\n\t * Discovers tests for the supplied request in the supplied phase using the\n\t * configured test engines to be used by the suite engine.\n\t *\n\t * <p>Applies {@linkplain org.junit.platform.launcher.EngineFilter engine\n\t * filters} and {@linkplain PostDiscoveryFilter post-discovery filters} and\n\t * {@linkplain TestDescriptor#prune() prunes} the resulting test tree.\n\t *\n\t * <p>Note: The test descriptors in the discovery result can safely be used\n\t * as non-root descriptors. Engine-test descriptor entries are pruned from\n\t * the returned result. As such execution by\n\t * {@link EngineExecutionOrchestrator} will not emit start or emit events\n\t * for engines without tests.\n\t */\n\tpublic LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, UniqueId parentId) {\n\t\tLauncherDiscoveryResult result = discover(request, Optional.empty(), parentId::appendEngine);\n\t\treturn result.withRetainedEngines(TestDescriptor::containsTests);\n\t}\n\n\tprivate LauncherDiscoveryResult discover(LauncherDiscoveryRequest request, Optional<LauncherPhase> phase,\n\t\t\tFunction<String, UniqueId> uniqueIdCreator) {\n\t\tDiscoveryIssueCollector issueCollector = new DiscoveryIssueCollector(request.getConfigurationParameters());\n\t\tLauncherDiscoveryListener listener = getLauncherDiscoveryListener(request, issueCollector);\n\t\tLauncherDiscoveryRequest delegatingRequest = new DelegatingLauncherDiscoveryRequest(request) {\n\t\t\t@Override\n\t\t\tpublic LauncherDiscoveryListener getDiscoveryListener() {\n\t\t\t\treturn listener;\n\t\t\t}\n\t\t};\n\t\tlistener.launcherDiscoveryStarted(request);\n\t\tLauncherDiscoveryResult discoveryResult;\n\t\ttry {\n\t\t\tMap<TestEngine, EngineResultInfo> testEngineResults = discoverSafely(delegatingRequest, phase,\n\t\t\t\tissueCollector, uniqueIdCreator);\n\t\t\tdiscoveryResult = new LauncherDiscoveryResult(testEngineResults, request.getConfigurationParameters(),\n\t\t\t\trequest.getOutputDirectoryCreator());\n\t\t}\n\t\tfinally {\n\t\t\tlistener.launcherDiscoveryFinished(request);\n\t\t}\n\t\tif (shouldReportDiscoveryIssues(request, phase)) {\n\t\t\treportDiscoveryIssues(discoveryResult);\n\t\t}\n\t\treturn discoveryResult;\n\t}\n\n\tprivate static boolean shouldReportDiscoveryIssues(LauncherDiscoveryRequest request,\n\t\t\tOptional<LauncherPhase> phase) {\n\t\tConfigurationParameters configurationParameters = request.getConfigurationParameters();\n\t\treturn getDiscoveryIssueFailurePhase(configurationParameters) //\n\t\t\t\t.orElse(phase.orElse(null)) == LauncherPhase.DISCOVERY;\n\t}\n\n\tprivate static void reportDiscoveryIssues(LauncherDiscoveryResult discoveryResult) {\n\t\tDiscoveryIssueException exception = null;\n\t\tfor (TestEngine testEngine : discoveryResult.getTestEngines()) {\n\t\t\tEngineResultInfo engineResult = discoveryResult.getEngineResult(testEngine);\n\t\t\tDiscoveryIssueNotifier discoveryIssueNotifier = engineResult.getDiscoveryIssueNotifier();\n\t\t\tdiscoveryIssueNotifier.logCriticalIssues(testEngine);\n\t\t\tdiscoveryIssueNotifier.logNonCriticalIssues(testEngine);\n\t\t\tif (exception == null) {\n\t\t\t\texception = discoveryIssueNotifier.createExceptionForCriticalIssues(testEngine);\n\t\t\t}\n\t\t}\n\t\tif (exception != null) {\n\t\t\tthrow exception;\n\t\t}\n\t}\n\n\tprivate Map<TestEngine, EngineResultInfo> discoverSafely(LauncherDiscoveryRequest request,\n\t\t\tOptional<LauncherPhase> phase, DiscoveryIssueCollector issueCollector,\n\t\t\tFunction<String, UniqueId> uniqueIdCreator) {\n\t\tMap<TestEngine, EngineResultInfo> testEngineDescriptors = new LinkedHashMap<>();\n\t\tEngineFilterer engineFilterer = new EngineFilterer(request.getEngineFilters());\n\n\t\tfor (TestEngine testEngine : this.testEngines) {\n\t\t\tboolean engineIsExcluded = engineFilterer.isExcluded(testEngine);\n\n\t\t\tif (engineIsExcluded) {\n\t\t\t\tlogger.debug(() -> \"Test discovery for engine '%s' was skipped due to an EngineFilter%s.\".formatted(\n\t\t\t\t\ttestEngine.getId(), phase.map(\" in %s phase\"::formatted).orElse(\"\")));\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlogger.debug(() -> \"Discovering tests%s in engine '%s'.\".formatted(\n\t\t\t\tphase.map(\" during Launcher %s phase\"::formatted).orElse(\"\"), testEngine.getId()));\n\n\t\t\tEngineResultInfo engineResult = discoverEngineRoot(testEngine, request, issueCollector, uniqueIdCreator);\n\t\t\ttestEngineDescriptors.put(testEngine, engineResult);\n\t\t}\n\n\t\tengineFilterer.performSanityChecks();\n\n\t\tList<PostDiscoveryFilter> filters = new ArrayList<>(postDiscoveryFilters);\n\t\tfilters.addAll(request.getPostDiscoveryFilters());\n\n\t\tapplyPostDiscoveryFilters(testEngineDescriptors, filters);\n\t\tprune(testEngineDescriptors);\n\n\t\treturn testEngineDescriptors;\n\t}\n\n\tprivate EngineResultInfo discoverEngineRoot(TestEngine testEngine, LauncherDiscoveryRequest request,\n\t\t\tDiscoveryIssueCollector issueCollector, Function<String, UniqueId> uniqueIdCreator) {\n\t\tUniqueId uniqueEngineId = uniqueIdCreator.apply(testEngine.getId());\n\t\tLauncherDiscoveryListener listener = request.getDiscoveryListener();\n\t\ttry {\n\t\t\tlistener.engineDiscoveryStarted(uniqueEngineId);\n\t\t\tTestDescriptor engineRoot = testEngine.discover(request, uniqueEngineId);\n\t\t\tdiscoveryResultValidator.validate(testEngine, engineRoot);\n\t\t\tlistener.engineDiscoveryFinished(uniqueEngineId, EngineDiscoveryResult.successful());\n\t\t\treturn EngineResultInfo.completed(engineRoot, issueCollector.toNotifier());\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\tJUnitException cause = null;\n\t\t\tif (throwable instanceof LinkageError error) {\n\t\t\t\tcause = ClasspathAlignmentChecker.check(error).orElse(null);\n\t\t\t}\n\t\t\tif (cause == null) {\n\t\t\t\tString message = \"TestEngine with ID '%s' failed to discover tests\".formatted(testEngine.getId());\n\t\t\t\tcause = new JUnitException(message, throwable);\n\t\t\t}\n\t\t\tlistener.engineDiscoveryFinished(uniqueEngineId, EngineDiscoveryResult.failed(cause));\n\t\t\treturn EngineResultInfo.errored(new EngineDescriptor(uniqueEngineId, testEngine.getId()),\n\t\t\t\tissueCollector.toNotifier(), cause);\n\t\t}\n\t}\n\n\tLauncherDiscoveryListener getLauncherDiscoveryListener(LauncherDiscoveryRequest discoveryRequest,\n\t\t\tDiscoveryIssueCollector issueCollector) {\n\t\tvar delegate = ListenerRegistry.copyOf(launcherDiscoveryListenerRegistry) //\n\t\t\t\t.add(discoveryRequest.getDiscoveryListener()) //\n\t\t\t\t.add(issueCollector) //\n\t\t\t\t.getCompositeListener();\n\t\treturn new DiscoveryIssueReportingDiscoveryListener(delegate);\n\t}\n\n\tprivate void applyPostDiscoveryFilters(Map<TestEngine, EngineResultInfo> testEngineDescriptors,\n\t\t\tList<PostDiscoveryFilter> filters) {\n\t\tFilter<TestDescriptor> postDiscoveryFilter = composeFilters(filters);\n\t\tMap<String, List<TestDescriptor>> excludedTestDescriptorsByReason = new LinkedHashMap<>();\n\t\tTestDescriptor.Visitor removeExcludedTestDescriptors = descriptor -> {\n\t\t\tFilterResult filterResult = postDiscoveryFilter.apply(descriptor);\n\t\t\tif (!descriptor.isRoot() && isExcluded(descriptor, filterResult)) {\n\t\t\t\tpopulateExclusionReasonInMap(filterResult.getReason(), descriptor, excludedTestDescriptorsByReason);\n\t\t\t\tdescriptor.removeFromHierarchy();\n\t\t\t}\n\t\t};\n\t\tacceptInAllTestEngines(testEngineDescriptors, removeExcludedTestDescriptors);\n\t\tlogTestDescriptorExclusionReasons(excludedTestDescriptorsByReason);\n\t}\n\n\tprivate void populateExclusionReasonInMap(Optional<String> reason, TestDescriptor testDescriptor,\n\t\t\tMap<String, List<TestDescriptor>> excludedTestDescriptorsByReason) {\n\t\texcludedTestDescriptorsByReason.computeIfAbsent(reason.orElse(\"Unknown\"), __ -> new ArrayList<>()).add(\n\t\t\ttestDescriptor);\n\t}\n\n\tprivate void logTestDescriptorExclusionReasons(Map<String, List<TestDescriptor>> excludedTestDescriptorsByReason) {\n\t\texcludedTestDescriptorsByReason.forEach((exclusionReason, testDescriptors) -> {\n\t\t\tString displayNames = testDescriptors.stream().map(TestDescriptor::getDisplayName).collect(joining(\", \"));\n\t\t\tlong containerCount = testDescriptors.stream().filter(TestDescriptor::isContainer).count();\n\t\t\tlong methodCount = testDescriptors.stream().filter(TestDescriptor::isTest).count();\n\t\t\tlogger.config(\n\t\t\t\t() -> \"%d containers and %d tests were %s\".formatted(containerCount, methodCount, exclusionReason));\n\t\t\tlogger.debug(\n\t\t\t\t() -> \"The following containers and tests were %s: %s\".formatted(exclusionReason, displayNames));\n\t\t});\n\t}\n\n\t/**\n\t * Prune all branches in the tree of {@link TestDescriptor TestDescriptors}\n\t * that do not have executable tests.\n\t *\n\t * <p>If a {@link TestEngine} ends up with no {@code TestDescriptors} after\n\t * pruning, it will <strong>not</strong> be removed.\n\t */\n\tprivate void prune(Map<TestEngine, EngineResultInfo> testEngineResults) {\n\t\tacceptInAllTestEngines(testEngineResults, TestDescriptor::prune);\n\t}\n\n\tprivate boolean isExcluded(TestDescriptor descriptor, FilterResult filterResult) {\n\t\treturn descriptor.getChildren().isEmpty() && filterResult.excluded();\n\t}\n\n\tprivate void acceptInAllTestEngines(Map<TestEngine, EngineResultInfo> testEngineResults,\n\t\t\tTestDescriptor.Visitor visitor) {\n\t\ttestEngineResults.values().forEach(result -> result.getRootDescriptor().accept(visitor));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Queue;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Perform common validation checks on the result from the `discover()` method.\n *\n * @since 1.3\n */\nclass EngineDiscoveryResultValidator {\n\n\t/**\n\t * Perform common validation checks.\n\t *\n\t * @throws org.junit.platform.commons.PreconditionViolationException if any check fails\n\t */\n\tvoid validate(TestEngine testEngine, TestDescriptor root) {\n\t\tPreconditions.notNull(root,\n\t\t\t() -> \"The discover() method for TestEngine with ID '%s' must return a non-null root TestDescriptor.\".formatted(\n\t\t\t\ttestEngine.getId()));\n\t\tOptional<String> cyclicGraphInfo = getCyclicGraphInfo(root);\n\t\tPreconditions.condition(cyclicGraphInfo.isEmpty(),\n\t\t\t() -> \"The discover() method for TestEngine with ID '%s' returned a cyclic graph; %s\".formatted(\n\t\t\t\ttestEngine.getId(), cyclicGraphInfo.get()));\n\t}\n\n\t/**\n\t * @return non-empty {@link Optional} if the tree contains a cycle\n\t */\n\tprivate Optional<String> getCyclicGraphInfo(TestDescriptor root) {\n\n\t\tMap<UniqueId, Optional<UniqueId>> visited = new HashMap<>();\n\t\tvisited.put(root.getUniqueId(), Optional.empty());\n\n\t\tQueue<TestDescriptor> queue = new ArrayDeque<>();\n\t\tqueue.add(root);\n\n\t\twhile (!queue.isEmpty()) {\n\t\t\tTestDescriptor parent = queue.remove();\n\t\t\tfor (TestDescriptor child : parent.getChildren()) {\n\t\t\t\tUniqueId uid = child.getUniqueId();\n\t\t\t\tif (visited.containsKey(uid)) { // id already known: cycle detected!\n\n\t\t\t\t\tList<UniqueId> path1 = findPath(visited, uid);\n\t\t\t\t\tList<UniqueId> path2 = findPath(visited, parent.getUniqueId());\n\t\t\t\t\tpath2.add(uid);\n\n\t\t\t\t\treturn Optional.of(\"%s exists in at least two paths:\\n(1) %s\\n(2) %s\".formatted(uid,\n\t\t\t\t\t\tformatted(path1), formatted(path2)));\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tvisited.put(uid, Optional.of(parent.getUniqueId()));\n\t\t\t\t}\n\t\t\t\tif (child.isContainer()) {\n\t\t\t\t\tqueue.add(child);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate String formatted(List<UniqueId> path) {\n\t\treturn path.stream().map(UniqueId::toString).collect(joining(\" -> \"));\n\t}\n\n\tprivate static List<UniqueId> findPath(Map<UniqueId, Optional<UniqueId>> visited, UniqueId target) {\n\t\tList<UniqueId> path = new ArrayList<>();\n\t\tpath.add(target);\n\t\tUniqueId current = target;\n\n\t\twhile (visited.containsKey(current)) {\n\t\t\tOptional<UniqueId> backTraced = visited.get(current);\n\t\t\tif (backTraced.isPresent()) {\n\t\t\t\tpath.add(0, backTraced.get());\n\t\t\t\tcurrent = backTraced.get();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn path;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineExecutionOrchestrator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.launcher.LauncherConstants.DRY_RUN_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherPhase.getDiscoveryIssueFailurePhase;\n\nimport java.util.Collection;\nimport java.util.Optional;\nimport java.util.function.Consumer;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;\n\n/**\n * Orchestrates test execution using the configured test engines.\n *\n * @since 1.7\n */\n@API(status = INTERNAL, since = \"1.7\", consumers = { \"org.junit.platform.testkit\", \"org.junit.platform.suite.engine\" })\npublic class EngineExecutionOrchestrator {\n\n\tprivate final ListenerRegistry<TestExecutionListener> listenerRegistry;\n\n\tpublic EngineExecutionOrchestrator() {\n\t\tthis(ListenerRegistry.forTestExecutionListeners());\n\t}\n\n\tEngineExecutionOrchestrator(ListenerRegistry<TestExecutionListener> listenerRegistry) {\n\t\tthis.listenerRegistry = listenerRegistry;\n\t}\n\n\tvoid execute(InternalTestPlan internalTestPlan, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCollection<? extends TestExecutionListener> listeners, CancellationToken cancellationToken) {\n\t\tConfigurationParameters configurationParameters = internalTestPlan.getConfigurationParameters();\n\t\tListenerRegistry<TestExecutionListener> testExecutionListenerListeners = buildListenerRegistryForExecution(\n\t\t\tlisteners);\n\t\twithInterceptedStreams(configurationParameters, testExecutionListenerListeners,\n\t\t\ttestExecutionListener -> execute(internalTestPlan, EngineExecutionListener.NOOP, testExecutionListener,\n\t\t\t\trequestLevelStore, cancellationToken));\n\t}\n\n\t/**\n\t * Executes tests for the supplied {@linkplain LauncherDiscoveryResult\n\t * discoveryResult} and notifies the supplied {@linkplain\n\t * EngineExecutionListener engineExecutionListener} and\n\t * {@linkplain TestExecutionListener testExecutionListener} of execution\n\t * events.\n\t */\n\t@API(status = INTERNAL, since = \"1.9\", consumers = { \"org.junit.platform.testkit\",\n\t\t\t\"org.junit.platform.suite.engine\" })\n\tpublic void execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener engineExecutionListener,\n\t\t\tTestExecutionListener testExecutionListener, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\tPreconditions.notNull(discoveryResult, \"discoveryResult must not be null\");\n\t\tPreconditions.notNull(engineExecutionListener, \"engineExecutionListener must not be null\");\n\t\tPreconditions.notNull(testExecutionListener, \"testExecutionListener must not be null\");\n\t\tPreconditions.notNull(requestLevelStore, \"requestLevelStore must not be null\");\n\t\tPreconditions.notNull(cancellationToken, \"cancellationToken must not be null\");\n\n\t\tInternalTestPlan internalTestPlan = InternalTestPlan.from(discoveryResult);\n\t\texecute(internalTestPlan, engineExecutionListener, testExecutionListener, requestLevelStore, cancellationToken);\n\t}\n\n\tprivate void execute(InternalTestPlan internalTestPlan, EngineExecutionListener parentEngineExecutionListener,\n\t\t\tTestExecutionListener testExecutionListener, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\tinternalTestPlan.markStarted();\n\n\t\t// Do not directly pass the internal test plan to test execution listeners.\n\t\t// Hyrum's Law indicates that someone will eventually come to depend on it.\n\t\tTestPlan testPlan = internalTestPlan.getDelegate();\n\t\tLauncherDiscoveryResult discoveryResult = internalTestPlan.getDiscoveryResult();\n\n\t\ttestExecutionListener.testPlanExecutionStarted(testPlan);\n\t\tif (isDryRun(internalTestPlan)) {\n\t\t\tdryRun(testPlan, testExecutionListener);\n\t\t}\n\t\telse {\n\t\t\texecute(discoveryResult,\n\t\t\t\tbuildEngineExecutionListener(parentEngineExecutionListener, testExecutionListener, testPlan),\n\t\t\t\trequestLevelStore, cancellationToken);\n\t\t}\n\t\ttestExecutionListener.testPlanExecutionFinished(testPlan);\n\t}\n\n\tprivate Boolean isDryRun(InternalTestPlan internalTestPlan) {\n\t\treturn internalTestPlan.getConfigurationParameters().getBoolean(DRY_RUN_PROPERTY_NAME).orElse(false);\n\t}\n\n\tprivate void dryRun(TestPlan testPlan, TestExecutionListener listener) {\n\t\ttestPlan.accept(new TestPlan.Visitor() {\n\t\t\t@Override\n\t\t\tpublic void preVisitContainer(TestIdentifier testIdentifier) {\n\t\t\t\tlistener.executionStarted(testIdentifier);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void visit(TestIdentifier testIdentifier) {\n\t\t\t\tif (!testIdentifier.isContainer()) {\n\t\t\t\t\tlistener.executionSkipped(testIdentifier, \"JUnit Platform dry-run mode is enabled\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void postVisitContainer(TestIdentifier testIdentifier) {\n\t\t\t\tlistener.executionFinished(testIdentifier, TestExecutionResult.successful());\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate static EngineExecutionListener buildEngineExecutionListener(\n\t\t\tEngineExecutionListener parentEngineExecutionListener, TestExecutionListener testExecutionListener,\n\t\t\tTestPlan testPlan) {\n\t\tvar registry = ListenerRegistry.forEngineExecutionListeners();\n\t\tregistry.add(new ExecutionListenerAdapter(testPlan, testExecutionListener));\n\t\tregistry.add(parentEngineExecutionListener);\n\t\tvar listener = registry.getCompositeListener();\n\t\tif (isMemoryCleanupEnabled(testPlan)) {\n\t\t\tlistener = new MemoryCleanupListener(listener, testPlan);\n\t\t}\n\t\treturn listener;\n\t}\n\n\tprivate static Boolean isMemoryCleanupEnabled(TestPlan testPlan) {\n\t\treturn testPlan.getConfigurationParameters() //\n\t\t\t\t.getBoolean(LauncherConstants.MEMORY_CLEANUP_ENABLED_PROPERTY_NAME) //\n\t\t\t\t.orElse(false);\n\t}\n\n\tprivate void withInterceptedStreams(ConfigurationParameters configurationParameters,\n\t\t\tListenerRegistry<TestExecutionListener> listenerRegistry, Consumer<TestExecutionListener> action) {\n\n\t\tTestExecutionListener testExecutionListener = listenerRegistry.getCompositeListener();\n\t\tOptional<StreamInterceptingTestExecutionListener> streamInterceptingTestExecutionListener = StreamInterceptingTestExecutionListener.create(\n\t\t\tconfigurationParameters, testExecutionListener::reportingEntryPublished);\n\t\tstreamInterceptingTestExecutionListener.ifPresent(listenerRegistry::add);\n\t\ttry {\n\t\t\taction.accept(listenerRegistry.getCompositeListener());\n\t\t}\n\t\tfinally {\n\t\t\tstreamInterceptingTestExecutionListener.ifPresent(StreamInterceptingTestExecutionListener::unregister);\n\t\t}\n\t}\n\n\t/**\n\t * Executes tests for the supplied {@linkplain LauncherDiscoveryResult\n\t * discovery results} and notifies the supplied {@linkplain\n\t * EngineExecutionListener listener} of execution events.\n\t */\n\tprivate void execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener engineExecutionListener,\n\t\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken) {\n\t\tPreconditions.notNull(discoveryResult, \"discoveryResult must not be null\");\n\t\tPreconditions.notNull(engineExecutionListener, \"engineExecutionListener must not be null\");\n\n\t\tConfigurationParameters configurationParameters = discoveryResult.getConfigurationParameters();\n\t\tEngineExecutionListener listener = selectExecutionListener(engineExecutionListener, configurationParameters);\n\n\t\tfor (TestEngine testEngine : discoveryResult.getTestEngines()) {\n\t\t\tfailOrExecuteEngine(discoveryResult, listener, testEngine, requestLevelStore, cancellationToken);\n\t\t}\n\t}\n\n\tprivate static EngineExecutionListener selectExecutionListener(EngineExecutionListener engineExecutionListener,\n\t\t\tConfigurationParameters configurationParameters) {\n\t\tboolean stackTracePruningEnabled = configurationParameters.getBoolean(STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME) //\n\t\t\t\t.orElse(true);\n\t\tif (stackTracePruningEnabled) {\n\t\t\treturn new StackTracePruningEngineExecutionListener(engineExecutionListener);\n\t\t}\n\t\treturn engineExecutionListener;\n\t}\n\n\tprivate void failOrExecuteEngine(LauncherDiscoveryResult discoveryResult, EngineExecutionListener listener,\n\t\t\tTestEngine testEngine, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\tEngineResultInfo engineDiscoveryResult = discoveryResult.getEngineResult(testEngine);\n\t\tDiscoveryIssueNotifier discoveryIssueNotifier = shouldReportDiscoveryIssues(discoveryResult) //\n\t\t\t\t? engineDiscoveryResult.getDiscoveryIssueNotifier() //\n\t\t\t\t: DiscoveryIssueNotifier.NO_ISSUES;\n\t\tTestDescriptor engineDescriptor = engineDiscoveryResult.getRootDescriptor();\n\t\tThrowable failure = engineDiscoveryResult.getCause() //\n\t\t\t\t.orElseGet(() -> discoveryIssueNotifier.createExceptionForCriticalIssues(testEngine));\n\t\tif (failure != null) {\n\t\t\tlistener.executionStarted(engineDescriptor);\n\t\t\tif (engineDiscoveryResult.getCause().isPresent()) {\n\t\t\t\tdiscoveryIssueNotifier.logCriticalIssues(testEngine);\n\t\t\t}\n\t\t\tdiscoveryIssueNotifier.logNonCriticalIssues(testEngine);\n\t\t\tlistener.executionFinished(engineDescriptor, TestExecutionResult.failed(failure));\n\t\t}\n\t\telse if (cancellationToken.isCancellationRequested()) {\n\t\t\tlistener.executionStarted(engineDescriptor);\n\t\t\tengineDescriptor.getChildren().forEach(child -> listener.executionSkipped(child, \"Execution cancelled\"));\n\t\t\tlistener.executionFinished(engineDescriptor, TestExecutionResult.aborted(null));\n\t\t}\n\t\telse {\n\t\t\texecuteEngine(engineDescriptor, listener, discoveryResult.getConfigurationParameters(), testEngine,\n\t\t\t\tdiscoveryResult.getOutputDirectoryCreator(), discoveryIssueNotifier, requestLevelStore,\n\t\t\t\tcancellationToken);\n\t\t}\n\t}\n\n\tprivate static boolean shouldReportDiscoveryIssues(LauncherDiscoveryResult discoveryResult) {\n\t\tConfigurationParameters configurationParameters = discoveryResult.getConfigurationParameters();\n\t\treturn getDiscoveryIssueFailurePhase(configurationParameters) //\n\t\t\t\t.orElse(LauncherPhase.EXECUTION) == LauncherPhase.EXECUTION;\n\t}\n\n\tprivate ListenerRegistry<TestExecutionListener> buildListenerRegistryForExecution(\n\t\t\tCollection<? extends TestExecutionListener> listeners) {\n\t\tif (listeners.isEmpty()) {\n\t\t\treturn this.listenerRegistry;\n\t\t}\n\t\treturn ListenerRegistry.copyOf(this.listenerRegistry).addAll(listeners);\n\t}\n\n\tprivate void executeEngine(TestDescriptor engineDescriptor, EngineExecutionListener listener,\n\t\t\tConfigurationParameters configurationParameters, TestEngine testEngine,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator, DiscoveryIssueNotifier discoveryIssueNotifier,\n\t\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken) {\n\n\t\tOutcomeDelayingEngineExecutionListener delayingListener = new OutcomeDelayingEngineExecutionListener(listener,\n\t\t\tengineDescriptor);\n\t\ttry {\n\t\t\ttestEngine.execute(ExecutionRequest.create(engineDescriptor, delayingListener, configurationParameters,\n\t\t\t\toutputDirectoryCreator, requestLevelStore, cancellationToken));\n\t\t\tdiscoveryIssueNotifier.logNonCriticalIssues(testEngine);\n\t\t\tdelayingListener.reportEngineOutcome();\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(throwable);\n\t\t\tJUnitException cause = null;\n\t\t\tif (throwable instanceof LinkageError error) {\n\t\t\t\tcause = ClasspathAlignmentChecker.check(error).orElse(null);\n\t\t\t}\n\t\t\tif (cause == null) {\n\t\t\t\tString message = \"TestEngine with ID '%s' failed to execute tests\".formatted(testEngine.getId());\n\t\t\t\tcause = new JUnitException(message, throwable);\n\t\t\t}\n\t\t\tdelayingListener.reportEngineStartIfNecessary();\n\t\t\tdiscoveryIssueNotifier.logNonCriticalIssues(testEngine);\n\t\t\tdelayingListener.reportEngineFailure(cause);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineFilterer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.stream.Collectors.joining;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toSet;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Set;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.EngineFilter;\n\nclass EngineFilterer {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(EngineFilterer.class);\n\n\tprivate final List<EngineFilter> engineFilters;\n\n\tprivate final Map<TestEngine, Boolean> checkedTestEngines = new HashMap<>();\n\n\tEngineFilterer(List<EngineFilter> engineFilters) {\n\t\tthis.engineFilters = engineFilters;\n\t}\n\n\tboolean isExcluded(TestEngine testEngine) {\n\t\tboolean excluded = engineFilters.stream() //\n\t\t\t\t.map(engineFilter -> engineFilter.apply(testEngine)) //\n\t\t\t\t.anyMatch(FilterResult::excluded);\n\t\tcheckedTestEngines.put(testEngine, excluded);\n\t\treturn excluded;\n\t}\n\n\tvoid performSanityChecks() {\n\t\tcheckNoUnmatchedIncludeFilter();\n\t\twarnIfAllEnginesExcluded();\n\t}\n\n\tprivate void warnIfAllEnginesExcluded() {\n\t\tif (!checkedTestEngines.isEmpty() && checkedTestEngines.values().stream().allMatch(excluded -> excluded)) {\n\t\t\tlogger.warn(() -> \"All TestEngines were excluded by EngineFilters.\\n\" + getStateDescription());\n\t\t}\n\t}\n\n\tprivate void checkNoUnmatchedIncludeFilter() {\n\t\tSortedSet<String> unmatchedEngineIdsOfIncludeFilters = getUnmatchedEngineIdsOfIncludeFilters();\n\t\tif (!unmatchedEngineIdsOfIncludeFilters.isEmpty()) {\n\t\t\tthrow new JUnitException(\"No TestEngine ID matched the following include EngineFilters: \"\n\t\t\t\t\t+ unmatchedEngineIdsOfIncludeFilters + \".\\n\" //\n\t\t\t\t\t+ \"Please fix/remove the filter or add the engine.\\n\" //\n\t\t\t\t\t+ getStateDescription());\n\t\t}\n\t}\n\n\tprivate SortedSet<String> getUnmatchedEngineIdsOfIncludeFilters() {\n\t\tSet<String> checkedTestEngineIds = checkedTestEngines.keySet().stream() //\n\t\t\t\t.map(TestEngine::getId) //\n\t\t\t\t.collect(toSet());\n\t\treturn engineFilters.stream() //\n\t\t\t\t.filter(EngineFilter::isIncludeFilter) //\n\t\t\t\t.map(EngineFilter::getEngineIds) //\n\t\t\t\t.flatMap(Collection::stream) //\n\t\t\t\t.filter(id -> !checkedTestEngineIds.contains(id)) //\n\t\t\t\t.collect(toCollection(TreeSet::new));\n\t}\n\n\tprivate String getStateDescription() {\n\t\treturn getRegisteredEnginesDescription() + \"\\n\" + getRegisteredFiltersDescription();\n\t}\n\n\tprivate String getRegisteredEnginesDescription() {\n\t\treturn TestEngineFormatter.format(\"Registered TestEngines\", checkedTestEngines.keySet());\n\t}\n\n\tprivate String getRegisteredFiltersDescription() {\n\t\treturn \"Registered EngineFilters:\" + engineFilters.stream() //\n\t\t\t\t.map(Objects::toString) //\n\t\t\t\t.collect(joining(\"\\n- \", \"\\n- \", \"\"));\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/EngineIdValidator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * @since 1.7\n */\nclass EngineIdValidator {\n\n\tprivate EngineIdValidator() {\n\t}\n\n\tstatic Iterable<TestEngine> validate(Iterable<TestEngine> testEngines) {\n\t\tSet<String> ids = new HashSet<>();\n\t\tfor (TestEngine testEngine : testEngines) {\n\t\t\t// check usage of reserved ID prefix\n\t\t\tif (!validateReservedIds(testEngine)) {\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t() -> \"Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID: '%s'\".formatted(\n\t\t\t\t\t\ttestEngine.getId()));\n\t\t\t}\n\n\t\t\t// check uniqueness\n\t\t\tif (!ids.add(testEngine.getId())) {\n\t\t\t\tthrow new JUnitException(\n\t\t\t\t\t\"Cannot create Launcher for multiple engines with the same ID '%s'.\".formatted(testEngine.getId()));\n\t\t\t}\n\t\t}\n\t\treturn testEngines;\n\t}\n\n\tprivate static Logger getLogger() {\n\t\t// Not a constant to avoid problems with building GraalVM native images\n\t\treturn LoggerFactory.getLogger(EngineIdValidator.class);\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/1557\n\tprivate static boolean validateReservedIds(TestEngine testEngine) {\n\t\tString engineId = Preconditions.notBlank(testEngine.getId(),\n\t\t\t() -> \"ID for TestEngine [%s] must not be null or blank\".formatted(testEngine.getClass().getName()));\n\t\tif (!engineId.startsWith(\"junit-\")) {\n\t\t\treturn true;\n\t\t}\n\t\treturn switch (engineId) {\n\t\t\tcase \"junit-jupiter\" -> {\n\t\t\t\tvalidateWellKnownClassName(testEngine, \"org.junit.jupiter.engine.JupiterTestEngine\");\n\t\t\t\tyield true;\n\t\t\t}\n\t\t\tcase \"junit-vintage\" -> {\n\t\t\t\tvalidateWellKnownClassName(testEngine, \"org.junit.vintage.engine.VintageTestEngine\");\n\t\t\t\tyield true;\n\t\t\t}\n\t\t\tcase \"junit-platform-suite\" -> {\n\t\t\t\tvalidateWellKnownClassName(testEngine, \"org.junit.platform.suite.engine.SuiteTestEngine\");\n\t\t\t\tyield true;\n\t\t\t}\n\t\t\tdefault -> false;\n\t\t};\n\t}\n\n\tprivate static void validateWellKnownClassName(TestEngine testEngine, String expectedClassName) {\n\t\tString actualClassName = testEngine.getClass().getName();\n\t\tif (actualClassName.equals(expectedClassName)) {\n\t\t\treturn;\n\t\t}\n\t\tthrow new JUnitException(\n\t\t\t\"Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID.\".formatted(\n\t\t\t\tactualClassName, testEngine.getId()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ExecutionListenerAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * An {@code ExecutionListenerAdapter} adapts a {@link TestPlan} and a corresponding\n * {@link TestExecutionListener} to the {@link EngineExecutionListener} API.\n *\n * @since 1.0\n */\nclass ExecutionListenerAdapter implements EngineExecutionListener {\n\n\tprivate final TestPlan testPlan;\n\tprivate final TestExecutionListener testExecutionListener;\n\n\tExecutionListenerAdapter(TestPlan testPlan, TestExecutionListener testExecutionListener) {\n\t\tthis.testPlan = testPlan;\n\t\tthis.testExecutionListener = testExecutionListener;\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\tTestIdentifier testIdentifier = TestIdentifier.from(testDescriptor);\n\t\tthis.testPlan.addInternal(testIdentifier);\n\t\tthis.testExecutionListener.dynamicTestRegistered(testIdentifier);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\tthis.testExecutionListener.executionStarted(getTestIdentifier(testDescriptor));\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tthis.testExecutionListener.executionSkipped(getTestIdentifier(testDescriptor), reason);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tthis.testExecutionListener.executionFinished(getTestIdentifier(testDescriptor), testExecutionResult);\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\tthis.testExecutionListener.reportingEntryPublished(getTestIdentifier(testDescriptor), entry);\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\tthis.testExecutionListener.fileEntryPublished(getTestIdentifier(testDescriptor), file);\n\t}\n\n\tprivate TestIdentifier getTestIdentifier(TestDescriptor testDescriptor) {\n\t\treturn this.testPlan.getTestIdentifier(testDescriptor.getUniqueId());\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/HierarchicalOutputDirectoryCreator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId.Segment;\n\n/**\n * Hierarchical {@link OutputDirectoryCreator} that creates directories based on\n * the unique ID segments of a {@link TestDescriptor}.\n *\n * @since 1.12\n */\nclass HierarchicalOutputDirectoryCreator implements OutputDirectoryCreator {\n\n\tprivate static final Pattern FORBIDDEN_CHARS = Pattern.compile(\"[^a-z0-9.,_\\\\-() ]\", Pattern.CASE_INSENSITIVE);\n\tprivate static final String REPLACEMENT = \"_\";\n\n\tprivate final Supplier<Path> rootDirSupplier;\n\n\tprivate volatile @Nullable Path rootDir;\n\n\tHierarchicalOutputDirectoryCreator(Supplier<Path> rootDirSupplier) {\n\t\tthis.rootDirSupplier = rootDirSupplier;\n\t}\n\n\t@Override\n\tpublic Path createOutputDirectory(TestDescriptor testDescriptor) throws IOException {\n\t\tPreconditions.notNull(testDescriptor, \"testDescriptor must not be null\");\n\n\t\tList<Segment> segments = testDescriptor.getUniqueId().getSegments();\n\t\tPath relativePath = segments.stream() //\n\t\t\t\t.skip(1) //\n\t\t\t\t.map(HierarchicalOutputDirectoryCreator::toSanitizedPath) //\n\t\t\t\t.reduce(toSanitizedPath(segments.get(0)), Path::resolve);\n\t\treturn Files.createDirectories(getRootDirectory().resolve(relativePath));\n\t}\n\n\t@Override\n\tpublic synchronized Path getRootDirectory() {\n\t\tvar currentRootDir = rootDir;\n\t\tif (currentRootDir == null) {\n\t\t\tcurrentRootDir = rootDirSupplier.get();\n\t\t\trootDir = currentRootDir;\n\t\t}\n\t\treturn currentRootDir;\n\t}\n\n\tprivate static Path toSanitizedPath(Segment segment) {\n\t\treturn Path.of(sanitizeName(segment.getValue()));\n\t}\n\n\tprivate static String sanitizeName(String value) {\n\t\treturn FORBIDDEN_CHARS.matcher(value).replaceAll(REPLACEMENT);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InterceptingLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.LauncherInterceptor;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.10\n */\nclass InterceptingLauncher extends DelegatingLauncher {\n\n\tprivate final LauncherInterceptor interceptor;\n\n\tInterceptingLauncher(Launcher delegate, LauncherInterceptor interceptor) {\n\t\tsuper(delegate);\n\t\tthis.interceptor = interceptor;\n\t}\n\n\t@Override\n\tpublic TestPlan discover(LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\treturn interceptor.intercept(() -> super.discover(discoveryRequest));\n\t}\n\n\t@Override\n\tpublic void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tinterceptor.<@Nullable Object> intercept(() -> {\n\t\t\tsuper.execute(discoveryRequest, listeners);\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t@Override\n\tpublic void execute(TestPlan testPlan, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(testPlan, \"testPlan must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tinterceptor.<@Nullable Object> intercept(() -> {\n\t\t\tsuper.execute(testPlan, listeners);\n\t\t\treturn null;\n\t\t});\n\t}\n\n\t@Override\n\tpublic void execute(LauncherExecutionRequest executionRequest) {\n\t\tPreconditions.notNull(executionRequest, \"executionRequest must not be null\");\n\t\tinterceptor.<@Nullable Object> intercept(() -> {\n\t\t\tsuper.execute(executionRequest);\n\t\t\treturn null;\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.4\n */\nclass InternalTestPlan extends TestPlan {\n\n\tprivate final AtomicBoolean executionStarted = new AtomicBoolean(false);\n\tprivate final LauncherDiscoveryResult discoveryResult;\n\tprivate final TestPlan delegate;\n\n\tstatic InternalTestPlan from(LauncherDiscoveryResult discoveryResult) {\n\t\tTestPlan delegate = TestPlan.from(discoveryResult.containsCriticalIssuesOrContainsTests(),\n\t\t\tdiscoveryResult.getEngineTestDescriptors(), discoveryResult.getConfigurationParameters(),\n\t\t\tdiscoveryResult.getOutputDirectoryCreator());\n\t\treturn new InternalTestPlan(discoveryResult, delegate);\n\t}\n\n\tprivate InternalTestPlan(LauncherDiscoveryResult discoveryResult, TestPlan delegate) {\n\t\tsuper(delegate.containsTests(), delegate.getConfigurationParameters(), delegate.getOutputDirectoryCreator());\n\t\tthis.discoveryResult = discoveryResult;\n\t\tthis.delegate = delegate;\n\t}\n\n\tvoid markStarted() {\n\t\tif (!executionStarted.compareAndSet(false, true)) {\n\t\t\tthrow new PreconditionViolationException(\"TestPlan must only be executed once\");\n\t\t}\n\t}\n\n\tLauncherDiscoveryResult getDiscoveryResult() {\n\t\treturn discoveryResult;\n\t}\n\n\tpublic TestPlan getDelegate() {\n\t\treturn delegate;\n\t}\n\n\t@Override\n\tpublic void addInternal(TestIdentifier testIdentifier) {\n\t\tdelegate.addInternal(testIdentifier);\n\t}\n\n\t@Override\n\tpublic void removeInternal(UniqueId uniqueId) {\n\t\tdelegate.removeInternal(uniqueId);\n\t}\n\n\t@Override\n\tpublic Set<TestIdentifier> getRoots() {\n\t\treturn delegate.getRoots();\n\t}\n\n\t@Override\n\tpublic Optional<TestIdentifier> getParent(TestIdentifier child) {\n\t\treturn delegate.getParent(child);\n\t}\n\n\t@Override\n\tpublic Set<TestIdentifier> getChildren(TestIdentifier parent) {\n\t\treturn delegate.getChildren(parent);\n\t}\n\n\t@Override\n\tpublic Set<TestIdentifier> getChildren(UniqueId parentId) {\n\t\treturn delegate.getChildren(parentId);\n\t}\n\n\t@Override\n\tpublic TestIdentifier getTestIdentifier(UniqueId uniqueId) {\n\t\treturn delegate.getTestIdentifier(uniqueId);\n\t}\n\n\t@Override\n\tpublic long countTestIdentifiers(Predicate<? super TestIdentifier> predicate) {\n\t\treturn delegate.countTestIdentifiers(predicate);\n\t}\n\n\t@Override\n\tpublic Set<TestIdentifier> getDescendants(TestIdentifier parent) {\n\t\treturn delegate.getDescendants(parent);\n\t}\n\n\t@Override\n\tpublic boolean containsTests() {\n\t\treturn delegate.containsTests();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/IterationOrder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\nenum IterationOrder {\n\n\tORIGINAL {\n\t\t@Override\n\t\t<T> void forEach(List<T> listeners, Consumer<T> action) {\n\t\t\tlisteners.forEach(action);\n\t\t}\n\t},\n\n\tREVERSED {\n\t\t@Override\n\t\t<T> void forEach(List<T> listeners, Consumer<T> action) {\n\t\t\tforEachInReverseOrder(listeners, action);\n\t\t}\n\t};\n\n\tabstract <T> void forEach(List<T> listeners, Consumer<T> action);\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfig.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * {@code LauncherConfig} defines the configuration API for creating\n * {@link Launcher} instances via the {@link LauncherFactory}.\n *\n * <h2>Example</h2>\n *\n * <pre class=\"code\">\n * LauncherConfig launcherConfig = LauncherConfig.builder()\n *   .enableTestEngineAutoRegistration(false)\n *   .enableTestExecutionListenerAutoRegistration(false)\n *   .addTestEngines(new CustomTestEngine())\n *   .addTestExecutionListeners(new CustomTestExecutionListener())\n *   .build();\n *\n * Launcher launcher = LauncherFactory.create(launcherConfig);\n * LauncherDiscoveryRequest discoveryRequest = ...\n * launcher.execute(discoveryRequest);\n * </pre>\n *\n * @since 1.3\n * @see #builder()\n * @see Launcher\n * @see LauncherFactory\n */\n@API(status = STABLE, since = \"1.7\")\npublic interface LauncherConfig {\n\n\t/**\n\t * The default {@code LauncherConfig} which uses automatic registration for\n\t * test engines, supported listeners, and post-discovery filters.\n\t */\n\tLauncherConfig DEFAULT = builder().build();\n\n\t/**\n\t * Determine if test engines should be discovered at runtime using the\n\t * {@link java.util.ServiceLoader ServiceLoader} mechanism and\n\t * automatically registered.\n\t *\n\t * @return {@code true} if test engines should be automatically registered\n\t */\n\tboolean isTestEngineAutoRegistrationEnabled();\n\n\t/**\n\t * Determine if launcher session listeners should be discovered at runtime\n\t * using the {@link java.util.ServiceLoader ServiceLoader} mechanism and\n\t * automatically registered.\n\t *\n\t * @return {@code true} if launcher session listeners should be\n\t * automatically registered\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tboolean isLauncherSessionListenerAutoRegistrationEnabled();\n\n\t/**\n\t * Determine if launcher discovery listeners should be discovered at runtime\n\t * using the {@link java.util.ServiceLoader ServiceLoader} mechanism and\n\t * automatically registered.\n\t *\n\t * @return {@code true} if launcher discovery listeners should be\n\t * automatically registered\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tboolean isLauncherDiscoveryListenerAutoRegistrationEnabled();\n\n\t/**\n\t * Determine if test execution listeners should be discovered at runtime\n\t * using the {@link java.util.ServiceLoader ServiceLoader} mechanism and\n\t * automatically registered.\n\t *\n\t * @return {@code true} if test execution listeners should be automatically\n\t * registered\n\t */\n\tboolean isTestExecutionListenerAutoRegistrationEnabled();\n\n\t/**\n\t * Determine if post discovery filters should be discovered at runtime\n\t * using the {@link java.util.ServiceLoader ServiceLoader} mechanism and\n\t * automatically registered.\n\t *\n\t * @return {@code true} if post discovery filters should be automatically\n\t * registered\n\t * @since 1.7\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tboolean isPostDiscoveryFilterAutoRegistrationEnabled();\n\n\t/**\n\t * Get the collection of additional test engines that should be added to\n\t * the {@link Launcher}.\n\t *\n\t * @return the collection of additional test engines; never {@code null} but\n\t * potentially empty\n\t */\n\tCollection<TestEngine> getAdditionalTestEngines();\n\n\t/**\n\t * Get the collection of additional launcher session listeners that should\n\t * be added to the {@link Launcher}.\n\t *\n\t * @return the collection of additional launcher session listeners; never\n\t * {@code null} but potentially empty\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tCollection<LauncherSessionListener> getAdditionalLauncherSessionListeners();\n\n\t/**\n\t * Get the collection of additional launcher discovery listeners that should\n\t * be added to the {@link Launcher}.\n\t *\n\t * @return the collection of additional launcher discovery listeners; never\n\t * {@code null} but potentially empty\n\t * @since 1.8\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tCollection<LauncherDiscoveryListener> getAdditionalLauncherDiscoveryListeners();\n\n\t/**\n\t * Get the collection of additional test execution listeners that should be\n\t * added to the {@link Launcher}.\n\t *\n\t * @return the collection of additional test execution listeners; never\n\t * {@code null} but potentially empty\n\t */\n\tCollection<TestExecutionListener> getAdditionalTestExecutionListeners();\n\n\t/**\n\t * Get the collection of additional post discovery filters that should be\n\t * added to the {@link Launcher}.\n\t *\n\t * @return the collection of additional post discovery filters; never\n\t * {@code null} but potentially empty\n\t * @since 1.7\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tCollection<PostDiscoveryFilter> getAdditionalPostDiscoveryFilters();\n\n\t/**\n\t * Create a new {@link LauncherConfig.Builder}.\n\t *\n\t * @return a new builder; never {@code null}\n\t */\n\tstatic Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\t/**\n\t * <em>Builder</em> API for {@link LauncherConfig}.\n\t */\n\tclass Builder {\n\n\t\tprivate boolean engineAutoRegistrationEnabled = true;\n\t\tprivate boolean launcherSessionListenerAutoRegistrationEnabled = true;\n\t\tprivate boolean launcherDiscoveryListenerAutoRegistrationEnabled = true;\n\t\tprivate boolean testExecutionListenerAutoRegistrationEnabled = true;\n\t\tprivate boolean postDiscoveryFilterAutoRegistrationEnabled = true;\n\t\tprivate final Collection<TestEngine> engines = new LinkedHashSet<>();\n\t\tprivate final Collection<LauncherSessionListener> sessionListeners = new LinkedHashSet<>();\n\t\tprivate final Collection<LauncherDiscoveryListener> discoveryListeners = new LinkedHashSet<>();\n\t\tprivate final Collection<TestExecutionListener> executionListeners = new LinkedHashSet<>();\n\t\tprivate final Collection<PostDiscoveryFilter> postDiscoveryFilters = new LinkedHashSet<>();\n\n\t\tprivate Builder() {\n\t\t\t/* no-op */\n\t\t}\n\n\t\t/**\n\t\t * Configure the auto-registration flag for launcher session\n\t\t * listeners.\n\t\t *\n\t\t * <p>Defaults to {@code true}.\n\t\t *\n\t\t * @param enabled {@code true} if launcher session listeners should be\n\t\t * automatically registered\n\t\t * @return this builder for method chaining\n\t\t * @since 1.8\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder enableLauncherSessionListenerAutoRegistration(boolean enabled) {\n\t\t\tthis.launcherSessionListenerAutoRegistrationEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the auto-registration flag for launcher discovery\n\t\t * listeners.\n\t\t *\n\t\t * <p>Defaults to {@code true}.\n\t\t *\n\t\t * @param enabled {@code true} if launcher discovery listeners should be\n\t\t * automatically registered\n\t\t * @return this builder for method chaining\n\t\t * @since 1.8\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder enableLauncherDiscoveryListenerAutoRegistration(boolean enabled) {\n\t\t\tthis.launcherDiscoveryListenerAutoRegistrationEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the auto-registration flag for test execution listeners.\n\t\t *\n\t\t * <p>Defaults to {@code true}.\n\t\t *\n\t\t * @param enabled {@code true} if test execution listeners should be\n\t\t * automatically registered\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder enableTestExecutionListenerAutoRegistration(boolean enabled) {\n\t\t\tthis.testExecutionListenerAutoRegistrationEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the auto-registration flag for test engines.\n\t\t *\n\t\t * <p>Defaults to {@code true}.\n\t\t *\n\t\t * @param enabled {@code true} if test engines should be automatically\n\t\t * registered\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder enableTestEngineAutoRegistration(boolean enabled) {\n\t\t\tthis.engineAutoRegistrationEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure the auto-registration flag for post discovery filters.\n\t\t *\n\t\t * <p>Defaults to {@code true}.\n\t\t *\n\t\t * @param enabled {@code true} if post discovery filters should be automatically\n\t\t * registered\n\t\t * @return this builder for method chaining\n\t\t * @since 1.7\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder enablePostDiscoveryFilterAutoRegistration(boolean enabled) {\n\t\t\tthis.postDiscoveryFilterAutoRegistrationEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied test engines to the configuration.\n\t\t *\n\t\t * @param engines additional test engines to register; never {@code null}\n\t\t * or containing {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder addTestEngines(TestEngine... engines) {\n\t\t\tPreconditions.notNull(engines, \"TestEngine array must not be null\");\n\t\t\tPreconditions.containsNoNullElements(engines, \"TestEngine array must not contain null elements\");\n\t\t\tCollections.addAll(this.engines, engines);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied launcher session listeners to the configuration.\n\t\t *\n\t\t * @param listeners additional launcher session listeners to register;\n\t\t * never {@code null} or containing {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder addLauncherSessionListeners(LauncherSessionListener... listeners) {\n\t\t\tPreconditions.notNull(listeners, \"LauncherSessionListener array must not be null\");\n\t\t\tPreconditions.containsNoNullElements(listeners,\n\t\t\t\t\"LauncherSessionListener array must not contain null elements\");\n\t\t\tCollections.addAll(this.sessionListeners, listeners);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied launcher discovery listeners to the configuration.\n\t\t *\n\t\t * @param listeners additional launcher discovery listeners to register;\n\t\t * never {@code null} or containing {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder addLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\t\tPreconditions.notNull(listeners, \"LauncherDiscoveryListener array must not be null\");\n\t\t\tPreconditions.containsNoNullElements(listeners,\n\t\t\t\t\"LauncherDiscoveryListener array must not contain null elements\");\n\t\t\tCollections.addAll(this.discoveryListeners, listeners);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied test execution listeners to the configuration.\n\t\t *\n\t\t * @param listeners additional test execution listeners to register;\n\t\t * never {@code null} or containing {@code null}\n\t\t * @return this builder for method chaining\n\t\t */\n\t\tpublic Builder addTestExecutionListeners(TestExecutionListener... listeners) {\n\t\t\tPreconditions.notNull(listeners, \"TestExecutionListener array must not be null\");\n\t\t\tPreconditions.containsNoNullElements(listeners,\n\t\t\t\t\"TestExecutionListener array must not contain null elements\");\n\t\t\tCollections.addAll(this.executionListeners, listeners);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied {@code filters} to the configuration.\n\t\t *\n\t\t * @param filters additional post discovery filters to register;\n\t\t * never {@code null} or containing {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.7\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder addPostDiscoveryFilters(PostDiscoveryFilter... filters) {\n\t\t\tPreconditions.notNull(filters, \"PostDiscoveryFilter array must not be null\");\n\t\t\tPreconditions.containsNoNullElements(filters, \"PostDiscoveryFilter array must not contain null elements\");\n\t\t\tCollections.addAll(this.postDiscoveryFilters, filters);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Build the {@link LauncherConfig} that has been configured via this\n\t\t * builder.\n\t\t */\n\t\tpublic LauncherConfig build() {\n\t\t\treturn new DefaultLauncherConfig(this.engineAutoRegistrationEnabled,\n\t\t\t\tthis.launcherSessionListenerAutoRegistrationEnabled,\n\t\t\t\tthis.launcherDiscoveryListenerAutoRegistrationEnabled,\n\t\t\t\tthis.testExecutionListenerAutoRegistrationEnabled, this.postDiscoveryFilterAutoRegistrationEnabled,\n\t\t\t\tthis.engines, this.sessionListeners, this.discoveryListeners, this.executionListeners,\n\t\t\t\tthis.postDiscoveryFilters);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherConfigurationParameters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 1.0\n */\nclass LauncherConfigurationParameters implements ConfigurationParameters {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(LauncherConfigurationParameters.class);\n\n\tstatic Builder builder() {\n\t\treturn new Builder();\n\t}\n\n\tprivate final List<ParameterProvider> providers;\n\n\tprivate LauncherConfigurationParameters(List<ParameterProvider> providers) {\n\t\tthis.providers = providers;\n\t}\n\n\t@Override\n\tpublic Optional<String> get(String key) {\n\t\treturn Optional.ofNullable(getProperty(key));\n\t}\n\n\t@Override\n\tpublic Optional<Boolean> getBoolean(String key) {\n\t\treturn get(key).map(Boolean::parseBoolean);\n\t}\n\n\t@Override\n\tpublic Set<String> keySet() {\n\t\treturn providers.stream().map(ParameterProvider::keySet).flatMap(Collection::stream).collect(\n\t\t\tCollectors.toSet());\n\t}\n\n\tprivate @Nullable String getProperty(String key) {\n\t\tPreconditions.notBlank(key, \"key must not be null or blank\");\n\t\treturn providers.stream() //\n\t\t\t\t.map(parameterProvider -> parameterProvider.getValue(key)) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.orElse(null);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn new ToStringBuilder(this) //\n\t\t\t\t.append(\"lookups\", providers) //\n\t\t\t\t.toString();\n\t}\n\n\tstatic final class Builder {\n\n\t\tprivate final Map<String, String> explicitParameters = new HashMap<>();\n\t\tprivate final List<String> configResources = new ArrayList<>();\n\t\tprivate boolean implicitProvidersEnabled = true;\n\t\tprivate String configFileName = ConfigurationParameters.CONFIG_FILE_NAME;\n\n\t\t@Nullable\n\t\tprivate ConfigurationParameters parentConfigurationParameters;\n\n\t\tprivate Builder() {\n\t\t}\n\n\t\tBuilder explicitParameters(Map<String, String> parameters) {\n\t\t\tPreconditions.notNull(parameters, \"configuration parameters must not be null\");\n\t\t\texplicitParameters.putAll(parameters);\n\t\t\treturn this;\n\t\t}\n\n\t\tBuilder configurationResources(List<String> configResources) {\n\t\t\tPreconditions.notNull(configResources, \"configResources must not be null\");\n\t\t\tthis.configResources.addAll(configResources);\n\t\t\treturn this;\n\t\t}\n\n\t\tBuilder enableImplicitProviders(boolean enabled) {\n\t\t\tthis.implicitProvidersEnabled = enabled;\n\t\t\treturn this;\n\t\t}\n\n\t\tBuilder configFileName(String configFileName) {\n\t\t\tPreconditions.notBlank(configFileName, \"configFileName must not be null or blank\");\n\t\t\tthis.configFileName = configFileName;\n\t\t\treturn this;\n\t\t}\n\n\t\tBuilder parentConfigurationParameters(ConfigurationParameters parameters) {\n\t\t\tPreconditions.notNull(parameters, \"parent configuration parameters must not be null\");\n\t\t\tthis.parentConfigurationParameters = parameters;\n\t\t\treturn this;\n\t\t}\n\n\t\tLauncherConfigurationParameters build() {\n\t\t\tList<ParameterProvider> parameterProviders = new ArrayList<>();\n\t\t\tif (!explicitParameters.isEmpty()) {\n\t\t\t\tparameterProviders.add(ParameterProvider.explicit(explicitParameters));\n\t\t\t}\n\n\t\t\tCollectionUtils.forEachInReverseOrder(configResources,\n\t\t\t\tconfigResource -> parameterProviders.add(ParameterProvider.propertiesFile(configResource)));\n\n\t\t\tif (parentConfigurationParameters != null) {\n\t\t\t\tparameterProviders.add(ParameterProvider.inherited(parentConfigurationParameters));\n\t\t\t}\n\n\t\t\tif (implicitProvidersEnabled) {\n\t\t\t\tparameterProviders.add(ParameterProvider.systemProperties());\n\t\t\t\tparameterProviders.add(ParameterProvider.propertiesFile(configFileName));\n\t\t\t}\n\t\t\treturn new LauncherConfigurationParameters(parameterProviders);\n\t\t}\n\t}\n\n\tprivate interface ParameterProvider {\n\n\t\t@Nullable\n\t\tString getValue(String key);\n\n\t\tSet<String> keySet();\n\n\t\tstatic ParameterProvider explicit(Map<String, String> configParams) {\n\t\t\treturn new ParameterProvider() {\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable String getValue(String key) {\n\t\t\t\t\treturn configParams.get(key);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Set<String> keySet() {\n\t\t\t\t\treturn configParams.keySet();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String toString() {\n\t\t\t\t\tToStringBuilder builder = new ToStringBuilder(\"explicit\");\n\t\t\t\t\tconfigParams.forEach(builder::append);\n\t\t\t\t\treturn builder.toString();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tstatic ParameterProvider systemProperties() {\n\t\t\treturn new ParameterProvider() {\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable String getValue(String key) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn System.getProperty(key);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception ignore) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Set<String> keySet() {\n\t\t\t\t\treturn System.getProperties().stringPropertyNames();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String toString() {\n\t\t\t\t\treturn \"systemProperties [...]\";\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tstatic ParameterProvider propertiesFile(String configFileName) {\n\t\t\tPreconditions.notBlank(configFileName, \"configFileName must not be null or blank\");\n\t\t\tProperties properties = loadClasspathResource(configFileName.strip());\n\t\t\treturn new ParameterProvider() {\n\t\t\t\t@Override\n\t\t\t\tpublic String getValue(String key) {\n\t\t\t\t\treturn properties.getProperty(key);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Set<String> keySet() {\n\t\t\t\t\treturn properties.stringPropertyNames();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String toString() {\n\t\t\t\t\tToStringBuilder builder = new ToStringBuilder(\"propertiesFile\");\n\t\t\t\t\tproperties.stringPropertyNames().forEach(key -> builder.append(key, getValue(key)));\n\t\t\t\t\treturn builder.toString();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tstatic ParameterProvider inherited(ConfigurationParameters configParams) {\n\t\t\treturn new ParameterProvider() {\n\t\t\t\t@Override\n\t\t\t\tpublic @Nullable String getValue(String key) {\n\t\t\t\t\treturn configParams.get(key).orElse(null);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic Set<String> keySet() {\n\t\t\t\t\treturn configParams.keySet();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String toString() {\n\t\t\t\t\tToStringBuilder builder = new ToStringBuilder(\"inherited\");\n\t\t\t\t\tbuilder.append(\"parent\", configParams);\n\t\t\t\t\treturn builder.toString();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t}\n\n\tprivate static Properties loadClasspathResource(String configFileName) {\n\t\tProperties props = new Properties();\n\n\t\ttry {\n\t\t\tURL configFileUrl = findConfigFile(configFileName);\n\t\t\tif (configFileUrl != null) {\n\t\t\t\tloadClasspathResource(configFileUrl, props);\n\t\t\t}\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tlogger.warn(ex,\n\t\t\t\t() -> \"Failed to load JUnit Platform configuration parameters from classpath resource [%s].\".formatted(\n\t\t\t\t\tconfigFileName));\n\t\t}\n\n\t\treturn props;\n\t}\n\n\tprivate static @Nullable URL findConfigFile(String configFileName) throws IOException {\n\n\t\tClassLoader classLoader = ClassLoaderUtils.getDefaultClassLoader();\n\t\tList<URL> urls = Collections.list(classLoader.getResources(configFileName));\n\n\t\tif (urls.size() == 1) {\n\t\t\treturn urls.get(0);\n\t\t}\n\n\t\tif (urls.size() > 1) {\n\n\t\t\tList<URI> resources = urls.stream() //\n\t\t\t\t\t.map(LauncherConfigurationParameters::toURI) //\n\t\t\t\t\t.distinct() //\n\t\t\t\t\t.toList();\n\n\t\t\tURL configFileUrl = resources.get(0).toURL();\n\n\t\t\tif (resources.size() > 1) {\n\t\t\t\tlogger.warn(() -> {\n\t\t\t\t\tString formattedResourceList = Stream.concat( //\n\t\t\t\t\t\tStream.of(configFileUrl + \" (*)\"), //\n\t\t\t\t\t\tresources.stream().skip(1).map(URI::toString) //\n\t\t\t\t\t).collect(joining(\"\\n- \", \"\\n- \", \"\"));\n\t\t\t\t\treturn \"Discovered %d '%s' configuration files on the classpath (see below); only the first (*) will be used.%s\".formatted(\n\t\t\t\t\t\tresources.size(), configFileName, formattedResourceList);\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn configFileUrl;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tprivate static void loadClasspathResource(URL configFileUrl, Properties props) throws IOException {\n\t\tlogger.config(() -> \"Loading JUnit Platform configuration parameters from classpath resource [%s].\".formatted(\n\t\t\tconfigFileUrl));\n\t\tURLConnection urlConnection = configFileUrl.openConnection();\n\t\turlConnection.setUseCaches(false);\n\t\ttry (InputStream inputStream = urlConnection.getInputStream()) {\n\t\t\tprops.load(inputStream);\n\t\t}\n\t}\n\n\tprivate static URI toURI(URL url) {\n\t\ttry {\n\t\t\treturn url.toURI();\n\t\t}\n\t\tcatch (URISyntaxException e) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.launcher.EngineFilter;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.core.LauncherConfigurationParameters.Builder;\nimport org.junit.platform.launcher.listeners.OutputDir;\nimport org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners;\n\n/**\n * The {@code LauncherDiscoveryRequestBuilder} provides a light-weight DSL for\n * generating a {@link LauncherDiscoveryRequest}.\n *\n * <h2>Example</h2>\n *\n * <pre class=\"code\">\n * import static org.junit.platform.engine.discovery.DiscoverySelectors.*;\n * import static org.junit.platform.engine.discovery.ClassNameFilter.*;\n * import static org.junit.platform.launcher.EngineFilter.*;\n * import static org.junit.platform.launcher.TagFilter.*;\n * import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\n *\n * // ...\n *\n * LauncherDiscoveryRequest discoveryRequest = discoveryRequest()\n *    .selectors(\n *        selectPackage(\"org.example.user\"),\n *        selectClass(\"org.example.payment.PaymentTests\"),\n *        selectClass(ShippingTests.class),\n *        selectMethod(\"org.example.order.OrderTests#test1\"),\n *        selectMethod(\"org.example.order.OrderTests#test2()\"),\n *        selectMethod(\"org.example.order.OrderTests#test3(java.lang.String)\"),\n *        selectMethod(\"org.example.order.OrderTests\", \"test4\"),\n *        selectMethod(OrderTests.class, \"test5\"),\n *        selectMethod(OrderTests.class, testMethod),\n *        selectUniqueId(\"unique-id-1\"),\n *        selectUniqueId(\"unique-id-2\")\n *    )\n *    .selectors(\n *        selectClasspathRoots(Set.of(Path.of(\"/my/local/path1\")))\n *    )\n *   .filters(\n *        includeEngines(\"junit-jupiter\", \"spek\"),\n *        // excludeEngines(\"junit-vintage\"),\n *        includeTags(\"fast\"),\n *        // excludeTags(\"slow\"),\n *        includeClassNamePatterns(\".*Test[s]?\")\n *        // includeClassNamePatterns(\"org\\.example\\.tests.*\")\n *    )\n *    .configurationParameter(\"key1\", \"value1\")\n *    .configurationParameters(configParameterMap)\n *    .build();</pre>\n *\n * @since 1.0\n * @see org.junit.platform.engine.discovery.DiscoverySelectors\n * @see org.junit.platform.engine.discovery.ClassNameFilter\n * @see org.junit.platform.launcher.EngineFilter\n * @see org.junit.platform.launcher.TagFilter\n * @see LauncherExecutionRequestBuilder\n */\n@API(status = STABLE, since = \"1.0\")\npublic final class LauncherDiscoveryRequestBuilder {\n\n\t/**\n\t * Property name used to set the default discovery listener that is added to all : {@value}\n\t *\n\t * <h4>Supported Values</h4>\n\t *\n\t * <p>Supported values are {@code \"logging\"} and {@code \"abortOnFailure\"}.\n\t *\n\t * <p>If not specified, the default is {@value #DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_VALUE}.\n\t */\n\tpublic static final String DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME = \"junit.platform.discovery.listener.default\";\n\n\tprivate static final String DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_VALUE = \"abortOnFailure\";\n\n\tprivate final List<DiscoverySelector> selectors = new ArrayList<>();\n\tprivate final List<EngineFilter> engineFilters = new ArrayList<>();\n\tprivate final List<DiscoveryFilter<?>> discoveryFilters = new ArrayList<>();\n\tprivate final List<PostDiscoveryFilter> postDiscoveryFilters = new ArrayList<>();\n\tprivate final Map<String, String> configurationParameters = new HashMap<>();\n\tprivate final List<String> configurationParametersResources = new ArrayList<>();\n\tprivate final List<LauncherDiscoveryListener> discoveryListeners = new ArrayList<>();\n\tprivate boolean implicitConfigurationParametersEnabled = true;\n\n\tprivate @Nullable ConfigurationParameters parentConfigurationParameters;\n\n\tprivate @Nullable OutputDirectoryCreator outputDirectoryCreator;\n\n\t/**\n\t * Create a new {@code LauncherDiscoveryRequestBuilder}.\n\t *\n\t * @return a new builder\n\t * @see #discoveryRequest()\n\t */\n\tpublic static LauncherDiscoveryRequestBuilder request() {\n\t\treturn new LauncherDiscoveryRequestBuilder();\n\t}\n\n\t/**\n\t * Create a new {@code LauncherDiscoveryRequestBuilder}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link #request()} and is intended\n\t * to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;}\n\t *\n\t * @return a new builder\n\t * @since 6.0\n\t * @see #request()\n\t */\n\t@API(status = STABLE, since = \"6.0\")\n\tpublic static LauncherDiscoveryRequestBuilder discoveryRequest() {\n\t\treturn request();\n\t}\n\n\tprivate LauncherDiscoveryRequestBuilder() {\n\t}\n\n\t/**\n\t * Add all supplied {@code selectors} to the request.\n\t *\n\t * @param selectors the {@code DiscoverySelectors} to add; never {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic LauncherDiscoveryRequestBuilder selectors(DiscoverySelector... selectors) {\n\t\tPreconditions.notNull(selectors, \"selectors array must not be null\");\n\t\tselectors(Arrays.asList(selectors));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add all supplied {@code selectors} to the request.\n\t *\n\t * @param selectors the {@code DiscoverySelectors} to add; never {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic LauncherDiscoveryRequestBuilder selectors(List<? extends DiscoverySelector> selectors) {\n\t\tPreconditions.notNull(selectors, \"selectors list must not be null\");\n\t\tPreconditions.containsNoNullElements(selectors, \"individual selectors must not be null\");\n\t\tthis.selectors.addAll(selectors);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add all supplied {@code filters} to the request.\n\t *\n\t * <p>The {@code filters} are combined using AND semantics, i.e. all of them\n\t * have to include a resource for it to end up in the test plan.\n\t *\n\t * <p><strong>Warning</strong>: be cautious when registering multiple competing\n\t * {@link EngineFilter#includeEngines include} {@code EngineFilters} or multiple\n\t * competing {@link EngineFilter#excludeEngines exclude} {@code EngineFilters}\n\t * for the same discovery request since doing so will likely lead to\n\t * undesirable results (i.e., zero engines being active).\n\t *\n\t * @param filters the {@code Filter}s to add; never {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic LauncherDiscoveryRequestBuilder filters(Filter<?>... filters) {\n\t\tPreconditions.notNull(filters, \"filters array must not be null\");\n\t\tPreconditions.containsNoNullElements(filters, \"individual filters must not be null\");\n\t\tArrays.stream(filters).forEach(this::storeFilter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add the supplied <em>configuration parameter</em> to the request.\n\t *\n\t * @param key the configuration parameter key under which to store the\n\t * value; never {@code null} or blank\n\t * @param value the value to store\n\t * @return this builder for method chaining\n\t */\n\tpublic LauncherDiscoveryRequestBuilder configurationParameter(String key, String value) {\n\t\tPreconditions.notBlank(key, \"configuration parameter key must not be null or blank\");\n\t\tthis.configurationParameters.put(key, value);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add all supplied configuration parameters to the request.\n\t *\n\t * @param configurationParameters the map of configuration parameters to add;\n\t * never {@code null}\n\t * @return this builder for method chaining\n\t * @see #configurationParameter(String, String)\n\t */\n\tpublic LauncherDiscoveryRequestBuilder configurationParameters(Map<String, String> configurationParameters) {\n\t\tPreconditions.notNull(configurationParameters, \"configuration parameters map must not be null\");\n\t\tconfigurationParameters.forEach(this::configurationParameter);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add all of the supplied configuration parameters resource files to the request.\n\t * @param paths the classpath locations of the properties files\n\t * never {@code null}\n\t * @return this builder for method chaining\n\t */\n\tpublic LauncherDiscoveryRequestBuilder configurationParametersResources(String... paths) {\n\t\tPreconditions.notNull(paths, \"property file paths must not be null\");\n\t\tCollections.addAll(configurationParametersResources, paths);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the parent configuration parameters to use for the request.\n\t *\n\t * <p>Any explicit configuration parameters configured via\n\t * {@link #configurationParameter(String, String)} or\n\t * {@link #configurationParameters(Map)} takes precedence over the supplied\n\t * configuration parameters.\n\t *\n\t * @param parentConfigurationParameters the parent instance to use for looking\n\t * up configuration parameters that have not been explicitly configured;\n\t * never {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.8\n\t * @see #configurationParameter(String, String)\n\t * @see #configurationParameters(Map)\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic LauncherDiscoveryRequestBuilder parentConfigurationParameters(\n\t\t\tConfigurationParameters parentConfigurationParameters) {\n\t\tPreconditions.notNull(parentConfigurationParameters, \"parent configuration parameters must not be null\");\n\t\tthis.parentConfigurationParameters = parentConfigurationParameters;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configure whether implicit configuration parameters should be considered.\n\t *\n\t * <p>By default, in addition to those parameters that are passed explicitly\n\t * to this builder, configuration parameters are read from system properties\n\t * and from the {@code junit-platform.properties} classpath resource.\n\t * Passing {@code false} to this method, disables the latter two sources so\n\t * that only explicit configuration parameters are taken into account.\n\t *\n\t * @param enabled {@code true} if implicit configuration parameters should be\n\t * considered\n\t * @return this builder for method chaining\n\t * @since 1.7\n\t * @see #configurationParameter(String, String)\n\t * @see #configurationParameters(Map)\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic LauncherDiscoveryRequestBuilder enableImplicitConfigurationParameters(boolean enabled) {\n\t\tthis.implicitConfigurationParametersEnabled = enabled;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add all supplied discovery listeners to the request.\n\t *\n\t * <p>In addition to the {@linkplain LauncherDiscoveryListener listeners}\n\t * registered using this method, this builder will add a default listener\n\t * to this request that can be specified using the\n\t * {@value #DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME}\n\t * configuration parameter.\n\t *\n\t * @param listeners the {@code LauncherDiscoveryListeners} to add; never\n\t * {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.6\n\t * @see LauncherDiscoveryListener\n\t * @see LauncherDiscoveryListeners\n\t * @see LauncherDiscoveryRequestBuilder#DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic LauncherDiscoveryRequestBuilder listeners(LauncherDiscoveryListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"discovery listener array must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"individual discovery listeners must not be null\");\n\t\tCollections.addAll(this.discoveryListeners, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the\n\t * {@link org.junit.platform.engine.reporting.OutputDirectoryProvider} to use for the request.\n\t *\n\t * <p>If not specified, a default provider will be used that can be\n\t * configured via the {@value LauncherConstants#OUTPUT_DIR_PROPERTY_NAME}\n\t * configuration parameter.\n\t *\n\t * @param outputDirectoryProvider the output directory provider to use;\n\t *                                never {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.12\n\t * @see org.junit.platform.engine.reporting.OutputDirectoryProvider\n\t * @see LauncherConstants#OUTPUT_DIR_PROPERTY_NAME\n\t * @deprecated Please use\n\t * {@link #outputDirectoryCreator(OutputDirectoryCreator)} instead\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@API(status = DEPRECATED, since = \"1.14\")\n\t@Deprecated(since = \"1.14\", forRemoval = true)\n\tpublic LauncherDiscoveryRequestBuilder outputDirectoryProvider(\n\t\t\torg.junit.platform.engine.reporting.OutputDirectoryProvider outputDirectoryProvider) {\n\t\treturn outputDirectoryCreator(outputDirectoryProvider);\n\t}\n\n\t/**\n\t * Set the {@link OutputDirectoryCreator} to use for the request.\n\t *\n\t * <p>If not specified, a default implementation will be used that can be\n\t * configured via the {@value LauncherConstants#OUTPUT_DIR_PROPERTY_NAME}\n\t * configuration parameter.\n\t *\n\t * @param outputDirectoryCreator the output directory creator to use;\n\t *                                never {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.14\n\t * @see OutputDirectoryCreator\n\t * @see LauncherConstants#OUTPUT_DIR_PROPERTY_NAME\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@API(status = MAINTAINED, since = \"1.14\")\n\tpublic LauncherDiscoveryRequestBuilder outputDirectoryCreator(OutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.outputDirectoryCreator = Preconditions.notNull(outputDirectoryCreator,\n\t\t\t\"outputDirectoryCreator must not be null\");\n\t\treturn this;\n\t}\n\n\tprivate void storeFilter(Filter<?> filter) {\n\t\tif (filter instanceof EngineFilter engineFilter) {\n\t\t\tthis.engineFilters.add(engineFilter);\n\t\t}\n\t\telse if (filter instanceof PostDiscoveryFilter postDiscoveryFilter) {\n\t\t\tthis.postDiscoveryFilters.add(postDiscoveryFilter);\n\t\t}\n\t\telse if (filter instanceof DiscoveryFilter<?> discoveryFilter) {\n\t\t\tthis.discoveryFilters.add(discoveryFilter);\n\t\t}\n\t\telse {\n\t\t\tthrow new PreconditionViolationException(\n\t\t\t\t\"Filter [%s] must implement %s, %s, or %s.\".formatted(filter, EngineFilter.class.getSimpleName(),\n\t\t\t\t\tPostDiscoveryFilter.class.getSimpleName(), DiscoveryFilter.class.getSimpleName()));\n\t\t}\n\t}\n\n\t/**\n\t * Builds this discovery request and returns a new builder for creating a\n\t * {@link org.junit.platform.launcher.LauncherExecutionRequest} that is\n\t * initialized to contain the resulting discovery request.\n\t *\n\t * @return a new {@link LauncherExecutionRequestBuilder}\n\t * @since 6.0\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic LauncherExecutionRequestBuilder forExecution() {\n\t\treturn LauncherExecutionRequestBuilder.request(build());\n\t}\n\n\t/**\n\t * Build the {@link LauncherDiscoveryRequest} that has been configured via\n\t * this builder.\n\t */\n\tpublic LauncherDiscoveryRequest build() {\n\t\tLauncherConfigurationParameters launcherConfigurationParameters = buildLauncherConfigurationParameters();\n\t\tLauncherDiscoveryListener discoveryListener = getLauncherDiscoveryListener(launcherConfigurationParameters);\n\t\tOutputDirectoryCreator outputDirectoryCreator = getOutputDirectoryCreator(launcherConfigurationParameters);\n\t\treturn new DefaultDiscoveryRequest(this.selectors, this.engineFilters, this.discoveryFilters,\n\t\t\tthis.postDiscoveryFilters, launcherConfigurationParameters, discoveryListener, outputDirectoryCreator);\n\t}\n\n\tprivate OutputDirectoryCreator getOutputDirectoryCreator(LauncherConfigurationParameters configurationParameters) {\n\t\tif (this.outputDirectoryCreator != null) {\n\t\t\treturn this.outputDirectoryCreator;\n\t\t}\n\t\treturn new HierarchicalOutputDirectoryCreator(\n\t\t\t() -> OutputDir.create(configurationParameters.get(OUTPUT_DIR_PROPERTY_NAME)).toPath());\n\t}\n\n\tprivate LauncherConfigurationParameters buildLauncherConfigurationParameters() {\n\t\tBuilder builder = LauncherConfigurationParameters.builder() //\n\t\t\t\t.explicitParameters(this.configurationParameters) //\n\t\t\t\t.configurationResources(this.configurationParametersResources) //\n\t\t\t\t.enableImplicitProviders(this.implicitConfigurationParametersEnabled);\n\n\t\tif (this.parentConfigurationParameters != null) {\n\t\t\tbuilder.parentConfigurationParameters(this.parentConfigurationParameters);\n\t\t}\n\n\t\treturn builder.build();\n\t}\n\n\tprivate LauncherDiscoveryListener getLauncherDiscoveryListener(ConfigurationParameters configurationParameters) {\n\t\tLauncherDiscoveryListener defaultDiscoveryListener = getDefaultLauncherDiscoveryListener(\n\t\t\tconfigurationParameters);\n\t\tif (this.discoveryListeners.isEmpty()) {\n\t\t\treturn defaultDiscoveryListener;\n\t\t}\n\t\tif (this.discoveryListeners.contains(defaultDiscoveryListener)) {\n\t\t\treturn LauncherDiscoveryListeners.composite(this.discoveryListeners);\n\t\t}\n\t\tList<LauncherDiscoveryListener> allDiscoveryListeners = new ArrayList<>(this.discoveryListeners.size() + 1);\n\t\tallDiscoveryListeners.addAll(this.discoveryListeners);\n\t\tallDiscoveryListeners.add(defaultDiscoveryListener);\n\t\treturn LauncherDiscoveryListeners.composite(allDiscoveryListeners);\n\t}\n\n\tprivate LauncherDiscoveryListener getDefaultLauncherDiscoveryListener(\n\t\t\tConfigurationParameters configurationParameters) {\n\t\tString value = configurationParameters.get(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME) //\n\t\t\t\t.orElse(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_VALUE);\n\t\treturn LauncherDiscoveryListeners.fromConfigurationParameter(\n\t\t\tDEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Collections.unmodifiableMap;\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * Represents the result of test discovery of the configured\n * {@linkplain TestEngine test engines}.\n *\n * @since 1.7\n */\n@API(status = INTERNAL, since = \"1.7\", consumers = { \"org.junit.platform.testkit\", \"org.junit.platform.suite.engine\" })\npublic class LauncherDiscoveryResult {\n\n\tprivate final Map<TestEngine, EngineResultInfo> testEngineResults;\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\n\tLauncherDiscoveryResult(Map<TestEngine, EngineResultInfo> testEngineResults,\n\t\t\tConfigurationParameters configurationParameters, OutputDirectoryCreator outputDirectoryCreator) {\n\t\tthis.testEngineResults = unmodifiableMap(new LinkedHashMap<>(testEngineResults));\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t}\n\n\tpublic TestDescriptor getEngineTestDescriptor(TestEngine testEngine) {\n\t\treturn getEngineResult(testEngine).getRootDescriptor();\n\t}\n\n\t@API(status = INTERNAL, since = \"1.13\")\n\tpublic List<DiscoveryIssue> getDiscoveryIssues(TestEngine testEngine) {\n\t\treturn getEngineResult(testEngine).getDiscoveryIssueNotifier().getAllIssues();\n\t}\n\n\tEngineResultInfo getEngineResult(TestEngine testEngine) {\n\t\treturn requireNonNull(this.testEngineResults.get(testEngine));\n\t}\n\n\tConfigurationParameters getConfigurationParameters() {\n\t\treturn this.configurationParameters;\n\t}\n\n\tOutputDirectoryCreator getOutputDirectoryCreator() {\n\t\treturn this.outputDirectoryCreator;\n\t}\n\n\tpublic Collection<TestEngine> getTestEngines() {\n\t\treturn this.testEngineResults.keySet();\n\t}\n\n\tboolean containsCriticalIssuesOrContainsTests() {\n\t\treturn this.testEngineResults.values().stream() //\n\t\t\t\t.anyMatch(EngineResultInfo::containsCriticalIssuesOrContainsTests);\n\t}\n\n\tCollection<TestDescriptor> getEngineTestDescriptors() {\n\t\treturn this.testEngineResults.values().stream() //\n\t\t\t\t.map(EngineResultInfo::getRootDescriptor) //\n\t\t\t\t.toList();\n\t}\n\n\tpublic LauncherDiscoveryResult withRetainedEngines(Predicate<? super TestDescriptor> predicate) {\n\t\tMap<TestEngine, EngineResultInfo> prunedTestEngineResults = retainEngines(predicate);\n\t\tif (prunedTestEngineResults.size() < this.testEngineResults.size()) {\n\t\t\treturn new LauncherDiscoveryResult(prunedTestEngineResults, this.configurationParameters,\n\t\t\t\tthis.outputDirectoryCreator);\n\t\t}\n\t\treturn this;\n\t}\n\n\tprivate Map<TestEngine, EngineResultInfo> retainEngines(Predicate<? super TestDescriptor> predicate) {\n\t\tvar retainedEngines = new LinkedHashMap<>(this.testEngineResults);\n\t\tretainedEngines.entrySet().removeIf(entry -> !predicate.test(entry.getValue().getRootDescriptor()));\n\t\treturn retainedEngines;\n\t}\n\n\tstatic class EngineResultInfo {\n\n\t\tstatic EngineResultInfo completed(TestDescriptor rootDescriptor,\n\t\t\t\tDiscoveryIssueNotifier discoveryIssueNotifier) {\n\t\t\treturn new EngineResultInfo(rootDescriptor, discoveryIssueNotifier, null);\n\t\t}\n\n\t\tstatic EngineResultInfo errored(TestDescriptor rootDescriptor, DiscoveryIssueNotifier discoveryIssueNotifier,\n\t\t\t\tThrowable cause) {\n\t\t\treturn new EngineResultInfo(rootDescriptor, discoveryIssueNotifier, cause);\n\t\t}\n\n\t\tprivate final TestDescriptor rootDescriptor;\n\n\t\t@Nullable\n\t\tprivate final Throwable cause;\n\n\t\tprivate final DiscoveryIssueNotifier discoveryIssueNotifier;\n\n\t\tEngineResultInfo(TestDescriptor rootDescriptor, DiscoveryIssueNotifier discoveryIssueNotifier,\n\t\t\t\t@Nullable Throwable cause) {\n\t\t\tthis.rootDescriptor = rootDescriptor;\n\t\t\tthis.discoveryIssueNotifier = discoveryIssueNotifier;\n\t\t\tthis.cause = cause;\n\t\t}\n\n\t\tTestDescriptor getRootDescriptor() {\n\t\t\treturn this.rootDescriptor;\n\t\t}\n\n\t\tDiscoveryIssueNotifier getDiscoveryIssueNotifier() {\n\t\t\treturn discoveryIssueNotifier;\n\t\t}\n\n\t\tOptional<Throwable> getCause() {\n\t\t\treturn Optional.ofNullable(this.cause);\n\t\t}\n\n\t\tboolean containsCriticalIssuesOrContainsTests() {\n\t\t\treturn cause != null //\n\t\t\t\t\t|| discoveryIssueNotifier.hasCriticalIssues() //\n\t\t\t\t\t|| TestDescriptor.containsTests(rootDescriptor);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherExecutionRequestBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Objects.requireNonNullElseGet;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * The {@code LauncherExecutionRequestBuilder} provides a light-weight DSL for\n * generating a {@link LauncherExecutionRequest}.\n *\n * <h2>Example</h2>\n *\n * <pre class=\"code\">\n * import static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\n * import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\n * import static org.junit.platform.launcher.core.LauncherExecutionRequestBuilder.executionRequest;\n *\n * import org.junit.platform.engine.CancellationToken;\n * import org.junit.platform.launcher.LauncherDiscoveryRequest;\n * import org.junit.platform.launcher.LauncherExecutionRequest;\n * import org.junit.platform.launcher.TestExecutionListener;\n *\n * TestExecutionListener listener = ...\n * CancellationToken cancellationToken = CancellationToken.create();\n *\n * LauncherDiscoveryRequest discoveryRequest = discoveryRequest()\n *    .selectors(selectPackage(\"org.example.user\"))\n *    .build();\n *\n * LauncherExecutionRequest executionRequest = executionRequest(discoveryRequest)\n *    .listeners(listener)\n *    .cancellationToken(cancellationToken)\n *    .build();</pre>\n *\n * @since 6.0\n * @see LauncherExecutionRequest\n * @see LauncherDiscoveryRequestBuilder\n */\n@API(status = MAINTAINED, since = \"6.0\")\npublic final class LauncherExecutionRequestBuilder {\n\n\t/**\n\t * Create a new {@code LauncherExecutionRequestBuilder} from the supplied\n\t * {@link LauncherDiscoveryRequest}.\n\t *\n\t * @return a new builder\n\t * @see #executionRequest(LauncherDiscoveryRequest)\n\t */\n\tpublic static LauncherExecutionRequestBuilder request(LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(discoveryRequest, \"LauncherDiscoveryRequest must not be null\");\n\t\treturn new LauncherExecutionRequestBuilder(discoveryRequest, null);\n\t}\n\n\t/**\n\t * Create a new {@code LauncherExecutionRequestBuilder} from the supplied\n\t * {@link LauncherDiscoveryRequest}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link #request(LauncherDiscoveryRequest)}\n\t * and is intended to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.platform.launcher.core.LauncherExecutionRequestBuilder.executionRequest;}\n\t *\n\t * @return a new builder\n\t * @see #request(LauncherDiscoveryRequest)\n\t */\n\tpublic static LauncherExecutionRequestBuilder executionRequest(LauncherDiscoveryRequest discoveryRequest) {\n\t\treturn request(discoveryRequest);\n\t}\n\n\t/**\n\t * Create a new {@code LauncherExecutionRequestBuilder} from the supplied\n\t * {@link TestPlan}.\n\t *\n\t * @return a new builder\n\t * @see #executionRequest(TestPlan)\n\t */\n\tpublic static LauncherExecutionRequestBuilder request(TestPlan testPlan) {\n\t\tPreconditions.notNull(testPlan, \"TestPlan must not be null\");\n\t\treturn new LauncherExecutionRequestBuilder(null, testPlan);\n\t}\n\n\t/**\n\t * Create a new {@code LauncherExecutionRequestBuilder} from the supplied\n\t * {@link TestPlan}.\n\t *\n\t * <p>This method is an <em>alias</em> for {@link #request(TestPlan)}\n\t * and is intended to be used when statically imported &mdash; for example, via:\n\t * {@code import static org.junit.platform.launcher.core.LauncherExecutionRequestBuilder.executionRequest;}\n\t *\n\t * @return a new builder\n\t * @see #request(TestPlan)\n\t */\n\tpublic static LauncherExecutionRequestBuilder executionRequest(TestPlan testPlan) {\n\t\treturn request(testPlan);\n\t}\n\n\tprivate final @Nullable LauncherDiscoveryRequest discoveryRequest;\n\tprivate final @Nullable TestPlan testPlan;\n\tprivate final Collection<TestExecutionListener> executionListeners = new ArrayList<>();\n\tprivate @Nullable CancellationToken cancellationToken;\n\n\tprivate LauncherExecutionRequestBuilder(@Nullable LauncherDiscoveryRequest discoveryRequest,\n\t\t\t@Nullable TestPlan testPlan) {\n\n\t\tthis.discoveryRequest = discoveryRequest;\n\t\tthis.testPlan = testPlan;\n\t}\n\n\t/**\n\t * Add all supplied execution listeners to the request.\n\t *\n\t * @param listeners the {@code TestExecutionListener} to add; never\n\t * {@code null}\n\t * @return this builder for method chaining\n\t * @see TestExecutionListener\n\t */\n\tpublic LauncherExecutionRequestBuilder listeners(TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"TestExecutionListener array must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"individual listeners must not be null\");\n\t\tCollections.addAll(this.executionListeners, listeners);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Set the cancellation token for the request.\n\t *\n\t * @param cancellationToken the {@code CancellationToken} to use; never\n\t * {@code null}.\n\t * @return this builder for method chaining\n\t * @see CancellationToken\n\t */\n\tpublic LauncherExecutionRequestBuilder cancellationToken(CancellationToken cancellationToken) {\n\t\tthis.cancellationToken = Preconditions.notNull(cancellationToken, \"CancellationToken must not be null\");\n\t\treturn this;\n\t}\n\n\t/**\n\t * Build the {@link LauncherExecutionRequest} that has been configured via\n\t * this builder.\n\t */\n\tpublic LauncherExecutionRequest build() {\n\t\treturn new DefaultLauncherExecutionRequest(this.discoveryRequest, this.testPlan, this.executionListeners,\n\t\t\trequireNonNullElseGet(this.cancellationToken, CancellationToken::disabled));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.launcher.LauncherConstants.DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.ENABLE_LAUNCHER_INTERCEPTORS;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ClassNamePatternFilterUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherInterceptor;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.jfr.JfrUtils;\n\n/**\n * Factory for creating {@link Launcher} instances by invoking {@link #create()}\n * or {@link #create(LauncherConfig)}.\n *\n * <p>By default, test engines are discovered at runtime using the\n * {@link java.util.ServiceLoader ServiceLoader} mechanism. For that purpose, a\n * text file named {@code META-INF/services/org.junit.platform.engine.TestEngine}\n * has to be added to the engine's JAR file in which the fully qualified name\n * of the implementation class of the {@link org.junit.platform.engine.TestEngine}\n * interface is declared.\n *\n * <p>By default, test execution listeners are discovered at runtime via the\n * {@link java.util.ServiceLoader ServiceLoader} mechanism and are\n * automatically registered with the {@link Launcher} created by this factory.\n * Users may register additional listeners using the\n * {@link Launcher#registerTestExecutionListeners(TestExecutionListener...)}\n * method on the created launcher instance.\n *\n * <p>For full control over automatic registration and programmatic registration\n * of test engines and listeners, supply an instance of {@link LauncherConfig}\n * to {@link #create(LauncherConfig)}.\n *\n * @since 1.0\n * @see Launcher\n * @see LauncherConfig\n */\n@API(status = STABLE, since = \"1.0\")\npublic class LauncherFactory {\n\n\tprivate LauncherFactory() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Factory method for opening a new {@link LauncherSession} using the\n\t * {@linkplain LauncherConfig#DEFAULT default} {@link LauncherConfig}.\n\t *\n\t * @throws PreconditionViolationException if no test engines are detected\n\t * @since 1.8\n\t * @see #openSession(LauncherConfig)\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static LauncherSession openSession() throws PreconditionViolationException {\n\t\treturn openSession(LauncherConfig.DEFAULT);\n\t}\n\n\t/**\n\t * Factory method for opening a new {@link LauncherSession} using the\n\t * supplied {@link LauncherConfig}.\n\t *\n\t * @param config the configuration for the session and the launcher; never\n\t * {@code null}\n\t * @throws PreconditionViolationException if the supplied configuration is\n\t * {@code null}, or if no test engines are detected\n\t * @since 1.8\n\t * @see #openSession()\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static LauncherSession openSession(LauncherConfig config) throws PreconditionViolationException {\n\t\tPreconditions.notNull(config, \"LauncherConfig must not be null\");\n\t\tLauncherConfigurationParameters configurationParameters = LauncherConfigurationParameters.builder().build();\n\t\treturn new DefaultLauncherSession(collectLauncherInterceptors(configurationParameters),\n\t\t\t() -> createLauncherSessionListener(config),\n\t\t\tsessionLevelStore -> createDefaultLauncher(config, configurationParameters, sessionLevelStore));\n\t}\n\n\t/**\n\t * Factory method for creating a new {@link Launcher} using the\n\t * {@linkplain LauncherConfig#DEFAULT default} {@link LauncherConfig}.\n\t *\n\t * @throws PreconditionViolationException if no test engines are detected\n\t * @see #create(LauncherConfig)\n\t */\n\tpublic static Launcher create() throws PreconditionViolationException {\n\t\treturn create(LauncherConfig.DEFAULT);\n\t}\n\n\t/**\n\t * Factory method for creating a new {@link Launcher} using the supplied\n\t * {@link LauncherConfig}.\n\t *\n\t * @param config the configuration for the launcher; never {@code null}\n\t * @throws PreconditionViolationException if the supplied configuration is\n\t * {@code null}, or if no test engines are detected\n\t * registered\n\t * @since 1.3\n\t * @see #create()\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static Launcher create(LauncherConfig config) throws PreconditionViolationException {\n\t\tPreconditions.notNull(config, \"LauncherConfig must not be null\");\n\t\tLauncherConfigurationParameters configurationParameters = LauncherConfigurationParameters.builder().build();\n\t\treturn new SessionPerRequestLauncher(\n\t\t\tsessionLevelStore -> createDefaultLauncher(config, configurationParameters, sessionLevelStore),\n\t\t\t() -> createLauncherSessionListener(config), () -> collectLauncherInterceptors(configurationParameters));\n\t}\n\n\tprivate static DefaultLauncher createDefaultLauncher(LauncherConfig config,\n\t\t\tLauncherConfigurationParameters configurationParameters,\n\t\t\tNamespacedHierarchicalStore<Namespace> sessionLevelStore) {\n\t\tSet<TestEngine> engines = collectTestEngines(config);\n\t\tList<PostDiscoveryFilter> filters = collectPostDiscoveryFilters(config);\n\t\tDefaultLauncher launcher = new DefaultLauncher(engines, filters, sessionLevelStore);\n\t\tJfrUtils.registerListeners(launcher);\n\t\tregisterLauncherDiscoveryListeners(config, launcher);\n\t\tregisterTestExecutionListeners(config, launcher, configurationParameters);\n\n\t\treturn launcher;\n\t}\n\n\tprivate static List<LauncherInterceptor> collectLauncherInterceptors(\n\t\t\tLauncherConfigurationParameters configurationParameters) {\n\t\tList<LauncherInterceptor> interceptors = new ArrayList<>();\n\t\tif (configurationParameters.getBoolean(ENABLE_LAUNCHER_INTERCEPTORS).orElse(false)) {\n\t\t\tServiceLoaderRegistry.load(LauncherInterceptor.class).forEach(interceptors::add);\n\t\t}\n\t\tinterceptors.add(ClasspathAlignmentCheckingLauncherInterceptor.INSTANCE);\n\t\treturn interceptors;\n\t}\n\n\tprivate static Set<TestEngine> collectTestEngines(LauncherConfig config) {\n\t\tSet<TestEngine> engines = new LinkedHashSet<>();\n\t\tif (config.isTestEngineAutoRegistrationEnabled()) {\n\t\t\tnew ServiceLoaderTestEngineRegistry().loadTestEngines().forEach(engines::add);\n\t\t}\n\t\tengines.addAll(config.getAdditionalTestEngines());\n\t\treturn engines;\n\t}\n\n\tprivate static LauncherSessionListener createLauncherSessionListener(LauncherConfig config) {\n\t\tListenerRegistry<LauncherSessionListener> listenerRegistry = ListenerRegistry.forLauncherSessionListeners();\n\t\tif (config.isLauncherSessionListenerAutoRegistrationEnabled()) {\n\t\t\tServiceLoaderRegistry.load(LauncherSessionListener.class).forEach(listenerRegistry::add);\n\t\t}\n\t\tconfig.getAdditionalLauncherSessionListeners().forEach(listenerRegistry::add);\n\t\treturn listenerRegistry.getCompositeListener();\n\t}\n\n\tprivate static List<PostDiscoveryFilter> collectPostDiscoveryFilters(LauncherConfig config) {\n\t\tList<PostDiscoveryFilter> filters = new ArrayList<>();\n\t\tif (config.isPostDiscoveryFilterAutoRegistrationEnabled()) {\n\t\t\tServiceLoaderRegistry.load(PostDiscoveryFilter.class).forEach(filters::add);\n\t\t}\n\t\tfilters.addAll(config.getAdditionalPostDiscoveryFilters());\n\t\treturn filters;\n\t}\n\n\tprivate static void registerLauncherDiscoveryListeners(LauncherConfig config, Launcher launcher) {\n\t\tif (config.isLauncherDiscoveryListenerAutoRegistrationEnabled()) {\n\t\t\tServiceLoaderRegistry.load(LauncherDiscoveryListener.class).forEach(\n\t\t\t\tlauncher::registerLauncherDiscoveryListeners);\n\t\t}\n\t\tconfig.getAdditionalLauncherDiscoveryListeners().forEach(launcher::registerLauncherDiscoveryListeners);\n\t}\n\n\tprivate static void registerTestExecutionListeners(LauncherConfig config, Launcher launcher,\n\t\t\tLauncherConfigurationParameters configurationParameters) {\n\t\tif (config.isTestExecutionListenerAutoRegistrationEnabled()) {\n\t\t\tloadAndFilterTestExecutionListeners(configurationParameters).forEach(\n\t\t\t\tlauncher::registerTestExecutionListeners);\n\t\t}\n\t\tconfig.getAdditionalTestExecutionListeners().forEach(launcher::registerTestExecutionListeners);\n\t}\n\n\tprivate static Iterable<TestExecutionListener> loadAndFilterTestExecutionListeners(\n\t\t\tConfigurationParameters configurationParameters) {\n\t\tPredicate<String> classNameFilter = configurationParameters.get(DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME) //\n\t\t\t\t.map(ClassNamePatternFilterUtils::excludeMatchingClassNames) //\n\t\t\t\t.orElse(__ -> true);\n\t\treturn ServiceLoaderRegistry.load(TestExecutionListener.class, classNameFilter);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherListenerRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.TestExecutionListener;\n\nclass LauncherListenerRegistry {\n\tfinal ListenerRegistry<LauncherDiscoveryListener> launcherDiscoveryListeners = ListenerRegistry.forLauncherDiscoveryListeners();\n\tfinal ListenerRegistry<TestExecutionListener> testExecutionListeners = ListenerRegistry.forTestExecutionListeners();\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherPhase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.launcher.LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME;\n\nimport java.util.Locale;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * The phase the {@link org.junit.platform.launcher.Launcher} is in.\n *\n * @since 1.13\n */\nenum LauncherPhase {\n\n\tDISCOVERY, EXECUTION;\n\n\tstatic Optional<LauncherPhase> getDiscoveryIssueFailurePhase(ConfigurationParameters configurationParameters) {\n\t\tFunction<String, @Nullable LauncherPhase> stringLauncherPhaseFunction = value -> {\n\t\t\ttry {\n\t\t\t\treturn LauncherPhase.valueOf(value.toUpperCase(Locale.ROOT));\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new JUnitException(\n\t\t\t\t\t\"Invalid LauncherPhase '%s' set via the '%s' configuration parameter.\".formatted(value,\n\t\t\t\t\t\tDISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME));\n\t\t\t}\n\t\t};\n\t\treturn configurationParameters.get(DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, stringLauncherPhaseFunction);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn name().toLowerCase(Locale.ENGLISH);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ListenerRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners;\nimport org.junit.platform.launcher.listeners.session.LauncherSessionListeners;\n\nclass ListenerRegistry<T> {\n\n\tprivate final Function<List<T>, T> compositeListenerFactory;\n\n\tstatic ListenerRegistry<LauncherSessionListener> forLauncherSessionListeners() {\n\t\treturn create(LauncherSessionListeners::composite);\n\t}\n\n\tstatic ListenerRegistry<LauncherDiscoveryListener> forLauncherDiscoveryListeners() {\n\t\treturn create(LauncherDiscoveryListeners::composite);\n\t}\n\n\tstatic ListenerRegistry<TestExecutionListener> forTestExecutionListeners() {\n\t\treturn create(CompositeTestExecutionListener::new);\n\t}\n\n\tstatic ListenerRegistry<EngineExecutionListener> forEngineExecutionListeners() {\n\t\treturn create(CompositeEngineExecutionListener::new);\n\t}\n\n\tstatic <T> ListenerRegistry<T> create(Function<List<T>, T> compositeListenerFactory) {\n\t\treturn new ListenerRegistry<>(compositeListenerFactory);\n\t}\n\n\tstatic <T> ListenerRegistry<T> copyOf(ListenerRegistry<T> source) {\n\t\tListenerRegistry<T> registry = new ListenerRegistry<>(source.compositeListenerFactory);\n\t\tif (!source.listeners.isEmpty()) {\n\t\t\tregistry.addAll(source.listeners);\n\t\t}\n\t\treturn registry;\n\t}\n\n\tprivate final ArrayList<T> listeners = new ArrayList<>();\n\n\tprivate ListenerRegistry(Function<List<T>, T> compositeListenerFactory) {\n\t\tthis.compositeListenerFactory = compositeListenerFactory;\n\t}\n\n\tListenerRegistry<T> add(T listener) {\n\t\tPreconditions.notNull(listener, \"listener must not be null\");\n\t\tthis.listeners.add(listener);\n\t\treturn this;\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tfinal ListenerRegistry<T> addAll(T... listeners) {\n\t\tPreconditions.notEmpty(listeners, \"listeners array must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(listeners, \"individual listeners must not be null\");\n\t\tCollections.addAll(this.listeners, listeners);\n\t\treturn this;\n\t}\n\n\tListenerRegistry<T> addAll(Collection<? extends T> listeners) {\n\t\tPreconditions.notEmpty(listeners, \"listeners collection must not be null or empty\");\n\t\tPreconditions.containsNoNullElements(listeners, \"individual listeners must not be null\");\n\t\tthis.listeners.addAll(listeners);\n\t\treturn this;\n\t}\n\n\tT getCompositeListener() {\n\t\tthis.listeners.trimToSize();\n\t\treturn compositeListenerFactory.apply(this.listeners);\n\t}\n\n\tList<T> getListeners() {\n\t\treturn Collections.unmodifiableList(this.listeners);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/MemoryCleanupListener.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 6.1\n */\nclass MemoryCleanupListener extends DelegatingEngineExecutionListener {\n\n\tprivate final TestPlan testPlan;\n\n\tMemoryCleanupListener(EngineExecutionListener delegate, TestPlan testPlan) {\n\t\tsuper(delegate);\n\t\tthis.testPlan = testPlan;\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tsuper.executionSkipped(testDescriptor, reason);\n\t\tcleanUp(testDescriptor);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tsuper.executionFinished(testDescriptor, testExecutionResult);\n\t\tcleanUp(testDescriptor);\n\t}\n\n\tprivate void cleanUp(TestDescriptor testDescriptor) {\n\t\ttestPlan.removeInternal(testDescriptor.getUniqueId());\n\t\tif (!testDescriptor.isRoot()) {\n\t\t\ttestDescriptor.removeFromHierarchy();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/OutcomeDelayingEngineExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * Delays reporting of engine skipped/finished events so that exceptions thrown\n * by engines can be reported to listeners.\n *\n * @since 1.6\n */\nclass OutcomeDelayingEngineExecutionListener extends DelegatingEngineExecutionListener {\n\n\tprivate final TestDescriptor engineDescriptor;\n\n\tprivate volatile boolean engineStarted;\n\n\tprivate volatile @Nullable Outcome outcome;\n\n\tprivate volatile @Nullable String skipReason;\n\n\tprivate volatile @Nullable TestExecutionResult executionResult;\n\n\tOutcomeDelayingEngineExecutionListener(EngineExecutionListener delegate, TestDescriptor engineDescriptor) {\n\t\tsuper(delegate);\n\t\tthis.engineDescriptor = engineDescriptor;\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tif (testDescriptor == engineDescriptor) {\n\t\t\toutcome = Outcome.SKIPPED;\n\t\t\tskipReason = reason;\n\t\t}\n\t\telse {\n\t\t\tsuper.executionSkipped(testDescriptor, reason);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\tif (testDescriptor == engineDescriptor) {\n\t\t\tengineStarted = true;\n\t\t}\n\t\tsuper.executionStarted(testDescriptor);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult executionResult) {\n\t\tif (testDescriptor == engineDescriptor) {\n\t\t\toutcome = Outcome.FINISHED;\n\t\t\tthis.executionResult = executionResult;\n\t\t}\n\t\telse {\n\t\t\tsuper.executionFinished(testDescriptor, executionResult);\n\t\t}\n\t}\n\n\tvoid reportEngineOutcome() {\n\t\tif (outcome == Outcome.FINISHED) {\n\t\t\tsuper.executionFinished(engineDescriptor, requireNonNull(executionResult));\n\t\t}\n\t\telse if (outcome == Outcome.SKIPPED) {\n\t\t\tsuper.executionSkipped(engineDescriptor, requireNonNull(skipReason));\n\t\t}\n\t}\n\n\tvoid reportEngineStartIfNecessary() {\n\t\tif (!engineStarted) {\n\t\t\tsuper.executionStarted(engineDescriptor);\n\t\t}\n\t}\n\n\tvoid reportEngineFailure(Throwable throwable) {\n\t\tOptional.ofNullable(this.executionResult) //\n\t\t\t\t.flatMap(TestExecutionResult::getThrowable) //\n\t\t\t\t.ifPresent(throwable::addSuppressed);\n\t\tsuper.executionFinished(engineDescriptor, TestExecutionResult.failed(throwable));\n\t}\n\n\tprivate enum Outcome {\n\t\tSKIPPED, FINISHED\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.ServiceLoaderUtils;\n\n/**\n * @since 1.8\n */\nclass ServiceLoaderRegistry {\n\n\tstatic <T> Iterable<T> load(Class<T> type) {\n\t\treturn load(type, __ -> true, instances -> logLoadedInstances(type, instances, null));\n\t}\n\n\tstatic <T> Iterable<T> load(@SuppressWarnings(\"SameParameterValue\") Class<T> type,\n\t\t\tPredicate<String> classNameFilter) {\n\t\tList<String> exclusions = new ArrayList<>();\n\t\tPredicate<String> collectingClassNameFilter = className -> {\n\t\t\tboolean included = classNameFilter.test(className);\n\t\t\tif (!included) {\n\t\t\t\texclusions.add(className);\n\t\t\t}\n\t\t\treturn included;\n\t\t};\n\t\treturn load(type, collectingClassNameFilter, instances -> logLoadedInstances(type, instances, exclusions));\n\t}\n\n\tprivate static <T> String logLoadedInstances(Class<T> type, List<T> instances, @Nullable List<String> exclusions) {\n\t\tString typeName = type.getSimpleName();\n\t\tif (exclusions == null) {\n\t\t\treturn \"Loaded %s instances: %s\".formatted(typeName, instances);\n\t\t}\n\t\treturn \"Loaded %s instances: %s (excluded classes: %s)\".formatted(typeName, instances, exclusions);\n\t}\n\n\tprivate static <T> List<T> load(Class<T> type, Predicate<String> classNameFilter,\n\t\t\tFunction<List<T>, String> logMessageSupplier) {\n\t\tServiceLoader<T> serviceLoader = ServiceLoader.load(type, ClassLoaderUtils.getDefaultClassLoader());\n\t\tPredicate<Class<? extends T>> providerPredicate = clazz -> classNameFilter.test(clazz.getName());\n\t\tList<T> instances = ServiceLoaderUtils.filter(serviceLoader, providerPredicate).toList();\n\t\tgetLogger().config(() -> logMessageSupplier.apply(instances));\n\t\treturn instances;\n\t}\n\n\tprivate static Logger getLogger() {\n\t\t// Not a constant to avoid problems with building GraalVM native images\n\t\treturn LoggerFactory.getLogger(ServiceLoaderRegistry.class);\n\t}\n\n\tprivate ServiceLoaderRegistry() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/ServiceLoaderTestEngineRegistry.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ServiceLoader;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.engine.TestEngine;\n\n/**\n * @since 1.0\n */\n@API(status = INTERNAL, since = \"1.0\", consumers = \"org.junit.platform.suite.engine\")\npublic final class ServiceLoaderTestEngineRegistry {\n\n\tpublic ServiceLoaderTestEngineRegistry() {\n\t}\n\n\tpublic Iterable<TestEngine> loadTestEngines() {\n\t\tIterable<TestEngine> testEngines = ServiceLoader.load(TestEngine.class,\n\t\t\tClassLoaderUtils.getDefaultClassLoader());\n\t\tgetLogger().config(() -> TestEngineFormatter.format(\"Discovered TestEngines\", testEngines));\n\t\treturn testEngines;\n\t}\n\n\tprivate static Logger getLogger() {\n\t\t// Not a constant to avoid problems with building GraalVM native images\n\t\treturn LoggerFactory.getLogger(ServiceLoaderTestEngineRegistry.class);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/SessionPerRequestLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.List;\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\nimport org.junit.platform.launcher.LauncherInterceptor;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.8\n */\nclass SessionPerRequestLauncher implements Launcher {\n\n\tprivate final LauncherListenerRegistry listenerRegistry = new LauncherListenerRegistry();\n\tprivate final Function<NamespacedHierarchicalStore<Namespace>, Launcher> launcherFactory;\n\tprivate final Supplier<LauncherSessionListener> sessionListenerSupplier;\n\tprivate final Supplier<List<LauncherInterceptor>> interceptorFactory;\n\n\tSessionPerRequestLauncher(Function<NamespacedHierarchicalStore<Namespace>, Launcher> launcherFactory,\n\t\t\tSupplier<LauncherSessionListener> sessionListenerSupplier,\n\t\t\tSupplier<List<LauncherInterceptor>> interceptorFactory) {\n\t\tthis.launcherFactory = launcherFactory;\n\t\tthis.sessionListenerSupplier = sessionListenerSupplier;\n\t\tthis.interceptorFactory = interceptorFactory;\n\t}\n\n\t@Override\n\tpublic void registerLauncherDiscoveryListeners(LauncherDiscoveryListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tlistenerRegistry.launcherDiscoveryListeners.addAll(listeners);\n\t}\n\n\t@Override\n\tpublic void registerTestExecutionListeners(TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\tlistenerRegistry.testExecutionListeners.addAll(listeners);\n\t}\n\n\t@Override\n\tpublic TestPlan discover(LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\ttry (LauncherSession session = createSession()) {\n\t\t\treturn session.getLauncher().discover(discoveryRequest);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void execute(LauncherDiscoveryRequest discoveryRequest, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(discoveryRequest, \"discoveryRequest must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\ttry (LauncherSession session = createSession()) {\n\t\t\tsession.getLauncher().execute(discoveryRequest, listeners);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void execute(TestPlan testPlan, TestExecutionListener... listeners) {\n\t\tPreconditions.notNull(testPlan, \"testPlan must not be null\");\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listener array must not contain null elements\");\n\t\ttry (LauncherSession session = createSession()) {\n\t\t\tsession.getLauncher().execute(testPlan, listeners);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void execute(LauncherExecutionRequest executionRequest) {\n\t\tPreconditions.notNull(executionRequest, \"executionRequest must not be null\");\n\t\ttry (LauncherSession session = createSession()) {\n\t\t\tsession.getLauncher().execute(executionRequest);\n\t\t}\n\t}\n\n\tprivate LauncherSession createSession() {\n\t\tLauncherSession session = new DefaultLauncherSession(interceptorFactory.get(), sessionListenerSupplier,\n\t\t\tthis.launcherFactory);\n\t\tLauncher launcher = session.getLauncher();\n\t\tlistenerRegistry.launcherDiscoveryListeners.getListeners().forEach(\n\t\t\tlauncher::registerLauncherDiscoveryListeners);\n\t\tlistenerRegistry.testExecutionListeners.getListeners().forEach(launcher::registerTestExecutionListeners);\n\t\treturn session;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StackTracePruningEngineExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * Prunes the stack trace in case of a failed event.\n *\n * @since 1.10\n * @see org.junit.platform.commons.util.ExceptionUtils#pruneStackTrace(Throwable, List)\n */\nclass StackTracePruningEngineExecutionListener extends DelegatingEngineExecutionListener {\n\n\tStackTracePruningEngineExecutionListener(EngineExecutionListener delegate) {\n\t\tsuper(delegate);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tList<String> testClassNames = getTestClassNames(testDescriptor);\n\t\tif (testExecutionResult.getThrowable().isPresent()) {\n\t\t\tThrowable throwable = testExecutionResult.getThrowable().get();\n\n\t\t\tExceptionUtils.findNestedThrowables(throwable).forEach(\n\t\t\t\tt -> ExceptionUtils.pruneStackTrace(t, testClassNames));\n\t\t}\n\t\tsuper.executionFinished(testDescriptor, testExecutionResult);\n\t}\n\n\tprivate static List<String> getTestClassNames(TestDescriptor testDescriptor) {\n\t\tStream<? extends TestDescriptor> self = Stream.of(testDescriptor);\n\t\tStream<? extends TestDescriptor> ancestors = testDescriptor.getAncestors().stream();\n\t\treturn Stream.concat(self, ancestors) //\n\t\t\t\t.map(TestDescriptor::getSource) //\n\t\t\t\t.flatMap(Optional::stream) //\n\t\t\t\t.map(source -> {\n\t\t\t\t\tif (source instanceof ClassSource classSource) {\n\t\t\t\t\t\treturn classSource.getClassName();\n\t\t\t\t\t}\n\t\t\t\t\telse if (source instanceof MethodSource methodSource) {\n\t\t\t\t\t\treturn methodSource.getClassName();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.toList();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_MAX_BUFFER_DEFAULT;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_MAX_BUFFER_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.BiConsumer;\n\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.core.CompositeTestExecutionListener.EagerTestExecutionListener;\n\n/**\n * @since 1.3\n */\nclass StreamInterceptingTestExecutionListener implements EagerTestExecutionListener {\n\n\tprivate final Optional<StreamInterceptor> stdoutInterceptor;\n\tprivate final Optional<StreamInterceptor> stderrInterceptor;\n\tprivate final BiConsumer<TestIdentifier, ReportEntry> reporter;\n\n\tstatic Optional<StreamInterceptingTestExecutionListener> create(ConfigurationParameters configurationParameters,\n\t\t\tBiConsumer<TestIdentifier, ReportEntry> reporter) {\n\n\t\tboolean captureStdout = configurationParameters.getBoolean(CAPTURE_STDOUT_PROPERTY_NAME).orElse(false);\n\t\tboolean captureStderr = configurationParameters.getBoolean(CAPTURE_STDERR_PROPERTY_NAME).orElse(false);\n\t\tif (!captureStdout && !captureStderr) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\tint maxSize = configurationParameters.get(CAPTURE_MAX_BUFFER_PROPERTY_NAME, Integer::valueOf) //\n\t\t\t\t.orElse(CAPTURE_MAX_BUFFER_DEFAULT);\n\n\t\tOptional<StreamInterceptor> stdoutInterceptor = captureStdout ? StreamInterceptor.registerStdout(maxSize)\n\t\t\t\t: Optional.empty();\n\t\tOptional<StreamInterceptor> stderrInterceptor = captureStderr ? StreamInterceptor.registerStderr(maxSize)\n\t\t\t\t: Optional.empty();\n\n\t\tif ((stdoutInterceptor.isEmpty() && captureStdout) || (stderrInterceptor.isEmpty() && captureStderr)) {\n\t\t\tstdoutInterceptor.ifPresent(StreamInterceptor::unregister);\n\t\t\tstderrInterceptor.ifPresent(StreamInterceptor::unregister);\n\t\t\treturn Optional.empty();\n\t\t}\n\t\treturn Optional.of(new StreamInterceptingTestExecutionListener(stdoutInterceptor, stderrInterceptor, reporter));\n\t}\n\n\tprivate StreamInterceptingTestExecutionListener(Optional<StreamInterceptor> stdoutInterceptor,\n\t\t\tOptional<StreamInterceptor> stderrInterceptor, BiConsumer<TestIdentifier, ReportEntry> reporter) {\n\t\tthis.stdoutInterceptor = stdoutInterceptor;\n\t\tthis.stderrInterceptor = stderrInterceptor;\n\t\tthis.reporter = reporter;\n\t}\n\n\tvoid unregister() {\n\t\tstdoutInterceptor.ifPresent(StreamInterceptor::unregister);\n\t\tstderrInterceptor.ifPresent(StreamInterceptor::unregister);\n\t}\n\n\t@Override\n\tpublic void executionJustStarted(TestIdentifier testIdentifier) {\n\t\tstdoutInterceptor.ifPresent(StreamInterceptor::capture);\n\t\tstderrInterceptor.ifPresent(StreamInterceptor::capture);\n\t}\n\n\t@Override\n\tpublic void executionJustFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tMap<String, String> map = new HashMap<>();\n\t\tString out = stdoutInterceptor.map(StreamInterceptor::consume).orElse(\"\");\n\t\tif (StringUtils.isNotBlank(out)) {\n\t\t\tmap.put(STDOUT_REPORT_ENTRY_KEY, out);\n\t\t}\n\t\tString err = stderrInterceptor.map(StreamInterceptor::consume).orElse(\"\");\n\t\tif (StringUtils.isNotBlank(err)) {\n\t\t\tmap.put(STDERR_REPORT_ENTRY_KEY, err);\n\t\t}\n\t\tif (!map.isEmpty()) {\n\t\t\treporter.accept(testIdentifier, ReportEntry.from(map));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/StreamInterceptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.nio.charset.Charset;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentLinkedDeque;\nimport java.util.function.Consumer;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.3\n */\nclass StreamInterceptor extends PrintStream {\n\n\tprivate final Deque<RewindableByteArrayOutputStream> mostRecentOutputs = new ConcurrentLinkedDeque<>();\n\n\tprivate final PrintStream originalStream;\n\tprivate final Consumer<PrintStream> unregisterAction;\n\tprivate final int maxNumberOfBytesPerThread;\n\n\tprivate final ThreadLocal<RewindableByteArrayOutputStream> output = ThreadLocal.withInitial(\n\t\tRewindableByteArrayOutputStream::new);\n\n\tstatic Optional<StreamInterceptor> registerStdout(int maxNumberOfBytesPerThread) {\n\t\treturn register(System.out, System::setOut, maxNumberOfBytesPerThread);\n\t}\n\n\tstatic Optional<StreamInterceptor> registerStderr(int maxNumberOfBytesPerThread) {\n\t\treturn register(System.err, System::setErr, maxNumberOfBytesPerThread);\n\t}\n\n\tstatic Optional<StreamInterceptor> register(PrintStream originalStream, Consumer<PrintStream> streamSetter,\n\t\t\tint maxNumberOfBytesPerThread) {\n\t\tif (originalStream instanceof StreamInterceptor) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\tStreamInterceptor interceptor = new StreamInterceptor(originalStream, streamSetter, maxNumberOfBytesPerThread);\n\t\tstreamSetter.accept(interceptor);\n\t\treturn Optional.of(interceptor);\n\t}\n\n\tprivate StreamInterceptor(PrintStream originalStream, Consumer<PrintStream> unregisterAction,\n\t\t\tint maxNumberOfBytesPerThread) {\n\t\tsuper(originalStream);\n\t\tthis.originalStream = originalStream;\n\t\tthis.unregisterAction = unregisterAction;\n\t\tthis.maxNumberOfBytesPerThread = maxNumberOfBytesPerThread;\n\t}\n\n\tvoid capture() {\n\t\tRewindableByteArrayOutputStream out = output.get();\n\t\tout.mark();\n\t\tpushToTop(out);\n\t}\n\n\tString consume() {\n\t\tRewindableByteArrayOutputStream out = output.get();\n\t\tString result = out.rewind();\n\t\tif (!out.isMarked()) {\n\t\t\tmostRecentOutputs.remove(out);\n\t\t}\n\t\treturn result;\n\t}\n\n\tvoid unregister() {\n\t\tunregisterAction.accept(originalStream);\n\t}\n\n\t@Override\n\tpublic void write(int b) {\n\t\tRewindableByteArrayOutputStream out = getOutput();\n\t\tif (out != null && out.size() < maxNumberOfBytesPerThread) {\n\t\t\tpushToTop(out);\n\t\t\tout.write(b);\n\t\t}\n\t\tsuper.write(b);\n\t}\n\n\t@Override\n\tpublic void write(byte[] b) {\n\t\twrite(b, 0, b.length);\n\t}\n\n\t@Override\n\tpublic void write(byte[] buf, int off, int len) {\n\t\tRewindableByteArrayOutputStream out = getOutput();\n\t\tif (out != null) {\n\t\t\tint actualLength = Math.max(0, Math.min(len, maxNumberOfBytesPerThread - out.size()));\n\t\t\tif (actualLength > 0) {\n\t\t\t\tpushToTop(out);\n\t\t\t\tout.write(buf, off, actualLength);\n\t\t\t}\n\t\t}\n\t\tsuper.write(buf, off, len);\n\t}\n\n\tprivate void pushToTop(RewindableByteArrayOutputStream out) {\n\t\tif (!out.equals(mostRecentOutputs.peek())) {\n\t\t\tmostRecentOutputs.remove(out);\n\t\t\tmostRecentOutputs.push(out);\n\t\t}\n\t}\n\n\tprivate @Nullable RewindableByteArrayOutputStream getOutput() {\n\t\tRewindableByteArrayOutputStream out = output.get();\n\t\treturn out.isMarked() ? out : mostRecentOutputs.peek();\n\t}\n\n\tstatic class RewindableByteArrayOutputStream extends ByteArrayOutputStream {\n\n\t\tprivate final Deque<Integer> markedPositions = new ArrayDeque<>();\n\n\t\tboolean isMarked() {\n\t\t\treturn !markedPositions.isEmpty();\n\t\t}\n\n\t\tvoid mark() {\n\t\t\tmarkedPositions.addFirst(count);\n\t\t}\n\n\t\tString rewind() {\n\t\t\tInteger position = markedPositions.pollFirst();\n\t\t\tif (position == null || position == count) {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t\tint length = count - position;\n\t\t\tcount -= length;\n\t\t\treturn new String(buf, position, length, Charset.defaultCharset());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/TestEngineFormatter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.lang.String.join;\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.util.ClassLoaderUtils;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.engine.TestEngine;\n\nclass TestEngineFormatter {\n\n\tprivate TestEngineFormatter() {\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tstatic String format(String title, Iterable<TestEngine> testEngines) {\n\t\treturn format(title, (Stream<TestEngine>) CollectionUtils.toStream(testEngines));\n\t}\n\n\tprivate static String format(String title, Stream<TestEngine> testEngines) {\n\t\tString details = testEngines //\n\t\t\t\t.map(engine -> \"- %s (%s)\".formatted(engine.getId(), join(\", \", computeAttributes(engine)))) //\n\t\t\t\t.collect(joining(\"\\n\"));\n\t\treturn title + \":\" + (details.isEmpty() ? \" <none>\" : \"\\n\" + details);\n\t}\n\n\tprivate static List<String> computeAttributes(TestEngine engine) {\n\t\tList<String> attributes = new ArrayList<>(4);\n\t\tengine.getGroupId().ifPresent(groupId -> attributes.add(\"group ID: \" + groupId));\n\t\tengine.getArtifactId().ifPresent(artifactId -> attributes.add(\"artifact ID: \" + artifactId));\n\t\tengine.getVersion().ifPresent(version -> attributes.add(\"version: \" + version));\n\t\tClassLoaderUtils.getLocation(engine).ifPresent(location -> attributes.add(\"location: \" + location));\n\t\treturn attributes;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Core support classes for the {@link org.junit.platform.launcher.Launcher Launcher}\n * including the {@link org.junit.platform.launcher.core.LauncherFactory LauncherFactory}\n * and the {@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder\n * LauncherDiscoveryRequestBuilder}.\n */\n\n@NullMarked\npackage org.junit.platform.launcher.core;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/jfr/FlightRecordingDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport jdk.jfr.Category;\nimport jdk.jfr.Event;\nimport jdk.jfr.Label;\nimport jdk.jfr.Name;\nimport jdk.jfr.StackTrace;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * A {@link LauncherDiscoveryListener} that generates Java Flight Recorder\n * events.\n *\n * @since 1.8\n * @see <a href=\"https://openjdk.java.net/jeps/328\">JEP 328: Flight Recorder</a>\n */\n@API(status = INTERNAL, since = \"6.0\")\nclass FlightRecordingDiscoveryListener implements LauncherDiscoveryListener {\n\n\tprivate final Map<org.junit.platform.engine.UniqueId, EngineDiscoveryEvent> engineDiscoveryEvents = new HashMap<>();\n\tprivate @Nullable LauncherDiscoveryEvent launcherDiscoveryEvent;\n\n\t@Override\n\tpublic void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t\tvar event = new LauncherDiscoveryEvent();\n\t\tif (event.isEnabled()) {\n\t\t\tevent.begin();\n\t\t\tthis.launcherDiscoveryEvent = event;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {\n\t\tLauncherDiscoveryEvent event = this.launcherDiscoveryEvent;\n\t\tthis.launcherDiscoveryEvent = null;\n\t\tif (event != null && event.shouldCommit()) {\n\t\t\tevent.selectors = request.getSelectorsByType(DiscoverySelector.class).size();\n\t\t\tevent.filters = request.getFiltersByType(DiscoveryFilter.class).size();\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryStarted(org.junit.platform.engine.UniqueId engineId) {\n\t\tvar event = new EngineDiscoveryEvent();\n\t\tif (event.isEnabled()) {\n\t\t\tevent.begin();\n\t\t\tthis.engineDiscoveryEvents.put(engineId, event);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryFinished(org.junit.platform.engine.UniqueId engineId, EngineDiscoveryResult result) {\n\t\tEngineDiscoveryEvent event = this.engineDiscoveryEvents.remove(engineId);\n\t\tif (event != null && event.shouldCommit()) {\n\t\t\tevent.uniqueId = engineId.toString();\n\t\t\tevent.result = result.getStatus().toString();\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void issueEncountered(org.junit.platform.engine.UniqueId engineId, DiscoveryIssue issue) {\n\t\tvar event = new DiscoveryIssueEvent();\n\t\tif (event.shouldCommit()) {\n\t\t\tevent.engineId = engineId.toString();\n\t\t\tevent.severity = issue.severity().name();\n\t\t\tevent.message = issue.message();\n\t\t\tevent.source = issue.source().map(Object::toString).orElse(null);\n\t\t\tevent.cause = issue.cause().map(ExceptionUtils::readStackTrace).orElse(null);\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Category({ \"JUnit\", \"Discovery\" })\n\t@StackTrace(false)\n\tabstract static class DiscoveryEvent extends Event {\n\t}\n\n\t@Label(\"Test Discovery\")\n\t@Name(\"org.junit.LauncherDiscovery\")\n\tstatic class LauncherDiscoveryEvent extends DiscoveryEvent {\n\n\t\t@Label(\"Number of selectors\")\n\t\tint selectors;\n\n\t\t@Label(\"Number of filters\")\n\t\tint filters;\n\t}\n\n\t@Label(\"Engine Discovery\")\n\t@Name(\"org.junit.EngineDiscovery\")\n\tstatic class EngineDiscoveryEvent extends DiscoveryEvent {\n\n\t\t@UniqueId\n\t\t@Label(\"Unique Id\")\n\t\t@Nullable\n\t\tString uniqueId;\n\n\t\t@Label(\"Result\")\n\t\t@Nullable\n\t\tString result;\n\t}\n\n\t@Label(\"Discovery Issue\")\n\t@Name(\"org.junit.DiscoveryIssue\")\n\tstatic class DiscoveryIssueEvent extends DiscoveryEvent {\n\n\t\t@Label(\"Engine Id\")\n\t\t@Nullable\n\t\tString engineId;\n\n\t\t@Label(\"Severity\")\n\t\t@Nullable\n\t\tString severity;\n\n\t\t@Label(\"Message\")\n\t\t@Nullable\n\t\tString message;\n\n\t\t@Label(\"Source\")\n\t\t@Nullable\n\t\tString source;\n\n\t\t@Label(\"Cause\")\n\t\t@Nullable\n\t\tString cause;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/jfr/FlightRecordingExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.stream.Collectors;\n\nimport jdk.jfr.Category;\nimport jdk.jfr.Event;\nimport jdk.jfr.Label;\nimport jdk.jfr.Name;\nimport jdk.jfr.StackTrace;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * A {@link TestExecutionListener} that generates Java Flight Recorder\n * events.\n *\n * @since 1.8\n * @see <a href=\"https://openjdk.java.net/jeps/328\">JEP 328: Flight Recorder</a>\n */\n@API(status = INTERNAL, since = \"6.0\")\nclass FlightRecordingExecutionListener implements TestExecutionListener {\n\n\tprivate final Map<org.junit.platform.engine.UniqueId, TestExecutionEvent> testExecutionEvents = new ConcurrentHashMap<>();\n\tprivate @Nullable TestPlanExecutionEvent testPlanExecutionEvent;\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan plan) {\n\t\tvar event = new TestPlanExecutionEvent();\n\t\tif (event.isEnabled()) {\n\t\t\tevent.begin();\n\t\t\tthis.testPlanExecutionEvent = event;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan plan) {\n\t\tvar event = this.testPlanExecutionEvent;\n\t\tthis.testPlanExecutionEvent = null;\n\t\tif (event != null && event.shouldCommit()) {\n\t\t\tevent.containsTests = plan.containsTests();\n\t\t\tevent.engineNames = plan.getRoots().stream().map(TestIdentifier::getDisplayName).collect(\n\t\t\t\tCollectors.joining(\", \"));\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier test, String reason) {\n\t\tvar event = new SkippedTestEvent();\n\t\tif (event.shouldCommit()) {\n\t\t\tevent.initialize(test);\n\t\t\tevent.reason = reason;\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier test) {\n\t\tvar event = new TestExecutionEvent();\n\t\tif (event.isEnabled()) {\n\t\t\tevent.begin();\n\t\t\tthis.testExecutionEvents.put(test.getUniqueIdObject(), event);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier test, TestExecutionResult result) {\n\t\tTestExecutionEvent event = this.testExecutionEvents.remove(test.getUniqueIdObject());\n\t\tif (event != null && event.shouldCommit()) {\n\t\t\tevent.end();\n\t\t\tevent.initialize(test);\n\t\t\tevent.result = result.getStatus().toString();\n\t\t\tOptional<Throwable> throwable = result.getThrowable();\n\t\t\tevent.exceptionClass = throwable.map(Throwable::getClass).orElse(null);\n\t\t\tevent.exceptionMessage = throwable.map(Throwable::getMessage).orElse(null);\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier test, ReportEntry reportEntry) {\n\t\tfor (var entry : reportEntry.getKeyValuePairs().entrySet()) {\n\t\t\tvar event = new ReportEntryEvent();\n\t\t\tif (event.shouldCommit()) {\n\t\t\t\tevent.uniqueId = test.getUniqueId();\n\t\t\t\tevent.key = entry.getKey();\n\t\t\t\tevent.value = entry.getValue();\n\t\t\t\tevent.commit();\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry file) {\n\t\tvar event = new FileEntryEvent();\n\t\tif (event.shouldCommit()) {\n\t\t\tevent.uniqueId = testIdentifier.getUniqueId();\n\t\t\tevent.path = file.getPath().toAbsolutePath().toString();\n\t\t\tevent.commit();\n\t\t}\n\t}\n\n\t@Category({ \"JUnit\", \"Execution\" })\n\t@StackTrace(false)\n\tabstract static class ExecutionEvent extends Event {\n\t}\n\n\t@Label(\"Test Execution\")\n\t@Name(\"org.junit.TestPlanExecution\")\n\tstatic class TestPlanExecutionEvent extends ExecutionEvent {\n\n\t\t@Label(\"Contains Tests\")\n\t\tboolean containsTests;\n\n\t\t@Label(\"Engine Names\")\n\t\t@Nullable\n\t\tString engineNames;\n\t}\n\n\tabstract static class TestEvent extends ExecutionEvent {\n\n\t\t@UniqueId\n\t\t@Label(\"Unique Id\")\n\t\t@Nullable\n\t\tString uniqueId;\n\n\t\t@Label(\"Display Name\")\n\t\t@Nullable\n\t\tString displayName;\n\n\t\t@Label(\"Tags\")\n\t\t@Nullable\n\t\tString tags;\n\n\t\t@Label(\"Type\")\n\t\t@Nullable\n\t\tString type;\n\n\t\tvoid initialize(TestIdentifier test) {\n\t\t\tthis.uniqueId = test.getUniqueId();\n\t\t\tthis.displayName = test.getDisplayName();\n\t\t\tthis.tags = test.getTags().isEmpty() ? null : test.getTags().toString();\n\t\t\tthis.type = test.getType().name();\n\t\t}\n\t}\n\n\t@Label(\"Skipped Test\")\n\t@Name(\"org.junit.SkippedTest\")\n\tstatic class SkippedTestEvent extends TestEvent {\n\t\t@Label(\"Reason\")\n\t\t@Nullable\n\t\tString reason;\n\t}\n\n\t@Label(\"Test\")\n\t@Name(\"org.junit.TestExecution\")\n\tstatic class TestExecutionEvent extends TestEvent {\n\n\t\t@Label(\"Result\")\n\t\t@Nullable\n\t\tString result;\n\n\t\t@Label(\"Exception Class\")\n\t\t@Nullable\n\t\tClass<?> exceptionClass;\n\n\t\t@Label(\"Exception Message\")\n\t\t@Nullable\n\t\tString exceptionMessage;\n\t}\n\n\t@Label(\"Report Entry\")\n\t@Name(\"org.junit.ReportEntry\")\n\tstatic class ReportEntryEvent extends ExecutionEvent {\n\n\t\t@UniqueId\n\t\t@Label(\"Unique Id\")\n\t\t@Nullable\n\t\tString uniqueId;\n\n\t\t@Label(\"Key\")\n\t\t@Nullable\n\t\tString key;\n\n\t\t@Label(\"Value\")\n\t\t@Nullable\n\t\tString value;\n\t}\n\n\t@Label(\"File Entry\")\n\t@Name(\"org.junit.FileEntry\")\n\tstatic class FileEntryEvent extends ExecutionEvent {\n\n\t\t@UniqueId\n\t\t@Label(\"Unique Id\")\n\t\t@Nullable\n\t\tString uniqueId;\n\n\t\t@Label(\"Path\")\n\t\t@Nullable\n\t\tString path;\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/jfr/JfrUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.launcher.Launcher;\n\n/**\n * Internal utility for Java Flight Recorder (JFR) support.\n *\n * @since 6.0\n */\n@API(status = INTERNAL, since = \"6.0\")\npublic class JfrUtils {\n\n\tpublic static void registerListeners(Launcher launcher) {\n\t\tif (isJfrAvailable()) {\n\t\t\tlauncher.registerLauncherDiscoveryListeners(new FlightRecordingDiscoveryListener());\n\t\t\tlauncher.registerTestExecutionListeners(new FlightRecordingExecutionListener());\n\t\t}\n\t}\n\n\tprivate static boolean isJfrAvailable() {\n\t\treturn System.getProperty(\"org.graalvm.nativeimage.imagecode\") == null //\n\t\t\t\t&& ReflectionSupport.tryToLoadClass(\"jdk.jfr.FlightRecorder\").toOptional().isPresent();\n\t}\n\n\tprivate JfrUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/jfr/UniqueId.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport jdk.jfr.MetadataDefinition;\nimport jdk.jfr.Name;\nimport jdk.jfr.Relational;\n\n@MetadataDefinition\n@Relational\n@Name(\"org.junit.UniqueId\")\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\n@interface UniqueId {\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/jfr/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Java Flight Recorder (JFR) support package.\n */\n\n@NullMarked\npackage org.junit.platform.launcher.jfr;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/LoggingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.function.BiConsumer;\nimport java.util.function.Supplier;\nimport java.util.logging.Level;\nimport java.util.logging.Logger;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Simple {@link TestExecutionListener} for logging informational messages\n * for all events via a {@link BiConsumer} that consumes {@code Throwable}\n * and {@code Supplier<String>}.\n *\n * @since 1.0\n * @see #forJavaUtilLogging()\n * @see #forJavaUtilLogging(Level)\n * @see LoggingListener#LoggingListener(BiConsumer)\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic class LoggingListener implements TestExecutionListener {\n\n\t/**\n\t * Create a {@code LoggingListener} which delegates to a\n\t * {@link java.util.logging.Logger} using a log level of\n\t * {@link Level#FINE FINE}.\n\t *\n\t * @see #forJavaUtilLogging(Level)\n\t * @see #forBiConsumer(BiConsumer)\n\t */\n\tpublic static LoggingListener forJavaUtilLogging() {\n\t\treturn forJavaUtilLogging(Level.FINE);\n\t}\n\n\t/**\n\t * Create a {@code LoggingListener} which delegates to a\n\t * {@link java.util.logging.Logger} using the supplied\n\t * {@linkplain Level log level}.\n\t *\n\t * @param logLevel the log level to use; never {@code null}\n\t * @see #forJavaUtilLogging()\n\t * @see #forBiConsumer(BiConsumer)\n\t */\n\tpublic static LoggingListener forJavaUtilLogging(Level logLevel) {\n\t\tPreconditions.notNull(logLevel, \"logLevel must not be null\");\n\t\tLogger logger = Logger.getLogger(LoggingListener.class.getName());\n\t\treturn new LoggingListener((t, messageSupplier) -> logger.log(logLevel, t, messageSupplier));\n\t}\n\n\t/**\n\t * Create a {@code LoggingListener} which delegates to the supplied\n\t * {@link BiConsumer} for consumption of logging messages.\n\t *\n\t * <p>The {@code BiConsumer's} arguments are a {@link Throwable} (potentially\n\t * {@code null}) and a {@link Supplier} (never {@code null}) for the log\n\t * message.\n\t *\n\t * @param logger a logger implemented as a {@code BiConsumer};\n\t * never {@code null}\n\t *\n\t * @see #forJavaUtilLogging()\n\t * @see #forJavaUtilLogging(Level)\n\t */\n\tpublic static LoggingListener forBiConsumer(BiConsumer<@Nullable Throwable, Supplier<String>> logger) {\n\t\treturn new LoggingListener(logger);\n\t}\n\n\tprivate final BiConsumer<@Nullable Throwable, Supplier<String>> logger;\n\n\tprivate LoggingListener(BiConsumer<@Nullable Throwable, Supplier<String>> logger) {\n\t\tPreconditions.notNull(logger, \"logger must not be null\");\n\t\tthis.logger = logger;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tlog(\"TestPlan Execution Started: %s\", testPlan);\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tlog(\"TestPlan Execution Finished: %s\", testPlan);\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\tlog(\"Dynamic Test Registered: %s - %s\", testIdentifier.getDisplayName(), testIdentifier.getUniqueId());\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tlog(\"Execution Started: %s - %s\", testIdentifier.getDisplayName(), testIdentifier.getUniqueId());\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tlog(\"Execution Skipped: %s - %s - %s\", testIdentifier.getDisplayName(), testIdentifier.getUniqueId(), reason);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tlogWithThrowable(\"Execution Finished: %s - %s - %s\", testExecutionResult.getThrowable().orElse(null),\n\t\t\ttestIdentifier.getDisplayName(), testIdentifier.getUniqueId(), testExecutionResult);\n\t}\n\n\tprivate void log(String message, Object... args) {\n\t\tlogWithThrowable(message, null, args);\n\t}\n\n\tprivate void logWithThrowable(String message, @Nullable Throwable t, Object... args) {\n\t\tthis.logger.accept(t, () -> message.formatted(args));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/MutableTestExecutionSummary.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static java.lang.String.join;\nimport static java.util.Collections.synchronizedList;\n\nimport java.io.PrintWriter;\nimport java.io.Serial;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Mutable, internal implementation of the {@link TestExecutionSummary} API.\n *\n * @since 1.0\n */\nclass MutableTestExecutionSummary implements TestExecutionSummary {\n\n\tprivate static final String TAB = \"  \";\n\tprivate static final String DOUBLE_TAB = TAB + TAB;\n\tprivate static final int DEFAULT_MAX_STACKTRACE_LINES = 10;\n\n\tprivate static final String CAUSED_BY = \"Caused by: \";\n\tprivate static final String SUPPRESSED = \"Suppressed: \";\n\tprivate static final String CIRCULAR = \"Circular reference: \";\n\n\tfinal AtomicLong containersFound = new AtomicLong();\n\tfinal AtomicLong containersStarted = new AtomicLong();\n\tfinal AtomicLong containersSkipped = new AtomicLong();\n\tfinal AtomicLong containersAborted = new AtomicLong();\n\tfinal AtomicLong containersSucceeded = new AtomicLong();\n\tfinal AtomicLong containersFailed = new AtomicLong();\n\n\tfinal AtomicLong testsFound = new AtomicLong();\n\tfinal AtomicLong testsStarted = new AtomicLong();\n\tfinal AtomicLong testsSkipped = new AtomicLong();\n\tfinal AtomicLong testsAborted = new AtomicLong();\n\tfinal AtomicLong testsSucceeded = new AtomicLong();\n\tfinal AtomicLong testsFailed = new AtomicLong();\n\n\tprivate final TestPlan testPlan;\n\tprivate final List<Failure> failures = synchronizedList(new ArrayList<>());\n\tprivate final Map<UniqueId, String> descriptions = new ConcurrentHashMap<>();\n\tprivate final long timeStarted;\n\tprivate final long timeStartedNanos;\n\tlong timeFinished;\n\tlong timeFinishedNanos;\n\n\tMutableTestExecutionSummary(TestPlan testPlan) {\n\t\tthis.testPlan = testPlan;\n\t\tthis.containersFound.set(testPlan.countTestIdentifiers(TestIdentifier::isContainer));\n\t\tthis.testsFound.set(testPlan.countTestIdentifiers(TestIdentifier::isTest));\n\t\tthis.timeStarted = System.currentTimeMillis();\n\t\tthis.timeStartedNanos = System.nanoTime();\n\t}\n\n\tvoid addFailure(TestIdentifier testIdentifier, Throwable throwable) {\n\t\tthis.failures.add(new DefaultFailure(testIdentifier, throwable));\n\t\tthis.descriptions.put(testIdentifier.getUniqueIdObject(), describeTest(testIdentifier));\n\t}\n\n\t@Override\n\tpublic long getTimeStarted() {\n\t\treturn this.timeStarted;\n\t}\n\n\t@Override\n\tpublic long getTimeFinished() {\n\t\treturn this.timeFinished;\n\t}\n\n\t@Override\n\tpublic long getTotalFailureCount() {\n\t\treturn getTestsFailedCount() + getContainersFailedCount();\n\t}\n\n\t@Override\n\tpublic long getContainersFoundCount() {\n\t\treturn this.containersFound.get();\n\t}\n\n\t@Override\n\tpublic long getContainersStartedCount() {\n\t\treturn this.containersStarted.get();\n\t}\n\n\t@Override\n\tpublic long getContainersSkippedCount() {\n\t\treturn this.containersSkipped.get();\n\t}\n\n\t@Override\n\tpublic long getContainersAbortedCount() {\n\t\treturn this.containersAborted.get();\n\t}\n\n\t@Override\n\tpublic long getContainersSucceededCount() {\n\t\treturn this.containersSucceeded.get();\n\t}\n\n\t@Override\n\tpublic long getContainersFailedCount() {\n\t\treturn this.containersFailed.get();\n\t}\n\n\t@Override\n\tpublic long getTestsFoundCount() {\n\t\treturn this.testsFound.get();\n\t}\n\n\t@Override\n\tpublic long getTestsStartedCount() {\n\t\treturn this.testsStarted.get();\n\t}\n\n\t@Override\n\tpublic long getTestsSkippedCount() {\n\t\treturn this.testsSkipped.get();\n\t}\n\n\t@Override\n\tpublic long getTestsAbortedCount() {\n\t\treturn this.testsAborted.get();\n\t}\n\n\t@Override\n\tpublic long getTestsSucceededCount() {\n\t\treturn this.testsSucceeded.get();\n\t}\n\n\t@Override\n\tpublic long getTestsFailedCount() {\n\t\treturn this.testsFailed.get();\n\t}\n\n\t@Override\n\tpublic void printTo(PrintWriter writer) {\n\t\t// @formatter:off\n\t\twriter.printf(\"%nTest run finished after %d ms%n\"\n\n\t\t\t+ \"[%10d containers found      ]%n\"\n\t\t\t+ \"[%10d containers skipped    ]%n\"\n\t\t\t+ \"[%10d containers started    ]%n\"\n\t\t\t+ \"[%10d containers aborted    ]%n\"\n\t\t\t+ \"[%10d containers successful ]%n\"\n\t\t\t+ \"[%10d containers failed     ]%n\"\n\n\t\t\t+ \"[%10d tests found           ]%n\"\n\t\t\t+ \"[%10d tests skipped         ]%n\"\n\t\t\t+ \"[%10d tests started         ]%n\"\n\t\t\t+ \"[%10d tests aborted         ]%n\"\n\t\t\t+ \"[%10d tests successful      ]%n\"\n\t\t\t+ \"[%10d tests failed          ]%n\"\n\t\t\t+ \"%n\",\n\n\t\t\tDuration.ofNanos(this.timeFinishedNanos - this.timeStartedNanos).toMillis(),\n\n\t\t\tgetContainersFoundCount(),\n\t\t\tgetContainersSkippedCount(),\n\t\t\tgetContainersStartedCount(),\n\t\t\tgetContainersAbortedCount(),\n\t\t\tgetContainersSucceededCount(),\n\t\t\tgetContainersFailedCount(),\n\n\t\t\tgetTestsFoundCount(),\n\t\t\tgetTestsSkippedCount(),\n\t\t\tgetTestsStartedCount(),\n\t\t\tgetTestsAbortedCount(),\n\t\t\tgetTestsSucceededCount(),\n\t\t\tgetTestsFailedCount()\n\t\t);\n\t\t// @formatter:on\n\n\t\twriter.flush();\n\t}\n\n\t@Override\n\tpublic void printFailuresTo(PrintWriter writer) {\n\t\tprintFailuresTo(writer, DEFAULT_MAX_STACKTRACE_LINES);\n\t}\n\n\t@Override\n\tpublic void printFailuresTo(PrintWriter writer, int maxStackTraceLines) {\n\t\tPreconditions.notNull(writer, \"PrintWriter must not be null\");\n\t\tPreconditions.condition(maxStackTraceLines >= 0, \"maxStackTraceLines must be a positive number\");\n\n\t\tif (getTotalFailureCount() > 0) {\n\t\t\twriter.printf(\"%nFailures (%d):%n\", getTotalFailureCount());\n\t\t\tthis.failures.forEach(failure -> {\n\t\t\t\tvar testIdentifier = failure.getTestIdentifier();\n\t\t\t\twriter.printf(\"%s%s%n\", TAB, descriptions.get(testIdentifier.getUniqueIdObject()));\n\t\t\t\tprintSource(writer, testIdentifier);\n\t\t\t\twriter.printf(\"%s=> %s%n\", DOUBLE_TAB, failure.getException());\n\t\t\t\tprintStackTrace(writer, failure.getException(), maxStackTraceLines);\n\t\t\t});\n\t\t\twriter.flush();\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<Failure> getFailures() {\n\t\treturn Collections.unmodifiableList(failures);\n\t}\n\n\tprivate String describeTest(TestIdentifier testIdentifier) {\n\t\tList<String> descriptionParts = new ArrayList<>();\n\t\tcollectTestDescription(testIdentifier, descriptionParts);\n\t\treturn join(\":\", descriptionParts);\n\t}\n\n\tprivate void collectTestDescription(TestIdentifier identifier, List<String> descriptionParts) {\n\t\tdescriptionParts.add(0, identifier.getDisplayName());\n\t\tthis.testPlan.getParent(identifier).ifPresent(parent -> collectTestDescription(parent, descriptionParts));\n\t}\n\n\tprivate static void printSource(PrintWriter writer, TestIdentifier testIdentifier) {\n\t\ttestIdentifier.getSource().ifPresent(source -> writer.printf(\"%s%s%n\", DOUBLE_TAB, source));\n\t}\n\n\tprivate static void printStackTrace(PrintWriter writer, Throwable throwable, int max) {\n\t\tif (throwable.getCause() != null\n\t\t\t\t|| (throwable.getSuppressed() != null && throwable.getSuppressed().length > 0)) {\n\t\t\tmax = max / 2;\n\t\t}\n\t\tprintStackTrace(writer, new StackTraceElement[] {}, throwable, \"\", DOUBLE_TAB + \" \", new HashSet<>(), max);\n\t\twriter.flush();\n\t}\n\n\tprivate static void printStackTrace(PrintWriter writer, StackTraceElement[] parentTrace, Throwable throwable,\n\t\t\tString caption, String indentation, Set<Throwable> seenThrowables, int max) {\n\t\tif (seenThrowables.contains(throwable)) {\n\t\t\twriter.printf(\"%s%s[%s%s]%n\", indentation, TAB, CIRCULAR, throwable);\n\t\t\treturn;\n\t\t}\n\t\tseenThrowables.add(throwable);\n\n\t\tStackTraceElement[] trace = throwable.getStackTrace();\n\t\tif (parentTrace.length > 0) {\n\t\t\twriter.printf(\"%s%s%s%n\", indentation, caption, throwable);\n\t\t}\n\t\tint duplicates = numberOfCommonFrames(trace, parentTrace);\n\t\tint numDistinctFrames = trace.length - duplicates;\n\t\tint numDisplayLines = Math.min(numDistinctFrames, max);\n\t\tfor (int i = 0; i < numDisplayLines; i++) {\n\t\t\twriter.printf(\"%s%s%s%n\", indentation, TAB, trace[i]);\n\t\t}\n\t\tif (trace.length > max || duplicates != 0) {\n\t\t\twriter.printf(\"%s%s%s%n\", indentation, TAB, \"[...]\");\n\t\t}\n\n\t\tfor (Throwable suppressed : throwable.getSuppressed()) {\n\t\t\tprintStackTrace(writer, trace, suppressed, SUPPRESSED, indentation + TAB, seenThrowables, max);\n\t\t}\n\t\tif (throwable.getCause() != null) {\n\t\t\tprintStackTrace(writer, trace, throwable.getCause(), CAUSED_BY, indentation, seenThrowables, max);\n\t\t}\n\t}\n\n\tprivate static int numberOfCommonFrames(StackTraceElement[] currentTrace, StackTraceElement[] parentTrace) {\n\t\tint currentIndex = currentTrace.length - 1;\n\t\tfor (int parentIndex = parentTrace.length - 1; currentIndex >= 0\n\t\t\t\t&& parentIndex >= 0; currentIndex--, parentIndex--) {\n\t\t\tif (!currentTrace[currentIndex].equals(parentTrace[parentIndex])) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn currentTrace.length - 1 - currentIndex;\n\t}\n\n\tprivate record DefaultFailure(TestIdentifier testIdentifier, Throwable exception) implements Failure {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t@Override\n\t\tpublic TestIdentifier getTestIdentifier() {\n\t\t\treturn testIdentifier;\n\t\t}\n\n\t\t@Override\n\t\tpublic Throwable getException() {\n\t\t\treturn exception;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/OutputDir.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.attribute.BasicFileAttributes;\nimport java.security.SecureRandom;\nimport java.util.Optional;\nimport java.util.function.BiPredicate;\nimport java.util.function.Supplier;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.StringUtils;\n\n@API(status = INTERNAL, since = \"1.9\")\npublic class OutputDir {\n\n\tprivate static final Pattern OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER_PATTERN = Pattern.compile(\n\t\tPattern.quote(OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER));\n\n\tpublic static OutputDir create(Optional<String> customDir) {\n\t\treturn create(customDir, () -> Path.of(\".\"));\n\t}\n\n\tstatic OutputDir create(Optional<String> customDir, Supplier<Path> currentWorkingDir) {\n\t\ttry {\n\t\t\treturn createSafely(customDir, currentWorkingDir);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to create output dir\", e);\n\t\t}\n\t}\n\n\t/**\n\t * Package private for testing purposes.\n\t */\n\tstatic OutputDir createSafely(Optional<String> customDir, Supplier<Path> currentWorkingDir) throws IOException {\n\t\treturn createSafely(customDir, currentWorkingDir, new SecureRandom());\n\t}\n\n\tprivate static OutputDir createSafely(Optional<String> customDir, Supplier<Path> currentWorkingDir,\n\t\t\tSecureRandom random) throws IOException {\n\t\tPath cwd = currentWorkingDir.get().toAbsolutePath();\n\t\tPath outputDir;\n\n\t\tif (customDir.isPresent() && StringUtils.isNotBlank(customDir.get())) {\n\t\t\toutputDir = cwd.resolve(expandPlaceholders(customDir.get(), random));\n\t\t}\n\t\telse if (Files.exists(cwd.resolve(\"pom.xml\"))) {\n\t\t\toutputDir = cwd.resolve(\"target\");\n\t\t}\n\t\telse if (containsFilesWithExtensions(cwd, \".gradle\", \".gradle.kts\")) {\n\t\t\toutputDir = cwd.resolve(\"build\");\n\t\t}\n\t\telse {\n\t\t\toutputDir = cwd;\n\t\t}\n\n\t\tif (!Files.exists(outputDir)) {\n\t\t\tFiles.createDirectories(outputDir);\n\t\t}\n\n\t\treturn new OutputDir(outputDir.normalize(), random);\n\t}\n\n\tprivate static String expandPlaceholders(String customDir, SecureRandom random) {\n\t\tString customPath = customDir;\n\t\twhile (customPath.contains(OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER)) {\n\t\t\tcustomPath = OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER_PATTERN.matcher(customPath) //\n\t\t\t\t\t.replaceFirst(String.valueOf(positiveLong(random)));\n\t\t}\n\t\treturn customPath;\n\t}\n\n\tprivate final Path path;\n\tprivate final SecureRandom random;\n\n\tprivate OutputDir(Path path, SecureRandom random) {\n\t\tthis.path = path;\n\t\tthis.random = random;\n\t}\n\n\tpublic Path toPath() {\n\t\treturn path;\n\t}\n\n\tpublic Path createFile(String prefix, String extension) throws UncheckedIOException {\n\t\tString filename = \"%s-%d.%s\".formatted(prefix, positiveLong(random), extension);\n\t\tPath outputFile = path.resolve(filename);\n\n\t\ttry {\n\t\t\tif (Files.exists(outputFile)) {\n\t\t\t\tFiles.delete(outputFile);\n\t\t\t}\n\t\t\treturn Files.createFile(outputFile);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to create output file: \" + outputFile, e);\n\t\t}\n\t}\n\n\tprivate static long positiveLong(SecureRandom random) {\n\t\tvar value = random.nextLong();\n\t\tif (value == Long.MIN_VALUE) {\n\t\t\t// ensure Math.abs returns positive value\n\t\t\tvalue++;\n\t\t}\n\t\treturn Math.abs(value);\n\t}\n\n\t/**\n\t * Determine if the supplied directory contains files with any of the\n\t * supplied extensions.\n\t */\n\tprivate static boolean containsFilesWithExtensions(Path dir, String... extensions) throws IOException {\n\t\tBiPredicate<Path, BasicFileAttributes> matcher = (path, basicFileAttributes) -> {\n\t\t\tif (basicFileAttributes.isRegularFile()) {\n\t\t\t\tfor (String extension : extensions) {\n\t\t\t\t\tif (path.getFileName().toString().endsWith(extension)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t};\n\t\ttry (Stream<Path> pathStream = Files.find(dir, 1, matcher)) {\n\t\t\treturn pathStream.findFirst().isPresent();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/SummaryGeneratingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Stream.concat;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Simple {@link TestExecutionListener} that generates a\n * {@linkplain TestExecutionSummary summary} of the test execution.\n *\n * @since 1.0\n * @see #getSummary()\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic class SummaryGeneratingListener implements TestExecutionListener {\n\n\tprivate @Nullable TestPlan testPlan;\n\n\tprivate @Nullable MutableTestExecutionSummary summary;\n\n\tpublic SummaryGeneratingListener() {\n\n\t}\n\n\t/**\n\t * Get the summary generated by this listener.\n\t */\n\tpublic TestExecutionSummary getSummary() {\n\t\treturn getMutableSummary();\n\t}\n\n\tprivate MutableTestExecutionSummary getMutableSummary() {\n\t\treturn Preconditions.notNull(this.summary, \"No tests have yet been executed\");\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tthis.testPlan = testPlan;\n\t\tthis.summary = new MutableTestExecutionSummary(testPlan);\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tvar summary = getMutableSummary();\n\t\tsummary.timeFinished = System.currentTimeMillis();\n\t\tsummary.timeFinishedNanos = System.nanoTime();\n\t}\n\n\t@Override\n\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\tvar summary = getMutableSummary();\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tsummary.containersFound.incrementAndGet();\n\t\t}\n\t\tif (testIdentifier.isTest()) {\n\t\t\tsummary.testsFound.incrementAndGet();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tvar testPlan = requireNonNull(this.testPlan);\n\t\t// @formatter:off\n\t\tlong skippedContainers = concat(Stream.of(testIdentifier), testPlan.getDescendants(testIdentifier).stream())\n\t\t\t\t.filter(TestIdentifier::isContainer)\n\t\t\t\t.count();\n\t\tlong skippedTests = concat(Stream.of(testIdentifier), testPlan.getDescendants(testIdentifier).stream())\n\t\t\t\t.filter(TestIdentifier::isTest)\n\t\t\t\t.count();\n\t\t// @formatter:on\n\t\tvar summary = getMutableSummary();\n\t\tsummary.containersSkipped.addAndGet(skippedContainers);\n\t\tsummary.testsSkipped.addAndGet(skippedTests);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tvar summary = getMutableSummary();\n\t\tif (testIdentifier.isContainer()) {\n\t\t\tsummary.containersStarted.incrementAndGet();\n\t\t}\n\t\tif (testIdentifier.isTest()) {\n\t\t\tsummary.testsStarted.incrementAndGet();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tvar summary = getMutableSummary();\n\n\t\tswitch (testExecutionResult.getStatus()) {\n\n\t\t\tcase SUCCESSFUL -> {\n\t\t\t\tif (testIdentifier.isContainer()) {\n\t\t\t\t\tsummary.containersSucceeded.incrementAndGet();\n\t\t\t\t}\n\t\t\t\tif (testIdentifier.isTest()) {\n\t\t\t\t\tsummary.testsSucceeded.incrementAndGet();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase ABORTED -> {\n\t\t\t\tif (testIdentifier.isContainer()) {\n\t\t\t\t\tsummary.containersAborted.incrementAndGet();\n\t\t\t\t}\n\t\t\t\tif (testIdentifier.isTest()) {\n\t\t\t\t\tsummary.testsAborted.incrementAndGet();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcase FAILED -> {\n\t\t\t\tif (testIdentifier.isContainer()) {\n\t\t\t\t\tsummary.containersFailed.incrementAndGet();\n\t\t\t\t}\n\t\t\t\tif (testIdentifier.isTest()) {\n\t\t\t\t\tsummary.testsFailed.incrementAndGet();\n\t\t\t\t}\n\t\t\t\ttestExecutionResult.getThrowable().ifPresent(\n\t\t\t\t\tthrowable -> summary.addFailure(testIdentifier, throwable));\n\t\t\t}\n\n\t\t\tdefault -> throw new PreconditionViolationException(\n\t\t\t\t\"Unsupported execution status:\" + testExecutionResult.getStatus());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/TestExecutionSummary.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.PrintWriter;\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * Summary of test plan execution.\n *\n * @since 1.0\n * @see SummaryGeneratingListener\n */\n@API(status = MAINTAINED, since = \"1.0\")\npublic interface TestExecutionSummary {\n\n\t/**\n\t * Get the timestamp (in milliseconds) when the test plan started.\n\t */\n\tlong getTimeStarted();\n\n\t/**\n\t * Get the timestamp (in milliseconds) when the test plan finished.\n\t */\n\tlong getTimeFinished();\n\n\t/**\n\t * Get the total number of {@linkplain #getContainersFailedCount failed\n\t * containers} and {@linkplain #getTestsFailedCount failed tests}.\n\t *\n\t * @see #getTestsFailedCount()\n\t * @see #getContainersFailedCount()\n\t */\n\tlong getTotalFailureCount();\n\n\t/**\n\t * Get the number of containers found.\n\t */\n\tlong getContainersFoundCount();\n\n\t/**\n\t * Get the number of containers started.\n\t */\n\tlong getContainersStartedCount();\n\n\t/**\n\t * Get the number of containers skipped.\n\t */\n\tlong getContainersSkippedCount();\n\n\t/**\n\t * Get the number of containers aborted.\n\t */\n\tlong getContainersAbortedCount();\n\n\t/**\n\t * Get the number of containers that succeeded.\n\t */\n\tlong getContainersSucceededCount();\n\n\t/**\n\t * Get the number of containers that failed.\n\t *\n\t * @see #getTestsFailedCount()\n\t * @see #getTotalFailureCount()\n\t */\n\tlong getContainersFailedCount();\n\n\t/**\n\t * Get the number of tests found.\n\t */\n\tlong getTestsFoundCount();\n\n\t/**\n\t * Get the number of tests started.\n\t */\n\tlong getTestsStartedCount();\n\n\t/**\n\t * Get the number of tests skipped.\n\t */\n\tlong getTestsSkippedCount();\n\n\t/**\n\t * Get the number of tests aborted.\n\t */\n\tlong getTestsAbortedCount();\n\n\t/**\n\t * Get the number of tests that succeeded.\n\t */\n\tlong getTestsSucceededCount();\n\n\t/**\n\t * Get the number of tests that failed.\n\t *\n\t * @see #getContainersFailedCount()\n\t * @see #getTotalFailureCount()\n\t */\n\tlong getTestsFailedCount();\n\n\t/**\n\t * Print this summary to the supplied {@link PrintWriter}.\n\t *\n\t * <p>This method does not print failure messages.\n\t *\n\t * @see #printFailuresTo(PrintWriter)\n\t */\n\tvoid printTo(PrintWriter writer);\n\n\t/**\n\t * Print failed containers and tests, including sources and exception\n\t * messages, to the supplied {@link PrintWriter}.\n\t *\n\t * @param writer the {@code PrintWriter} to which to print; never {@code null}\n\t * @see #printTo(PrintWriter)\n\t * @see #printFailuresTo(PrintWriter, int)\n\t */\n\tvoid printFailuresTo(PrintWriter writer);\n\n\t/**\n\t * Print failed containers and tests, including sources and exception\n\t * messages, to the supplied {@link PrintWriter}.\n\t *\n\t * <p>The maximum number of lines to print for exception stack traces (if any)\n\t * can be specified via the {@code maxStackTraceLines} argument.\n\t *\n\t * <p>By default, this method delegates to {@link #printFailuresTo(PrintWriter)},\n\t * effectively ignoring the {@code maxStackTraceLines} argument. Concrete\n\t * implementations of this interface should therefore override this default\n\t * method in order to honor the {@code maxStackTraceLines} argument.\n\t *\n\t * @param writer the {@code PrintWriter} to which to print; never {@code null}\n\t * @param maxStackTraceLines the maximum number of lines to print for exception\n\t * stack traces; must be a positive value\n\t * @since 1.6\n\t * @see #printTo(PrintWriter)\n\t * @see #printFailuresTo(PrintWriter)\n\t */\n\t@API(status = MAINTAINED, since = \"1.6\")\n\tdefault void printFailuresTo(PrintWriter writer, int maxStackTraceLines) {\n\t\tprintFailuresTo(writer);\n\t}\n\n\t/**\n\t * Get an immutable list of the failures of the test plan execution.\n\t */\n\tList<Failure> getFailures();\n\n\t/**\n\t * Failure of a test or container.\n\t */\n\tinterface Failure extends Serializable {\n\n\t\t/**\n\t\t * Get the identifier of the failed test or container.\n\t\t *\n\t\t * @return the {@link TestIdentifier} for this failure; never {@code null}\n\t\t */\n\t\tTestIdentifier getTestIdentifier();\n\n\t\t/**\n\t\t * Get the {@link Throwable} causing the failure.\n\t\t *\n\t\t * @return the {@link Throwable} for this failure; never {@code null}\n\t\t */\n\t\tThrowable getException();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * {@code UniqueIdTrackingListener} is a {@link TestExecutionListener} that tracks\n * the {@linkplain TestIdentifier#getUniqueId() unique IDs} of all\n * {@linkplain TestIdentifier#isTest() tests} that were\n * {@linkplain #executionFinished executed} during the execution of the\n * {@link TestPlan} and generates a file containing the unique IDs once execution\n * of the {@code TestPlan} has {@linkplain #testPlanExecutionFinished(TestPlan)\n * finished}.\n *\n * <p>Tests are tracked regardless of their {@link TestExecutionResult} or whether\n * they were skipped, and the unique IDs are written to an output file, one ID\n * per line, encoding using UTF-8.\n *\n * <p>The output file can be used to execute the same set of tests again without\n * having to query the user configuration for the test plan and without having to\n * perform test discovery again. This can be useful for test environments such as\n * within a native image &mdash; for example, a GraalVM native image &mdash; in\n * order to rerun the exact same tests from a standard JVM test run within a\n * native image.\n *\n * <h2>Configuration and Defaults</h2>\n *\n * <p>The {@code OUTPUT_DIR} is the directory in which this listener generates\n * the output file. The exact path of the generated file is\n * {@code OUTPUT_DIR/OUTPUT_FILE_PREFIX-<random number>.txt}, where\n * {@code <random number>} is a pseudo-random number. The inclusion of a random\n * number in the file name ensures that multiple concurrently executing test\n * plans do not overwrite each other's results.\n *\n * <p>The value of the {@code OUTPUT_FILE_PREFIX} defaults to\n * {@link #DEFAULT_OUTPUT_FILE_PREFIX}, but a custom prefix can be set via the\n * {@link #OUTPUT_FILE_PREFIX_PROPERTY_NAME} configuration property.\n *\n * <p>The {@code OUTPUT_DIR} can be set to a custom directory via the\n * {@link #OUTPUT_DIR_PROPERTY_NAME} configuration property. Otherwise the\n * following algorithm is used to select a default output directory.\n *\n * <ul>\n * <li>If the current working directory of the Java process contains a file named\n * {@code pom.xml}, the output directory will be {@code ./target}, following the\n * conventions of Maven.</li>\n * <li>If the current working directory of the Java process contains a file with\n * the extension {@code .gradle} or {@code .gradle.kts}, the output directory\n * will be {@code ./build}, following the conventions of Gradle.</li>\n * <li>Otherwise, the current working directory of the Java process will be used\n * as the output directory.</li>\n * </ul>\n *\n * <p>For example, in a project using Gradle as the build tool, the file generated\n * by this listener would be {@code ./build/junit-platform-unique-ids-<random number>.txt}\n * by default.\n *\n * <p>Configuration properties can be set via JVM system properties, via a\n * {@code junit-platform.properties} file in the root of the classpath, or as\n * JUnit Platform {@linkplain ConfigurationParameters configuration parameters}.\n *\n * @since 1.8\n */\n@API(status = STABLE, since = \"1.11\")\npublic class UniqueIdTrackingListener implements TestExecutionListener {\n\n\t/**\n\t * Property name used to enable the {@code UniqueIdTrackingListener}: {@value}\n\t *\n\t * <p>The {@code UniqueIdTrackingListener} is registered automatically via\n\t * Java's {@link java.util.ServiceLoader} mechanism but disabled by default.\n\t *\n\t * <p>Set the value of this property to {@code true} to enable this listener.\n\t */\n\tpublic static final String LISTENER_ENABLED_PROPERTY_NAME = \"junit.platform.listeners.uid.tracking.enabled\";\n\n\t/**\n\t * Property name used to set the path to the output directory for the file\n\t * generated by the {@code UniqueIdTrackingListener}: {@value}\n\t *\n\t * <p>For details on the default output directory, see the\n\t * {@linkplain UniqueIdTrackingListener class-level Javadoc}.\n\t */\n\tpublic static final String OUTPUT_DIR_PROPERTY_NAME = \"junit.platform.listeners.uid.tracking.output.dir\";\n\n\t/**\n\t * Property name used to set the prefix for the name of the file generated\n\t * by the {@code UniqueIdTrackingListener}: {@value}\n\t *\n\t * <p>Defaults to {@link #DEFAULT_OUTPUT_FILE_PREFIX}.\n\t */\n\tpublic static final String OUTPUT_FILE_PREFIX_PROPERTY_NAME = \"junit.platform.listeners.uid.tracking.output.file.prefix\";\n\n\t/**\n\t * The default prefix for the name of the file generated by the\n\t * {@code UniqueIdTrackingListener}: {@value}\n\t *\n\t * @see #OUTPUT_FILE_PREFIX_PROPERTY_NAME\n\t */\n\tpublic static final String DEFAULT_OUTPUT_FILE_PREFIX = \"junit-platform-unique-ids\";\n\n\tstatic final String WORKING_DIR_PROPERTY_NAME = \"junit.platform.listeners.uid.tracking.working.dir\";\n\n\tprivate final Logger logger = LoggerFactory.getLogger(UniqueIdTrackingListener.class);\n\n\tprivate final List<String> uniqueIds = new ArrayList<>();\n\n\tprivate boolean enabled;\n\n\tprivate @Nullable TestPlan testPlan;\n\n\tpublic UniqueIdTrackingListener() {\n\t\t// to avoid missing-explicit-ctor warning\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tthis.enabled = testPlan.getConfigurationParameters().getBoolean(LISTENER_ENABLED_PROPERTY_NAME).orElse(false);\n\t\tthis.testPlan = testPlan;\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tif (this.enabled) {\n\t\t\t// When a container is skipped, there are no events for its children.\n\t\t\t// Therefore, in order to track them, we need to traverse the subtree.\n\t\t\ttrackTestUidRecursively(testIdentifier);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tif (this.enabled) {\n\t\t\ttrackTestUid(testIdentifier);\n\t\t}\n\t}\n\n\tprivate void trackTestUidRecursively(TestIdentifier testIdentifier) {\n\t\tboolean tracked = trackTestUid(testIdentifier);\n\t\tif (!tracked) {\n\t\t\trequireNonNull(this.testPlan).getChildren(testIdentifier).forEach(this::trackTestUidRecursively);\n\t\t}\n\t}\n\n\tprivate boolean trackTestUid(TestIdentifier testIdentifier) {\n\t\tif (testIdentifier.isTest()) {\n\t\t\tthis.uniqueIds.add(testIdentifier.getUniqueId());\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tif (this.enabled) {\n\t\t\tPath outputFile;\n\t\t\ttry {\n\t\t\t\toutputFile = createOutputFile(testPlan.getConfigurationParameters());\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tlogger.error(ex, () -> \"Failed to create output file\");\n\t\t\t\t// Abort since we cannot generate the file.\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlogger.debug(() -> \"Writing unique IDs to output file \" + outputFile.toAbsolutePath());\n\t\t\ttry (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(outputFile, StandardCharsets.UTF_8))) {\n\t\t\t\tthis.uniqueIds.forEach(writer::println);\n\t\t\t\twriter.flush();\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tlogger.error(ex, () -> \"Failed to write unique IDs to output file \" + outputFile.toAbsolutePath());\n\t\t\t}\n\t\t}\n\t\tthis.testPlan = null;\n\t}\n\n\tprivate Path createOutputFile(ConfigurationParameters configurationParameters) {\n\t\tString prefix = configurationParameters.get(OUTPUT_FILE_PREFIX_PROPERTY_NAME) //\n\t\t\t\t.orElse(DEFAULT_OUTPUT_FILE_PREFIX);\n\t\tSupplier<Path> workingDirSupplier = () -> configurationParameters.get(WORKING_DIR_PROPERTY_NAME) //\n\t\t\t\t.map(Path::of) //\n\t\t\t\t.orElseGet(() -> Path.of(\".\"));\n\t\treturn OutputDir.create(configurationParameters.get(OUTPUT_DIR_PROPERTY_NAME), workingDirSupplier) //\n\t\t\t\t.createFile(prefix, \"txt\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\n/**\n * @since 1.6\n * @see LauncherDiscoveryListeners#abortOnFailure()\n */\nfinal class AbortOnFailureLauncherDiscoveryListener implements LauncherDiscoveryListener {\n\n\t@Override\n\tpublic void engineDiscoveryFinished(UniqueId engineId, EngineDiscoveryResult result) {\n\t\tresult.getThrowable().ifPresent(ExceptionUtils::throwAsUncheckedException);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn getClass() == obj.getClass();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 0;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport java.util.List;\n\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * @since 1.6\n * @see LauncherDiscoveryListeners#composite(List)\n */\nclass CompositeLauncherDiscoveryListener implements LauncherDiscoveryListener {\n\n\tprivate final List<LauncherDiscoveryListener> listeners;\n\n\tCompositeLauncherDiscoveryListener(List<LauncherDiscoveryListener> listeners) {\n\t\tthis.listeners = List.copyOf(listeners);\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t\tlisteners.forEach(delegate -> delegate.launcherDiscoveryStarted(request));\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {\n\t\tforEachInReverseOrder(listeners, delegate -> delegate.launcherDiscoveryFinished(request));\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryStarted(UniqueId engineId) {\n\t\tlisteners.forEach(delegate -> delegate.engineDiscoveryStarted(engineId));\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryFinished(UniqueId engineId, EngineDiscoveryResult result) {\n\t\tforEachInReverseOrder(listeners, delegate -> delegate.engineDiscoveryFinished(engineId, result));\n\t}\n\n\t@Override\n\tpublic void selectorProcessed(UniqueId engineId, DiscoverySelector selector, SelectorResolutionResult result) {\n\t\tlisteners.forEach(delegate -> delegate.selectorProcessed(engineId, selector, result));\n\t}\n\n\t@Override\n\tpublic void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t\tlisteners.forEach(delegate -> delegate.issueEncountered(engineId, issue));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LauncherDiscoveryListeners.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\n/**\n * Collection of {@code static} factory methods for creating\n * {@link LauncherDiscoveryListener LauncherDiscoveryListeners}.\n *\n * @since 1.6\n */\n@API(status = STABLE, since = \"1.10\")\npublic class LauncherDiscoveryListeners {\n\n\tprivate LauncherDiscoveryListeners() {\n\t}\n\n\t/**\n\t * Create a {@link LauncherDiscoveryListener} that aborts test discovery on\n\t * failures.\n\t *\n\t * <p>The following events are considered failures:\n\t *\n\t * <ul>\n\t *     <li>\n\t *         any recoverable {@link Throwable} thrown by\n\t *         {@link TestEngine#discover}.\n\t *     </li>\n\t * </ul>\n\t */\n\tpublic static LauncherDiscoveryListener abortOnFailure() {\n\t\treturn new AbortOnFailureLauncherDiscoveryListener();\n\t}\n\n\t/**\n\t * Create a {@link LauncherDiscoveryListener} that logs test discovery\n\t * events based on their severity.\n\t *\n\t * <p>For example, failures during test discovery are logged as errors.\n\t */\n\tpublic static LauncherDiscoveryListener logging() {\n\t\treturn new LoggingLauncherDiscoveryListener();\n\t}\n\n\t@API(status = INTERNAL, since = \"1.6\")\n\tpublic static LauncherDiscoveryListener composite(List<LauncherDiscoveryListener> listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listeners must not contain any null elements\");\n\t\tif (listeners.isEmpty()) {\n\t\t\treturn LauncherDiscoveryListener.NOOP;\n\t\t}\n\t\tif (listeners.size() == 1) {\n\t\t\treturn listeners.get(0);\n\t\t}\n\t\treturn new CompositeLauncherDiscoveryListener(listeners);\n\t}\n\n\t@API(status = INTERNAL, since = \"1.6\")\n\tpublic static LauncherDiscoveryListener fromConfigurationParameter(String key, String value) {\n\t\treturn Arrays.stream(LauncherDiscoveryListenerType.values()) //\n\t\t\t\t.filter(type -> type.parameterValue.equalsIgnoreCase(value)) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.map(type -> type.creator.get()) //\n\t\t\t\t.orElseThrow(() -> newInvalidConfigurationParameterException(key, value));\n\t}\n\n\tprivate static JUnitException newInvalidConfigurationParameterException(String key, String value) {\n\t\tString allowedValues = Arrays.stream(LauncherDiscoveryListenerType.values()) //\n\t\t\t\t.map(type -> type.parameterValue) //\n\t\t\t\t.collect(joining(\"', '\", \"'\", \"'\"));\n\t\treturn new JUnitException(\"Invalid value of configuration parameter '\" + key + \"': \" //\n\t\t\t\t+ value + \" (allowed are \" + allowedValues + \")\");\n\t}\n\n\tprivate enum LauncherDiscoveryListenerType {\n\n\t\tLOGGING(\"logging\", LauncherDiscoveryListeners::logging),\n\n\t\tABORT_ON_FAILURE(\"abortOnFailure\", LauncherDiscoveryListeners::abortOnFailure);\n\n\t\tprivate final String parameterValue;\n\t\tprivate final Supplier<LauncherDiscoveryListener> creator;\n\n\t\tLauncherDiscoveryListenerType(String parameterValue, Supplier<LauncherDiscoveryListener> creator) {\n\t\t\tthis.parameterValue = parameterValue;\n\t\t\tthis.creator = creator;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static org.junit.platform.launcher.EngineDiscoveryResult.Status.FAILED;\n\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * @since 1.6\n * @see LauncherDiscoveryListeners#logging()\n */\nfinal class LoggingLauncherDiscoveryListener implements LauncherDiscoveryListener {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(LoggingLauncherDiscoveryListener.class);\n\n\t@Override\n\tpublic void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t\tlogger.trace(() -> \"Test discovery started\");\n\t}\n\n\t@Override\n\tpublic void launcherDiscoveryFinished(LauncherDiscoveryRequest request) {\n\t\tlogger.trace(() -> \"Test discovery finished\");\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryStarted(UniqueId engineId) {\n\t\tlogger.trace(() -> \"Engine \" + engineId + \" has started discovering tests\");\n\t}\n\n\t@Override\n\tpublic void engineDiscoveryFinished(UniqueId engineId, EngineDiscoveryResult result) {\n\t\tif (result.getStatus() == FAILED) {\n\t\t\tOptional<Throwable> failure = result.getThrowable();\n\t\t\tif (failure.isPresent()) {\n\t\t\t\tlogger.error(failure.get().getCause(), () -> failure.get().getMessage());\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlogger.error(() -> \"Engine \" + engineId + \" failed to discover tests\");\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tlogger.trace(() -> \"Engine \" + engineId + \" has finished discovering tests\");\n\t\t}\n\t}\n\n\t@Override\n\tpublic void selectorProcessed(UniqueId engineId, DiscoverySelector selector, SelectorResolutionResult result) {\n\t\tswitch (result.getStatus()) {\n\t\t\tcase RESOLVED -> logger.debug(() -> selector + \" was resolved by \" + engineId);\n\t\t\tcase FAILED -> logger.error(result.getThrowable().orElse(null),\n\t\t\t\t() -> \"Resolution of \" + selector + \" by \" + engineId + \" failed\");\n\t\t\tcase UNRESOLVED -> {\n\t\t\t\tConsumer<Supplier<String>> loggingConsumer = logger::debug;\n\t\t\t\tif (selector instanceof UniqueIdSelector uniqueIdSelector) {\n\t\t\t\t\tUniqueId uniqueId = uniqueIdSelector.getUniqueId();\n\t\t\t\t\tif (uniqueId.hasPrefix(engineId)) {\n\t\t\t\t\t\tloggingConsumer = logger::warn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tloggingConsumer.accept(() -> selector + \" could not be resolved by \" + engineId);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t\tlogger.trace(() -> \"Issue encountered during discovery by TestEngine with ID '\" + engineId + \"': \" + issue);\n\t}\n\n\t@Override\n\tpublic boolean equals(Object obj) {\n\t\tif (this == obj) {\n\t\t\treturn true;\n\t\t}\n\t\tif (obj == null) {\n\t\t\treturn false;\n\t\t}\n\t\treturn getClass() == obj.getClass();\n\t}\n\n\t@Override\n\tpublic int hashCode() {\n\t\treturn 1;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/discovery/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common {@link org.junit.platform.launcher.LauncherDiscoveryListener}\n * implementations and factory methods.\n *\n * @see org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners\n * @since 1.6\n */\n\n@NullMarked\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common {@link org.junit.platform.launcher.TestExecutionListener\n * TestExecutionListener} implementations and related support classes for\n * the {@link org.junit.platform.launcher.Launcher Launcher}.\n */\n\n@NullMarked\npackage org.junit.platform.launcher.listeners;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.session;\n\nimport static org.junit.platform.commons.util.CollectionUtils.forEachInReverseOrder;\n\nimport java.util.List;\n\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\n\n/**\n * @since 1.8\n * @see LauncherSessionListeners#composite(List)\n */\nclass CompositeLauncherSessionListener implements LauncherSessionListener {\n\n\tprivate final List<LauncherSessionListener> listeners;\n\n\tCompositeLauncherSessionListener(List<LauncherSessionListener> listeners) {\n\t\tthis.listeners = List.copyOf(listeners);\n\t}\n\n\t@Override\n\tpublic void launcherSessionOpened(LauncherSession session) {\n\t\tlisteners.forEach(delegate -> delegate.launcherSessionOpened(session));\n\t}\n\n\t@Override\n\tpublic void launcherSessionClosed(LauncherSession session) {\n\t\tforEachInReverseOrder(listeners, delegate -> delegate.launcherSessionClosed(session));\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/LauncherSessionListeners.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.session;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.launcher.LauncherSessionListener;\n\n/**\n * Collection of {@code static} factory methods for creating\n * {@link LauncherSessionListener LauncherSessionListeners}.\n *\n * @since 1.8\n */\n@API(status = INTERNAL, since = \"1.8\")\npublic class LauncherSessionListeners {\n\n\tprivate LauncherSessionListeners() {\n\t}\n\n\tpublic static LauncherSessionListener composite(List<LauncherSessionListener> listeners) {\n\t\tPreconditions.notNull(listeners, \"listeners must not be null\");\n\t\tPreconditions.containsNoNullElements(listeners, \"listeners must not contain any null elements\");\n\t\tif (listeners.isEmpty()) {\n\t\t\treturn LauncherSessionListener.NOOP;\n\t\t}\n\t\tif (listeners.size() == 1) {\n\t\t\treturn listeners.get(0);\n\t\t}\n\t\treturn new CompositeLauncherSessionListener(listeners);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/listeners/session/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Common {@link org.junit.platform.launcher.LauncherSessionListener}\n * implementations and factory methods.\n *\n * @see org.junit.platform.launcher.listeners.session.LauncherSessionListeners\n * @since 1.8\n */\n\n@NullMarked\npackage org.junit.platform.launcher.listeners.session;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Public API for configuring and launching test plans.\n *\n * <p>This API is typically used by IDEs and build tools.\n */\n\n@NullMarked\npackage org.junit.platform.launcher;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/DequeStack.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport java.util.ArrayDeque;\nimport java.util.Deque;\n\nimport org.junit.platform.commons.util.Preconditions;\n\n/**\n * @since 1.1\n */\nclass DequeStack<T> implements Stack<T> {\n\n\tprivate final Deque<T> deque = new ArrayDeque<>();\n\n\t@Override\n\tpublic void push(T t) {\n\t\tdeque.addFirst(t);\n\t}\n\n\t@Override\n\tpublic T peek() {\n\t\treturn Preconditions.notNull(deque.peek(), () -> \"stack is empty\");\n\t}\n\n\t@Override\n\tpublic T pop() {\n\t\treturn Preconditions.notNull(deque.pollFirst(), () -> \"stack is empty\");\n\t}\n\n\t@Override\n\tpublic boolean isEmpty() {\n\t\treturn deque.isEmpty();\n\t}\n\n\t@Override\n\tpublic int size() {\n\t\treturn deque.size();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.platform.launcher.tagexpression.Operator.Associativity.Left;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.missingOperatorBetween;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.missingRhsOperand;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.problemParsing;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.success;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.1\n */\nclass Operator {\n\n\tenum Associativity {\n\t\tLeft, Right\n\t}\n\n\tinterface TagExpressionCreator {\n\t\tParseStatus createExpressionAndAddTo(Stack<TokenWith<TagExpression>> expressions, Token operatorToken);\n\t}\n\n\tstatic Operator nullaryOperator(String representation, int precedence) {\n\t\treturn new Operator(representation, precedence, 0, null, (expressions, operatorToken) -> success());\n\t}\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tstatic Operator unaryOperator(String representation, int precedence, Associativity associativity,\n\t\t\tFunction<TagExpression, TagExpression> unaryExpression) {\n\n\t\treturn new Operator(representation, precedence, 1, associativity, (expressions, operatorToken) -> {\n\t\t\tTokenWith<TagExpression> rhs = expressions.pop();\n\t\t\tif (operatorToken.isLeftOf(rhs.token())) {\n\t\t\t\tToken combinedToken = operatorToken.concatenate(rhs.token());\n\t\t\t\texpressions.push(new TokenWith<>(combinedToken, unaryExpression.apply(rhs.element())));\n\t\t\t\treturn success();\n\t\t\t}\n\t\t\treturn missingRhsOperand(operatorToken, representation);\n\t\t});\n\t}\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tstatic Operator binaryOperator(String representation, int precedence, Associativity associativity,\n\t\t\tBiFunction<TagExpression, TagExpression, TagExpression> binaryExpression) {\n\n\t\treturn new Operator(representation, precedence, 2, associativity, (expressions, operatorToken) -> {\n\t\t\tTokenWith<TagExpression> rhs = expressions.pop();\n\t\t\tTokenWith<TagExpression> lhs = expressions.pop();\n\t\t\tToken lhsToken = lhs.token();\n\t\t\tif (lhsToken.isLeftOf(operatorToken) && operatorToken.isLeftOf(rhs.token())) {\n\t\t\t\tToken combinedToken = lhsToken.concatenate(operatorToken).concatenate(rhs.token());\n\t\t\t\texpressions.push(new TokenWith<>(combinedToken, binaryExpression.apply(lhs.element(), rhs.element())));\n\t\t\t\treturn success();\n\t\t\t}\n\t\t\tif (rhs.token().isLeftOf(operatorToken)) {\n\t\t\t\treturn missingRhsOperand(operatorToken, representation);\n\t\t\t}\n\t\t\tif (operatorToken.isLeftOf(lhsToken)) {\n\t\t\t\treturn missingOperatorBetween(lhs, rhs);\n\t\t\t}\n\t\t\treturn problemParsing(operatorToken, representation);\n\t\t});\n\t}\n\n\tprivate final String representation;\n\tprivate final int precedence;\n\tprivate final int arity;\n\n\tprivate final @Nullable Associativity associativity;\n\n\tprivate final TagExpressionCreator tagExpressionCreator;\n\n\tprivate Operator(String representation, int precedence, int arity, @Nullable Associativity associativity,\n\t\t\tTagExpressionCreator tagExpressionCreator) {\n\n\t\tthis.representation = representation;\n\t\tthis.precedence = precedence;\n\t\tthis.arity = arity;\n\t\tthis.associativity = associativity;\n\t\tthis.tagExpressionCreator = tagExpressionCreator;\n\t}\n\n\tboolean represents(String token) {\n\t\treturn representation.equals(token);\n\t}\n\n\tString representation() {\n\t\treturn representation;\n\t}\n\n\tboolean hasLowerPrecedenceThan(Operator operator) {\n\t\treturn this.precedence < operator.precedence;\n\t}\n\n\tboolean hasSamePrecedenceAs(Operator operator) {\n\t\treturn this.precedence == operator.precedence;\n\t}\n\n\tboolean isLeftAssociative() {\n\t\treturn Left == associativity;\n\t}\n\n\tParseStatus createAndAddExpressionTo(Stack<TokenWith<TagExpression>> expressions, Token operatorToken) {\n\t\tif (expressions.size() < arity) {\n\t\t\tString message = createMissingOperandMessage(expressions, operatorToken);\n\t\t\treturn ParseStatus.errorAt(operatorToken, representation, message);\n\t\t}\n\t\treturn tagExpressionCreator.createExpressionAndAddTo(expressions, operatorToken);\n\t}\n\n\tprivate String createMissingOperandMessage(Stack<TokenWith<TagExpression>> expressions, Token operatorToken) {\n\t\tif (1 == arity) {\n\t\t\treturn missingOneOperand(associativity == Left ? \"lhs\" : \"rhs\");\n\t\t}\n\n\t\tif (2 == arity) {\n\t\t\tint mismatch = arity - expressions.size();\n\t\t\tif (2 == mismatch) {\n\t\t\t\treturn \"missing lhs and rhs operand\";\n\t\t\t}\n\t\t\treturn missingOneOperand(\n\t\t\t\toperatorToken.isLeftOf(requireNonNull(expressions.peek()).token()) ? \"lhs\" : \"rhs\");\n\t\t}\n\t\treturn \"missing operand\";\n\t}\n\n\tprivate String missingOneOperand(String side) {\n\t\treturn \"missing \" + side + \" operand\";\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Operators.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static java.util.function.Function.identity;\nimport static java.util.stream.Collectors.toMap;\nimport static org.junit.platform.launcher.tagexpression.Operator.Associativity.Left;\nimport static org.junit.platform.launcher.tagexpression.Operator.Associativity.Right;\n\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.1\n */\nclass Operators {\n\n\tprivate static final Operator Not = Operator.unaryOperator(\"!\", 3, Right, TagExpressions::not);\n\tprivate static final Operator And = Operator.binaryOperator(\"&\", 2, Left, TagExpressions::and);\n\tprivate static final Operator Or = Operator.binaryOperator(\"|\", 1, Left, TagExpressions::or);\n\n\tprivate final Map<String, Operator> representationToOperator = Stream.of(Not, And, Or).collect(\n\t\ttoMap(Operator::representation, identity()));\n\n\t@Nullable\n\tOperator operatorFor(String token) {\n\t\treturn representationToOperator.get(token);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\n\n/**\n * The result of attempting to parse a {@link TagExpression}.\n *\n * <p>An instance of this type either contains a successfully parsed\n * {@link TagExpression} or an <em>error message</em> describing the parse\n * error.\n *\n * @since 1.1\n * @see TagExpression#parseFrom(String)\n */\n@API(status = INTERNAL, since = \"1.1\")\npublic interface ParseResult {\n\n\t/**\n\t * Return the parsed {@link TagExpression} or throw an exception with the\n\t * contained parse error.\n\t *\n\t * @param exceptionCreator will be called with the error message in case\n\t * this parse result contains a parse error; never {@code null}.\n\t */\n\tdefault TagExpression tagExpressionOrThrow(Function<String, RuntimeException> exceptionCreator) {\n\t\tif (errorMessage().isPresent()) {\n\t\t\tthrow exceptionCreator.apply(errorMessage().get());\n\t\t}\n\t\treturn tagExpression().orElseThrow();\n\t}\n\n\t/**\n\t * Return the contained parse error message, if any.\n\t */\n\tdefault Optional<String> errorMessage() {\n\t\treturn Optional.empty();\n\t}\n\n\t/**\n\t * Return the contained {@link TagExpression}, if any.\n\t */\n\tdefault Optional<TagExpression> tagExpression() {\n\t\treturn Optional.empty();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseResults.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport java.util.Optional;\n\n/**\n * @since 1.1\n */\nclass ParseResults {\n\n\tprivate ParseResults() {\n\t\t/* no-op */\n\t}\n\n\tstatic ParseResult success(TagExpression tagExpression) {\n\t\treturn new ParseResult() {\n\t\t\t@Override\n\t\t\tpublic Optional<TagExpression> tagExpression() {\n\t\t\t\treturn Optional.of(tagExpression);\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic ParseResult error(String errorMessage) {\n\t\treturn new ParseResult() {\n\t\t\t@Override\n\t\t\tpublic Optional<String> errorMessage() {\n\t\t\t\treturn Optional.of(errorMessage);\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ParseStatus.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.1\n */\nclass ParseStatus {\n\n\tstatic ParseStatus success() {\n\t\treturn error(null);\n\t}\n\n\tstatic ParseStatus problemParsing(Token token, String representation) {\n\t\treturn errorAt(token, representation, \"problem parsing\");\n\t}\n\n\tstatic ParseStatus missingOpeningParenthesis(Token token, String representation) {\n\t\treturn errorAt(token, representation, \"missing opening parenthesis\");\n\t}\n\n\tstatic ParseStatus missingClosingParenthesis(Token token, String representation) {\n\t\treturn errorAt(token, representation, \"missing closing parenthesis\");\n\t}\n\n\tstatic ParseStatus missingRhsOperand(Token token, String representation) {\n\t\treturn errorAt(token, representation, \"missing rhs operand\");\n\t}\n\n\tstatic ParseStatus errorAt(Token token, String operatorRepresentation, String message) {\n\t\treturn error(\n\t\t\tmessage + \" for '\" + operatorRepresentation + \"' at index \" + format(token.strippedTokenStartIndex()));\n\t}\n\n\tstatic ParseStatus missingOperatorBetween(TokenWith<TagExpression> lhs, TokenWith<TagExpression> rhs) {\n\t\tString lhsString = \"'\" + lhs.element() + \"' at index \" + format(lhs.token().lastCharacterIndex());\n\t\tString rhsString = \"'\" + rhs.element() + \"' at index \" + format(rhs.token().strippedTokenStartIndex());\n\t\treturn error(\"missing operator between \" + lhsString + \" and \" + rhsString);\n\t}\n\n\tstatic ParseStatus emptyTagExpression() {\n\t\treturn error(\"empty tag expression\");\n\t}\n\n\tprivate static String format(int indexInTagExpression) {\n\t\treturn \"<\" + indexInTagExpression + \">\";\n\t}\n\n\tprivate static ParseStatus error(@Nullable String errorMessage) {\n\t\treturn new ParseStatus(errorMessage);\n\t}\n\n\tfinal @Nullable String errorMessage;\n\n\tprivate ParseStatus(@Nullable String errorMessage) {\n\t\tthis.errorMessage = errorMessage;\n\t}\n\n\tpublic ParseStatus process(Supplier<ParseStatus> step) {\n\t\tif (isSuccess()) {\n\t\t\treturn step.get();\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic boolean isError() {\n\t\treturn !isSuccess();\n\t}\n\n\tprivate boolean isSuccess() {\n\t\treturn errorMessage == null;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Parser.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.1\n */\nclass Parser {\n\n\tprivate final Tokenizer tokenizer = new Tokenizer();\n\n\tParseResult parse(@Nullable String infixTagExpression) {\n\t\treturn constructExpressionFrom(tokensDerivedFrom(infixTagExpression));\n\t}\n\n\tprivate List<Token> tokensDerivedFrom(@Nullable String infixTagExpression) {\n\t\treturn tokenizer.tokenize(infixTagExpression);\n\t}\n\n\tprivate ParseResult constructExpressionFrom(List<Token> tokens) {\n\t\treturn new ShuntingYard(tokens).execute();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/ShuntingYard.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static java.lang.Integer.MIN_VALUE;\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.platform.launcher.tagexpression.Operator.nullaryOperator;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.emptyTagExpression;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.missingClosingParenthesis;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.missingOpeningParenthesis;\nimport static org.junit.platform.launcher.tagexpression.ParseStatus.success;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.any;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.none;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.tag;\n\nimport java.util.List;\n\n/**\n * This is based on a modified version of the\n * <a href=\"https://en.wikipedia.org/wiki/Shunting-yard_algorithm\">\n * Shunting-yard algorithm</a>.\n *\n * @since 1.1\n */\nclass ShuntingYard {\n\n\tprivate static final Operator RightParenthesis = nullaryOperator(\")\", -1);\n\tprivate static final Operator LeftParenthesis = nullaryOperator(\"(\", -2);\n\tprivate static final Operator Sentinel = nullaryOperator(\"sentinel\", MIN_VALUE);\n\tprivate static final Token SentinelToken = new Token(-1, \"\");\n\n\tprivate final Operators validOperators = new Operators();\n\tprivate final Stack<TokenWith<TagExpression>> expressions = new DequeStack<>();\n\tprivate final Stack<TokenWith<Operator>> operators = new DequeStack<>();\n\tprivate final List<Token> tokens;\n\n\tShuntingYard(List<Token> tokens) {\n\t\tthis.tokens = tokens;\n\t\tpushOperatorAt(SentinelToken, Sentinel);\n\t}\n\n\tpublic ParseResult execute() {\n\t\t// @formatter:off\n\t\tParseStatus parseStatus = processTokens()\n\t\t\t\t.process(this::consumeRemainingOperators)\n\t\t\t\t.process(this::ensureOnlySingleExpressionRemains);\n\t\t// @formatter:on\n\t\tif (parseStatus.isError()) {\n\t\t\treturn ParseResults.error(requireNonNull(parseStatus.errorMessage));\n\t\t}\n\t\treturn ParseResults.success(expressions.pop().element());\n\t}\n\n\tprivate ParseStatus processTokens() {\n\t\tParseStatus parseStatus = success();\n\t\tfor (Token token : tokens) {\n\t\t\tparseStatus = parseStatus.process(() -> process(token));\n\t\t}\n\t\treturn parseStatus;\n\t}\n\n\tprivate ParseStatus process(Token token) {\n\t\tString trimmed = token.string();\n\t\tif (LeftParenthesis.represents(trimmed)) {\n\t\t\tpushOperatorAt(token, LeftParenthesis);\n\t\t\treturn success();\n\t\t}\n\t\tif (RightParenthesis.represents(trimmed)) {\n\t\t\treturn findMatchingLeftParenthesis(token);\n\t\t}\n\t\tvar operator = validOperators.operatorFor(trimmed);\n\t\tif (operator != null) {\n\t\t\treturn findOperands(token, operator);\n\t\t}\n\t\tpushExpressionAt(token, convertLeafTokenToExpression(trimmed));\n\t\treturn success();\n\t}\n\n\tprivate TagExpression convertLeafTokenToExpression(String trimmed) {\n\t\tif (\"any()\".equalsIgnoreCase(trimmed)) {\n\t\t\treturn any();\n\t\t}\n\t\tif (\"none()\".equalsIgnoreCase(trimmed)) {\n\t\t\treturn none();\n\t\t}\n\t\treturn tag(trimmed);\n\t}\n\n\tprivate ParseStatus findMatchingLeftParenthesis(Token token) {\n\t\twhile (!operators.isEmpty()) {\n\t\t\tTokenWith<Operator> tokenWithWithOperator = operators.pop();\n\t\t\tOperator operator = tokenWithWithOperator.element();\n\t\t\tif (LeftParenthesis.equals(operator)) {\n\t\t\t\treturn success();\n\t\t\t}\n\t\t\tParseStatus parseStatus = operator.createAndAddExpressionTo(expressions, tokenWithWithOperator.token());\n\t\t\tif (parseStatus.isError()) {\n\t\t\t\treturn parseStatus;\n\t\t\t}\n\t\t}\n\t\treturn missingOpeningParenthesis(token, RightParenthesis.representation());\n\t}\n\n\tprivate ParseStatus findOperands(Token token, Operator currentOperator) {\n\t\twhile (currentOperator.hasLowerPrecedenceThan(previousOperator())\n\t\t\t\t|| (currentOperator.hasSamePrecedenceAs(previousOperator()) && currentOperator.isLeftAssociative())) {\n\t\t\tTokenWith<Operator> tokenWithWithOperator = operators.pop();\n\t\t\tParseStatus parseStatus = tokenWithWithOperator.element().createAndAddExpressionTo(expressions,\n\t\t\t\ttokenWithWithOperator.token());\n\t\t\tif (parseStatus.isError()) {\n\t\t\t\treturn parseStatus;\n\t\t\t}\n\t\t}\n\t\tpushOperatorAt(token, currentOperator);\n\t\treturn success();\n\t}\n\n\tprivate Operator previousOperator() {\n\t\treturn operators.peek().element();\n\t}\n\n\tprivate void pushExpressionAt(Token token, TagExpression tagExpression) {\n\t\texpressions.push(new TokenWith<>(token, tagExpression));\n\t}\n\n\tprivate void pushOperatorAt(Token token, Operator operator) {\n\t\toperators.push(new TokenWith<>(token, operator));\n\t}\n\n\tprivate ParseStatus consumeRemainingOperators() {\n\t\twhile (!operators.isEmpty()) {\n\t\t\tTokenWith<Operator> tokenWithWithOperator = operators.pop();\n\t\t\tOperator operator = tokenWithWithOperator.element();\n\t\t\tif (LeftParenthesis.equals(operator)) {\n\t\t\t\treturn missingClosingParenthesis(tokenWithWithOperator.token(), operator.representation());\n\t\t\t}\n\t\t\tParseStatus parseStatus = operator.createAndAddExpressionTo(expressions, tokenWithWithOperator.token());\n\t\t\tif (parseStatus.isError()) {\n\t\t\t\treturn parseStatus;\n\t\t\t}\n\t\t}\n\t\treturn success();\n\t}\n\n\tprivate ParseStatus ensureOnlySingleExpressionRemains() {\n\t\tif (expressions.size() == 1) {\n\t\t\treturn success();\n\t\t}\n\t\tif (expressions.isEmpty()) {\n\t\t\treturn emptyTagExpression();\n\t\t}\n\t\tTokenWith<TagExpression> rhs = expressions.pop();\n\t\tTokenWith<TagExpression> lhs = expressions.pop();\n\t\treturn ParseStatus.missingOperatorBetween(lhs, rhs);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Stack.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\n/**\n * @since 1.1\n */\ninterface Stack<T> {\n\n\tvoid push(T t);\n\n\tT peek();\n\n\tT pop();\n\n\tboolean isEmpty();\n\n\tint size();\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpression.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.Collection;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestTag;\n\n/**\n * A tag expression can be evaluated against a collection of\n * {@linkplain TestTag tags} to determine if they match the expression.\n *\n * @since 1.1\n */\n@API(status = INTERNAL, since = \"1.1\")\npublic interface TagExpression {\n\n\t/**\n\t * Attempt to parse a {@link TagExpression} from the supplied <em>tag\n\t * expression string</em>.\n\t *\n\t * @param infixTagExpression the tag expression string to parse; may be\n\t * {@code null}.\n\t * @see ParseResult\n\t */\n\t@API(status = INTERNAL, since = \"1.1\")\n\tstatic ParseResult parseFrom(@Nullable String infixTagExpression) {\n\t\treturn new Parser().parse(infixTagExpression);\n\t}\n\n\t/**\n\t * Evaluate this tag expression against the supplied collection of\n\t * {@linkplain TestTag tags}.\n\t *\n\t * @param tags the tags this tag expression is to be evaluated against\n\t * @return {@code true}, if the tags match this tag expression; {@code false}, otherwise\n\t */\n\tboolean evaluate(Collection<TestTag> tags);\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TagExpressions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport java.util.Collection;\n\nimport org.junit.platform.engine.TestTag;\n\n/**\n * @since 1.1\n */\nclass TagExpressions {\n\n\tstatic TagExpression tag(String tag) {\n\t\tTestTag testTag = TestTag.create(tag);\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn tags.contains(testTag);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn testTag.getName();\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic TagExpression none() {\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn tags.isEmpty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"none()\";\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic TagExpression any() {\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn !tags.isEmpty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"any()\";\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic TagExpression not(TagExpression toNegate) {\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn !toNegate.evaluate(tags);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"!\" + toNegate;\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic TagExpression and(TagExpression lhs, TagExpression rhs) {\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn lhs.evaluate(tags) && rhs.evaluate(tags);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"(\" + lhs + \" & \" + rhs + \")\";\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic TagExpression or(TagExpression lhs, TagExpression rhs) {\n\t\treturn new TagExpression() {\n\t\t\t@Override\n\t\t\tpublic boolean evaluate(Collection<TestTag> tags) {\n\t\t\t\treturn lhs.evaluate(tags) || rhs.evaluate(tags);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn \"(\" + lhs + \" | \" + rhs + \")\";\n\t\t\t}\n\t\t};\n\t}\n\n\tprivate TagExpressions() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Token.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\n/**\n * @since 1.1\n */\nrecord Token(int startIndex, String rawString) {\n\n\tString string() {\n\t\treturn rawString.strip();\n\t}\n\n\tpublic int strippedTokenStartIndex() {\n\t\treturn startIndex + rawString.indexOf(string());\n\t}\n\n\tpublic boolean isLeftOf(Token token) {\n\t\treturn lastCharacterIndex() < token.startIndex;\n\t}\n\n\tpublic int lastCharacterIndex() {\n\t\treturn endIndexExclusive() - 1;\n\t}\n\n\tpublic int endIndexExclusive() {\n\t\treturn startIndex + rawString.length();\n\t}\n\n\tpublic Token concatenate(Token rightOfThis) {\n\t\tString concatenatedRawString = this.rawString + rightOfThis.rawString;\n\t\treturn new Token(startIndex, concatenatedRawString);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/TokenWith.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\n/**\n * @since 1.1\n */\nrecord TokenWith<T>(Token token, T element) {\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/Tokenizer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static java.util.regex.Pattern.CASE_INSENSITIVE;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * @since 1.1\n */\nclass Tokenizer {\n\n\tprivate static final Pattern PATTERN = Pattern.compile(\"\\\\s*(?:(?:any|none)\\\\(\\\\)|[()!|&]|[^\\\\s()!|&]+)\",\n\t\tCASE_INSENSITIVE);\n\n\tList<Token> tokenize(@Nullable String infixTagExpression) {\n\t\tif (infixTagExpression == null) {\n\t\t\treturn List.of();\n\t\t}\n\t\tList<Token> parts = new ArrayList<>();\n\t\tMatcher matcher = PATTERN.matcher(infixTagExpression);\n\t\twhile (matcher.find()) {\n\t\t\tparts.add(new Token(matcher.start(), matcher.group()));\n\t\t}\n\t\treturn List.copyOf(parts);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/main/java/org/junit/platform/launcher/tagexpression/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * The tag expression language parser and related support classes.\n */\n\n@NullMarked\npackage org.junit.platform.launcher.tagexpression;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-launcher/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener",
    "content": "org.junit.platform.launcher.listeners.UniqueIdTrackingListener\n"
  },
  {
    "path": "junit-platform-launcher/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/ConfigurationParametersFactoryForTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.stream.Collectors.toMap;\nimport static org.junit.platform.commons.util.StringUtils.nullSafeToString;\n\nimport java.util.Map;\n\nimport org.junit.platform.engine.ConfigurationParameters;\n\npublic class ConfigurationParametersFactoryForTests {\n\n\tprivate ConfigurationParametersFactoryForTests() {\n\t}\n\n\tpublic static ConfigurationParameters create(Map<String, ?> configParams) {\n\t\treturn LauncherConfigurationParameters.builder() //\n\t\t\t\t.explicitParameters(withStringValues(configParams)) //\n\t\t\t\t.enableImplicitProviders(false) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static Map<String, String> withStringValues(Map<String, ?> configParams) {\n\t\treturn configParams.entrySet().stream().collect(toMap(Map.Entry::getKey, e -> nullSafeToString(e.getValue())));\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/LauncherFactoryForTestingPurposesOnly.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.launcher.Launcher;\n\n/**\n * @since 1.0\n */\npublic class LauncherFactoryForTestingPurposesOnly {\n\n\tpublic static Launcher createLauncher(TestEngine... engines) {\n\t\treturn LauncherFactory.create(createLauncherConfigBuilderWithDisabledServiceLoading() //\n\t\t\t\t.addTestEngines(engines) //\n\t\t\t\t.build());\n\t}\n\n\tpublic static LauncherConfig.Builder createLauncherConfigBuilderWithDisabledServiceLoading() {\n\t\treturn LauncherConfig.builder() //\n\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t.enableLauncherDiscoveryListenerAutoRegistration(false) //\n\t\t\t\t.enableTestExecutionListenerAutoRegistration(false) //\n\t\t\t\t.enablePostDiscoveryFilterAutoRegistration(false) //\n\t\t\t\t.enableLauncherSessionListenerAutoRegistration(false);\n\t}\n\n\tprivate LauncherFactoryForTestingPurposesOnly() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/NamespacedHierarchicalStoreProviders.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;\n\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\npublic class NamespacedHierarchicalStoreProviders {\n\n\tpublic static NamespacedHierarchicalStore<Namespace> dummyNamespacedHierarchicalStore() {\n\t\treturn new NamespacedHierarchicalStore<>(dummyNamespacedHierarchicalStoreWithNoParent(), closeAutoCloseables());\n\t}\n\n\tpublic static NamespacedHierarchicalStore<Namespace> dummyNamespacedHierarchicalStoreWithNoParent() {\n\t\treturn new NamespacedHierarchicalStore<>(null, closeAutoCloseables());\n\t}\n\n\tprivate NamespacedHierarchicalStoreProviders() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-launcher/src/testFixtures/java/org/junit/platform/launcher/core/OutputDirectoryCreators.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport java.nio.file.Path;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.OutputDirectoryCreator;\n\npublic class OutputDirectoryCreators {\n\n\tpublic static OutputDirectoryCreator dummyOutputDirectoryCreator() {\n\t\treturn new HierarchicalOutputDirectoryCreator(() -> {\n\t\t\tthrow new JUnitException(\"This should not be called; use a real implementation instead\");\n\t\t});\n\t}\n\n\tpublic static OutputDirectoryCreator hierarchicalOutputDirectoryCreator(Path rootDir) {\n\t\treturn new HierarchicalOutputDirectoryCreator(() -> rootDir);\n\t}\n\n\tprivate OutputDirectoryCreators() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/LICENSE-open-test-reporting.md",
    "content": "Apache License\n==============\n\n_Version 2.0, January 2004_\n_&lt;<https://www.apache.org/licenses/>&gt;_\n\n### Terms and Conditions for use, reproduction, and distribution\n\n#### 1. Definitions\n\n“License” shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n“Licensor” shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n“Legal Entity” shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, “control” means **(i)** the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the\noutstanding shares, or **(iii)** beneficial ownership of such entity.\n\n“You” (or “Your”) shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n“Source” form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n“Object” form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n“Work” shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n“Derivative Works” shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n“Contribution” shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n“submitted” means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as “Not a Contribution.”\n\n“Contributor” shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n#### 2. Grant of Copyright License\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n#### 3. Grant of Patent License\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n#### 4. Redistribution\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\n* **(a)** You must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\n* **(b)** You must cause any modified files to carry prominent notices stating that You\nchanged the files; and\n* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\n* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\n\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n#### 5. Submission of Contributions\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n#### 6. Trademarks\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n#### 7. Disclaimer of Warranty\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an “AS IS” BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n#### 8. Limitation of Liability\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n#### 9. Accepting Warranty or Additional Liability\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\n_END OF TERMS AND CONDITIONS_\n\n### APPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets `[]` replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same “printed page” as the copyright notice for easier identification within\nthird-party archives.\n\n    Copyright [yyyy] [name of copyright owner]\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"
  },
  {
    "path": "junit-platform-reporting/junit-platform-reporting.gradle.kts",
    "content": "import junitbuild.extensions.javaModuleName\n\nplugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"junitbuild.shadow-conventions\")\n\t`java-test-fixtures`\n}\n\ndescription = \"JUnit Platform Reporting\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformLauncher)\n\n\timplementation(libs.openTestReporting.tooling.spi)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tshadowed(libs.openTestReporting.events)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n\tosgiVerification(libs.openTestReporting.tooling.spi)\n\n\ttestFixturesApi(projects.junitJupiterApi)\n}\n\ntasks {\n\tshadowJar {\n\t\tlistOf(\"events\", \"schema\").forEach { name ->\n\t\t\tval packageName = \"org.opentest4j.reporting.${name}\"\n\t\t\trelocate(packageName, \"org.junit.platform.reporting.shadow.${packageName}\")\n\t\t}\n\t\tfrom(projectDir) {\n\t\t\tinclude(\"LICENSE-open-test-reporting.md\")\n\t\t\tinto(\"META-INF\")\n\t\t}\n\t}\n\tcompileJava {\n\t\toptions.compilerArgs.addAll(listOf(\n\t\t\t\"--add-modules\", \"org.opentest4j.reporting.events\",\n\t\t\t\"--add-reads\", \"${javaModuleName}=org.opentest4j.reporting.events\"\n\t\t))\n\t}\n\tjavadoc {\n\t\t(options as StandardJavadocDocletOptions).apply {\n\t\t\taddStringOption(\"-add-modules\", \"org.opentest4j.reporting.events\")\n\t\t\taddStringOption(\"-add-reads\", \"${javaModuleName}=org.opentest4j.reporting.events\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Defines the JUnit Platform Reporting API.\n *\n * @since 1.4\n */\nmodule org.junit.platform.reporting {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires java.xml;\n\trequires org.junit.platform.commons;\n\trequires transitive org.junit.platform.engine;\n\trequires transitive org.junit.platform.launcher;\n\trequires org.opentest4j.reporting.tooling.spi;\n\n\t// exports org.junit.platform.reporting; empty package\n\texports org.junit.platform.reporting.legacy;\n\texports org.junit.platform.reporting.legacy.xml;\n\texports org.junit.platform.reporting.open.xml;\n\n\tprovides org.junit.platform.launcher.TestExecutionListener\n\t\t\twith org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener;\n\n\tprovides org.opentest4j.reporting.tooling.spi.htmlreport.Contributor\n\t\t\twith org.junit.platform.reporting.open.xml.JUnitContributor;\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/LegacyReportingUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Utility methods for dealing with legacy reporting infrastructure, such as\n * reporting systems built on the Ant-based XML reporting format for JUnit 4.\n *\n * <p>This class was formerly from {@code junit-platform-launcher} in the\n * {@link org.junit.platform.launcher.listeners} package.\n *\n * @since 1.0.3\n */\n@API(status = MAINTAINED, since = \"1.6\")\npublic final class LegacyReportingUtils {\n\n\tprivate LegacyReportingUtils() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Get the class name for the supplied {@link TestIdentifier} using the\n\t * supplied {@link TestPlan}.\n\t *\n\t * <p>This implementation attempts to find the closest test identifier with\n\t * a {@link ClassSource} by traversing the hierarchy upwards towards the\n\t * root starting with the supplied test identifier. In case no such source\n\t * is found, it falls back to using the parent's\n\t * {@linkplain TestIdentifier#getLegacyReportingName legacy reporting name}.\n\t *\n\t * @param testPlan the test plan that contains the {@code TestIdentifier};\n\t * never {@code null}\n\t * @param testIdentifier the identifier to determine the class name for;\n\t * never {@code null}\n\t * @see TestIdentifier#getLegacyReportingName\n\t */\n\tpublic static String getClassName(TestPlan testPlan, TestIdentifier testIdentifier) {\n\t\tPreconditions.notNull(testPlan, \"testPlan must not be null\");\n\t\tPreconditions.notNull(testIdentifier, \"testIdentifier must not be null\");\n\t\tfor (TestIdentifier current = testIdentifier; current != null; current = getParent(testPlan, current)) {\n\t\t\tClassSource source = getClassSource(current);\n\t\t\tif (source != null) {\n\t\t\t\treturn source.getClassName();\n\t\t\t}\n\t\t}\n\t\treturn getParentLegacyReportingName(testPlan, testIdentifier);\n\t}\n\n\tprivate static @Nullable TestIdentifier getParent(TestPlan testPlan, TestIdentifier testIdentifier) {\n\t\treturn testPlan.getParent(testIdentifier).orElse(null);\n\t}\n\n\tprivate static @Nullable ClassSource getClassSource(TestIdentifier current) {\n\t\t// @formatter:off\n\t\treturn current.getSource()\n\t\t\t\t.filter(ClassSource.class::isInstance)\n\t\t\t\t.map(ClassSource.class::cast)\n\t\t\t\t.orElse(null);\n\t\t// @formatter:on\n\t}\n\n\tprivate static String getParentLegacyReportingName(TestPlan testPlan, TestIdentifier testIdentifier) {\n\t\t// @formatter:off\n\t\treturn testPlan.getParent(testIdentifier)\n\t\t\t\t.map(TestIdentifier::getLegacyReportingName)\n\t\t\t\t.orElse(\"<unrooted>\");\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for legacy reporting formats.\n */\n\n@NullMarked\npackage org.junit.platform.reporting.legacy;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Clock;\nimport java.time.ZoneId;\n\nimport javax.xml.stream.XMLStreamException;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * {@code LegacyXmlReportGeneratingListener} is a {@link TestExecutionListener} that\n * generates a separate XML report for each {@linkplain TestPlan#getRoots() root}\n * in the {@link TestPlan}.\n *\n * <p>Note that the generated XML format is compatible with the <em>legacy</em>\n * de facto standard for JUnit 4 based test reports that was made popular by the\n * Ant build system.\n *\n * @since 1.4\n * @see org.junit.platform.launcher.listeners.LoggingListener\n * @see org.junit.platform.launcher.listeners.SummaryGeneratingListener\n */\n@API(status = STABLE, since = \"1.7\")\npublic class LegacyXmlReportGeneratingListener implements TestExecutionListener {\n\n\tprivate final Path reportsDir;\n\tprivate final PrintWriter out;\n\tprivate final Clock clock;\n\n\tprivate @Nullable XmlReportData reportData;\n\n\tpublic LegacyXmlReportGeneratingListener(Path reportsDir, PrintWriter out) {\n\t\tthis(reportsDir, out, Clock.system(ZoneId.systemDefault()));\n\t}\n\n\t// For tests only\n\tLegacyXmlReportGeneratingListener(String reportsDir, PrintWriter out, Clock clock) {\n\t\tthis(Path.of(reportsDir), out, clock);\n\t}\n\n\tprivate LegacyXmlReportGeneratingListener(Path reportsDir, PrintWriter out, Clock clock) {\n\t\tthis.reportsDir = reportsDir;\n\t\tthis.out = out;\n\t\tthis.clock = clock;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tthis.reportData = new XmlReportData(testPlan, clock);\n\t\ttry {\n\t\t\tFiles.createDirectories(this.reportsDir);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tprintException(\"Could not create reports directory: \" + this.reportsDir, e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\tthis.reportData = null;\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\trequiredReportData().markSkipped(testIdentifier, reason);\n\t\twriteXmlReportInCaseOfRoot(testIdentifier);\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\trequiredReportData().markStarted(testIdentifier);\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\trequiredReportData().addReportEntry(testIdentifier, entry);\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult result) {\n\t\trequiredReportData().markFinished(testIdentifier, result);\n\t\twriteXmlReportInCaseOfRoot(testIdentifier);\n\t}\n\n\tprivate void writeXmlReportInCaseOfRoot(TestIdentifier testIdentifier) {\n\t\tif (isRoot(testIdentifier)) {\n\t\t\tString rootName = testIdentifier.getUniqueIdObject().getSegments().get(0).getValue();\n\t\t\twriteXmlReportSafely(testIdentifier, rootName);\n\t\t}\n\t}\n\n\tprivate void writeXmlReportSafely(TestIdentifier testIdentifier, String rootName) {\n\t\tPath xmlFile = this.reportsDir.resolve(\"TEST-\" + rootName + \".xml\");\n\t\ttry (Writer fileWriter = Files.newBufferedWriter(xmlFile)) {\n\t\t\tnew XmlReportWriter(requiredReportData()).writeXmlReport(testIdentifier, fileWriter);\n\t\t}\n\t\tcatch (XMLStreamException | IOException e) {\n\t\t\tprintException(\"Could not write XML report: \" + xmlFile, e);\n\t\t}\n\t}\n\n\tprivate XmlReportData requiredReportData() {\n\t\treturn requireNonNull(this.reportData);\n\t}\n\n\tprivate boolean isRoot(TestIdentifier testIdentifier) {\n\t\treturn testIdentifier.getParentIdObject().isEmpty();\n\t}\n\n\tprivate void printException(String message, Exception exception) {\n\t\tout.println(message);\n\t\texception.printStackTrace(out);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportData.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static java.util.Collections.emptyList;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.4\n */\nclass XmlReportData {\n\n\tprivate static final int MILLIS_PER_SECOND = 1000;\n\n\tprivate final Map<TestIdentifier, TestExecutionResult> finishedTests = new ConcurrentHashMap<>();\n\tprivate final Map<TestIdentifier, String> skippedTests = new ConcurrentHashMap<>();\n\tprivate final Map<TestIdentifier, Instant> startInstants = new ConcurrentHashMap<>();\n\tprivate final Map<TestIdentifier, Instant> endInstants = new ConcurrentHashMap<>();\n\tprivate final Map<TestIdentifier, List<ReportEntry>> reportEntries = new ConcurrentHashMap<>();\n\n\tprivate final TestPlan testPlan;\n\tprivate final Clock clock;\n\n\tXmlReportData(TestPlan testPlan, Clock clock) {\n\t\tthis.testPlan = testPlan;\n\t\tthis.clock = clock;\n\t}\n\n\tTestPlan getTestPlan() {\n\t\treturn this.testPlan;\n\t}\n\n\tClock getClock() {\n\t\treturn this.clock;\n\t}\n\n\tvoid markSkipped(TestIdentifier testIdentifier, @Nullable String reason) {\n\t\tthis.skippedTests.put(testIdentifier, reason == null ? \"\" : reason);\n\t}\n\n\tvoid markStarted(TestIdentifier testIdentifier) {\n\t\tthis.startInstants.put(testIdentifier, this.clock.instant());\n\t}\n\n\tvoid markFinished(TestIdentifier testIdentifier, TestExecutionResult result) {\n\t\tthis.endInstants.put(testIdentifier, this.clock.instant());\n\t\tif (result.getStatus() == ABORTED) {\n\t\t\tString reason = result.getThrowable().map(ExceptionUtils::readStackTrace).orElse(\"\");\n\t\t\tthis.skippedTests.put(testIdentifier, reason);\n\t\t}\n\t\telse {\n\t\t\tthis.finishedTests.put(testIdentifier, result);\n\t\t}\n\t}\n\n\tvoid addReportEntry(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tList<ReportEntry> entries = this.reportEntries.computeIfAbsent(testIdentifier, key -> new ArrayList<>());\n\t\tentries.add(entry);\n\t}\n\n\tboolean wasSkipped(TestIdentifier testIdentifier) {\n\t\treturn findSkippedAncestor(testIdentifier).isPresent();\n\t}\n\n\tdouble getDurationInSeconds(TestIdentifier testIdentifier) {\n\t\tInstant startInstant = this.startInstants.getOrDefault(testIdentifier, Instant.EPOCH);\n\t\tInstant endInstant = this.endInstants.getOrDefault(testIdentifier, startInstant);\n\t\treturn Duration.between(startInstant, endInstant).toMillis() / (double) MILLIS_PER_SECOND;\n\t}\n\n\t@Nullable\n\tString getSkipReason(TestIdentifier testIdentifier) {\n\t\treturn findSkippedAncestor(testIdentifier).map(skippedTestIdentifier -> {\n\t\t\tString reason = this.skippedTests.get(skippedTestIdentifier);\n\t\t\tif (!testIdentifier.equals(skippedTestIdentifier)) {\n\t\t\t\treason = \"parent was skipped: \" + reason;\n\t\t\t}\n\t\t\treturn reason;\n\t\t}).orElse(null);\n\t}\n\n\tList<TestExecutionResult> getResults(TestIdentifier testIdentifier) {\n\t\treturn getAncestors(testIdentifier).stream() //\n\t\t\t\t.map(this.finishedTests::get) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.toList();\n\t}\n\n\tList<ReportEntry> getReportEntries(TestIdentifier testIdentifier) {\n\t\treturn this.reportEntries.getOrDefault(testIdentifier, emptyList());\n\t}\n\n\tprivate Optional<TestIdentifier> findSkippedAncestor(TestIdentifier testIdentifier) {\n\t\treturn findAncestor(testIdentifier, this.skippedTests::containsKey);\n\t}\n\n\tprivate Optional<TestIdentifier> findAncestor(TestIdentifier testIdentifier, Predicate<TestIdentifier> predicate) {\n\t\tOptional<TestIdentifier> current = Optional.of(testIdentifier);\n\t\twhile (current.isPresent()) {\n\t\t\tif (predicate.test(current.get())) {\n\t\t\t\treturn current;\n\t\t\t}\n\t\t\tcurrent = this.testPlan.getParent(current.get());\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate List<TestIdentifier> getAncestors(TestIdentifier testIdentifier) {\n\t\tTestIdentifier current = testIdentifier;\n\t\tList<TestIdentifier> ancestors = new ArrayList<>();\n\t\twhile (current != null) {\n\t\t\tancestors.add(current);\n\t\t\tcurrent = this.testPlan.getParent(current).orElse(null);\n\t\t}\n\t\treturn ancestors;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/XmlReportWriter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static java.text.MessageFormat.format;\nimport static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;\nimport static java.util.Collections.emptyList;\nimport static java.util.Comparator.naturalOrder;\nimport static java.util.function.Function.identity;\nimport static java.util.stream.Collectors.counting;\nimport static java.util.stream.Collectors.groupingBy;\nimport static java.util.stream.Collectors.mapping;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toList;\nimport static java.util.stream.Collectors.toMap;\nimport static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.ERROR;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.FAILURE;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.SKIPPED;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type.SUCCESS;\n\nimport java.io.IOException;\nimport java.io.Writer;\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.text.NumberFormat;\nimport java.time.LocalDateTime;\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.EnumSet;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.TreeSet;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport javax.xml.stream.XMLOutputFactory;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamWriter;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.reporting.legacy.LegacyReportingUtils;\nimport org.junit.platform.reporting.legacy.xml.XmlReportWriter.AggregatedTestResult.Type;\n\n/**\n * {@code XmlReportWriter} writes an XML report whose format is compatible\n * with the de facto standard for JUnit 4 based test reports that was made\n * popular by the Ant build system.\n *\n * @since 1.4\n */\nclass XmlReportWriter {\n\n\tstatic final char ILLEGAL_CHARACTER_REPLACEMENT = '\\uFFFD';\n\n\tprivate static final Map<Character, String> REPLACEMENTS_IN_ATTRIBUTE_VALUES = Map.of( //\n\t\t'\\n', \"&#10;\", //\n\t\t'\\r', \"&#13;\", //\n\t\t'\\t', \"&#9;\" //\n\t);\n\n\t// Using zero-width assertions in the split pattern simplifies the splitting process: All split parts\n\t// (including the first and last one) can be used directly, without having to re-add separator characters.\n\tprivate static final Pattern CDATA_SPLIT_PATTERN = Pattern.compile(\"(?<=]])(?=>)\");\n\n\tprivate final XmlReportData reportData;\n\n\tXmlReportWriter(XmlReportData reportData) {\n\t\tthis.reportData = reportData;\n\t}\n\n\tvoid writeXmlReport(TestIdentifier rootDescriptor, Writer out) throws XMLStreamException {\n\t\tTestPlan testPlan = this.reportData.getTestPlan();\n\t\tMap<TestIdentifier, AggregatedTestResult> tests = testPlan.getDescendants(rootDescriptor) //\n\t\t\t\t.stream() //\n\t\t\t\t.filter(testIdentifier -> shouldInclude(testPlan, testIdentifier)) //\n\t\t\t\t.collect(toMap(identity(), this::toAggregatedResult)); //\n\t\twriteXmlReport(rootDescriptor, tests, out);\n\t}\n\n\tprivate AggregatedTestResult toAggregatedResult(TestIdentifier testIdentifier) {\n\t\tif (this.reportData.wasSkipped(testIdentifier)) {\n\t\t\treturn AggregatedTestResult.skipped();\n\t\t}\n\t\treturn AggregatedTestResult.nonSkipped(this.reportData.getResults(testIdentifier));\n\t}\n\n\tprivate boolean shouldInclude(TestPlan testPlan, TestIdentifier testIdentifier) {\n\t\treturn testIdentifier.isTest() || testPlan.getChildren(testIdentifier).isEmpty();\n\t}\n\n\tprivate void writeXmlReport(TestIdentifier testIdentifier, Map<TestIdentifier, AggregatedTestResult> tests,\n\t\t\tWriter out) throws XMLStreamException {\n\n\t\ttry (var report = new XmlReport(out)) {\n\t\t\treport.write(testIdentifier, tests);\n\t\t}\n\t}\n\n\tprivate class XmlReport implements AutoCloseable {\n\n\t\tprivate final XMLStreamWriter xml;\n\t\tprivate final ReplacingWriter out;\n\n\t\tXmlReport(Writer out) throws XMLStreamException {\n\t\t\tthis.out = new ReplacingWriter(out);\n\t\t\tXMLOutputFactory factory = XMLOutputFactory.newInstance();\n\t\t\tthis.xml = factory.createXMLStreamWriter(this.out);\n\t\t}\n\n\t\tvoid write(TestIdentifier testIdentifier, Map<TestIdentifier, AggregatedTestResult> tests)\n\t\t\t\tthrows XMLStreamException {\n\t\t\txml.writeStartDocument(\"UTF-8\", \"1.0\");\n\t\t\tnewLine();\n\t\t\twriteTestsuite(testIdentifier, tests);\n\t\t\txml.writeEndDocument();\n\t\t}\n\n\t\tprivate void writeTestsuite(TestIdentifier testIdentifier, Map<TestIdentifier, AggregatedTestResult> tests)\n\t\t\t\tthrows XMLStreamException {\n\n\t\t\t// NumberFormat is not thread-safe. Thus, we instantiate it here and pass it to\n\t\t\t// writeTestcase instead of using a constant\n\t\t\tNumberFormat numberFormat = NumberFormat.getInstance(Locale.US);\n\n\t\t\txml.writeStartElement(\"testsuite\");\n\n\t\t\twriteSuiteAttributes(testIdentifier, tests.values(), numberFormat);\n\n\t\t\tnewLine();\n\t\t\twriteSystemProperties();\n\n\t\t\tfor (Entry<TestIdentifier, AggregatedTestResult> entry : tests.entrySet()) {\n\t\t\t\twriteTestcase(entry.getKey(), entry.getValue(), numberFormat);\n\t\t\t}\n\n\t\t\twriteOutputElement(\"system-out\", formatNonStandardAttributesAsString(testIdentifier));\n\n\t\t\txml.writeEndElement();\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate void writeSuiteAttributes(TestIdentifier testIdentifier, Collection<AggregatedTestResult> testResults,\n\t\t\t\tNumberFormat numberFormat) throws XMLStreamException {\n\n\t\t\twriteAttributeSafely(\"name\", testIdentifier.getDisplayName());\n\t\t\twriteTestCounts(testResults);\n\t\t\twriteAttributeSafely(\"time\", getTime(testIdentifier, numberFormat));\n\t\t\twriteAttributeSafely(\"hostname\", getHostname().orElse(\"<unknown host>\"));\n\t\t\twriteAttributeSafely(\"timestamp\", ISO_LOCAL_DATE_TIME.format(getCurrentDateTime()));\n\t\t}\n\n\t\tprivate void writeTestCounts(Collection<AggregatedTestResult> testResults) throws XMLStreamException {\n\t\t\tMap<Type, Long> counts = testResults.stream().map(it -> it.type).collect(\n\t\t\t\tgroupingBy(identity(), counting()));\n\t\t\tlong total = counts.values().stream().mapToLong(Long::longValue).sum();\n\t\t\twriteAttributeSafely(\"tests\", String.valueOf(total));\n\t\t\twriteAttributeSafely(\"skipped\", counts.getOrDefault(SKIPPED, 0L).toString());\n\t\t\twriteAttributeSafely(\"failures\", counts.getOrDefault(FAILURE, 0L).toString());\n\t\t\twriteAttributeSafely(\"errors\", counts.getOrDefault(ERROR, 0L).toString());\n\t\t}\n\n\t\tprivate void writeSystemProperties() throws XMLStreamException {\n\t\t\txml.writeStartElement(\"properties\");\n\t\t\tnewLine();\n\t\t\tProperties systemProperties = System.getProperties();\n\t\t\tfor (String propertyName : new TreeSet<>(systemProperties.stringPropertyNames())) {\n\t\t\t\txml.writeEmptyElement(\"property\");\n\t\t\t\twriteAttributeSafely(\"name\", propertyName);\n\t\t\t\twriteAttributeSafely(\"value\", systemProperties.getProperty(propertyName));\n\t\t\t\tnewLine();\n\t\t\t}\n\t\t\txml.writeEndElement();\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate void writeTestcase(TestIdentifier testIdentifier, AggregatedTestResult testResult,\n\t\t\t\tNumberFormat numberFormat) throws XMLStreamException {\n\n\t\t\txml.writeStartElement(\"testcase\");\n\n\t\t\twriteAttributeSafely(\"name\", getName(testIdentifier));\n\t\t\twriteAttributeSafely(\"classname\", getClassName(testIdentifier));\n\t\t\twriteAttributeSafely(\"time\", getTime(testIdentifier, numberFormat));\n\t\t\tnewLine();\n\n\t\t\twriteSkippedOrErrorOrFailureElement(testIdentifier, testResult);\n\n\t\t\tList<String> systemOutElements = new ArrayList<>();\n\t\t\tList<String> systemErrElements = new ArrayList<>();\n\t\t\tsystemOutElements.add(formatNonStandardAttributesAsString(testIdentifier));\n\t\t\tcollectReportEntries(testIdentifier, systemOutElements, systemErrElements);\n\t\t\twriteOutputElements(\"system-out\", systemOutElements);\n\t\t\twriteOutputElements(\"system-err\", systemErrElements);\n\n\t\t\txml.writeEndElement();\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate String getName(TestIdentifier testIdentifier) {\n\t\t\treturn testIdentifier.getLegacyReportingName();\n\t\t}\n\n\t\tprivate String getClassName(TestIdentifier testIdentifier) {\n\t\t\treturn LegacyReportingUtils.getClassName(reportData.getTestPlan(), testIdentifier);\n\t\t}\n\n\t\tprivate void writeSkippedOrErrorOrFailureElement(TestIdentifier testIdentifier, AggregatedTestResult testResult)\n\t\t\t\tthrows XMLStreamException {\n\n\t\t\tif (testResult.type == SKIPPED) {\n\t\t\t\twriteSkippedElement(reportData.getSkipReason(testIdentifier), xml);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tMap<Type, List<Optional<Throwable>>> throwablesByType = testResult.getThrowablesByType();\n\t\t\t\tfor (Type type : EnumSet.of(FAILURE, ERROR)) {\n\t\t\t\t\tfor (Optional<Throwable> throwable : throwablesByType.getOrDefault(type, emptyList())) {\n\t\t\t\t\t\twriteErrorOrFailureElement(type, throwable.orElse(null), xml);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void writeSkippedElement(@Nullable String reason, XMLStreamWriter writer) throws XMLStreamException {\n\t\t\tif (isNotBlank(reason)) {\n\t\t\t\twriter.writeStartElement(\"skipped\");\n\t\t\t\twriteCDataSafely(reason);\n\t\t\t\twriter.writeEndElement();\n\t\t\t}\n\t\t\telse {\n\t\t\t\twriter.writeEmptyElement(\"skipped\");\n\t\t\t}\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate void writeErrorOrFailureElement(Type type, @Nullable Throwable throwable, XMLStreamWriter writer)\n\t\t\t\tthrows XMLStreamException {\n\n\t\t\tString elementName = type == FAILURE ? \"failure\" : \"error\";\n\t\t\tif (throwable != null) {\n\t\t\t\twriter.writeStartElement(elementName);\n\t\t\t\twriteFailureAttributesAndContent(throwable);\n\t\t\t\twriter.writeEndElement();\n\t\t\t}\n\t\t\telse {\n\t\t\t\twriter.writeEmptyElement(elementName);\n\t\t\t}\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate void writeFailureAttributesAndContent(Throwable throwable) throws XMLStreamException {\n\n\t\t\tif (throwable.getMessage() != null) {\n\t\t\t\twriteAttributeSafely(\"message\", throwable.getMessage());\n\t\t\t}\n\t\t\twriteAttributeSafely(\"type\", throwable.getClass().getName());\n\t\t\twriteCDataSafely(readStackTrace(throwable));\n\t\t}\n\n\t\tprivate void collectReportEntries(TestIdentifier testIdentifier, List<String> systemOutElements,\n\t\t\t\tList<String> systemErrElements) {\n\t\t\tList<ReportEntry> entries = reportData.getReportEntries(testIdentifier);\n\t\t\tif (!entries.isEmpty()) {\n\t\t\t\tList<String> systemOutElementsForCapturedOutput = new ArrayList<>();\n\t\t\t\tStringBuilder formattedReportEntries = new StringBuilder();\n\t\t\t\tfor (int i = 0; i < entries.size(); i++) {\n\t\t\t\t\tReportEntry reportEntry = entries.get(i);\n\t\t\t\t\tMap<String, String> keyValuePairs = new LinkedHashMap<>(reportEntry.getKeyValuePairs());\n\t\t\t\t\tremoveIfPresentAndAddAsSeparateElement(keyValuePairs, STDOUT_REPORT_ENTRY_KEY,\n\t\t\t\t\t\tsystemOutElementsForCapturedOutput);\n\t\t\t\t\tremoveIfPresentAndAddAsSeparateElement(keyValuePairs, STDERR_REPORT_ENTRY_KEY, systemErrElements);\n\t\t\t\t\tif (!keyValuePairs.isEmpty()) {\n\t\t\t\t\t\tbuildReportEntryDescription(reportEntry.getTimestamp(), keyValuePairs, i + 1,\n\t\t\t\t\t\t\tformattedReportEntries);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsystemOutElements.add(formattedReportEntries.toString().strip());\n\t\t\t\tsystemOutElements.addAll(systemOutElementsForCapturedOutput);\n\t\t\t}\n\t\t}\n\n\t\tprivate void removeIfPresentAndAddAsSeparateElement(Map<String, String> keyValuePairs, String key,\n\t\t\t\tList<String> elements) {\n\t\t\tString value = keyValuePairs.remove(key);\n\t\t\tif (value != null) {\n\t\t\t\telements.add(value);\n\t\t\t}\n\t\t}\n\n\t\tprivate void buildReportEntryDescription(LocalDateTime timestamp, Map<String, String> keyValuePairs,\n\t\t\t\tint entryNumber, StringBuilder result) {\n\t\t\tresult.append(\n\t\t\t\tformat(\"Report Entry #{0} (timestamp: {1})\\n\", entryNumber, ISO_LOCAL_DATE_TIME.format(timestamp)));\n\t\t\tkeyValuePairs.forEach((key, value) -> result.append(format(\"\\t- {0}: {1}\\n\", key, value)));\n\t\t}\n\n\t\tprivate String getTime(TestIdentifier testIdentifier, NumberFormat numberFormat) {\n\t\t\treturn numberFormat.format(reportData.getDurationInSeconds(testIdentifier));\n\t\t}\n\n\t\tprivate Optional<String> getHostname() {\n\t\t\ttry {\n\t\t\t\treturn Optional.ofNullable(InetAddress.getLocalHost().getHostName());\n\t\t\t}\n\t\t\tcatch (UnknownHostException e) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t}\n\n\t\tprivate LocalDateTime getCurrentDateTime() {\n\t\t\treturn LocalDateTime.now(reportData.getClock()).withNano(0);\n\t\t}\n\n\t\tprivate String formatNonStandardAttributesAsString(TestIdentifier testIdentifier) {\n\t\t\tvar allDisplayNames = getSelfAndAncestors(testIdentifier) //\n\t\t\t\t\t.map(TestIdentifier::getDisplayName) //\n\t\t\t\t\t.collect(toCollection(ArrayDeque::new));\n\t\t\tvar fullDisplayName = String.join(\" > \", (Iterable<String>) allDisplayNames::descendingIterator);\n\t\t\treturn \"unique-id: \" + testIdentifier.getUniqueId() //\n\t\t\t\t\t+ \"\\ndisplay-name: \" + fullDisplayName;\n\t\t}\n\n\t\t@SuppressWarnings({ \"DataFlowIssue\", \"ConstantValue\" })\n\t\tprivate Stream<TestIdentifier> getSelfAndAncestors(TestIdentifier testIdentifier) {\n\t\t\tvar testPlan = reportData.getTestPlan();\n\t\t\treturn Stream.iterate(testIdentifier, Objects::nonNull, it -> testPlan.getParent(it).orElse(null));\n\t\t}\n\n\t\tprivate void writeOutputElements(String elementName, List<String> elements) throws XMLStreamException {\n\t\t\tfor (String content : elements) {\n\t\t\t\twriteOutputElement(elementName, content);\n\t\t\t}\n\t\t}\n\n\t\tprivate void writeOutputElement(String elementName, String content) throws XMLStreamException {\n\t\t\txml.writeStartElement(elementName);\n\t\t\twriteCDataSafely(\"\\n\" + content + \"\\n\");\n\t\t\txml.writeEndElement();\n\t\t\tnewLine();\n\t\t}\n\n\t\tprivate void writeAttributeSafely(String name, String value) throws XMLStreamException {\n\t\t\t// Workaround for XMLStreamWriter implementations that don't escape\n\t\t\t// '\\n', '\\r', and '\\t' characters in attribute values\n\t\t\txml.flush();\n\t\t\tout.setWhitespaceReplacingEnabled(true);\n\t\t\txml.writeAttribute(name, replaceIllegalCharacters(value));\n\t\t\txml.flush();\n\t\t\tout.setWhitespaceReplacingEnabled(false);\n\t\t}\n\n\t\tprivate void writeCDataSafely(String data) throws XMLStreamException {\n\t\t\tfor (String safeDataPart : CDATA_SPLIT_PATTERN.split(replaceIllegalCharacters(data))) {\n\t\t\t\txml.writeCData(safeDataPart);\n\t\t\t}\n\t\t}\n\n\t\tprivate void newLine() throws XMLStreamException {\n\t\t\txml.writeCharacters(\"\\n\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws XMLStreamException {\n\t\t\txml.flush();\n\t\t\txml.close();\n\t\t}\n\t}\n\n\tstatic String replaceIllegalCharacters(String text) {\n\t\tif (text.codePoints().allMatch(XmlReportWriter::isAllowedXmlCharacter)) {\n\t\t\treturn text;\n\t\t}\n\t\tStringBuilder result = new StringBuilder(text.length() * 2);\n\t\ttext.codePoints().forEach(codePoint -> {\n\t\t\tif (isAllowedXmlCharacter(codePoint)) {\n\t\t\t\tresult.appendCodePoint(codePoint);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tresult.append(ILLEGAL_CHARACTER_REPLACEMENT);\n\t\t\t}\n\t\t});\n\t\treturn result.toString();\n\t}\n\n\tstatic boolean isAllowedXmlCharacter(int codePoint) {\n\t\t// source: https://www.w3.org/TR/xml/#charsets\n\t\treturn codePoint == 0x9 //\n\t\t\t\t|| codePoint == 0xA //\n\t\t\t\t|| codePoint == 0xD //\n\t\t\t\t|| (codePoint >= 0x20 && codePoint <= 0xD7FF) //\n\t\t\t\t|| (codePoint >= 0xE000 && codePoint <= 0xFFFD) //\n\t\t\t\t|| (codePoint >= 0x10000 && codePoint <= 0x10FFFF);\n\t}\n\n\tstatic class AggregatedTestResult {\n\n\t\tprivate static final AggregatedTestResult SKIPPED_RESULT = new AggregatedTestResult(SKIPPED, emptyList());\n\n\t\tpublic static AggregatedTestResult skipped() {\n\t\t\treturn SKIPPED_RESULT;\n\t\t}\n\n\t\tpublic static AggregatedTestResult nonSkipped(List<TestExecutionResult> executionResults) {\n\t\t\tType type = executionResults.stream() //\n\t\t\t\t\t.map(Type::from) //\n\t\t\t\t\t.max(naturalOrder()) //\n\t\t\t\t\t.orElse(SUCCESS);\n\t\t\treturn new AggregatedTestResult(type, executionResults);\n\t\t}\n\n\t\tprivate final Type type;\n\t\tprivate final List<TestExecutionResult> executionResults;\n\n\t\tprivate AggregatedTestResult(Type type, List<TestExecutionResult> executionResults) {\n\t\t\tthis.type = type;\n\t\t\tthis.executionResults = executionResults;\n\t\t}\n\n\t\tpublic Map<Type, List<Optional<Throwable>>> getThrowablesByType() {\n\t\t\treturn executionResults.stream() //\n\t\t\t\t\t.collect(groupingBy(Type::from, mapping(TestExecutionResult::getThrowable, toList())));\n\t\t}\n\n\t\tenum Type {\n\n\t\t\tSUCCESS, SKIPPED, FAILURE, ERROR;\n\n\t\t\tprivate static Type from(TestExecutionResult executionResult) {\n\t\t\t\tif (executionResult.getStatus() == FAILED) {\n\t\t\t\t\treturn isFailure(executionResult) ? FAILURE : ERROR;\n\t\t\t\t}\n\t\t\t\treturn SUCCESS;\n\t\t\t}\n\n\t\t\tprivate static boolean isFailure(TestExecutionResult result) {\n\t\t\t\tOptional<Throwable> throwable = result.getThrowable();\n\t\t\t\treturn throwable.isPresent() && throwable.get() instanceof AssertionError;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class ReplacingWriter extends Writer {\n\n\t\tprivate final Writer delegate;\n\t\tprivate boolean whitespaceReplacingEnabled;\n\n\t\tReplacingWriter(Writer delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}\n\n\t\tvoid setWhitespaceReplacingEnabled(boolean whitespaceReplacingEnabled) {\n\t\t\tthis.whitespaceReplacingEnabled = whitespaceReplacingEnabled;\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(char[] cbuf, int off, int len) throws IOException {\n\t\t\tif (!whitespaceReplacingEnabled) {\n\t\t\t\tdelegate.write(cbuf, off, len);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tStringBuilder stringBuilder = new StringBuilder(len * 2);\n\t\t\tfor (int i = off; i < off + len; i++) {\n\t\t\t\tchar c = cbuf[i];\n\t\t\t\tString replacement = REPLACEMENTS_IN_ATTRIBUTE_VALUES.get(c);\n\t\t\t\tif (replacement != null) {\n\t\t\t\t\tstringBuilder.append(replacement);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tstringBuilder.append(c);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdelegate.write(stringBuilder.toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(int c) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\tsuper.write(c);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelegate.write(c);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(char[] cbuf) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\tsuper.write(cbuf);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelegate.write(cbuf);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(String str) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\tsuper.write(str);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelegate.write(str);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void write(String str, int off, int len) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\tsuper.write(str, off, len);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tdelegate.write(str, off, len);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Writer append(CharSequence csq) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\treturn super.append(csq);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn delegate.append(csq);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Writer append(CharSequence csq, int start, int end) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\treturn super.append(csq, start, end);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn delegate.append(csq, start, end);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic Writer append(char c) throws IOException {\n\t\t\tif (whitespaceReplacingEnabled) {\n\t\t\t\treturn super.append(c);\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn delegate.append(c);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void flush() throws IOException {\n\t\t\tdelegate.flush();\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws IOException {\n\t\t\tdelegate.close();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/legacy/xml/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for generating XML reports using a format which is compatible with\n * the de facto standard for JUnit 4 based test reports that was made popular\n * by the Ant build system.\n */\n\n@NullMarked\npackage org.junit.platform.reporting.legacy.xml;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/GitInfoCollector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.io.UncheckedIOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Path;\nimport java.util.Optional;\nimport java.util.concurrent.TimeUnit;\nimport java.util.function.BiConsumer;\n\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.commons.util.StringUtils;\n\n/**\n * @since 1.13.2\n */\ninterface GitInfoCollector {\n\n\tstatic Optional<GitInfoCollector> get(Path workingDir) {\n\t\tProcessExecutor executor = new ProcessExecutor(workingDir);\n\t\tboolean gitInstalled = executor.exec(\"git\", \"--version\").isPresent();\n\t\treturn gitInstalled ? Optional.of(new CliGitInfoCollector(executor)) : Optional.empty();\n\t}\n\n\tOptional<String> getOriginUrl();\n\n\tOptional<String> getBranch();\n\n\tOptional<String> getCommitHash();\n\n\tOptional<String> getStatus();\n\n\tclass CliGitInfoCollector implements GitInfoCollector {\n\n\t\tprivate static final String ALLOWED_USERNAME = \"git\";\n\t\tprivate static final String USER_INFO_REPLACEMENT = \"***\";\n\t\tprivate static final String USER_INFO_SEPARATOR = \"@\";\n\n\t\tprivate final ProcessExecutor executor;\n\n\t\tCliGitInfoCollector(ProcessExecutor executor) {\n\t\t\tthis.executor = executor;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getOriginUrl() {\n\t\t\treturn executor.exec(\"git\", \"config\", \"--get\", \"remote.origin.url\") //\n\t\t\t\t\t.filter(StringUtils::isNotBlank) //\n\t\t\t\t\t.flatMap(this::toGitUrl) //\n\t\t\t\t\t.flatMap(this::obfuscateUserInfo);\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getBranch() {\n\t\t\treturn executor.exec(\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\") //\n\t\t\t\t\t.filter(StringUtils::isNotBlank);\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getCommitHash() {\n\t\t\treturn executor.exec(\"git\", \"rev-parse\", \"--verify\", \"HEAD\") //\n\t\t\t\t\t.filter(StringUtils::isNotBlank);\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> getStatus() {\n\t\t\treturn executor.exec(\"git\", \"status\", \"--porcelain\");\n\t\t}\n\n\t\tprivate Optional<String> obfuscateUserInfo(GitUrl gitUrl) {\n\t\t\ttry {\n\t\t\t\tif (gitUrl.uri.getUserInfo() != null) {\n\t\t\t\t\tURI newUri = new URI(gitUrl.uri.getScheme(), USER_INFO_REPLACEMENT, gitUrl.uri.getHost(),\n\t\t\t\t\t\tgitUrl.uri.getPort(), gitUrl.uri.getPath(), gitUrl.uri.getQuery(), gitUrl.uri.getFragment());\n\t\t\t\t\treturn Optional.of(newUri.toString().substring(gitUrl.addedPrefix.length()));\n\t\t\t\t}\n\t\t\t\tif (gitUrl.uri.getAuthority() != null && gitUrl.uri.getAuthority().contains(USER_INFO_SEPARATOR)) {\n\t\t\t\t\tString oldAuthority = gitUrl.uri.getAuthority();\n\t\t\t\t\tString[] parts = oldAuthority.split(USER_INFO_SEPARATOR, 2);\n\t\t\t\t\tif (!ALLOWED_USERNAME.equals(parts[0])) {\n\t\t\t\t\t\tString newAuthority = USER_INFO_REPLACEMENT + USER_INFO_SEPARATOR + parts[1];\n\t\t\t\t\t\tURI newUri = new URI(gitUrl.uri.getScheme(), newAuthority, gitUrl.uri.getPath(),\n\t\t\t\t\t\t\tgitUrl.uri.getQuery(), gitUrl.uri.getFragment());\n\t\t\t\t\t\treturn Optional.of(newUri.toString().substring(gitUrl.addedPrefix.length()));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn Optional.of(gitUrl.rawValue);\n\t\t\t}\n\t\t\tcatch (URISyntaxException e) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t}\n\n\t\tprivate Optional<GitUrl> toGitUrl(String remoteUrl) {\n\t\t\ttry {\n\t\t\t\treturn Optional.of(new GitUrl(remoteUrl, new URI(remoteUrl), \"\"));\n\t\t\t}\n\t\t\tcatch (URISyntaxException ex) {\n\t\t\t\ttry {\n\t\t\t\t\treturn Optional.of(new GitUrl(remoteUrl, new URI(\"ssh://\" + remoteUrl), \"ssh://\"));\n\t\t\t\t}\n\t\t\t\tcatch (URISyntaxException ignore) {\n\t\t\t\t\treturn Optional.empty();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tclass ProcessExecutor {\n\n\t\tprivate final Path workingDir;\n\n\t\tProcessExecutor(Path workingDir) {\n\t\t\tthis.workingDir = workingDir;\n\t\t}\n\n\t\tOptional<String> exec(String... args) {\n\n\t\t\tProcess process = startProcess(args);\n\n\t\t\ttry (Reader out = newBufferedReader(process.getInputStream());\n\t\t\t\t\tReader err = newBufferedReader(process.getErrorStream())) {\n\n\t\t\t\tStringBuilder output = new StringBuilder();\n\t\t\t\treadAllChars(out, (chars, numChars) -> output.append(chars, 0, numChars));\n\n\t\t\t\treadAllChars(err, (__, ___) -> {\n\t\t\t\t\t// ignore\n\t\t\t\t});\n\n\t\t\t\tboolean terminated = process.waitFor(10, TimeUnit.SECONDS);\n\t\t\t\treturn terminated && process.exitValue() == 0 ? Optional.of(trimAtEnd(output)) : Optional.empty();\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(e);\n\t\t\t}\n\t\t\tcatch (IOException ignore) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tprocess.destroyForcibly();\n\t\t\t}\n\t\t}\n\n\t\tprivate static BufferedReader newBufferedReader(InputStream stream) {\n\t\t\treturn new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset()));\n\t\t}\n\n\t\tprivate Process startProcess(String[] command) {\n\t\t\tProcess process;\n\t\t\ttry {\n\t\t\t\tprocess = new ProcessBuilder().directory(workingDir.toFile()).command(command).start();\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to start process\", e);\n\t\t\t}\n\t\t\treturn process;\n\t\t}\n\n\t\tprivate static void readAllChars(Reader reader, BiConsumer<char[], Integer> consumer) throws IOException {\n\t\t\tchar[] buffer = new char[1024];\n\t\t\tint numChars;\n\t\t\twhile ((numChars = reader.read(buffer)) != -1) {\n\t\t\t\tconsumer.accept(buffer, numChars);\n\t\t\t}\n\t\t}\n\n\t\tprivate static String trimAtEnd(StringBuilder value) {\n\t\t\tint endIndex = value.length();\n\t\t\tfor (int i = value.length() - 1; i >= 0; i--) {\n\t\t\t\tif (Character.isWhitespace(value.charAt(i))) {\n\t\t\t\t\tendIndex--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn value.substring(0, endIndex);\n\t\t}\n\t}\n\n\tclass GitUrl {\n\n\t\tprivate final String rawValue;\n\t\tprivate final URI uri;\n\t\tprivate final String addedPrefix;\n\n\t\tGitUrl(String rawValue, URI uri, String addedPrefix) {\n\t\t\tthis.rawValue = rawValue;\n\t\t\tthis.uri = uri;\n\t\t\tthis.addedPrefix = addedPrefix;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitContributor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport static java.util.Collections.emptyList;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.opentest4j.reporting.schema.Namespace;\nimport org.opentest4j.reporting.tooling.spi.htmlreport.Contributor;\nimport org.opentest4j.reporting.tooling.spi.htmlreport.KeyValuePairs;\nimport org.opentest4j.reporting.tooling.spi.htmlreport.Section;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\n\n/**\n * Contributes a section containing JUnit-specific metadata for each test node\n * to the open-test-reporting HTML report.\n *\n * @since 1.12\n */\n@SuppressWarnings(\"exports\") // we don't want to export 'org.opentest4j.reporting.tooling.spi' transitively\n@API(status = INTERNAL, since = \"1.12\")\npublic class JUnitContributor implements Contributor {\n\n\tpublic JUnitContributor() {\n\t}\n\n\t@Override\n\tpublic List<Section> contributeSectionsForTestNode(Context context) {\n\t\treturn findChild(context.element(), Namespace.REPORTING_CORE, \"metadata\") //\n\t\t\t\t.map(metadata -> {\n\t\t\t\t\tMap<String, String> table = new LinkedHashMap<>();\n\t\t\t\t\tfindChild(metadata, JUnitFactory.NAMESPACE, \"type\") //\n\t\t\t\t\t\t\t.map(Node::getTextContent) //\n\t\t\t\t\t\t\t.ifPresent(value -> table.put(\"Type\", value));\n\t\t\t\t\tfindChild(metadata, JUnitFactory.NAMESPACE, \"uniqueId\") //\n\t\t\t\t\t\t\t.map(Node::getTextContent) //\n\t\t\t\t\t\t\t.ifPresent(value -> table.put(\"Unique ID\", value));\n\t\t\t\t\tfindChild(metadata, JUnitFactory.NAMESPACE, \"legacyReportingName\") //\n\t\t\t\t\t\t\t.map(Node::getTextContent) //\n\t\t\t\t\t\t\t.ifPresent(value -> table.put(\"Legacy reporting name\", value));\n\t\t\t\t\treturn table;\n\t\t\t\t}) //\n\t\t\t\t.filter(table -> !table.isEmpty()) //\n\t\t\t\t.map(table -> List.of(Section.builder() //\n\t\t\t\t\t\t.title(\"JUnit metadata\") //\n\t\t\t\t\t\t.order(15) //\n\t\t\t\t\t\t.addBlock(KeyValuePairs.builder().content(table).build()) //\n\t\t\t\t\t\t.build())) //\n\t\t\t\t.orElse(emptyList());\n\t}\n\n\tprivate static Optional<Node> findChild(Node parent, Namespace namespace, String localName) {\n\t\tNodeList children = parent.getChildNodes();\n\t\tfor (int i = 0; i < children.getLength(); i++) {\n\t\t\tNode child = children.item(i);\n\t\t\tif (localName.equals(child.getLocalName()) && namespace.getUri().equals(child.getNamespaceURI())) {\n\t\t\t\treturn Optional.of(child);\n\t\t\t}\n\t\t}\n\t\treturn Optional.empty();\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/JUnitFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport org.junit.platform.engine.TestDescriptor;\nimport org.opentest4j.reporting.events.api.Factory;\nimport org.opentest4j.reporting.schema.Namespace;\n\nclass JUnitFactory {\n\n\tstatic Namespace NAMESPACE = Namespace.of(\"https://schemas.junit.org/open-test-reporting\");\n\n\tprivate JUnitFactory() {\n\t}\n\n\tstatic Factory<UniqueId> uniqueId(String uniqueId) {\n\t\treturn context -> new UniqueId(context, uniqueId);\n\t}\n\n\tstatic Factory<LegacyReportingName> legacyReportingName(String legacyReportingName) {\n\t\treturn context -> new LegacyReportingName(context, legacyReportingName);\n\t}\n\n\tstatic Factory<Type> type(TestDescriptor.Type type) {\n\t\treturn context -> new Type(context, type);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/LegacyReportingName.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport org.opentest4j.reporting.events.api.ChildElement;\nimport org.opentest4j.reporting.events.api.Context;\nimport org.opentest4j.reporting.events.core.Metadata;\nimport org.opentest4j.reporting.schema.QualifiedName;\n\nclass LegacyReportingName extends ChildElement<Metadata, LegacyReportingName> {\n\n\tstatic final QualifiedName ELEMENT = QualifiedName.of(JUnitFactory.NAMESPACE, \"legacyReportingName\");\n\n\tLegacyReportingName(Context context, String value) {\n\t\tsuper(context, ELEMENT);\n\t\twithContent(value);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\nimport static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;\nimport static org.junit.platform.reporting.open.xml.JUnitFactory.legacyReportingName;\nimport static org.junit.platform.reporting.open.xml.JUnitFactory.type;\nimport static org.junit.platform.reporting.open.xml.JUnitFactory.uniqueId;\nimport static org.opentest4j.reporting.events.core.CoreFactory.attachments;\nimport static org.opentest4j.reporting.events.core.CoreFactory.cpuCores;\nimport static org.opentest4j.reporting.events.core.CoreFactory.data;\nimport static org.opentest4j.reporting.events.core.CoreFactory.directorySource;\nimport static org.opentest4j.reporting.events.core.CoreFactory.file;\nimport static org.opentest4j.reporting.events.core.CoreFactory.fileSource;\nimport static org.opentest4j.reporting.events.core.CoreFactory.hostName;\nimport static org.opentest4j.reporting.events.core.CoreFactory.infrastructure;\nimport static org.opentest4j.reporting.events.core.CoreFactory.metadata;\nimport static org.opentest4j.reporting.events.core.CoreFactory.operatingSystem;\nimport static org.opentest4j.reporting.events.core.CoreFactory.output;\nimport static org.opentest4j.reporting.events.core.CoreFactory.reason;\nimport static org.opentest4j.reporting.events.core.CoreFactory.result;\nimport static org.opentest4j.reporting.events.core.CoreFactory.sources;\nimport static org.opentest4j.reporting.events.core.CoreFactory.tag;\nimport static org.opentest4j.reporting.events.core.CoreFactory.tags;\nimport static org.opentest4j.reporting.events.core.CoreFactory.uriSource;\nimport static org.opentest4j.reporting.events.core.CoreFactory.userName;\nimport static org.opentest4j.reporting.events.git.GitFactory.branch;\nimport static org.opentest4j.reporting.events.git.GitFactory.commit;\nimport static org.opentest4j.reporting.events.git.GitFactory.repository;\nimport static org.opentest4j.reporting.events.git.GitFactory.status;\nimport static org.opentest4j.reporting.events.java.JavaFactory.classSource;\nimport static org.opentest4j.reporting.events.java.JavaFactory.classpathResourceSource;\nimport static org.opentest4j.reporting.events.java.JavaFactory.fileEncoding;\nimport static org.opentest4j.reporting.events.java.JavaFactory.heapSize;\nimport static org.opentest4j.reporting.events.java.JavaFactory.javaVersion;\nimport static org.opentest4j.reporting.events.java.JavaFactory.methodSource;\nimport static org.opentest4j.reporting.events.java.JavaFactory.packageSource;\nimport static org.opentest4j.reporting.events.java.JavaFactory.throwable;\nimport static org.opentest4j.reporting.events.root.RootFactory.finished;\nimport static org.opentest4j.reporting.events.root.RootFactory.reported;\nimport static org.opentest4j.reporting.events.root.RootFactory.started;\n\nimport java.io.IOException;\nimport java.io.OutputStreamWriter;\nimport java.io.UncheckedIOException;\nimport java.io.Writer;\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport java.net.UnknownHostException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.ClasspathResourceSource;\nimport org.junit.platform.engine.support.descriptor.CompositeTestSource;\nimport org.junit.platform.engine.support.descriptor.DirectorySource;\nimport org.junit.platform.engine.support.descriptor.FileSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.descriptor.PackageSource;\nimport org.junit.platform.engine.support.descriptor.UriSource;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.opentest4j.reporting.events.api.DocumentWriter;\nimport org.opentest4j.reporting.events.api.NamespaceRegistry;\nimport org.opentest4j.reporting.events.core.Attachments;\nimport org.opentest4j.reporting.events.core.Infrastructure;\nimport org.opentest4j.reporting.events.core.Result;\nimport org.opentest4j.reporting.events.core.Sources;\nimport org.opentest4j.reporting.events.root.Events;\nimport org.opentest4j.reporting.schema.Namespace;\n\n/**\n * Open Test Reporting events XML generating test execution listener.\n *\n * @since 1.9\n */\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic class OpenTestReportGeneratingListener implements TestExecutionListener {\n\n\tstatic final String ENABLED_PROPERTY_NAME = \"junit.platform.reporting.open.xml.enabled\";\n\tstatic final String GIT_ENABLED_PROPERTY_NAME = \"junit.platform.reporting.open.xml.git.enabled\";\n\tstatic final String SOCKET_PROPERTY_NAME = \"junit.platform.reporting.open.xml.socket\";\n\n\tprivate final AtomicInteger idCounter = new AtomicInteger();\n\tprivate final Map<UniqueId, String> inProgressIds = new ConcurrentHashMap<>();\n\tprivate DocumentWriter<Events> eventsFileWriter = DocumentWriter.noop();\n\tprivate final Path workingDir;\n\n\tprivate @Nullable Path outputDir;\n\n\t@SuppressWarnings(\"unused\") // Used via ServiceLoader\n\tpublic OpenTestReportGeneratingListener() {\n\t\tthis(Path.of(\".\").toAbsolutePath());\n\t}\n\n\tOpenTestReportGeneratingListener(Path workingDir) {\n\t\tthis.workingDir = workingDir;\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tConfigurationParameters config = testPlan.getConfigurationParameters();\n\t\tif (isEnabled(config)) {\n\t\t\tNamespaceRegistry namespaceRegistry = NamespaceRegistry.builder(Namespace.REPORTING_CORE) //\n\t\t\t\t\t.add(\"e\", Namespace.REPORTING_EVENTS) //\n\t\t\t\t\t.add(\"git\", Namespace.REPORTING_GIT) //\n\t\t\t\t\t.add(\"java\", Namespace.REPORTING_JAVA) //\n\t\t\t\t\t.add(\"junit\", JUnitFactory.NAMESPACE, \"https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\") //\n\t\t\t\t\t.build();\n\t\t\toutputDir = testPlan.getOutputDirectoryCreator().getRootDirectory();\n\t\t\ttry {\n\t\t\t\teventsFileWriter = createDocumentWriter(config, namespaceRegistry);\n\t\t\t\treportInfrastructure(config);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new JUnitException(\"Failed to initialize XML events writer\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate DocumentWriter<Events> createDocumentWriter(ConfigurationParameters config,\n\t\t\tNamespaceRegistry namespaceRegistry) throws Exception {\n\t\treturn config.get(SOCKET_PROPERTY_NAME, Integer::valueOf) //\n\t\t\t\t.map(port -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tSocket socket = new Socket(InetAddress.getLoopbackAddress(), port);\n\t\t\t\t\t\tWriter writer = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8);\n\t\t\t\t\t\treturn Events.createDocumentWriter(namespaceRegistry, writer);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tthrow new JUnitException(\"Failed to connect to socket on port \" + port, e);\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.orElseGet(() -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tPath eventsXml = requireNonNull(outputDir).resolve(\"open-test-report.xml\");\n\t\t\t\t\t\treturn Events.createDocumentWriter(namespaceRegistry, eventsXml);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tthrow new JUnitException(\"Failed to create XML events file\", e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t}\n\n\tprivate boolean isEnabled(ConfigurationParameters config) {\n\t\treturn config.getBoolean(ENABLED_PROPERTY_NAME).orElse(false);\n\t}\n\n\tprivate boolean isGitEnabled(ConfigurationParameters config) {\n\t\treturn config.getBoolean(GIT_ENABLED_PROPERTY_NAME).orElse(false);\n\t}\n\n\t@SuppressWarnings(\"EmptyCatch\")\n\tprivate void reportInfrastructure(ConfigurationParameters config) {\n\t\teventsFileWriter.append(infrastructure(), infrastructure -> {\n\t\t\ttry {\n\t\t\t\tString hostName = InetAddress.getLocalHost().getHostName();\n\t\t\t\tinfrastructure.append(hostName(hostName));\n\t\t\t}\n\t\t\tcatch (UnknownHostException ignored) {\n\t\t\t}\n\t\t\tinfrastructure //\n\t\t\t\t\t.append(userName(System.getProperty(\"user.name\"))) //\n\t\t\t\t\t.append(operatingSystem(System.getProperty(\"os.name\"))) //\n\t\t\t\t\t.append(cpuCores(Runtime.getRuntime().availableProcessors())) //\n\t\t\t\t\t.append(javaVersion(System.getProperty(\"java.version\"))) //\n\t\t\t\t\t.append(fileEncoding(System.getProperty(\"file.encoding\"))) //\n\t\t\t\t\t.append(heapSize(), heapSize -> heapSize.withMax(Runtime.getRuntime().maxMemory()));\n\n\t\t\tif (isGitEnabled(config)) {\n\t\t\t\tGitInfoCollector.get(workingDir).ifPresent(git -> addGitInfo(infrastructure, git));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void addGitInfo(Infrastructure infrastructure, GitInfoCollector git) {\n\t\tgit.getOriginUrl() //\n\t\t\t\t.ifPresent(\n\t\t\t\t\tgitUrl -> infrastructure.append(repository(), repository -> repository.withOriginUrl(gitUrl)));\n\t\tgit.getBranch() //\n\t\t\t\t.ifPresent(branch -> infrastructure.append(branch(branch)));\n\t\tgit.getCommitHash() //\n\t\t\t\t.ifPresent(gitCommitHash -> infrastructure.append(commit(gitCommitHash)));\n\t\tgit.getStatus() //\n\t\t\t\t.ifPresent(statusOutput -> infrastructure.append(status(statusOutput),\n\t\t\t\t\tstatus -> status.withClean(statusOutput.isEmpty())));\n\t}\n\n\t@Override\n\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\ttry {\n\t\t\teventsFileWriter.close();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to close XML events file\", e);\n\t\t}\n\t\tfinally {\n\t\t\teventsFileWriter = DocumentWriter.noop();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\tString id = String.valueOf(idCounter.incrementAndGet());\n\t\treportStarted(testIdentifier, id);\n\t\teventsFileWriter.append(finished(id, Instant.now()), //\n\t\t\tfinished -> finished.append(result(Result.Status.SKIPPED), result -> {\n\t\t\t\tif (isNotBlank(reason)) {\n\t\t\t\t\tresult.append(reason(reason));\n\t\t\t\t}\n\t\t\t}));\n\t}\n\n\t@Override\n\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\tString id = String.valueOf(idCounter.incrementAndGet());\n\t\tinProgressIds.put(testIdentifier.getUniqueIdObject(), id);\n\t\treportStarted(testIdentifier, id);\n\t}\n\n\tprivate void reportStarted(TestIdentifier testIdentifier, String id) {\n\t\teventsFileWriter.append(started(id, Instant.now(), testIdentifier.getDisplayName()), started -> {\n\t\t\ttestIdentifier.getParentIdObject().ifPresent(parentId -> started.withParentId(inProgressIds.get(parentId)));\n\t\t\tstarted.append(metadata(), metadata -> {\n\t\t\t\tif (!testIdentifier.getTags().isEmpty()) {\n\t\t\t\t\tmetadata.append(tags(), tags -> //\n\t\t\t\t\ttestIdentifier.getTags().forEach(tag -> tags.append(tag(tag.getName()))));\n\t\t\t\t}\n\t\t\t\tmetadata.append(uniqueId(testIdentifier.getUniqueId())) //\n\t\t\t\t\t\t.append(legacyReportingName(testIdentifier.getLegacyReportingName())) //\n\t\t\t\t\t\t.append(type(testIdentifier.getType()));\n\t\t\t});\n\t\t\ttestIdentifier.getSource().ifPresent(\n\t\t\t\tsource -> started.append(sources(), sources -> addTestSource(source, sources)));\n\t\t});\n\t}\n\n\tprivate void addTestSource(TestSource source, Sources sources) {\n\t\tif (source instanceof CompositeTestSource compositeSource) {\n\t\t\tcompositeSource.getSources().forEach(it -> addTestSource(it, sources));\n\t\t}\n\t\telse if (source instanceof ClassSource classSource) {\n\t\t\tsources.append(classSource(classSource.getClassName()), //\n\t\t\t\telement -> classSource.getPosition().ifPresent(\n\t\t\t\t\tfilePosition -> element.addFilePosition(filePosition.getLine(), filePosition.getColumn())));\n\t\t}\n\t\telse if (source instanceof MethodSource methodSource) {\n\t\t\tsources.append(methodSource(methodSource.getClassName(), methodSource.getMethodName()), element -> {\n\t\t\t\tString methodParameterTypes = methodSource.getMethodParameterTypes();\n\t\t\t\tif (methodParameterTypes != null) {\n\t\t\t\t\telement.withMethodParameterTypes(methodParameterTypes);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\telse if (source instanceof ClasspathResourceSource classpathResourceSource) {\n\t\t\tsources.append(classpathResourceSource(classpathResourceSource.getClasspathResourceName()), //\n\t\t\t\telement -> classpathResourceSource.getPosition().ifPresent(\n\t\t\t\t\tfilePosition -> element.addFilePosition(filePosition.getLine(), filePosition.getColumn())));\n\t\t}\n\t\telse if (source instanceof PackageSource packageSource) {\n\t\t\tsources.append(packageSource(packageSource.getPackageName()));\n\t\t}\n\t\telse if (source instanceof FileSource fileSource) {\n\t\t\tsources.append(fileSource(fileSource.getFile()), //\n\t\t\t\telement -> fileSource.getPosition().ifPresent(\n\t\t\t\t\tfilePosition -> element.addFilePosition(filePosition.getLine(), filePosition.getColumn())));\n\t\t}\n\t\telse if (source instanceof DirectorySource directorySource) {\n\t\t\tsources.append(directorySource(directorySource.getFile()));\n\t\t}\n\t\telse if (source instanceof UriSource uriSource) {\n\t\t\tsources.append(uriSource(uriSource.getUri()));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\tString id = inProgressIds.get(testIdentifier.getUniqueIdObject());\n\t\teventsFileWriter.append(reported(id, Instant.now()), //\n\t\t\treported -> reported.append(attachments(), //\n\t\t\t\tattachments -> {\n\t\t\t\t\tMap<String, String> keyValuePairs = entry.getKeyValuePairs();\n\t\t\t\t\tif (keyValuePairs.containsKey(STDOUT_REPORT_ENTRY_KEY)\n\t\t\t\t\t\t\t|| keyValuePairs.containsKey(STDERR_REPORT_ENTRY_KEY)) {\n\t\t\t\t\t\tattachOutput(attachments, entry.getTimestamp(), keyValuePairs.get(STDOUT_REPORT_ENTRY_KEY),\n\t\t\t\t\t\t\t\"stdout\");\n\t\t\t\t\t\tattachOutput(attachments, entry.getTimestamp(), keyValuePairs.get(STDERR_REPORT_ENTRY_KEY),\n\t\t\t\t\t\t\t\"stderr\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tattachments.append(data(entry.getTimestamp()), //\n\t\t\t\t\t\t\tdata -> keyValuePairs.forEach(data::addEntry));\n\t\t\t\t\t}\n\t\t\t\t}));\n\t}\n\n\tprivate static void attachOutput(Attachments attachments, LocalDateTime timestamp, @Nullable String content,\n\t\t\tString source) {\n\t\tif (content != null) {\n\t\t\tattachments.append(output(timestamp), output -> output.withSource(source).withContent(content));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void fileEntryPublished(TestIdentifier testIdentifier, FileEntry entry) {\n\t\tString id = inProgressIds.get(testIdentifier.getUniqueIdObject());\n\t\teventsFileWriter.append(reported(id, Instant.now()), //\n\t\t\treported -> reported.append(attachments(), attachments -> attachments.append(file(entry.getTimestamp()), //\n\t\t\t\tfile -> {\n\t\t\t\t\tfile.withPath(requireNonNull(outputDir).relativize(entry.getPath()).toString());\n\t\t\t\t\tentry.getMediaType().ifPresent(file::withMediaType);\n\t\t\t\t})));\n\t}\n\n\t@Override\n\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\tString id = inProgressIds.remove(testIdentifier.getUniqueIdObject());\n\t\teventsFileWriter.append(finished(id, Instant.now()), //\n\t\t\tfinished -> finished.append(result(convertStatus(testExecutionResult.getStatus())), //\n\t\t\t\tresult -> testExecutionResult.getThrowable() //\n\t\t\t\t\t\t.ifPresent(throwable -> result.append(throwable(throwable)))));\n\t}\n\n\tprivate Result.Status convertStatus(TestExecutionResult.Status status) {\n\t\treturn switch (status) {\n\t\t\tcase FAILED -> Result.Status.FAILED;\n\t\t\tcase SUCCESSFUL -> Result.Status.SUCCESSFUL;\n\t\t\tcase ABORTED -> Result.Status.ABORTED;\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/Type.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport org.junit.platform.engine.TestDescriptor;\nimport org.opentest4j.reporting.events.api.ChildElement;\nimport org.opentest4j.reporting.events.api.Context;\nimport org.opentest4j.reporting.events.core.Metadata;\nimport org.opentest4j.reporting.schema.QualifiedName;\n\nclass Type extends ChildElement<Metadata, Type> {\n\n\tstatic final QualifiedName ELEMENT = QualifiedName.of(JUnitFactory.NAMESPACE, \"type\");\n\n\tType(Context context, TestDescriptor.Type type) {\n\t\tsuper(context, ELEMENT);\n\t\twithContent(type.toString());\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/UniqueId.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport org.opentest4j.reporting.events.api.ChildElement;\nimport org.opentest4j.reporting.events.api.Context;\nimport org.opentest4j.reporting.events.core.Metadata;\nimport org.opentest4j.reporting.schema.QualifiedName;\n\nclass UniqueId extends ChildElement<Metadata, UniqueId> {\n\n\tstatic final QualifiedName ELEMENT = QualifiedName.of(JUnitFactory.NAMESPACE, \"uniqueId\");\n\n\tUniqueId(Context context, String value) {\n\t\tsuper(context, ELEMENT);\n\t\twithContent(value);\n\t}\n}\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/open/xml/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Support for generating Open Test Reporting compatible XML event reports.\n */\n\n@NullMarked\npackage org.junit.platform.reporting.open.xml;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-reporting/src/main/java/org/junit/platform/reporting/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * JUnit Platform Reporting.\n */\n\n@NullMarked\npackage org.junit.platform.reporting;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-reporting/src/main/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener",
    "content": "org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener\n"
  },
  {
    "path": "junit-platform-reporting/src/main/resources/META-INF/services/org.opentest4j.reporting.tooling.spi.htmlreport.Contributor",
    "content": "org.junit.platform.reporting.open.xml.JUnitContributor\n"
  },
  {
    "path": "junit-platform-reporting/src/main/resources/org/junit/platform/reporting/open/xml/catalog.xml",
    "content": "<?xml version=\"1.0\"?>\n<catalog xmlns=\"urn:oasis:names:tc:entity:xmlns:xml:catalog\">\n    <system systemId=\"https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\" uri=\"junit.xsd\"/>\n</catalog>\n"
  },
  {
    "path": "junit-platform-reporting/src/main/resources/org/junit/platform/reporting/open/xml/junit.xsd",
    "content": "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           targetNamespace=\"https://schemas.junit.org/open-test-reporting\"\n           elementFormDefault=\"qualified\">\n\n    <!-- metadata -->\n    <xs:element name=\"uniqueId\">\n        <xs:complexType>\n            <xs:simpleContent>\n                <xs:extension base=\"xs:string\"/>\n            </xs:simpleContent>\n        </xs:complexType>\n    </xs:element>\n    <xs:element name=\"legacyReportingName\">\n        <xs:complexType>\n            <xs:simpleContent>\n                <xs:extension base=\"xs:string\"/>\n            </xs:simpleContent>\n        </xs:complexType>\n    </xs:element>\n    <xs:element name=\"type\">\n        <xs:simpleType>\n            <xs:restriction base=\"xs:string\">\n                <xs:enumeration value=\"TEST\"/>\n                <xs:enumeration value=\"CONTAINER\"/>\n                <xs:enumeration value=\"CONTAINER_AND_TEST\"/>\n            </xs:restriction>\n        </xs:simpleType>\n    </xs:element>\n\n</xs:schema>\n"
  },
  {
    "path": "junit-platform-reporting/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-reporting/src/testFixtures/java/org/junit/platform/reporting/testutil/FileUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.testutil;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\npublic class FileUtils {\n\n\tpublic static Path findPath(Path rootDir, String syntaxAndPattern) {\n\t\tvar matcher = rootDir.getFileSystem().getPathMatcher(syntaxAndPattern);\n\t\ttry (var files = Files.walk(rootDir)) {\n\t\t\treturn files.filter(matcher::matches).findFirst() //\n\t\t\t\t\t.orElseThrow(() -> new AssertionError(\n\t\t\t\t\t\t\"Failed to find file matching '%s' in %s\".formatted(syntaxAndPattern, rootDir)));\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t}\n\n\tprivate FileUtils() {\n\t}\n}\n"
  },
  {
    "path": "junit-platform-suite/junit-platform-suite.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-aggregator-conventions\")\n}\n\ndescription = \"JUnit Platform Suite (Aggregator)\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformSuiteApi)\n\timplementation(projects.junitPlatformSuiteEngine)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-platform-suite/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Aggregates all JUnit Platform Suite modules.\n *\n * @since 1.8\n */\nmodule org.junit.platform.suite {\n\trequires transitive org.junit.platform.suite.api;\n\trequires transitive org.junit.platform.suite.engine;\n}\n"
  },
  {
    "path": "junit-platform-suite/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-suite-api/junit-platform-suite-api.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n}\n\ndescription = \"JUnit Platform Suite API\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformCommons)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\njavadocConventions {\n\taddExtraModuleReferences(projects.junitPlatformEngine, projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Annotations for configuring a test suite on the JUnit Platform.\n *\n * @since 1.0\n */\nmodule org.junit.platform.suite.api {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.junit.platform.commons;\n\n\texports org.junit.platform.suite.api;\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/AfterSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @AfterSuite} is used to signal that the annotated method should be\n * executed <em>after</em> <strong>all</strong> tests in the current test suite.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @AfterSuite} methods must have a {@code void} return type, must\n * be {@code static} and must not be {@code private}.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @AfterSuite} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @AfterSuite} methods from superclasses will be\n * executed after {@code @AfterSuite} methods in subclasses.\n *\n * <p>The JUnit Platform Suite Engine does not guarantee the execution order of\n * multiple {@code @AfterSuite} methods that are declared within a single test\n * class or test interface. While it may at times appear that these methods are\n * invoked in alphabetical order, they are in fact sorted using an algorithm\n * that is deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @AfterSuite} methods are in no way linked to\n * {@code @BeforeSuite} methods. Consequently, there are no guarantees with regard\n * to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeSuite} methods {@code createA()} and {@code createB()} as well as\n * two {@code @AfterSuite} methods {@code destroyA()} and {@code destroyB()}, the\n * order in which the {@code @BeforeSuite} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterSuite} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeSuite} method and at most one\n * {@code @AfterSuite} method per test class or test interface unless there are no\n * dependencies between the {@code @BeforeSuite} methods or between the\n * {@code @AfterSuite} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @AfterSuite} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @AfterSuite}.\n *\n * @since 1.11\n * @see BeforeSuite\n * @see Suite\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic @interface AfterSuite {\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/BeforeSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @BeforeSuite} is used to signal that the annotated method should be\n * executed <em>before</em> <strong>all</strong> tests in the current test suite.\n *\n * <h2>Method Signatures</h2>\n *\n * <p>{@code @BeforeSuite} methods must have a {@code void} return type, must\n * be {@code static} and must not be {@code private}.\n *\n * <h2>Inheritance and Execution Order</h2>\n *\n * <p>{@code @BeforeSuite} methods are inherited from superclasses as long as they\n * are not <em>overridden</em> according to the visibility rules of the Java\n * language. Furthermore, {@code @BeforeSuite} methods from superclasses will be\n * executed before {@code @BeforeSuite} methods in subclasses.\n *\n * <p>The JUnit Platform Suite Engine does not guarantee the execution order of\n * multiple {@code @BeforeSuite} methods that are declared within a single test\n * class or test interface. While it may at times appear that these methods are\n * invoked in alphabetical order, they are in fact sorted using an algorithm\n * that is deterministic but intentionally non-obvious.\n *\n * <p>In addition, {@code @BeforeSuite} methods are in no way linked to\n * {@code @AfterSuite} methods. Consequently, there are no guarantees with regard\n * to their <em>wrapping</em> behavior. For example, given two\n * {@code @BeforeSuite} methods {@code createA()} and {@code createB()} as well as\n * two {@code @AfterSuite} methods {@code destroyA()} and {@code destroyB()}, the\n * order in which the {@code @BeforeSuite} methods are executed (e.g.\n * {@code createA()} before {@code createB()}) does not imply any order for the\n * seemingly corresponding {@code @AfterSuite} methods. In other words,\n * {@code destroyA()} might be called before <em>or</em> after\n * {@code destroyB()}. The JUnit Team therefore recommends that developers\n * declare at most one {@code @BeforeSuite} method and at most one\n * {@code @AfterSuite} method per test class or test interface unless there are no\n * dependencies between the {@code @BeforeSuite} methods or between the\n * {@code @AfterSuite} methods.\n *\n * <h2>Composition</h2>\n *\n * <p>{@code @BeforeSuite} may be used as a meta-annotation in order to create\n * a custom <em>composed annotation</em> that inherits the semantics of\n * {@code @BeforeSuite}.\n *\n * @since 1.11\n * @see AfterSuite\n * @see Suite\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic @interface BeforeSuite {\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ConfigurationParameter} is a {@linkplain Repeatable repeatable}\n * annotation that specifies a configuration {@link #key key} and\n * {@link #value value} pair to be added to the discovery request when running\n * a test suite on the JUnit Platform.\n *\n * @since 1.8\n * @see ConfigurationParametersResource\n * @see DisableParentConfigurationParameters\n * @see Suite\n * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder#configurationParameter(String, String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\n@Repeatable(ConfigurationParameters.class)\npublic @interface ConfigurationParameter {\n\n\t/**\n\t * The configuration parameter key under which to add the {@link #value() value}\n\t * to the discovery request; never {@code null} or blank.\n\t */\n\tString key();\n\n\t/**\n\t * The value to add to the discovery request for the specified {@link #key() key}.\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParameters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ConfigurationParameters} is a container for one or more\n * {@link ConfigurationParameter @ConfigurationParameter} declarations.\n *\n * <p>Note, however, that use of the {@code @ConfigurationParameters} container\n * is completely optional since {@code @ConfigurationParameter} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.8\n * @see ConfigurationParameter\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface ConfigurationParameters {\n\n\t/**\n\t * An array of one or more {@link ConfigurationParameter @ConfigurationParameter}\n\t * declarations.\n\t */\n\tConfigurationParameter[] value();\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ConfigurationParametersResource} is a\n * {@linkplain Repeatable repeatable} annotation that specifies a configuration\n * file in Java's properties format on the classpath to be added to the\n * discovery request when running a test suite on the JUnit Platform.\n *\n * @since 1.11\n * @see ConfigurationParameter\n * @see DisableParentConfigurationParameters\n * @see Suite\n * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder#configurationParametersResources(String...)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\n@Repeatable(ConfigurationParametersResources.class)\npublic @interface ConfigurationParametersResource {\n\n\t/**\n\t * The classpath location for the desired properties file; never {@code null} or blank.\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ConfigurationParametersResources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ConfigurationParametersResources} is a container for one or more\n * {@link ConfigurationParametersResource @ConfigurationParametersResource} declarations.\n *\n * <p>Note, however, that use of the {@code @ConfigurationParametersResources} container\n * is completely optional since {@code @ConfigurationParametersResource} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.11\n * @see ConfigurationParametersResource\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic @interface ConfigurationParametersResources {\n\n\t/**\n\t * An array of one or more {@link ConfigurationParametersResource @ConfigurationParameterResource}\n\t * declarations.\n\t */\n\tConfigurationParametersResource[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/DisableParentConfigurationParameters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * Disable parent configuration parameters.\n *\n * <p>By default, a suite discovers tests using the configuration parameters\n * explicitly configured via {@link ConfigurationParameter @ConfigurationParameter}\n * and {@link ConfigurationParametersResource} as well as the configuration\n * parameters from the discovery request that was used to discover the suite.\n *\n * <p>Annotating a suite with this annotation disables the latter source so\n * that only explicit configuration parameters and resources are taken into\n * account.\n *\n * @since 1.8\n * @see ConfigurationParameter\n * @see ConfigurationParametersResource\n * @see Suite\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface DisableParentConfigurationParameters {\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeClassNamePatterns.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ExcludeClassNamePatterns} specifies regular expressions that are used\n * to match against fully qualified class names when running a test suite on the\n * JUnit Platform.\n *\n * <p>The patterns are combined using OR semantics: if the fully qualified name\n * of a class matches against at least one of the patterns, the class will be\n * excluded from the test plan.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.ClassNameFilter#excludeClassNamePatterns\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface ExcludeClassNamePatterns {\n\n\t/**\n\t * Regular expressions used to match against fully qualified class names.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeEngines.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ExcludeEngines} specifies the {@linkplain #value IDs} of\n * {@link org.junit.platform.engine.TestEngine TestEngines} to be excluded\n * when running a test suite on the JUnit Platform.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.launcher.EngineFilter#excludeEngines\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface ExcludeEngines {\n\n\t/**\n\t * One or more TestEngine IDs to be excluded from the test plan.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludePackages.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ExcludePackages} specifies the {@linkplain #value packages} to be\n * excluded when running a test suite on the JUnit Platform.\n\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.PackageNameFilter#excludePackageNames(String...)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface ExcludePackages {\n\n\t/**\n\t * One or more packages to exclude.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/ExcludeTags.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @ExcludeTags} specifies the\n * {@linkplain #value tags or tag expressions} to be excluded when running a\n * test suite on the JUnit Platform.\n *\n * <h2>Tag Expressions</h2>\n *\n * <p>Tag expressions are boolean expressions with the following allowed\n * operators: {@code !} (not), {@code &} (and) and {@code |} (or). Parentheses\n * can be used to adjust for operator precedence. Please refer to the\n * <a href=\"https://docs.junit.org/current/running-tests/tags.html#expressions\">JUnit User Guide</a>\n * for usage examples.\n *\n * <h2>Syntax Rules for Tags</h2>\n * <ul>\n * <li>A tag must not be blank.</li>\n * <li>A stripped tag must not contain whitespace.</li>\n * <li>A stripped tag must not contain ISO control characters.</li>\n * <li>A stripped tag must not contain <em>reserved characters</em>.</li>\n * </ul>\n *\n * <p><em>Reserved characters</em> that are not permissible as part of a tag name.\n *\n * <ul>\n * <li>{@code \",\"}</li>\n * <li>{@code \"(\"}</li>\n * <li>{@code \")\"}</li>\n * <li>{@code \"&\"}</li>\n * <li>{@code \"|\"}</li>\n * <li>{@code \"!\"}</li>\n * </ul>\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.launcher.TagFilter#excludeTags\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface ExcludeTags {\n\n\t/**\n\t * One or more tags to exclude.\n\t *\n\t * <p>Note: each tag will be {@linkplain String#strip() stripped} and\n\t * validated according to the <em>Syntax Rules for Tags</em> (see\n\t * {@linkplain ExcludeTags class-level Javadoc} for details).\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeClassNamePatterns.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @IncludeClassNamePatterns} specifies regular expressions that are used\n * to match against fully qualified class names when running a test suite on the\n * JUnit Platform.\n *\n * <p>The patterns are combined using OR semantics: if the fully qualified name\n * of a class matches against at least one of the patterns, the class will be\n * included in the test plan.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN\n * @see org.junit.platform.engine.discovery.ClassNameFilter#includeClassNamePatterns\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface IncludeClassNamePatterns {\n\n\t/**\n\t * Regular expressions used to match against fully qualified class names.\n\t *\n\t * <p>The default pattern matches against classes whose names either begin\n\t * with {@code Test} or end with {@code Test} or {@code Tests} (in any package).\n\t */\n\t// Implementation notes:\n\t// - Test.* :: \"Test\" prefix for classes in default package\n\t// - .+[.$]Test.* :: \"Test\" prefix for top-level and nested classes in a named package\n\t// - .*Tests? :: \"Test\" and \"Tests\" suffixes in any package\n\tString[] value() default \"^(Test.*|.+[.$]Test.*|.*Tests?)$\";\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeEngines.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @IncludeEngines} specifies the {@linkplain #value IDs} of\n * {@link org.junit.platform.engine.TestEngine TestEngines} to be included\n * when running a test suite on the JUnit Platform.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.launcher.EngineFilter#includeEngines\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface IncludeEngines {\n\n\t/**\n\t * One or more TestEngine IDs to be included in the test plan.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludePackages.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @IncludePackages} specifies the {@linkplain #value packages} to be\n * included when running a test suite on the JUnit Platform.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.PackageNameFilter#includePackageNames(String...)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface IncludePackages {\n\n\t/**\n\t * One or more packages to include.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/IncludeTags.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @IncludeTags} specifies the\n * {@linkplain #value tags or tag expressions} to be included when running a\n * test suite on the JUnit Platform.\n *\n * <h2>Tag Expressions</h2>\n *\n * <p>Tag expressions are boolean expressions with the following allowed\n * operators: {@code !} (not), {@code &} (and) and {@code |} (or). Parentheses\n * can be used to adjust for operator precedence. Please refer to the\n * <a href=\"https://docs.junit.org/current/running-tests/tags.html#expressions\">JUnit User Guide</a>\n * for usage examples.\n *\n * <h2>Syntax Rules for Tags</h2>\n * <ul>\n * <li>A tag must not be blank.</li>\n * <li>A stripped tag must not contain whitespace.</li>\n * <li>A stripped tag must not contain ISO control characters.</li>\n * <li>A stripped tag must not contain <em>reserved characters</em>.</li>\n * </ul>\n *\n * <p><em>Reserved characters</em> that are not permissible as part of a tag name.\n *\n * <ul>\n * <li>{@code \",\"}</li>\n * <li>{@code \"(\"}</li>\n * <li>{@code \")\"}</li>\n * <li>{@code \"&\"}</li>\n * <li>{@code \"|\"}</li>\n * <li>{@code \"!\"}</li>\n * </ul>\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.launcher.TagFilter#includeTags\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface IncludeTags {\n\n\t/**\n\t * One or more tags to include.\n\t *\n\t * <p>Note: each tag will be {@linkplain String#strip() stripped} and\n\t * validated according to the <em>Syntax Rules for Tags</em> (see\n\t * {@linkplain IncludeTags class-level Javadoc} for details).\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Select.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Select} is a {@linkplain Repeatable repeatable} annotation that\n * specifies which tests to <em>select</em> based on prefixed\n * {@linkplain org.junit.platform.engine.DiscoverySelectorIdentifier selector identifiers}.\n *\n * @since 1.11\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#parse(String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\n@Repeatable(Selects.class)\npublic @interface Select {\n\n\t/**\n\t * One or more prefixed\n\t * {@linkplain org.junit.platform.engine.DiscoverySelectorIdentifier selector identifiers}\n\t * to select.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasses.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectClasses} specifies the classes to <em>select</em> when running\n * a test suite on the JUnit Platform.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectClass(Class)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectClass(String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface SelectClasses {\n\n\t/**\n\t * One or more classes to select.\n\t *\n\t * <p>May be use in conjunction with or instead of {@link #names() names}.\n\t */\n\tClass<?>[] value() default {};\n\n\t/**\n\t * One or more classes to select by their fully qualified names.\n\t *\n\t * <p>May be use in conjunction with or instead of {@link #value() value}.\n\t *\n\t * <p>This attribute is intended to be used when a class cannot be referenced\n\t * directly from where this annotation is used &mdash; for example, when a\n\t * class is not visible due to being private or package-private.\n\t *\n\t * @since 1.10\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tString[] names() default {};\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectClasspathResource} is a {@linkplain Repeatable repeatable}\n * annotation that specifies a classpath resource to <em>select</em> when running\n * a test suite on the JUnit Platform.\n *\n * @since 1.8\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectClasspathResource(String, org.junit.platform.engine.discovery.FilePosition)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\n@Repeatable(SelectClasspathResources.class)\npublic @interface SelectClasspathResource {\n\n\t/**\n\t * The name of the classpath resource to select.\n\t */\n\tString value();\n\n\t/**\n\t * The line number within the classpath resource; ignored if not greater than\n\t * zero.\n\t */\n\tint line() default 0;\n\n\t/**\n\t * The column number within the classpath resource; ignored if the line number\n\t * is ignored or if not greater than zero.\n\t */\n\tint column() default 0;\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectClasspathResources.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectClasspathResources} is a container for one or more\n * {@link SelectClasspathResource @SelectClasspathResource} declarations.\n *\n * <p>Note, however, that use of the {@code @SelectClasspathResources} container is\n * completely optional since {@code @SelectClasspathResource} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.8\n * @see SelectClasspathResource\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface SelectClasspathResources {\n\n\t/**\n\t * An array of one or more {@link SelectClasspathResource @SelectClasspathResource}\n\t * declarations.\n\t */\n\tSelectClasspathResource[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectDirectories.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectDirectories} specifies the directories to <em>select</em> when\n * running a test suite on the JUnit Platform.\n *\n * @since 1.8\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectDirectory(String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface SelectDirectories {\n\n\t/**\n\t * One or more directories to select.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFile.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectFile} is a {@linkplain Repeatable repeatable} annotation that\n * specifies a file to <em>select</em> when running a test suite on the JUnit\n * Platform.\n *\n * @since 1.8\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectFile(String, org.junit.platform.engine.discovery.FilePosition)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\n@Repeatable(SelectFiles.class)\npublic @interface SelectFile {\n\n\t/**\n\t * The file to select.\n\t */\n\tString value();\n\n\t/**\n\t * The line number within the file; ignored if not greater than zero.\n\t */\n\tint line() default 0;\n\n\t/**\n\t * The column number within the file; ignored if the line number is ignored\n\t * or if not greater than zero.\n\t */\n\tint column() default 0;\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectFiles.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectFiles} is a container for one or more\n * {@link SelectFile @SelectFile} declarations.\n *\n * <p>Note, however, that use of the {@code @SelectFiles} container is\n * completely optional since {@code @SelectFile} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.8\n * @see SelectFile\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface SelectFiles {\n\n\t/**\n\t * An array of one or more {@link SelectFile @SelectFile} declarations.\n\t */\n\tSelectFile[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectMethod} is a {@linkplain Repeatable repeatable} annotation that\n * specifies a method to <em>select</em> when running a test suite on the JUnit\n * Platform.\n *\n * @since 1.10\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(String)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(String, String, String)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(String, String, Class...)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(Class, String, String)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(Class, String, Class...)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\n@Repeatable(SelectMethods.class)\npublic @interface SelectMethod {\n\n\t/**\n\t * The <em>fully qualified method name</em> of the method to select.\n\t *\n\t * <p>The following formats are supported.\n\t *\n\t * <ul>\n\t * <li>{@code [fully qualified class name]#[methodName]}</li>\n\t * <li>{@code [fully qualified class name]#[methodName](parameter type list)}\n\t * </ul>\n\t *\n\t * <p>The <em>parameter type list</em> is a comma-separated list of primitive\n\t * names or fully qualified class names for the types of parameters accepted\n\t * by the method.\n\t *\n\t * <p>Array parameter types may be specified using either the JVM's internal\n\t * String representation (e.g., {@code [[I} for {@code int[][]},\n\t * {@code [Ljava.lang.String;} for {@code java.lang.String[]}, etc.) or\n\t * <em>source code syntax</em> (e.g., {@code int[][]}, {@code java.lang.String[]},\n\t * etc.).\n\t *\n\t * <table class=\"plain\">\n\t * <caption>Examples</caption>\n\t * <tr><th>Method</th><th>Fully Qualified Method Name</th></tr>\n\t * <tr><td>{@code java.lang.String.chars()}</td><td>{@code java.lang.String#chars}</td></tr>\n\t * <tr><td>{@code java.lang.String.chars()}</td><td>{@code java.lang.String#chars()}</td></tr>\n\t * <tr><td>{@code java.lang.String.equalsIgnoreCase(String)}</td><td>{@code java.lang.String#equalsIgnoreCase(java.lang.String)}</td></tr>\n\t * <tr><td>{@code java.lang.String.substring(int, int)}</td><td>{@code java.lang.String#substring(int, int)}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code example.Calc#avg([I)}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code example.Calc#avg(int[])}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code example.Matrix#multiply([[D)}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code example.Matrix#multiply(double[][])}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code example.Service#process([Ljava.lang.String;)}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code example.Service#process(java.lang.String[])}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code example.Service#process([[Ljava.lang.String;)}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code example.Service#process(java.lang.String[][])}</td></tr>\n\t * </table>\n\t *\n\t * <p>Cannot be combined with any other attribute.\n\t *\n\t * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectMethod(String)\n\t */\n\tString value() default \"\";\n\n\t/**\n\t * The class in which the method is declared, or a subclass thereof.\n\t *\n\t * <p>Cannot be used in conjunction with {@link #typeName()}.\n\t */\n\tClass<?> type() default Class.class;\n\n\t/**\n\t * The <em>fully qualified class name</em> in which the method is declared, or a subclass thereof.\n\t *\n\t * <p>Cannot be used in conjunction with {@link #type()}.\n\t */\n\tString typeName() default \"\";\n\n\t/**\n\t * The name of the method to select; never blank unless {@link #value()} is used.\n\t */\n\tString name() default \"\";\n\n\t/**\n\t * The parameter types of the method to select.\n\t *\n\t * <p>Cannot be used in conjunction with {@link #parameterTypeNames()}.\n\t */\n\tClass<?>[] parameterTypes() default {};\n\n\t/**\n\t * The parameter types of the method to select.\n\t *\n\t * <p>This is typically a comma-separated list of atomic types, fully\n\t * qualified class names, or array types; however, the exact syntax\n\t * depends on the underlying test engine.\n\t *\n\t * <p>If the method takes no parameters, this attribute must be an\n\t * empty string.\n\t *\n\t * <p>Array parameter types may be specified using either the JVM's internal\n\t * String representation (e.g., {@code [[I} for {@code int[][]},\n\t * {@code [Ljava.lang.String;} for {@code java.lang.String[]}, etc.) or\n\t * <em>source code syntax</em> (e.g., {@code int[][]}, {@code java.lang.String[]},\n\t * etc.).\n\t *\n\t * <table class=\"plain\">\n\t * <caption>Examples</caption>\n\t * <tr><th>Method</th><th>Parameter types list</th></tr>\n\t * <tr><td>{@code java.lang.String.chars()}</td><td>The empty string</td></tr>\n\t * <tr><td>{@code java.lang.String.equalsIgnoreCase(String)}</td><td>{@code java.lang.String}</td></tr>\n\t * <tr><td>{@code java.lang.String.substring(int, int)}</td><td>{@code int, int}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code [I}</td></tr>\n\t * <tr><td>{@code example.Calc.avg(int[])}</td><td>{@code int[]}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code [[D}</td></tr>\n\t * <tr><td>{@code example.Matrix.multiply(double[][])}</td><td>{@code double[][]}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code [Ljava.lang.String;}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[])}</td><td>{@code java.lang.String[]}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code [[Ljava.lang.String;}</td></tr>\n\t * <tr><td>{@code example.Service.process(String[][])}</td><td>{@code java.lang.String[][]}</td></tr>\n\t * </table>\n\t *\n\t * <p>Cannot be used in conjunction with {@link #parameterTypes()}.\n\t */\n\tString parameterTypeNames() default \"\";\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectMethods} is a container for one or more\n * {@link SelectMethod @SelectMethod} declarations.\n *\n * <p>Note, however, that use of the {@code @SelectMethods} container is\n * completely optional since {@code @SelectMethod} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.10\n * @see SelectMethod\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic @interface SelectMethods {\n\n\t/**\n\t * An array of one or more {@link SelectMethod @SelectMethod} declarations.\n\t */\n\tSelectMethod[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectModules.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectModules} specifies the modules to <em>select</em> when running\n * a test suite on the JUnit Platform.\n *\n * @since 1.8\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectModules(Set)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface SelectModules {\n\n\t/**\n\t * One or more modules to select.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectPackages.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectPackages} specifies the names of packages to <em>select</em>\n * when running a test suite on the JUnit Platform.\n *\n * @since 1.0\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectPackage(String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.0\")\npublic @interface SelectPackages {\n\n\t/**\n\t * One or more fully qualified package names to select.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SelectUris.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SelectUris} specifies the URIs to <em>select</em> when running a test\n * suite on the JUnit Platform.\n *\n * @since 1.8\n * @see Suite\n * @see org.junit.platform.engine.discovery.DiscoverySelectors#selectUri(String)\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\npublic @interface SelectUris {\n\n\t/**\n\t * One or more URIs to select.\n\t */\n\tString[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Selects.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @Selects} is a container for one or more\n * {@link Select @Select} declarations.\n *\n * <p>Note, however, that use of the {@code @Selects} container is\n * completely optional since {@code @Select} is a\n * {@linkplain java.lang.annotation.Repeatable repeatable} annotation.\n *\n * @since 1.11\n * @see Select\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = MAINTAINED, since = \"1.13.3\")\npublic @interface Selects {\n\n\t/**\n\t * An array of one or more {@link Select @Select} declarations.\n\t */\n\tSelect[] value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/Suite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static org.apiguardian.api.API.Status.STABLE;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.annotation.Testable;\n\n/**\n * {@code @Suite} marks a class as a test suite on the JUnit Platform.\n *\n * <p>Selector and filter annotations are used to control the contents of the\n * suite. Additionally, configuration can be passed to the suite via the\n * configuration annotations.\n *\n * <p>When the {@link IncludeClassNamePatterns @IncludeClassNamePatterns}\n * annotation is not present, the default include pattern\n * {@value org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN}\n * will be used in order to avoid loading classes unnecessarily (see {@link\n * org.junit.platform.engine.discovery.ClassNameFilter#STANDARD_INCLUDE_PATTERN\n * ClassNameFilter#STANDARD_INCLUDE_PATTERN}).\n *\n * <p>By default a suite discovers tests using the configuration parameters\n * explicitly configured by {@link ConfigurationParameter @ConfigurationParameter}\n * and the configuration parameters from the discovery request that discovered\n * the suite. Annotating a suite with\n * {@link DisableParentConfigurationParameters @DisableParentConfigurationParameters}\n * disables the latter as a source of parameters so that only explicit configuration\n * parameters are taken into account.\n *\n * <p>Note: Depending on the declared selectors, different suites may contain the\n * same tests, potentially with different configurations. Moreover, tests in a suite\n * are executed in addition to the tests executed by every other test engine, which\n * can result in the same tests being executed twice. To prevent that, configure\n * your build tool to include only the {@code junit-platform-suite} engine, or use\n * a custom naming pattern. For example, name all suites {@code *Suite} and all\n * tests {@code *Test}, and configure your build tool to include only the former.\n * Alternatively, consider using tags to select specific groups of tests.\n *\n * @since 1.8\n * @see Select\n * @see SelectClasses\n * @see SelectClasspathResource\n * @see SelectDirectories\n * @see SelectFile\n * @see SelectModules\n * @see SelectPackages\n * @see SelectUris\n * @see IncludeClassNamePatterns\n * @see ExcludeClassNamePatterns\n * @see IncludeEngines\n * @see ExcludeEngines\n * @see IncludePackages\n * @see ExcludePackages\n * @see IncludeTags\n * @see ExcludeTags\n * @see SuiteDisplayName\n * @see ConfigurationParameter\n * @see ConfigurationParametersResource\n * @see DisableParentConfigurationParameters\n * @see org.junit.platform.launcher.LauncherDiscoveryRequest\n * @see org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder\n * @see org.junit.platform.launcher.Launcher\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Inherited\n@Documented\n@API(status = STABLE, since = \"1.10\")\n@Testable\npublic @interface Suite {\n\n\t/**\n\t * Fail suite if no tests were discovered.\n\t *\n\t * @since 1.9\n\t */\n\t@API(status = STABLE, since = \"1.11\")\n\tboolean failIfNoTests() default true;\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/SuiteDisplayName.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.api;\n\nimport static java.lang.annotation.ElementType.TYPE;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport org.apiguardian.api.API;\n\n/**\n * {@code @SuiteDisplayName} is used to declare a {@linkplain #value custom\n * display name} for the annotated test class that is executed as a test suite\n * on the JUnit Platform.\n *\n * <p>Display names are typically used for test reporting in IDEs and build\n * tools and may contain spaces, special characters, and even emoji.\n *\n * <h2>Execution</h2>\n * <p>Test suites can be run on the JUnit Platform the\n * {@code junit-platform-suite-engine} module.\n *\n * @since 1.1\n * @see Suite\n */\n@Retention(RUNTIME)\n@Target(TYPE)\n@Documented\n@API(status = MAINTAINED, since = \"1.1\")\npublic @interface SuiteDisplayName {\n\n\t/**\n\t * Custom display name for the annotated class.\n\t *\n\t * @return a custom display name; never blank or consisting solely of\n\t * whitespace\n\t */\n\tString value();\n\n}\n"
  },
  {
    "path": "junit-platform-suite-api/src/main/java/org/junit/platform/suite/api/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Annotations for configuring a <em>test suite</em> on the JUnit Platform.\n *\n * <h2>Execution</h2>\n * <p>Test suites can be run on the JUnit Platform the\n * {@code junit-platform-suite-engine} module.\n */\n\n@NullMarked\npackage org.junit.platform.suite.api;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-suite-api/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-suite-engine/junit-platform-suite-engine.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n}\n\ndescription = \"JUnit Platform Suite Engine\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformEngine)\n\tapi(projects.junitPlatformSuiteApi)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\timplementation(projects.junitPlatformLauncher)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Provides a {@link org.junit.platform.engine.TestEngine} for running\n * declarative test suites.\n *\n * @since 1.8\n * @provides org.junit.platform.engine.TestEngine\n */\nmodule org.junit.platform.suite.engine {\n\n\trequires static org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires org.junit.platform.suite.api;\n\trequires org.junit.platform.commons;\n\trequires org.junit.platform.engine;\n\trequires org.junit.platform.launcher;\n\n\tprovides org.junit.platform.engine.TestEngine\n\t\t\twith org.junit.platform.suite.engine.SuiteTestEngine;\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/AdditionalDiscoverySelectors.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.FilePosition;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\n\n/**\n * @since 1.8\n */\nclass AdditionalDiscoverySelectors {\n\n\tstatic List<UriSelector> selectUris(String... uris) {\n\t\tPreconditions.notNull(uris, \"URI list must not be null\");\n\t\tPreconditions.containsNoNullElements(uris, \"Individual URIs must not be null\");\n\n\t\t// @formatter:off\n\t\treturn uniqueStreamOf(uris)\n\t\t\t\t.filter(StringUtils::isNotBlank)\n\t\t\t\t.map(DiscoverySelectors::selectUri)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tstatic List<DirectorySelector> selectDirectories(String... paths) {\n\t\tPreconditions.notNull(paths, \"Directory paths must not be null\");\n\t\tPreconditions.containsNoNullElements(paths, \"Individual directory paths must not be null\");\n\n\t\t// @formatter:off\n\t\treturn uniqueStreamOf(paths)\n\t\t\t\t.filter(StringUtils::isNotBlank)\n\t\t\t\t.map(DiscoverySelectors::selectDirectory)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tstatic List<PackageSelector> selectPackages(String... packageNames) {\n\t\tPreconditions.notNull(packageNames, \"Package names must not be null\");\n\t\tPreconditions.containsNoNullElements(packageNames, \"Individual package names must not be null\");\n\n\t\t// @formatter:off\n\t\treturn uniqueStreamOf(packageNames)\n\t\t\t\t.map(DiscoverySelectors::selectPackage)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tstatic List<ModuleSelector> selectModules(String... moduleNames) {\n\t\tPreconditions.notNull(moduleNames, \"Module names must not be null\");\n\t\tPreconditions.containsNoNullElements(moduleNames, \"Individual module names must not be null\");\n\n\t\treturn DiscoverySelectors.selectModules(uniqueStreamOf(moduleNames).collect(Collectors.toSet()));\n\t}\n\n\tstatic FileSelector selectFile(String path, int line, int column) {\n\t\tPreconditions.notBlank(path, \"File path must not be null or blank\");\n\n\t\tif (line <= 0) {\n\t\t\treturn DiscoverySelectors.selectFile(path);\n\t\t}\n\t\tif (column <= 0) {\n\t\t\treturn DiscoverySelectors.selectFile(path, FilePosition.from(line));\n\t\t}\n\t\treturn DiscoverySelectors.selectFile(path, FilePosition.from(line, column));\n\t}\n\n\tstatic ClasspathResourceSelector selectClasspathResource(String classpathResourceName, int line, int column) {\n\t\tPreconditions.notBlank(classpathResourceName, \"Classpath resource name must not be null or blank\");\n\n\t\tif (line <= 0) {\n\t\t\treturn DiscoverySelectors.selectClasspathResource(classpathResourceName);\n\t\t}\n\t\tif (column <= 0) {\n\t\t\treturn DiscoverySelectors.selectClasspathResource(classpathResourceName, FilePosition.from(line));\n\t\t}\n\t\treturn DiscoverySelectors.selectClasspathResource(classpathResourceName, FilePosition.from(line, column));\n\t}\n\n\tstatic List<? extends DiscoverySelector> parseIdentifiers(String[] identifiers) {\n\t\treturn DiscoverySelectors.parseAll(identifiers).toList();\n\t}\n\n\tprivate static <T> Stream<T> uniqueStreamOf(T[] elements) {\n\t\treturn Arrays.stream(elements).distinct();\n\t}\n\n\tprivate AdditionalDiscoverySelectors() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/ClassSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\n\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.EngineDiscoveryListener;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.UniqueId.Segment;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.SelectorResolver;\n\n/**\n * @since 1.8\n */\nfinal class ClassSelectorResolver implements SelectorResolver {\n\n\tprivate final IsSuiteClass isSuiteClass;\n\tprivate final Predicate<String> classNameFilter;\n\tprivate final SuiteEngineDescriptor suiteEngineDescriptor;\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\tprivate final EngineDiscoveryListener discoveryListener;\n\tprivate final DiscoveryIssueReporter issueReporter;\n\n\tClassSelectorResolver(Predicate<String> classNameFilter, SuiteEngineDescriptor suiteEngineDescriptor,\n\t\t\tConfigurationParameters configurationParameters, OutputDirectoryCreator outputDirectoryCreator,\n\t\t\tEngineDiscoveryListener discoveryListener, DiscoveryIssueReporter issueReporter) {\n\n\t\tthis.isSuiteClass = new IsSuiteClass(issueReporter);\n\t\tthis.classNameFilter = classNameFilter;\n\t\tthis.suiteEngineDescriptor = suiteEngineDescriptor;\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t\tthis.discoveryListener = discoveryListener;\n\t\tthis.issueReporter = issueReporter;\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ClassSelector selector, Context context) {\n\t\tClass<?> testClass = selector.getJavaClass();\n\t\tif (isSuiteClass.test(testClass)) {\n\t\t\tif (classNameFilter.test(testClass.getName())) {\n\t\t\t\t// @formatter:off\n\t\t\t\tOptional<SuiteTestDescriptor> suiteWithDiscoveryRequest = context\n\t\t\t\t\t\t.addToParent(parent -> newSuiteDescriptor(testClass, parent))\n\t\t\t\t\t\t.map(suite -> suite.addDiscoveryRequestFrom(testClass));\n\t\t\t\treturn toResolution(suiteWithDiscoveryRequest);\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\t@Override\n\tpublic Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\tUniqueId uniqueId = selector.getUniqueId();\n\t\tUniqueId engineId = suiteEngineDescriptor.getUniqueId();\n\t\tList<Segment> resolvedSegments = engineId.getSegments();\n\t\t// @formatter:off\n\t\treturn uniqueId.getSegments()\n\t\t\t\t.stream()\n\t\t\t\t.skip(resolvedSegments.size())\n\t\t\t\t.findFirst()\n\t\t\t\t.filter(suiteSegment -> SuiteTestDescriptor.SEGMENT_TYPE.equals(suiteSegment.getType()))\n\t\t\t\t.flatMap(ClassSelectorResolver::tryLoadSuiteClass)\n\t\t\t\t.filter(isSuiteClass)\n\t\t\t\t.map(suiteClass -> context\n\t\t\t\t\t\t.addToParent(parent -> newSuiteDescriptor(suiteClass, parent))\n\t\t\t\t\t\t.map(suite -> uniqueId.equals(suite.getUniqueId())\n\t\t\t\t\t\t\t\t// The uniqueId selector either targeted a class annotated with @Suite;\n\t\t\t\t\t\t\t\t? suite.addDiscoveryRequestFrom(suiteClass)\n\t\t\t\t\t\t\t\t// or a specific test in that suite\n\t\t\t\t\t\t\t\t: suite.addDiscoveryRequestFrom(uniqueId)))\n\t\t\t\t.map(ClassSelectorResolver::toResolution)\n\t\t\t\t.orElseGet(Resolution::unresolved);\n\t\t// @formatter:on\n\t}\n\n\tprivate static Optional<Class<?>> tryLoadSuiteClass(UniqueId.Segment segment) {\n\t\treturn ReflectionSupport.tryToLoadClass(segment.getValue()).toOptional();\n\t}\n\n\tprivate static Resolution toResolution(Optional<SuiteTestDescriptor> suite) {\n\t\treturn suite.map(Match::exact).map(Resolution::match).orElseGet(Resolution::unresolved);\n\t}\n\n\tprivate Optional<SuiteTestDescriptor> newSuiteDescriptor(Class<?> suiteClass, TestDescriptor parent) {\n\t\tUniqueId id = parent.getUniqueId().append(SuiteTestDescriptor.SEGMENT_TYPE, suiteClass.getName());\n\t\tif (containsCycle(id)) {\n\t\t\tissueReporter.reportIssue(\n\t\t\t\tDiscoveryIssue.builder(Severity.INFO, createConfigContainsCycleMessage(suiteClass, id)) //\n\t\t\t\t\t\t.source(ClassSource.from(suiteClass)));\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\treturn Optional.of(new SuiteTestDescriptor(id, suiteClass, configurationParameters, outputDirectoryCreator,\n\t\t\tdiscoveryListener, issueReporter));\n\t}\n\n\tprivate static boolean containsCycle(UniqueId id) {\n\t\tList<Segment> segments = id.getSegments();\n\t\tList<Segment> engineAndSuiteSegment = segments.subList(segments.size() - 2, segments.size());\n\t\tList<Segment> ancestorSegments = segments.subList(0, segments.size() - 2);\n\t\tfor (int i = 0; i < ancestorSegments.size() - 1; i++) {\n\t\t\tList<Segment> candidate = ancestorSegments.subList(i, i + 2);\n\t\t\tif (engineAndSuiteSegment.equals(candidate)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate static String createConfigContainsCycleMessage(Class<?> suiteClass, UniqueId suiteId) {\n\t\treturn \"The suite configuration of [%s] resulted in a cycle [%s] and will not be discovered a second time.\".formatted(\n\t\t\tsuiteClass.getName(), suiteId);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/DiscoverySelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver;\n\n/**\n * @since 1.8\n */\nfinal class DiscoverySelectorResolver {\n\n\t// @formatter:off\n\tprivate static final EngineDiscoveryRequestResolver<SuiteEngineDescriptor> resolver = EngineDiscoveryRequestResolver.<SuiteEngineDescriptor>builder()\n\t\t\t.addClassContainerSelectorResolverWithContext(context -> new IsSuiteClass(context.getIssueReporter()))\n\t\t\t.addSelectorResolver(context -> new ClassSelectorResolver(\n\t\t\t\t\tcontext.getClassNameFilter(),\n\t\t\t\t\tcontext.getEngineDescriptor(),\n\t\t\t\t\tcontext.getDiscoveryRequest().getConfigurationParameters(),\n\t\t\t\t\tcontext.getDiscoveryRequest().getOutputDirectoryCreator(),\n\t\t\t\t\tcontext.getDiscoveryRequest().getDiscoveryListener(),\n\t\t\t\t\tcontext.getIssueReporter()))\n\t\t\t.build();\n\t// @formatter:on\n\n\tprivate static void discoverSuites(SuiteEngineDescriptor engineDescriptor) {\n\t\t// @formatter:off\n\t\tengineDescriptor.getChildren().stream()\n\t\t\t\t.map(SuiteTestDescriptor.class::cast)\n\t\t\t\t.forEach(SuiteTestDescriptor::discover);\n\t\t// @formatter:on\n\t}\n\n\tvoid resolveSelectors(EngineDiscoveryRequest request, SuiteEngineDescriptor engineDescriptor) {\n\t\tDiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.deduplicating(\n\t\t\tDiscoveryIssueReporter.forwarding(request.getDiscoveryListener(), engineDescriptor.getUniqueId()));\n\t\tresolver.resolve(request, engineDescriptor, issueReporter);\n\t\tdiscoverSuites(engineDescriptor);\n\t\tengineDescriptor.accept(TestDescriptor::prune);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/IsSuiteClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.junit.platform.commons.support.ModifierSupport.isAbstract;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotAbstract;\nimport static org.junit.platform.commons.support.ModifierSupport.isNotPrivate;\n\nimport java.util.function.Predicate;\n\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter.Condition;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * @since 1.8\n */\nfinal class IsSuiteClass implements Predicate<Class<?>> {\n\n\tprivate final Condition<Class<?>> condition;\n\n\tIsSuiteClass(DiscoveryIssueReporter issueReporter) {\n\t\tthis.condition = isNotPrivateUnlessAbstract(issueReporter) //\n\t\t\t\t.and(isNotLocal(issueReporter)) //\n\t\t\t\t.and(isNotInner(issueReporter));\n\t}\n\n\t@Override\n\tpublic boolean test(Class<?> testClass) {\n\t\treturn hasSuiteAnnotation(testClass) //\n\t\t\t\t&& condition.check(testClass) //\n\t\t\t\t&& isNotAbstract(testClass);\n\t}\n\n\tprivate boolean hasSuiteAnnotation(Class<?> testClass) {\n\t\treturn AnnotationSupport.isAnnotated(testClass, Suite.class);\n\t}\n\n\tprivate static Condition<Class<?>> isNotPrivateUnlessAbstract(DiscoveryIssueReporter issueReporter) {\n\t\t// Allow abstract test classes to be private because @Suite is inherited and subclasses may widen access.\n\t\treturn issueReporter.createReportingCondition(testClass -> isNotPrivate(testClass) || isAbstract(testClass),\n\t\t\ttestClass -> createIssue(testClass, \"must not be private.\"));\n\t}\n\n\tprivate static Condition<Class<?>> isNotLocal(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(testClass -> !testClass.isLocalClass(),\n\t\t\ttestClass -> createIssue(testClass, \"must not be a local class.\"));\n\t}\n\n\tprivate static Condition<Class<?>> isNotInner(DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(testClass -> !ReflectionUtils.isInnerClass(testClass),\n\t\t\ttestClass -> createIssue(testClass, \"must not be an inner class. Did you forget to declare it static?\"));\n\t}\n\n\tprivate static DiscoveryIssue createIssue(Class<?> testClass, String detailMessage) {\n\t\tString message = \"@Suite class '%s' %s It will not be executed.\".formatted(testClass.getName(), detailMessage);\n\t\treturn DiscoveryIssue.builder(DiscoveryIssue.Severity.WARNING, message) //\n\t\t\t\t.source(ClassSource.from(testClass)) //\n\t\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/LifecycleMethodUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedMethods;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.platform.commons.support.HierarchyTraversalMode;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.suite.api.AfterSuite;\nimport org.junit.platform.suite.api.BeforeSuite;\n\n/**\n * Collection of utilities for working with test lifecycle methods.\n *\n * @since 1.11\n */\nfinal class LifecycleMethodUtils {\n\n\tprivate LifecycleMethodUtils() {\n\t\t/* no-op */\n\t}\n\n\tstatic List<Method> findBeforeSuiteMethods(Class<?> testClass, DiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckStaticAndNonPrivate(testClass, BeforeSuite.class, HierarchyTraversalMode.TOP_DOWN,\n\t\t\tissueReporter);\n\t}\n\n\tstatic List<Method> findAfterSuiteMethods(Class<?> testClass, DiscoveryIssueReporter issueReporter) {\n\t\treturn findMethodsAndCheckStaticAndNonPrivate(testClass, AfterSuite.class, HierarchyTraversalMode.BOTTOM_UP,\n\t\t\tissueReporter);\n\t}\n\n\tprivate static List<Method> findMethodsAndCheckStaticAndNonPrivate(Class<?> testClass,\n\t\t\tClass<? extends Annotation> annotationType, HierarchyTraversalMode traversalMode,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\n\t\treturn findAnnotatedMethods(testClass, annotationType, traversalMode).stream() //\n\t\t\t\t.filter(//\n\t\t\t\t\treturnsPrimitiveVoid(annotationType, issueReporter) //\n\t\t\t\t\t\t\t.and(isStatic(annotationType, issueReporter)) //\n\t\t\t\t\t\t\t.and(isNotPrivate(annotationType, issueReporter)) //\n\t\t\t\t\t\t\t.and(hasNoParameters(annotationType, issueReporter)) //\n\t\t\t\t\t\t\t.toPredicate()) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate static DiscoveryIssueReporter.Condition<Method> isStatic(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isStatic, method -> {\n\t\t\tString message = \"@%s method '%s' must be static.\".formatted(annotationType.getSimpleName(),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createError(message, method);\n\t\t});\n\t}\n\n\tprivate static DiscoveryIssueReporter.Condition<Method> isNotPrivate(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ModifierSupport::isNotPrivate, method -> {\n\t\t\tString message = \"@%s method '%s' must not be private.\".formatted(annotationType.getSimpleName(),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createError(message, method);\n\t\t});\n\t}\n\n\tprivate static DiscoveryIssueReporter.Condition<Method> returnsPrimitiveVoid(\n\t\t\tClass<? extends Annotation> annotationType, DiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(ReflectionUtils::returnsPrimitiveVoid, method -> {\n\t\t\tString message = \"@%s method '%s' must not return a value.\".formatted(annotationType.getSimpleName(),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createError(message, method);\n\t\t});\n\t}\n\n\tprivate static DiscoveryIssueReporter.Condition<Method> hasNoParameters(Class<? extends Annotation> annotationType,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\treturn issueReporter.createReportingCondition(method -> method.getParameterCount() == 0, method -> {\n\t\t\tString message = \"@%s method '%s' must not accept parameters.\".formatted(annotationType.getSimpleName(),\n\t\t\t\tmethod.toGenericString());\n\t\t\treturn createError(message, method);\n\t\t});\n\t}\n\n\tprivate static DiscoveryIssue createError(String message, Method method) {\n\t\treturn DiscoveryIssue.builder(Severity.ERROR, message).source(MethodSource.from(method)).build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/NoTestsDiscoveredException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport java.io.Serial;\n\nimport org.junit.platform.commons.JUnitException;\n\nclass NoTestsDiscoveredException extends JUnitException {\n\n\t@Serial\n\tprivate static final long serialVersionUID = 1L;\n\n\tNoTestsDiscoveredException(Class<?> suiteClass) {\n\t\tsuper(\"Suite [%s] did not discover any tests\".formatted(suiteClass.getName()));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteEngineDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * @since 1.8\n */\nfinal class SuiteEngineDescriptor extends EngineDescriptor {\n\n\tstatic final String ENGINE_ID = \"junit-platform-suite\";\n\n\tSuiteEngineDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, \"JUnit Platform Suite\");\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncher.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static java.util.Collections.emptyList;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.EngineDiscoveryOrchestrator;\nimport org.junit.platform.launcher.core.EngineExecutionOrchestrator;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult;\nimport org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\n\n/**\n * @since 1.8\n */\nclass SuiteLauncher {\n\n\tprivate final EngineExecutionOrchestrator executionOrchestrator = new EngineExecutionOrchestrator();\n\tprivate final EngineDiscoveryOrchestrator discoveryOrchestrator;\n\n\tstatic SuiteLauncher create() {\n\t\tSet<TestEngine> engines = new LinkedHashSet<>();\n\t\tnew ServiceLoaderTestEngineRegistry().loadTestEngines().forEach(engines::add);\n\t\treturn new SuiteLauncher(engines);\n\t}\n\n\tprivate SuiteLauncher(Set<TestEngine> testEngines) {\n\t\tPreconditions.condition(hasTestEngineOtherThanSuiteEngine(testEngines),\n\t\t\t() -> \"Cannot create SuiteLauncher without at least one other TestEngine; \"\n\t\t\t\t\t+ \"consider adding an engine implementation JAR to the classpath\");\n\t\tthis.discoveryOrchestrator = new EngineDiscoveryOrchestrator(testEngines, emptyList());\n\t}\n\n\tprivate boolean hasTestEngineOtherThanSuiteEngine(Set<TestEngine> testEngines) {\n\t\treturn testEngines.stream().anyMatch(testEngine -> !SuiteEngineDescriptor.ENGINE_ID.equals(testEngine.getId()));\n\t}\n\n\tLauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest, UniqueId parentId) {\n\t\treturn discoveryOrchestrator.discover(discoveryRequest, parentId);\n\t}\n\n\tTestExecutionSummary execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener executionListener,\n\t\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken) {\n\t\tSummaryGeneratingListener listener = new SummaryGeneratingListener();\n\t\texecutionOrchestrator.execute(discoveryResult, executionListener, listener, requestLevelStore,\n\t\t\tcancellationToken);\n\t\treturn listener.getSummary();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteLauncherDiscoveryRequestBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.STANDARD_INCLUDE_PATTERN;\nimport static org.junit.platform.suite.engine.AdditionalDiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.suite.engine.AdditionalDiscoverySelectors.selectFile;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.AnnotatedElement;\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.PackageNameFilter;\nimport org.junit.platform.launcher.EngineFilter;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.TagFilter;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.suite.api.ConfigurationParameter;\nimport org.junit.platform.suite.api.ConfigurationParametersResource;\nimport org.junit.platform.suite.api.DisableParentConfigurationParameters;\nimport org.junit.platform.suite.api.ExcludeClassNamePatterns;\nimport org.junit.platform.suite.api.ExcludeEngines;\nimport org.junit.platform.suite.api.ExcludePackages;\nimport org.junit.platform.suite.api.ExcludeTags;\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.IncludePackages;\nimport org.junit.platform.suite.api.IncludeTags;\nimport org.junit.platform.suite.api.Select;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.SelectClasspathResource;\nimport org.junit.platform.suite.api.SelectDirectories;\nimport org.junit.platform.suite.api.SelectFile;\nimport org.junit.platform.suite.api.SelectMethod;\nimport org.junit.platform.suite.api.SelectModules;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.SelectUris;\n\n/**\n * The {@code SuiteLauncherDiscoveryRequestBuilder} provides a light-weight DSL\n * for generating a {@link LauncherDiscoveryRequest} specifically tailored for\n * suite execution.\n *\n * @since 1.8 (originally in junit-platform-suite-commons)\n * @see org.junit.platform.engine.discovery.DiscoverySelectors\n * @see org.junit.platform.engine.discovery.ClassNameFilter\n * @see org.junit.platform.launcher.EngineFilter\n * @see org.junit.platform.launcher.TagFilter\n */\nfinal class SuiteLauncherDiscoveryRequestBuilder {\n\n\tprivate final LauncherDiscoveryRequestBuilder delegate = LauncherDiscoveryRequestBuilder.request();\n\tprivate final Set<String> selectedClassNames = new LinkedHashSet<>();\n\tprivate boolean includeClassNamePatternsUsed;\n\tprivate boolean filterStandardClassNamePatterns = false;\n\n\tprivate @Nullable ConfigurationParameters parentConfigurationParameters;\n\n\tprivate boolean enableParentConfigurationParameters = true;\n\n\tprivate SuiteLauncherDiscoveryRequestBuilder() {\n\t}\n\n\t/**\n\t * Create a new {@code SuiteLauncherDiscoveryRequestBuilder}.\n\t *\n\t * @return a new builder\n\t */\n\tstatic SuiteLauncherDiscoveryRequestBuilder request() {\n\t\treturn new SuiteLauncherDiscoveryRequestBuilder();\n\t}\n\n\t/**\n\t * Add all supplied {@code selectors} to the request.\n\t *\n\t * @param selectors the {@code DiscoverySelectors} to add; never {@code null}\n\t */\n\tvoid selectors(DiscoverySelector... selectors) {\n\t\tthis.delegate.selectors(selectors);\n\t}\n\n\t/**\n\t * Add all supplied {@code selectors} to the request.\n\t *\n\t * @param selectors the {@code DiscoverySelectors} to add; never {@code null}\n\t */\n\tprivate void selectors(List<? extends DiscoverySelector> selectors) {\n\t\tthis.delegate.selectors(selectors);\n\t}\n\n\t/**\n\t * Add all supplied {@code filters} to the request.\n\t * <p>The {@code filters} are combined using AND semantics, i.e. all of them\n\t * have to include a resource for it to end up in the test plan.\n\t * <p><strong>Warning</strong>: be cautious when registering multiple competing\n\t * {@link EngineFilter#includeEngines include} {@code EngineFilters} or multiple\n\t * competing {@link EngineFilter#excludeEngines exclude} {@code EngineFilters}\n\t * for the same discovery request since doing so will likely lead to\n\t * undesirable results (i.e., zero engines being active).\n\t *\n\t * @param filters the {@code Filter}s to add; never {@code null}\n\t */\n\tprivate void filters(Filter<?>... filters) {\n\t\tthis.delegate.filters(filters);\n\t}\n\n\t/**\n\t * Specify whether to filter standard class name patterns.\n\t * <p>If set to {@code true}, standard class name patterns are filtered.\n\t *\n\t * @return this builder for method chaining\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder filterStandardClassNamePatterns() {\n\t\tthis.filterStandardClassNamePatterns = true;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add the supplied <em>configuration parameter</em> to the request.\n\t *\n\t * @param key the configuration parameter key under which to store the\n\t * value; never {@code null} or blank\n\t * @param value the value to store\n\t * @return this builder for method chaining\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder configurationParameter(String key, String value) {\n\t\tthis.delegate.configurationParameter(key, value);\n\t\treturn this;\n\t}\n\n\tvoid configurationParametersResource(String resourceFile) {\n\t\tthis.delegate.configurationParametersResources(resourceFile);\n\t}\n\n\t/**\n\t * Set the parent configuration parameters to use for the request.\n\t *\n\t * <p>Any explicit configuration parameters configured via\n\t * {@link #configurationParameter(String, String)} takes precedence over the\n\t * supplied configuration parameters.\n\t *\n\t * @param parentConfigurationParameters the parent instance to use for looking\n\t * up configuration parameters that have not been explicitly configured;\n\t * never {@code null}\n\t * @return this builder for method chaining\n\t * @see #configurationParameter(String, String)\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder parentConfigurationParameters(\n\t\t\tConfigurationParameters parentConfigurationParameters) {\n\t\tthis.parentConfigurationParameters = parentConfigurationParameters;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Configure whether implicit configuration parameters should be considered.\n\t * <p>By default, in addition to those parameters that are passed explicitly\n\t * to this builder, configuration parameters are read from system properties\n\t * and from the {@code junit-platform.properties} classpath resource.\n\t * Passing {@code false} to this method, disables the latter two sources so\n\t * that only explicit configuration parameters are taken into account.\n\t *\n\t * @return this builder for method chaining\n\t * @see #configurationParameter(String, String)\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder disableImplicitConfigurationParameters() {\n\t\tthis.delegate.enableImplicitConfigurationParameters(false);\n\t\treturn this;\n\t}\n\n\tSuiteLauncherDiscoveryRequestBuilder outputDirectoryCreator(OutputDirectoryCreator outputDirectoryCreator) {\n\t\tdelegate.outputDirectoryCreator(outputDirectoryCreator);\n\t\treturn this;\n\t}\n\n\tvoid listener(LauncherDiscoveryListener listener) {\n\t\tdelegate.listeners(listener);\n\t}\n\n\t/**\n\t * Apply a suite's annotation-based configuration to this builder.\n\t *\n\t * <p>This will apply the configuration from the following annotations.\n\t * <ul>\n\t *   <li>{@link ConfigurationParameter}</li>\n\t *   <li>{@link DisableParentConfigurationParameters}</li>\n\t * </ul>\n\t *\n\t * @param suiteClass the class to apply the configuration annotations from;\n\t * never {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.11\n\t * @see org.junit.platform.suite.api.Suite\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder applyConfigurationParametersFromSuite(Class<?> suiteClass) {\n\t\tPreconditions.notNull(suiteClass, \"Suite class must not be null\");\n\n\t\t// @formatter:off\n\t\tfindRepeatableAnnotations(suiteClass, ConfigurationParameter.class)\n\t\t\t\t.forEach(configuration -> configurationParameter(configuration.key(), configuration.value()));\n\t\tfindRepeatableAnnotations(suiteClass, ConfigurationParametersResource.class)\n\t\t\t\t.forEach(configResource -> configurationParametersResource(configResource.value()));\n\t\tfindAnnotation(suiteClass, DisableParentConfigurationParameters.class)\n\t\t\t\t.ifPresent(__ -> this.enableParentConfigurationParameters = false);\n\t\t// @formatter:on\n\t\treturn this;\n\t}\n\n\t/**\n\t * Apply a suite's annotation-based discovery selectors and filters to this\n\t * builder.\n\t *\n\t * <p>This will apply the configuration from the following annotations.\n\t * <ul>\n\t *   <li>{@link ExcludeClassNamePatterns}</li>\n\t *   <li>{@link ExcludeEngines}</li>\n\t *   <li>{@link ExcludePackages}</li>\n\t *   <li>{@link ExcludeTags}</li>\n\t *   <li>{@link IncludeClassNamePatterns}</li>\n\t *   <li>{@link IncludeEngines}</li>\n\t *   <li>{@link IncludePackages}</li>\n\t *   <li>{@link IncludeTags}</li>\n\t *   <li>{@link SelectClasses}</li>\n\t *   <li>{@link SelectClasspathResource}</li>\n\t *   <li>{@link SelectDirectories}</li>\n\t *   <li>{@link SelectFile}</li>\n\t *   <li>{@link SelectMethod}</li>\n\t *   <li>{@link SelectModules}</li>\n\t *   <li>{@link SelectUris}</li>\n\t *   <li>{@link SelectPackages}</li>\n\t *   <li>{@link Select}</li>\n\t * </ul>\n\t *\n\t * @param suiteClass the class to apply the discovery selectors and filter\n\t * annotations from; never {@code null}\n\t * @return this builder for method chaining\n\t * @since 1.11\n\t * @see org.junit.platform.suite.api.Suite\n\t */\n\tSuiteLauncherDiscoveryRequestBuilder applySelectorsAndFiltersFromSuite(Class<?> suiteClass) {\n\t\tPreconditions.notNull(suiteClass, \"Suite class must not be null\");\n\n\t\taddExcludeFilters(suiteClass);\n\t\t// Process @SelectClasses and @SelectMethod before @IncludeClassNamePatterns, since the names\n\t\t// of selected classes get automatically added to the include filter.\n\t\taddClassAndMethodSelectors(suiteClass);\n\t\taddIncludeFilters(suiteClass);\n\t\taddOtherSelectors(suiteClass);\n\t\treturn this;\n\t}\n\n\tprivate void addExcludeFilters(Class<?> suiteClass) {\n\t\tfindAnnotationValues(suiteClass, ExcludeClassNamePatterns.class, ExcludeClassNamePatterns::value) //\n\t\t\t\t.flatMap(SuiteLauncherDiscoveryRequestBuilder::stripped) //\n\t\t\t\t.map(ClassNameFilter::excludeClassNamePatterns) //\n\t\t\t\t.ifPresent(this::filters);\n\t\tfindAnnotationValues(suiteClass, ExcludeEngines.class, ExcludeEngines::value) //\n\t\t\t\t.map(EngineFilter::excludeEngines) //\n\t\t\t\t.ifPresent(this::filters);\n\t\tfindAnnotationValues(suiteClass, ExcludePackages.class, ExcludePackages::value) //\n\t\t\t\t.map(PackageNameFilter::excludePackageNames) //\n\t\t\t\t.ifPresent(this::filters);\n\t\tfindAnnotationValues(suiteClass, ExcludeTags.class, ExcludeTags::value) //\n\t\t\t\t.map(TagFilter::excludeTags) //\n\t\t\t\t.ifPresent(this::filters);\n\t}\n\n\tprivate void addClassAndMethodSelectors(Class<?> suiteClass) {\n\t\tfindAnnotation(suiteClass, SelectClasses.class) //\n\t\t\t\t.map(annotation -> selectClasses(suiteClass, annotation)) //\n\t\t\t\t.ifPresent(this::selectors);\n\t\tfindRepeatableAnnotations(suiteClass, SelectMethod.class) //\n\t\t\t\t.stream() //\n\t\t\t\t.map(annotation -> selectMethod(suiteClass, annotation)) //\n\t\t\t\t.forEach(this::selectors);\n\t}\n\n\tprivate void addIncludeFilters(Class<?> suiteClass) {\n\t\tfindAnnotationValues(suiteClass, IncludeClassNamePatterns.class, IncludeClassNamePatterns::value) //\n\t\t\t\t.flatMap(SuiteLauncherDiscoveryRequestBuilder::stripped) //\n\t\t\t\t.map(this::createIncludeClassNameFilter) //\n\t\t\t\t.ifPresent(filters -> {\n\t\t\t\t\tthis.includeClassNamePatternsUsed = true;\n\t\t\t\t\tfilters(filters);\n\t\t\t\t});\n\t\tfindAnnotationValues(suiteClass, IncludeEngines.class, IncludeEngines::value) //\n\t\t\t\t.map(EngineFilter::includeEngines) //\n\t\t\t\t.ifPresent(this::filters);\n\t\tfindAnnotationValues(suiteClass, IncludePackages.class, IncludePackages::value) //\n\t\t\t\t.map(PackageNameFilter::includePackageNames) //\n\t\t\t\t.ifPresent(this::filters);\n\t\tfindAnnotationValues(suiteClass, IncludeTags.class, IncludeTags::value) //\n\t\t\t\t.map(TagFilter::includeTags) //\n\t\t\t\t.ifPresent(this::filters);\n\t}\n\n\tprivate void addOtherSelectors(Class<?> suiteClass) {\n\t\tfindRepeatableAnnotations(suiteClass, SelectClasspathResource.class) //\n\t\t\t\t.stream() //\n\t\t\t\t.map(annotation -> selectClasspathResource(annotation.value(), annotation.line(), annotation.column())) //\n\t\t\t\t.forEach(this::selectors);\n\t\tfindAnnotationValues(suiteClass, SelectDirectories.class, SelectDirectories::value) //\n\t\t\t\t.map(AdditionalDiscoverySelectors::selectDirectories) //\n\t\t\t\t.ifPresent(this::selectors);\n\t\tfindRepeatableAnnotations(suiteClass, SelectFile.class) //\n\t\t\t\t.stream() //\n\t\t\t\t.map(annotation -> selectFile(annotation.value(), annotation.line(), annotation.column())) //\n\t\t\t\t.forEach(this::selectors);\n\t\tfindAnnotationValues(suiteClass, SelectModules.class, SelectModules::value) //\n\t\t\t\t.map(AdditionalDiscoverySelectors::selectModules) //\n\t\t\t\t.ifPresent(this::selectors);\n\t\tfindAnnotationValues(suiteClass, SelectUris.class, SelectUris::value) //\n\t\t\t\t.map(AdditionalDiscoverySelectors::selectUris) //\n\t\t\t\t.ifPresent(this::selectors);\n\t\tfindAnnotationValues(suiteClass, SelectPackages.class, SelectPackages::value) //\n\t\t\t\t.map(AdditionalDiscoverySelectors::selectPackages) //\n\t\t\t\t.ifPresent(this::selectors);\n\t\tfindAnnotationValues(suiteClass, Select.class, Select::value) //\n\t\t\t\t.map(AdditionalDiscoverySelectors::parseIdentifiers) //\n\t\t\t\t.ifPresent(this::selectors);\n\t}\n\n\t/**\n\t * Build the {@link LauncherDiscoveryRequest} that has been configured via\n\t * this builder.\n\t */\n\tLauncherDiscoveryRequest build() {\n\t\tif (this.filterStandardClassNamePatterns && !this.includeClassNamePatternsUsed) {\n\t\t\tthis.delegate.filters(createIncludeClassNameFilter(STANDARD_INCLUDE_PATTERN));\n\t\t}\n\n\t\tif (this.enableParentConfigurationParameters && this.parentConfigurationParameters != null) {\n\t\t\tthis.delegate.parentConfigurationParameters(this.parentConfigurationParameters);\n\t\t}\n\n\t\treturn this.delegate.build();\n\t}\n\n\tprivate List<ClassSelector> selectClasses(Class<?> suiteClass, SelectClasses annotation) {\n\t\treturn toClassSelectors(suiteClass, annotation) //\n\t\t\t\t.distinct() //\n\t\t\t\t.peek(selector -> this.selectedClassNames.add(selector.getClassName())) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate static Stream<ClassSelector> toClassSelectors(Class<?> suiteClass, SelectClasses annotation) {\n\t\tPreconditions.condition(annotation.value().length > 0 || annotation.names().length > 0,\n\t\t\t() -> \"@SelectClasses on class [%s] must declare at least one class reference or name\".formatted(\n\t\t\t\tsuiteClass.getName()));\n\t\treturn Stream.concat(//\n\t\t\tDiscoverySelectors.selectClasses(annotation.value()).stream(), //\n\t\t\tDiscoverySelectors.selectClassesByName(annotation.names()).stream() //\n\t\t);\n\t}\n\n\tprivate MethodSelector selectMethod(Class<?> suiteClass, SelectMethod annotation) {\n\t\tMethodSelector methodSelector = toMethodSelector(suiteClass, annotation);\n\t\tthis.selectedClassNames.add(methodSelector.getClassName());\n\t\treturn methodSelector;\n\t}\n\n\tprivate MethodSelector toMethodSelector(Class<?> suiteClass, SelectMethod annotation) {\n\t\tif (!annotation.value().isEmpty()) {\n\t\t\treturn toMethodSelectorFromFQMN(suiteClass, annotation);\n\t\t}\n\n\t\tClass<?> type = annotation.type() == Class.class ? null : annotation.type();\n\t\tString typeName = annotation.typeName().isEmpty() ? null : annotation.typeName().strip();\n\t\tString methodName = Preconditions.notBlank(annotation.name(),\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass, \"method name must not be blank\"));\n\t\tClass<?>[] parameterTypes = annotation.parameterTypes().length == 0 ? null : annotation.parameterTypes();\n\t\tString parameterTypeNames = annotation.parameterTypeNames().strip();\n\t\tif (parameterTypes != null) {\n\t\t\tPreconditions.condition(parameterTypeNames.isEmpty(),\n\t\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\t\"either parameter type names or parameter types must be set but not both\"));\n\t\t}\n\t\treturn toMethodSelector(suiteClass, type, typeName, parameterTypes, methodName, parameterTypeNames);\n\t}\n\n\tprivate static MethodSelector toMethodSelectorFromFQMN(Class<?> suiteClass, SelectMethod annotation) {\n\t\tPreconditions.condition(annotation.type() == Class.class,\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"type must not be set in conjunction with fully qualified method name\"));\n\t\tPreconditions.condition(annotation.typeName().isEmpty(),\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"type name must not be set in conjunction with fully qualified method name\"));\n\t\tPreconditions.condition(annotation.name().isEmpty(),\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"method name must not be set in conjunction with fully qualified method name\"));\n\t\tPreconditions.condition(annotation.parameterTypes().length == 0,\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"parameter types must not be set in conjunction with fully qualified method name\"));\n\t\tPreconditions.condition(annotation.parameterTypeNames().isEmpty(),\n\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"parameter type names must not be set in conjunction with fully qualified method name\"));\n\n\t\treturn DiscoverySelectors.selectMethod(annotation.value());\n\t}\n\n\tprivate static MethodSelector toMethodSelector(Class<?> suiteClass, @Nullable Class<?> type,\n\t\t\t@Nullable String typeName, Class<?> @Nullable [] parameterTypes, String methodName,\n\t\t\tString parameterTypeNames) {\n\t\tif (type == null) {\n\t\t\tString nonBlankTypeName = Preconditions.notBlank(typeName,\n\t\t\t\t() -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\t\"type must be set or type name must not be blank\"));\n\t\t\treturn parameterTypes == null //\n\t\t\t\t\t? DiscoverySelectors.selectMethod(nonBlankTypeName, methodName, parameterTypeNames) //\n\t\t\t\t\t: DiscoverySelectors.selectMethod(nonBlankTypeName, methodName, parameterTypes);\n\t\t}\n\t\telse {\n\t\t\tPreconditions.condition(typeName == null, () -> prefixErrorMessageForInvalidSelectMethodUsage(suiteClass,\n\t\t\t\t\"either type name or type must be set but not both\"));\n\t\t\treturn parameterTypes == null //\n\t\t\t\t\t? DiscoverySelectors.selectMethod(type, methodName, parameterTypeNames) //\n\t\t\t\t\t: DiscoverySelectors.selectMethod(type, methodName, parameterTypes);\n\t\t}\n\t}\n\n\tprivate static String prefixErrorMessageForInvalidSelectMethodUsage(Class<?> suiteClass, String detailMessage) {\n\t\treturn \"@SelectMethod on class [%s]: %s\".formatted(suiteClass.getName(), detailMessage);\n\t}\n\n\tprivate ClassNameFilter createIncludeClassNameFilter(String... patterns) {\n\t\tString[] combinedPatterns = Stream.concat(//\n\t\t\tthis.selectedClassNames.stream().map(Pattern::quote), //\n\t\t\tArrays.stream(patterns)//\n\t\t).toArray(String[]::new);\n\t\treturn ClassNameFilter.includeClassNamePatterns(combinedPatterns);\n\t}\n\n\tprivate static <A extends Annotation, V> Optional<V[]> findAnnotationValues(AnnotatedElement element,\n\t\t\tClass<A> annotationType, Function<A, V[]> valueExtractor) {\n\t\treturn findAnnotation(element, annotationType).map(valueExtractor).filter(values -> values.length > 0);\n\t}\n\n\tprivate static Optional<String[]> stripped(String[] patterns) {\n\t\tif (patterns.length == 0) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\t// @formatter:off\n\t\treturn Optional.of(Arrays.stream(patterns)\n\t\t\t\t.filter(StringUtils::isNotBlank)\n\t\t\t\t.map(String::strip)\n\t\t\t\t.toArray(String[]::new));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.function.Predicate.isEqual;\nimport static java.util.stream.Collectors.joining;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilder.request;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.EngineDiscoveryListener;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.UniqueId.Segment;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.api.SuiteDisplayName;\n\n/**\n * {@link TestDescriptor} for tests based on the JUnit Platform Suite API.\n *\n * <h2>Default Display Names</h2>\n *\n * <p>The default display name is the simple name of the class.\n *\n * @since 1.8\n * @see SuiteDisplayName\n */\nfinal class SuiteTestDescriptor extends AbstractTestDescriptor {\n\n\tstatic final String SEGMENT_TYPE = \"suite\";\n\n\tprivate final SuiteLauncherDiscoveryRequestBuilder discoveryRequestBuilder = request();\n\tprivate final ConfigurationParameters configurationParameters;\n\tprivate final OutputDirectoryCreator outputDirectoryCreator;\n\tprivate final Boolean failIfNoTests;\n\tprivate final Class<?> suiteClass;\n\tprivate final LifecycleMethods lifecycleMethods;\n\n\tprivate @Nullable LauncherDiscoveryResult launcherDiscoveryResult;\n\n\tprivate @Nullable SuiteLauncher launcher;\n\n\tSuiteTestDescriptor(UniqueId id, Class<?> suiteClass, ConfigurationParameters configurationParameters,\n\t\t\tOutputDirectoryCreator outputDirectoryCreator, EngineDiscoveryListener discoveryListener,\n\t\t\tDiscoveryIssueReporter issueReporter) {\n\t\tsuper(id, getSuiteDisplayName(suiteClass, issueReporter), ClassSource.from(suiteClass));\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.outputDirectoryCreator = outputDirectoryCreator;\n\t\tthis.failIfNoTests = getFailIfNoTests(suiteClass);\n\t\tthis.suiteClass = suiteClass;\n\t\tthis.lifecycleMethods = new LifecycleMethods(suiteClass, issueReporter);\n\t\tthis.discoveryRequestBuilder.listener(DiscoveryIssueForwardingListener.create(id, discoveryListener));\n\t}\n\n\tprivate static Boolean getFailIfNoTests(Class<?> suiteClass) {\n\t\t// @formatter:off\n\t\treturn findAnnotation(suiteClass, Suite.class)\n\t\t\t\t.map(Suite::failIfNoTests)\n\t\t\t\t.orElseThrow(() -> new JUnitException(\"Suite [%s] was not annotated with @Suite\".formatted(suiteClass.getName())));\n\t\t// @formatter:on\n\t}\n\n\tSuiteTestDescriptor addDiscoveryRequestFrom(Class<?> suiteClass) {\n\t\tPreconditions.condition(launcherDiscoveryResult == null,\n\t\t\t\"discovery request cannot be modified after discovery\");\n\t\tdiscoveryRequestBuilder.applySelectorsAndFiltersFromSuite(suiteClass);\n\t\treturn this;\n\t}\n\n\tSuiteTestDescriptor addDiscoveryRequestFrom(UniqueId uniqueId) {\n\t\tPreconditions.condition(launcherDiscoveryResult == null,\n\t\t\t\"discovery request cannot be modified after discovery\");\n\t\tdiscoveryRequestBuilder.selectors(DiscoverySelectors.selectUniqueId(uniqueId));\n\t\treturn this;\n\t}\n\n\tvoid discover() {\n\t\tif (launcherDiscoveryResult != null) {\n\t\t\treturn;\n\t\t}\n\n\t\t// @formatter:off\n\t\tLauncherDiscoveryRequest request = discoveryRequestBuilder\n\t\t\t\t.filterStandardClassNamePatterns()\n\t\t\t\t.disableImplicitConfigurationParameters()\n\t\t\t\t.parentConfigurationParameters(configurationParameters)\n\t\t\t\t.applyConfigurationParametersFromSuite(suiteClass)\n\t\t\t\t.outputDirectoryCreator(outputDirectoryCreator)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tthis.launcher = SuiteLauncher.create();\n\t\tthis.launcherDiscoveryResult = launcher.discover(request, getUniqueId());\n\t\t// @formatter:off\n\t\tlauncherDiscoveryResult.getTestEngines()\n\t\t\t\t.stream()\n\t\t\t\t.map(testEngine -> launcherDiscoveryResult.getEngineTestDescriptor(testEngine))\n\t\t\t\t.forEach(this::addChild);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n\tprivate static String getSuiteDisplayName(Class<?> suiteClass, DiscoveryIssueReporter issueReporter) {\n\t\t// @formatter:off\n\t\tvar nonBlank = issueReporter.createReportingCondition(StringUtils::isNotBlank, __ -> {\n\t\t\tString message = \"@SuiteDisplayName on %s must be declared with a non-blank value.\".formatted(\n\t\t\t\t\tsuiteClass.getName());\n\t\t\treturn DiscoveryIssue.builder(DiscoveryIssue.Severity.WARNING, message)\n\t\t\t\t\t.source(ClassSource.from(suiteClass))\n\t\t\t\t\t.build();\n\t\t}).toPredicate();\n\n\t\treturn findAnnotation(suiteClass, SuiteDisplayName.class)\n\t\t\t\t.map(SuiteDisplayName::value)\n\t\t\t\t.filter(nonBlank)\n\t\t\t\t.orElse(suiteClass.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\tvoid execute(EngineExecutionListener executionListener, NamespacedHierarchicalStore<Namespace> requestLevelStore,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\tif (cancellationToken.isCancellationRequested()) {\n\t\t\texecutionListener.executionSkipped(this, \"Execution cancelled\");\n\t\t\treturn;\n\t\t}\n\n\t\texecutionListener.executionStarted(this);\n\t\tThrowableCollector throwableCollector = new OpenTest4JAwareThrowableCollector();\n\n\t\texecuteBeforeSuiteMethods(throwableCollector);\n\n\t\tTestExecutionSummary summary = executeTests(executionListener, requestLevelStore, cancellationToken,\n\t\t\tthrowableCollector);\n\n\t\texecuteAfterSuiteMethods(throwableCollector);\n\n\t\tTestExecutionResult testExecutionResult = computeTestExecutionResult(summary, throwableCollector);\n\t\texecutionListener.executionFinished(this, testExecutionResult);\n\t}\n\n\tprivate void executeBeforeSuiteMethods(ThrowableCollector throwableCollector) {\n\t\tif (throwableCollector.isNotEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tfor (Method beforeSuiteMethod : lifecycleMethods.beforeSuite) {\n\t\t\tthrowableCollector.execute(() -> ReflectionSupport.invokeMethod(beforeSuiteMethod, null));\n\t\t\tif (throwableCollector.isNotEmpty()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate @Nullable TestExecutionSummary executeTests(EngineExecutionListener executionListener,\n\t\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken,\n\t\t\tThrowableCollector throwableCollector) {\n\n\t\tif (throwableCollector.isNotEmpty()) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// #2838: The discovery result from a suite may have been filtered by\n\t\t// post discovery filters from the launcher. The discovery result should\n\t\t// be pruned accordingly.\n\t\tLauncherDiscoveryResult discoveryResult = requireNonNull(this.launcherDiscoveryResult).withRetainedEngines(\n\t\t\tgetChildren()::contains);\n\n\t\treturn requireNonNull(launcher).execute(discoveryResult, executionListener, requestLevelStore,\n\t\t\tcancellationToken);\n\t}\n\n\tprivate void executeAfterSuiteMethods(ThrowableCollector throwableCollector) {\n\t\tfor (Method afterSuiteMethod : lifecycleMethods.afterSuite) {\n\t\t\tthrowableCollector.execute(() -> ReflectionSupport.invokeMethod(afterSuiteMethod, null));\n\t\t}\n\t}\n\n\tprivate TestExecutionResult computeTestExecutionResult(@Nullable TestExecutionSummary summary,\n\t\t\tThrowableCollector throwableCollector) {\n\t\tvar throwable = throwableCollector.getThrowable();\n\t\tif (throwable != null) {\n\t\t\treturn TestExecutionResult.failed(throwable);\n\t\t}\n\t\tif (failIfNoTests && requireNonNull(summary).getTestsFoundCount() == 0) {\n\t\t\treturn TestExecutionResult.failed(new NoTestsDiscoveredException(suiteClass));\n\t\t}\n\t\treturn TestExecutionResult.successful();\n\t}\n\n\t@Override\n\tpublic boolean mayRegisterTests() {\n\t\t// While a suite will not register new tests after discovery, we pretend\n\t\t// it does. This allows the suite to fail if no tests were discovered.\n\t\t// Otherwise, the empty suite would be pruned.\n\t\treturn true;\n\t}\n\n\tprivate static class LifecycleMethods {\n\n\t\tfinal List<Method> beforeSuite;\n\t\tfinal List<Method> afterSuite;\n\n\t\tLifecycleMethods(Class<?> suiteClass, DiscoveryIssueReporter issueReporter) {\n\t\t\tbeforeSuite = LifecycleMethodUtils.findBeforeSuiteMethods(suiteClass, issueReporter);\n\t\t\tafterSuite = LifecycleMethodUtils.findAfterSuiteMethods(suiteClass, issueReporter);\n\t\t}\n\t}\n\n\tprivate record DiscoveryIssueForwardingListener(EngineDiscoveryListener discoveryListener,\n\t\t\tBiFunction<UniqueId, DiscoveryIssue, DiscoveryIssue> issueTransformer)\n\t\t\timplements LauncherDiscoveryListener {\n\n\t\tprivate static final Predicate<Segment> SUITE_SEGMENTS = where(Segment::getType, isEqual(SEGMENT_TYPE));\n\n\t\tstatic DiscoveryIssueForwardingListener create(UniqueId id, EngineDiscoveryListener discoveryListener) {\n\t\t\tboolean isNestedSuite = id.getSegments().stream().filter(SUITE_SEGMENTS).count() > 1;\n\t\t\tif (isNestedSuite) {\n\t\t\t\treturn new DiscoveryIssueForwardingListener(discoveryListener, (__, issue) -> issue);\n\t\t\t}\n\t\t\treturn new DiscoveryIssueForwardingListener(discoveryListener,\n\t\t\t\t(engineUniqueId, issue) -> issue.withMessage(message -> {\n\t\t\t\t\tString engineId = engineUniqueId.getLastSegment().getValue();\n\t\t\t\t\tif (SuiteEngineDescriptor.ENGINE_ID.equals(engineId)) {\n\t\t\t\t\t\treturn message;\n\t\t\t\t\t}\n\t\t\t\t\tString suitePath = engineUniqueId.getSegments().stream() //\n\t\t\t\t\t\t\t.filter(SUITE_SEGMENTS) //\n\t\t\t\t\t\t\t.map(Segment::getValue) //\n\t\t\t\t\t\t\t.collect(joining(\" > \"));\n\t\t\t\t\tif (message.endsWith(\".\")) {\n\t\t\t\t\t\tmessage = message.substring(0, message.length() - 1);\n\t\t\t\t\t}\n\t\t\t\t\treturn \"[%s] %s (via @Suite %s).\".formatted(engineId, message, suitePath);\n\t\t\t\t}));\n\t\t}\n\n\t\t@Override\n\t\tpublic void issueEncountered(UniqueId engineUniqueId, DiscoveryIssue issue) {\n\t\t\tDiscoveryIssue transformedIssue = this.issueTransformer.apply(engineUniqueId, issue);\n\t\t\tthis.discoveryListener.issueEncountered(engineUniqueId, transformedIssue);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/SuiteTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.LinkedHashSet;\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * The JUnit Platform Suite {@link org.junit.platform.engine.TestEngine TestEngine}.\n *\n * @since 1.8\n */\n@API(status = INTERNAL, since = \"1.8\")\npublic final class SuiteTestEngine implements TestEngine {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn SuiteEngineDescriptor.ENGINE_ID;\n\t}\n\n\t/**\n\t * Returns {@code org.junit.platform} as the group ID.\n\t */\n\t@Override\n\tpublic Optional<String> getGroupId() {\n\t\treturn Optional.of(\"org.junit.platform\");\n\t}\n\n\t/**\n\t * Returns {@code junit-platform-suite-engine} as the artifact ID.\n\t */\n\t@Override\n\tpublic Optional<String> getArtifactId() {\n\t\treturn Optional.of(\"junit-platform-suite-engine\");\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tSuiteEngineDescriptor engineDescriptor = new SuiteEngineDescriptor(uniqueId);\n\t\tnew DiscoverySelectorResolver().resolveSelectors(discoveryRequest, engineDescriptor);\n\t\treturn engineDescriptor;\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\tSuiteEngineDescriptor suiteEngineDescriptor = (SuiteEngineDescriptor) request.getRootTestDescriptor();\n\t\tEngineExecutionListener engineExecutionListener = request.getEngineExecutionListener();\n\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore = request.getStore();\n\t\tCancellationToken cancellationToken = request.getCancellationToken();\n\n\t\tengineExecutionListener.executionStarted(suiteEngineDescriptor);\n\n\t\t// Create a mutable copy so test descriptors can be made available for\n\t\t// GC immediately after execution.\n\t\tvar children = new LinkedHashSet<>(suiteEngineDescriptor.getChildren());\n\t\tfor (var iterator = children.iterator(); iterator.hasNext();) {\n\t\t\tvar suiteTestDescriptor = (SuiteTestDescriptor) iterator.next();\n\t\t\tsuiteTestDescriptor.execute(engineExecutionListener, requestLevelStore, cancellationToken);\n\t\t\titerator.remove();\n\t\t}\n\t\tengineExecutionListener.executionFinished(suiteEngineDescriptor, TestExecutionResult.successful());\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/java/org/junit/platform/suite/engine/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Core package for the JUnit Platform Suite test engine.\n */\n\n@NullMarked\npackage org.junit.platform.suite.engine;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-suite-engine/src/main/resources/META-INF/services/org.junit.platform.engine.TestEngine",
    "content": "org.junit.platform.suite.engine.SuiteTestEngine\n"
  },
  {
    "path": "junit-platform-suite-engine/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-platform-testkit/LICENSE.md",
    "content": "Eclipse Public License - v 2.0\n==============================\n\nTHE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.\n\n### 1. Definitions\n\n“Contribution” means:\n* **a)** in the case of the initial Contributor, the initial content Distributed under this Agreement, and\n* **b)** in the case of each subsequent Contributor:\n\t* **i)** changes to the Program, and\n\t* **ii)** additions to the Program;\nwhere such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works.\n\n“Contributor” means any person or entity that Distributes the Program.\n\n“Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.\n\n“Program” means the Contributions Distributed in accordance with this Agreement.\n\n“Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors.\n\n“Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship.\n\n“Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof.\n\n“Distribute” means the acts of **a)** distributing or **b)** making available in any manner that enables the transfer of a copy.\n\n“Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files.\n\n“Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor.\n\n### 2. Grant of Rights\n\n**a)** Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works.\n\n**b)** Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.\n\n**c)** Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.\n\n**d)** Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.\n\n**e)** Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3).\n\n### 3. Requirements\n\n**3.1** If a Contributor Distributes the Program in any form, then:\n\n* **a)** the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and\n\n* **b)** the Contributor may Distribute the Program under a license different than this Agreement, provided that such license:\n\t* **i)** effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;\n\t* **ii)** effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;\n\t* **iii)** does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and\n\t* **iv)** requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3.\n\n**3.2** When the Program is Distributed as Source Code:\n\n* **a)** it must be made available under this Agreement, or if the Program **(i)** is combined with other material in a separate file or files made available under a Secondary License, and **(ii)** the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and\n* **b)** a copy of this Agreement must be included with each copy of the Program.\n\n**3.3** Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (“notices”) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices.\n\n### 4. Commercial Distribution\n\nCommercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: **a)** promptly notify the Commercial Contributor in writing of such claim, and **b)** allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.\n\nFor example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.\n\n### 5. No Warranty\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.\n\n### 6. Disclaimer of Liability\n\nEXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n### 7. General\n\nIf any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.\n\nIf Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.\n\nAll Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.\n\nEveryone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version.\n\nExcept as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement.\n\n#### Exhibit A - Form of Secondary Licenses Notice\n\n> “This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.”\n\nSimply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses.\n\nIf it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n"
  },
  {
    "path": "junit-platform-testkit/junit-platform-testkit.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n}\n\ndescription = \"JUnit Platform Test Kit\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(libs.assertj)\n\tapi(libs.opentest4j)\n\tapi(projects.junitPlatformLauncher)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\tosgiVerification(projects.junitJupiterEngine)\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Defines the Test Kit API for the JUnit Platform.\n *\n * @since 1.4\n * @uses org.junit.platform.engine.TestEngine\n */\nmodule org.junit.platform.testkit {\n\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.assertj.core;\n\trequires org.junit.platform.commons;\n\trequires transitive org.junit.platform.engine;\n\trequires transitive org.junit.platform.launcher;\n\trequires transitive org.opentest4j;\n\n\t// exports org.junit.platform.testkit; empty package\n\texports org.junit.platform.testkit.engine;\n\n\tuses org.junit.platform.engine.TestEngine;\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Assertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * {@code Assertions} is a collection of selected assertion utility methods\n * from JUnit Jupiter for use within the JUnit Platform Test Kit.\n *\n * @since 1.4\n */\nclass Assertions {\n\n\t@FunctionalInterface\n\tinterface Executable {\n\t\tvoid execute() throws Throwable;\n\t}\n\n\tstatic void assertAll(String heading, Stream<Executable> executables) {\n\t\tPreconditions.notNull(executables, \"executables stream must not be null\");\n\n\t\tList<Throwable> failures = executables //\n\t\t\t\t.map(executable -> {\n\t\t\t\t\tPreconditions.notNull(executable, \"individual executables must not be null\");\n\t\t\t\t\ttry {\n\t\t\t\t\t\texecutable.execute();\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable t) {\n\t\t\t\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\t\t\t\treturn t;\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.filter(Objects::nonNull) //\n\t\t\t\t.toList();\n\n\t\tif (!failures.isEmpty()) {\n\t\t\tMultipleFailuresError multipleFailuresError = new MultipleFailuresError(heading, failures);\n\t\t\tfailures.forEach(multipleFailuresError::addSuppressed);\n\t\t\tthrow multipleFailuresError;\n\t\t}\n\t}\n\n\tstatic void assertEquals(long expected, long actual, String message) {\n\t\tif (expected != actual) {\n\t\t\tfailNotEqual(expected, actual, message);\n\t\t}\n\t}\n\n\tprivate static void failNotEqual(long expected, long actual, String message) {\n\t\tfail(format(expected, actual, message), expected, actual);\n\t}\n\n\tprivate static void fail(String message, Object expected, Object actual) {\n\t\tthrow new AssertionFailedError(message, expected, actual);\n\t}\n\n\tprivate static String format(long expected, long actual, String message) {\n\t\treturn buildPrefix(message) + formatValues(expected, actual);\n\t}\n\n\tprivate static String buildPrefix(String message) {\n\t\treturn (StringUtils.isNotBlank(message) ? message + \" ==> \" : \"\");\n\t}\n\n\tprivate static String formatValues(long expected, long actual) {\n\t\treturn \"expected: <%d> but was: <%d>\".formatted(expected, actual);\n\t}\n\n\tprivate Assertions() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineDiscoveryResults.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\n\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * {@code EngineDiscoveryResults} represents the results of test discovery\n * by a {@link org.junit.platform.engine.TestEngine TestEngine} on the JUnit\n * Platform and provides access to the {@link TestDescriptor} of the engine\n * and any {@link DiscoveryIssue DiscoveryIssues} that were encountered.\n *\n * @since 1.13\n */\n@API(status = EXPERIMENTAL, since = \"6.0\")\npublic class EngineDiscoveryResults {\n\n\tprivate final TestDescriptor engineDescriptor;\n\tprivate final List<DiscoveryIssue> discoveryIssues;\n\n\tEngineDiscoveryResults(TestDescriptor engineDescriptor, List<DiscoveryIssue> discoveryIssues) {\n\t\tthis.engineDescriptor = Preconditions.notNull(engineDescriptor, \"Engine descriptor must not be null\");\n\t\tthis.discoveryIssues = List.copyOf(\n\t\t\tPreconditions.notNull(discoveryIssues, \"Discovery issues list must not be null\"));\n\t\tPreconditions.containsNoNullElements(discoveryIssues, \"Discovery issues list must not contain null elements\");\n\t}\n\n\t/**\n\t * {@return the root {@link TestDescriptor} of the engine}\n\t */\n\tpublic TestDescriptor getEngineDescriptor() {\n\t\treturn engineDescriptor;\n\t}\n\n\t/**\n\t * {@return the issues that were encountered during discovery}\n\t */\n\tpublic List<DiscoveryIssue> getDiscoveryIssues() {\n\t\treturn discoveryIssues;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineExecutionResults.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.testkit.engine.Event.byTestDescriptor;\n\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * {@code EngineExecutionResults} provides a fluent API for processing the\n * results of executing a test plan on the JUnit Platform for a given\n * {@link org.junit.platform.engine.TestEngine TestEngine}.\n *\n * @since 1.4\n * @see #allEvents()\n * @see #containerEvents()\n * @see #testEvents()\n * @see ExecutionRecorder\n * @see Events\n * @see Executions\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class EngineExecutionResults {\n\n\tprivate final Events allEvents;\n\tprivate final Events testEvents;\n\tprivate final Events containerEvents;\n\n\t/**\n\t * Construct {@link EngineExecutionResults} from the supplied list of recorded\n\t * {@linkplain Event events}.\n\t *\n\t * @param events the list of events; never {@code null} or\n\t * containing {@code null} elements\n\t */\n\tEngineExecutionResults(List<Event> events) {\n\t\tPreconditions.notNull(events, \"Event list must not be null\");\n\t\tPreconditions.containsNoNullElements(events, \"Event list must not contain null elements\");\n\n\t\tthis.allEvents = new Events(events, \"All\");\n\t\tthis.testEvents = new Events(filterEvents(events, TestDescriptor::isTest), \"Test\");\n\t\tthis.containerEvents = new Events(filterEvents(events, TestDescriptor::isContainer), \"Container\");\n\t}\n\n\t/**\n\t * Get all recorded events.\n\t *\n\t * @since 1.6\n\t * @see #containerEvents()\n\t * @see #testEvents()\n\t */\n\tpublic Events allEvents() {\n\t\treturn this.allEvents;\n\t}\n\n\t/**\n\t * Get recorded events for containers.\n\t *\n\t * <p>In this context, the word \"container\" applies to {@link TestDescriptor\n\t * TestDescriptors} that return {@code true} from {@link TestDescriptor#isContainer()}.\n\t *\n\t * @since 1.6\n\t * @see #allEvents()\n\t * @see #testEvents()\n\t */\n\tpublic Events containerEvents() {\n\t\treturn this.containerEvents;\n\t}\n\n\t/**\n\t * Get recorded events for tests.\n\t *\n\t * <p>In this context, the word \"test\" applies to {@link TestDescriptor\n\t * TestDescriptors} that return {@code true} from {@link TestDescriptor#isTest()}.\n\t *\n\t * @since 1.6\n\t * @see #allEvents()\n\t * @see #containerEvents()\n\t */\n\tpublic Events testEvents() {\n\t\treturn this.testEvents;\n\t}\n\n\t/**\n\t * Filter the supplied list of events using the supplied predicate.\n\t */\n\tprivate static Stream<Event> filterEvents(List<Event> events, Predicate<? super TestDescriptor> predicate) {\n\t\treturn events.stream().filter(byTestDescriptor(predicate));\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EngineTestKit.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Collections.singleton;\nimport static java.util.Objects.requireNonNullElseGet;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;\n\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ServiceLoader;\nimport java.util.function.Consumer;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.core.EngineDiscoveryOrchestrator;\nimport org.junit.platform.launcher.core.EngineExecutionOrchestrator;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult;\nimport org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry;\n\n/**\n * {@code EngineTestKit} provides support for discovering and executing tests\n * for a given {@link TestEngine} and provides convenient access to the results.\n *\n * <p>For <em>discovery</em>, {@link EngineDiscoveryResults} provides access to\n * the {@link TestDescriptor} of the engine and any {@link DiscoveryIssue\n * DiscoveryIssues} that were encountered.\n *\n * <p>For <em>execution</em>, {@link EngineExecutionResults} provides a fluent\n * API to verify the expected results.\n *\n * @since 1.4\n * @see #engine(String)\n * @see #engine(TestEngine)\n * @see #discover(String, LauncherDiscoveryRequest)\n * @see #discover(TestEngine, LauncherDiscoveryRequest)\n * @see #execute(String, LauncherDiscoveryRequest)\n * @see #execute(TestEngine, LauncherDiscoveryRequest)\n * @see EngineDiscoveryResults\n * @see EngineExecutionResults\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic final class EngineTestKit {\n\n\t/**\n\t * Create an execution {@link Builder} for the {@link TestEngine} with the\n\t * supplied ID.\n\t *\n\t * <p>The {@code TestEngine} will be loaded via Java's {@link ServiceLoader}\n\t * mechanism, analogous to the manner in which test engines are loaded in\n\t * the JUnit Platform Launcher API.\n\t *\n\t * <h4>Example Usage</h4>\n\t *\n\t * <pre class=\"code\">\n\t * EngineTestKit\n\t *     .engine(\"junit-jupiter\")\n\t *     .selectors(selectClass(MyTests.class))\n\t *     .execute()\n\t *     .testEvents()\n\t *     .assertStatistics(stats -&gt; stats.started(2).finished(2));\n\t * </pre>\n\t *\n\t * @param engineId the ID of the {@code TestEngine} to use; must not be\n\t * {@code null} or <em>blank</em>\n\t * @return the engine execution {@code Builder}\n\t * @throws PreconditionViolationException if the supplied ID is {@code null}\n\t * or <em>blank</em>, or if the {@code TestEngine} with the supplied ID\n\t * cannot be loaded\n\t * @see #engine(TestEngine)\n\t * @see #execute(String, LauncherDiscoveryRequest)\n\t * @see #execute(TestEngine, LauncherDiscoveryRequest)\n\t */\n\tpublic static Builder engine(String engineId) {\n\t\tPreconditions.notBlank(engineId, \"TestEngine ID must not be null or blank\");\n\t\treturn engine(loadTestEngine(engineId.strip()));\n\t}\n\n\t/**\n\t * Create an execution {@link Builder} for the supplied {@link TestEngine}.\n\t *\n\t * <h4>Example Usage</h4>\n\t *\n\t * <pre class=\"code\">\n\t * EngineTestKit\n\t *     .engine(new MyTestEngine())\n\t *     .selectors(selectClass(MyTests.class))\n\t *     .execute()\n\t *     .testEvents()\n\t *     .assertStatistics(stats -&gt; stats.started(2).finished(2));\n\t * </pre>\n\t *\n\t * @param testEngine the {@code TestEngine} to use; must not be {@code null}\n\t * @return the engine execution {@code Builder}\n\t * @throws PreconditionViolationException if the {@code TestEngine} is\n\t * {@code null}\n\t * @see #engine(String)\n\t * @see #execute(String, LauncherDiscoveryRequest)\n\t * @see #execute(TestEngine, LauncherDiscoveryRequest)\n\t */\n\tpublic static Builder engine(TestEngine testEngine) {\n\t\tPreconditions.notNull(testEngine, \"TestEngine must not be null\");\n\t\treturn new Builder(testEngine);\n\t}\n\n\t/**\n\t * Discover tests for the given {@link LauncherDiscoveryRequest} using the\n\t * {@link TestEngine} with the supplied ID.\n\t *\n\t * <p>The {@code TestEngine} will be loaded via Java's {@link ServiceLoader}\n\t * mechanism, analogous to the manner in which test engines are loaded in\n\t * the JUnit Platform Launcher API.\n\t *\n\t * <p>{@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder}\n\t * provides a convenient way to build an appropriate discovery request to\n\t * supply to this method. As an alternative, consider using\n\t * {@link #engine(TestEngine)} for a more fluent API.\n\t *\n\t * @param engineId the ID of the {@code TestEngine} to use; must not be\n\t * {@code null} or <em>blank</em>\n\t * @param discoveryRequest the {@code LauncherDiscoveryRequest} to use\n\t * @return the results of the discovery\n\t * @throws PreconditionViolationException for invalid arguments or if the\n\t * {@code TestEngine} with the supplied ID cannot be loaded\n\t * @since 1.13\n\t * @see #discover(TestEngine, LauncherDiscoveryRequest)\n\t * @see #engine(String)\n\t * @see #engine(TestEngine)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static EngineDiscoveryResults discover(String engineId, LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notBlank(engineId, \"TestEngine ID must not be null or blank\");\n\t\treturn discover(loadTestEngine(engineId.strip()), discoveryRequest);\n\t}\n\n\t/**\n\t * Discover tests for the given {@link LauncherDiscoveryRequest} using the\n\t * supplied {@link TestEngine}.\n\t *\n\t * <p>{@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder}\n\t * provides a convenient way to build an appropriate discovery request to\n\t * supply to this method. As an alternative, consider using\n\t * {@link #engine(TestEngine)} for a more fluent API.\n\t *\n\t * @param testEngine the {@code TestEngine} to use; must not be {@code null}\n\t * @param discoveryRequest the {@code EngineDiscoveryResults} to use; must\n\t * not be {@code null}\n\t * @return the recorded {@code EngineExecutionResults}\n\t * @throws PreconditionViolationException for invalid arguments\n\t * @since 1.13\n\t * @see #discover(String, LauncherDiscoveryRequest)\n\t * @see #engine(String)\n\t * @see #engine(TestEngine)\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static EngineDiscoveryResults discover(TestEngine testEngine, LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(testEngine, \"TestEngine must not be null\");\n\t\tPreconditions.notNull(discoveryRequest, \"EngineDiscoveryRequest must not be null\");\n\t\tLauncherDiscoveryResult discoveryResult = discoverUsingOrchestrator(testEngine, discoveryRequest);\n\t\tTestDescriptor engineDescriptor = discoveryResult.getEngineTestDescriptor(testEngine);\n\t\tList<DiscoveryIssue> discoveryIssues = discoveryResult.getDiscoveryIssues(testEngine);\n\t\treturn new EngineDiscoveryResults(engineDescriptor, discoveryIssues);\n\t}\n\n\t/**\n\t * Execute tests for the given {@link LauncherDiscoveryRequest} using the\n\t * {@link TestEngine} with the supplied ID.\n\t *\n\t * <p>The {@code TestEngine} will be loaded via Java's {@link ServiceLoader}\n\t * mechanism, analogous to the manner in which test engines are loaded in\n\t * the JUnit Platform Launcher API.\n\t *\n\t * <p>{@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder}\n\t * provides a convenient way to build an appropriate discovery request to\n\t * supply to this method. As an alternative, consider using\n\t * {@link #engine(TestEngine)} for a more fluent API.\n\t *\n\t * @param engineId the ID of the {@code TestEngine} to use; must not be\n\t * {@code null} or <em>blank</em>\n\t * @param discoveryRequest the {@code LauncherDiscoveryRequest} to use\n\t * @return the results of the execution\n\t * @throws PreconditionViolationException for invalid arguments or if the\n\t * {@code TestEngine} with the supplied ID cannot be loaded\n\t * @since 1.7\n\t * @see #execute(TestEngine, LauncherDiscoveryRequest)\n\t * @see #engine(String)\n\t * @see #engine(TestEngine)\n\t */\n\tpublic static EngineExecutionResults execute(String engineId, LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notBlank(engineId, \"TestEngine ID must not be null or blank\");\n\t\treturn execute(loadTestEngine(engineId.strip()), discoveryRequest);\n\t}\n\n\t/**\n\t * Execute tests for the given {@link LauncherDiscoveryRequest} using the\n\t * supplied {@link TestEngine}.\n\t *\n\t * <p>{@link org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder}\n\t * provides a convenient way to build an appropriate discovery request to\n\t * supply to this method. As an alternative, consider using\n\t * {@link #engine(TestEngine)} for a more fluent API.\n\t *\n\t * @param testEngine the {@code TestEngine} to use; must not be {@code null}\n\t * @param discoveryRequest the {@code LauncherDiscoveryRequest} to use; must\n\t * not be {@code null}\n\t * @return the recorded {@code EngineExecutionResults}\n\t * @throws PreconditionViolationException for invalid arguments\n\t * @since 1.7\n\t * @see #execute(String, LauncherDiscoveryRequest)\n\t * @see #engine(String)\n\t * @see #engine(TestEngine)\n\t */\n\tpublic static EngineExecutionResults execute(TestEngine testEngine, LauncherDiscoveryRequest discoveryRequest) {\n\t\tPreconditions.notNull(testEngine, \"TestEngine must not be null\");\n\t\tPreconditions.notNull(discoveryRequest, \"EngineDiscoveryRequest must not be null\");\n\n\t\tExecutionRecorder executionRecorder = new ExecutionRecorder();\n\t\texecuteUsingLauncherOrchestration(testEngine, discoveryRequest, executionRecorder,\n\t\t\tCancellationToken.disabled());\n\t\treturn executionRecorder.getExecutionResults();\n\t}\n\n\tprivate static void executeUsingLauncherOrchestration(TestEngine testEngine,\n\t\t\tLauncherDiscoveryRequest discoveryRequest, EngineExecutionListener engineExecutionListener,\n\t\t\tCancellationToken cancellationToken) {\n\n\t\tLauncherDiscoveryResult discoveryResult = discoverUsingOrchestrator(testEngine, discoveryRequest);\n\t\tTestDescriptor engineTestDescriptor = discoveryResult.getEngineTestDescriptor(testEngine);\n\t\tPreconditions.notNull(engineTestDescriptor, \"TestEngine did not yield a TestDescriptor\");\n\t\tTestExecutionListener noopTestExecutionListener = new TestExecutionListener() {\n\n\t\t};\n\t\twithRequestLevelStore(store -> new EngineExecutionOrchestrator().execute(discoveryResult,\n\t\t\tengineExecutionListener, noopTestExecutionListener, store, cancellationToken));\n\t}\n\n\tprivate static void withRequestLevelStore(Consumer<NamespacedHierarchicalStore<Namespace>> action) {\n\t\ttry (NamespacedHierarchicalStore<Namespace> sessionLevelStore = newStore(null);\n\t\t\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore = newStore(sessionLevelStore)) {\n\t\t\taction.accept(requestLevelStore);\n\t\t}\n\t}\n\n\tprivate static NamespacedHierarchicalStore<Namespace> newStore(\n\t\t\t@Nullable NamespacedHierarchicalStore<Namespace> parentStore) {\n\t\treturn new NamespacedHierarchicalStore<>(parentStore, closeAutoCloseables());\n\t}\n\n\tprivate static LauncherDiscoveryResult discoverUsingOrchestrator(TestEngine testEngine,\n\t\t\tLauncherDiscoveryRequest discoveryRequest) {\n\t\treturn new EngineDiscoveryOrchestrator(singleton(testEngine), emptySet()) //\n\t\t\t\t.discover(discoveryRequest);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static TestEngine loadTestEngine(String engineId) {\n\t\tIterable<TestEngine> testEngines = new ServiceLoaderTestEngineRegistry().loadTestEngines();\n\t\treturn ((Stream<TestEngine>) CollectionUtils.toStream(testEngines)) //\n\t\t\t\t.filter((TestEngine engine) -> engineId.equals(engine.getId()))//\n\t\t\t\t.findFirst()//\n\t\t\t\t.orElseThrow(() -> new PreconditionViolationException(\n\t\t\t\t\t\"Failed to load TestEngine with ID [%s]\".formatted(engineId)));\n\t}\n\n\tprivate EngineTestKit() {\n\t\t/* no-op */\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t/**\n\t * {@link TestEngine} execution builder.\n\t *\n\t * <p>See {@link EngineTestKit#engine(String)} and\n\t * {@link EngineTestKit#engine(TestEngine)} for example usage.\n\t *\n\t * @since 1.4\n\t * @see #selectors(DiscoverySelector...)\n\t * @see #filters(Filter...)\n\t * @see #configurationParameter(String, String)\n\t * @see #configurationParameters(Map)\n\t * @see #execute()\n\t */\n\tpublic static final class Builder {\n\n\t\tprivate final LauncherDiscoveryRequestBuilder requestBuilder = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.outputDirectoryCreator(DisabledOutputDirectoryCreator.INSTANCE);\n\t\tprivate final TestEngine testEngine;\n\t\tprivate @Nullable CancellationToken cancellationToken;\n\n\t\tprivate Builder(TestEngine testEngine) {\n\t\t\tthis.testEngine = testEngine;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied {@linkplain DiscoverySelector discovery selectors}.\n\t\t *\n\t\t * <p>Built-in discovery selectors can be created via the static factory\n\t\t * methods in {@link org.junit.platform.engine.discovery.DiscoverySelectors}.\n\t\t *\n\t\t * @param selectors the discovery selectors to add; never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @see #selectors(List)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t * @see #execute()\n\t\t */\n\t\tpublic Builder selectors(DiscoverySelector... selectors) {\n\t\t\tthis.requestBuilder.selectors(selectors);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied {@linkplain DiscoverySelector discovery selectors}.\n\t\t *\n\t\t * <p>Built-in discovery selectors can be created via the static factory\n\t\t * methods in {@link org.junit.platform.engine.discovery.DiscoverySelectors}.\n\t\t *\n\t\t * @param selectors the discovery selectors to add; never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 6.0\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t * @see #execute()\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"6.0\")\n\t\tpublic Builder selectors(List<? extends DiscoverySelector> selectors) {\n\t\t\tthis.requestBuilder.selectors(selectors);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied {@linkplain Filter filters}.\n\t\t *\n\t\t * <p>Built-in discovery filters can be created via the static factory\n\t\t * methods in {@link org.junit.platform.engine.discovery.ClassNameFilter}\n\t\t * and {@link org.junit.platform.engine.discovery.PackageNameFilter}.\n\t\t *\n\t\t * <p>Built-in post-discovery filters can be created via the static\n\t\t * factory methods in {@link org.junit.platform.launcher.TagFilter}.\n\t\t *\n\t\t * @param filters the filters to add; never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.7\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t * @see #execute()\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder filters(Filter<?>... filters) {\n\t\t\tthis.requestBuilder.filters(filters);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add the supplied <em>configuration parameter</em>.\n\t\t *\n\t\t * @param key the configuration parameter key under which to store the\n\t\t * value; never {@code null} or blank\n\t\t * @param value the value to store\n\t\t * @return this builder for method chaining\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameters(Map)\n\t\t * @see #execute()\n\t\t * @see org.junit.platform.engine.ConfigurationParameters\n\t\t */\n\t\tpublic Builder configurationParameter(String key, String value) {\n\t\t\tthis.requestBuilder.configurationParameter(key, value);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Add all of the supplied <em>configuration parameters</em>.\n\t\t *\n\t\t * @param configurationParameters the map of configuration parameters to add;\n\t\t * never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #execute()\n\t\t * @see org.junit.platform.engine.ConfigurationParameters\n\t\t */\n\t\tpublic Builder configurationParameters(Map<String, String> configurationParameters) {\n\t\t\tthis.requestBuilder.configurationParameters(configurationParameters);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Configure whether implicit configuration parameters should be\n\t\t * considered.\n\t\t *\n\t\t * <p>By default, only configuration parameters that are passed\n\t\t * explicitly to this builder are taken into account. Passing\n\t\t * {@code true} to this method, enables additionally reading\n\t\t * configuration parameters from implicit sources, i.e. system\n\t\t * properties and the {@code junit-platform.properties} classpath\n\t\t * resource.\n\t\t *\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t */\n\t\t@API(status = STABLE, since = \"1.10\")\n\t\tpublic Builder enableImplicitConfigurationParameters(boolean enabled) {\n\t\t\tthis.requestBuilder.enableImplicitConfigurationParameters(enabled);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the\n\t\t * {@link org.junit.platform.engine.reporting.OutputDirectoryProvider}\n\t\t * to use.\n\t\t *\n\t\t * <p>If not specified, a default provider will be used that throws an\n\t\t * exception when attempting to create output directories. This is done\n\t\t * to avoid accidentally writing output files to the file system.\n\t\t *\n\t\t * @param outputDirectoryProvider the output directory provider to use;\n\t\t * never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.12\n\t\t * @see org.junit.platform.engine.reporting.OutputDirectoryProvider\n\t\t * @deprecated Please use\n\t\t * {@link #outputDirectoryCreator(OutputDirectoryCreator)} instead\n\t\t */\n\t\t@SuppressWarnings(\"removal\")\n\t\t@Deprecated(since = \"1.14\", forRemoval = true)\n\t\t@API(status = DEPRECATED, since = \"1.14\")\n\t\tpublic Builder outputDirectoryProvider(\n\t\t\t\torg.junit.platform.engine.reporting.OutputDirectoryProvider outputDirectoryProvider) {\n\t\t\treturn outputDirectoryCreator(outputDirectoryProvider);\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link OutputDirectoryCreator} to use.\n\t\t *\n\t\t * <p>If not specified, a default implementation will be used that\n\t\t * throws an exception when attempting to create output directories.\n\t\t * This is done to avoid accidentally writing output files to the file\n\t\t * system.\n\t\t *\n\t\t * @param outputDirectoryCreator the output directory creator to use;\n\t\t * never {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 1.12\n\t\t * @see OutputDirectoryCreator\n\t\t */\n\t\t@API(status = MAINTAINED, since = \"1.14\")\n\t\tpublic Builder outputDirectoryCreator(OutputDirectoryCreator outputDirectoryCreator) {\n\t\t\tthis.requestBuilder.outputDirectoryCreator(outputDirectoryCreator);\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Set the {@link CancellationToken} to use for execution.\n\t\t *\n\t\t * @param cancellationToken the cancellation token to use; never\n\t\t * {@code null}\n\t\t * @return this builder for method chaining\n\t\t * @since 6.0\n\t\t * @see CancellationToken\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tpublic Builder cancellationToken(CancellationToken cancellationToken) {\n\t\t\tthis.cancellationToken = Preconditions.notNull(cancellationToken, \"cancellationToken must not be null\");\n\t\t\treturn this;\n\t\t}\n\n\t\t/**\n\t\t * Discover tests for the configured {@link TestEngine},\n\t\t * {@linkplain DiscoverySelector discovery selectors},\n\t\t * {@linkplain DiscoveryFilter discovery filters}, and\n\t\t * <em>configuration parameters</em>.\n\t\t *\n\t\t * @return the recorded {@code EngineDiscoveryResults}\n\t\t * @since 1.13\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t */\n\t\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\t\tpublic EngineDiscoveryResults discover() {\n\t\t\tLauncherDiscoveryRequest request = this.requestBuilder.build();\n\t\t\treturn EngineTestKit.discover(this.testEngine, request);\n\t\t}\n\n\t\t/**\n\t\t * Execute tests for the configured {@link TestEngine},\n\t\t * {@linkplain DiscoverySelector discovery selectors},\n\t\t * {@linkplain DiscoveryFilter discovery filters}, and\n\t\t * <em>configuration parameters</em>.\n\t\t *\n\t\t * @return the recorded {@code EngineExecutionResults}\n\t\t * @see #selectors(DiscoverySelector...)\n\t\t * @see #filters(Filter...)\n\t\t * @see #configurationParameter(String, String)\n\t\t * @see #configurationParameters(Map)\n\t\t */\n\t\tpublic EngineExecutionResults execute() {\n\t\t\tLauncherDiscoveryRequest request = this.requestBuilder.build();\n\t\t\tExecutionRecorder executionRecorder = new ExecutionRecorder();\n\t\t\tEngineTestKit.executeUsingLauncherOrchestration(this.testEngine, request, executionRecorder,\n\t\t\t\trequireNonNullElseGet(this.cancellationToken, CancellationToken::disabled));\n\t\t\treturn executionRecorder.getExecutionResults();\n\t\t}\n\n\t\tprivate static class DisabledOutputDirectoryCreator implements OutputDirectoryCreator {\n\n\t\t\tprivate static final OutputDirectoryCreator INSTANCE = new DisabledOutputDirectoryCreator();\n\n\t\t\tprivate static final String FAILURE_MESSAGE = \"Writing outputs is disabled by default when using EngineTestKit. \"\n\t\t\t\t\t+ \"To enable, configure a custom OutputDirectoryCreator via EngineTestKit#outputDirectoryCreator.\";\n\n\t\t\tprivate DisabledOutputDirectoryCreator() {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Path getRootDirectory() {\n\t\t\t\tthrow new JUnitException(FAILURE_MESSAGE);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Path createOutputDirectory(TestDescriptor testDescriptor) {\n\t\t\t\tthrow new JUnitException(FAILURE_MESSAGE);\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Event.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.time.Instant;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * {@code Event} represents a single event fired during execution of\n * a test plan on the JUnit Platform.\n *\n * @since 1.4\n * @see EventType\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class Event {\n\n\t// --- Factories -----------------------------------------------------------\n\n\t/**\n\t * Create an {@code Event} for a reporting entry published for the\n\t * supplied {@link TestDescriptor} and {@link ReportEntry}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @param entry the {@code ReportEntry} that was published; never {@code null}\n\t * @return the newly created {@code Event}\n\t * @see EventType#REPORTING_ENTRY_PUBLISHED\n\t */\n\tpublic static Event reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\tPreconditions.notNull(entry, \"ReportEntry must not be null\");\n\t\treturn new Event(EventType.REPORTING_ENTRY_PUBLISHED, testDescriptor, entry);\n\t}\n\n\t/**\n\t * Create an {@code Event} for a published file for the supplied\n\t * {@link TestDescriptor} and {@link FileEntry}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @param file the {@code FileEntry} that was published; never {@code null}\n\t * @return the newly created {@code Event}\n\t * @since 1.12\n\t * @see EventType#FILE_ENTRY_PUBLISHED\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Event fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\tPreconditions.notNull(file, \"FileEntry must not be null\");\n\t\treturn new Event(EventType.FILE_ENTRY_PUBLISHED, testDescriptor, file);\n\t}\n\n\t/**\n\t * Create an {@code Event} for the dynamic registration of the\n\t * supplied {@link TestDescriptor}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @return the newly created {@code Event}\n\t * @see EventType#DYNAMIC_TEST_REGISTERED\n\t */\n\tpublic static Event dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\treturn new Event(EventType.DYNAMIC_TEST_REGISTERED, testDescriptor, null);\n\t}\n\n\t/**\n\t * Create a <em>skipped</em> {@code Event} for the supplied\n\t * {@link TestDescriptor} and {@code reason}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @param reason the reason the execution was skipped; may be {@code null}\n\t * @return the newly created {@code Event}\n\t * @see EventType#SKIPPED\n\t */\n\tpublic static Event executionSkipped(TestDescriptor testDescriptor, @Nullable String reason) {\n\t\treturn new Event(EventType.SKIPPED, testDescriptor, reason);\n\t}\n\n\t/**\n\t * Create a <em>started</em> {@code Event} for the supplied\n\t * {@link TestDescriptor}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @return the newly created {@code Event}\n\t * @see EventType#STARTED\n\t */\n\tpublic static Event executionStarted(TestDescriptor testDescriptor) {\n\t\treturn new Event(EventType.STARTED, testDescriptor, null);\n\t}\n\n\t/**\n\t * Create a <em>finished</em> {@code Event} for the supplied\n\t * {@link TestDescriptor} and {@link TestExecutionResult}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @param result the {@code TestExecutionResult} for the supplied\n\t * {@code TestDescriptor}; never {@code null}\n\t * @return the newly created {@code Event}\n\t * @see EventType#FINISHED\n\t */\n\tpublic static Event executionFinished(TestDescriptor testDescriptor, TestExecutionResult result) {\n\t\tPreconditions.notNull(result, \"Event of type FINISHED cannot have a null TestExecutionResult\");\n\t\treturn new Event(EventType.FINISHED, testDescriptor, result);\n\t}\n\n\t// --- Predicates ----------------------------------------------------------\n\n\t/**\n\t * Create a {@link Predicate} for {@linkplain Event events} whose payload\n\t * types match the supplied {@code payloadType} and whose payloads match the\n\t * supplied {@code payloadPredicate}.\n\t *\n\t * @param payloadType the required payload type\n\t * @param payloadPredicate a {@code Predicate} to match against payloads\n\t * @return the resulting {@code Predicate}\n\t */\n\tpublic static <T> Predicate<Event> byPayload(Class<T> payloadType, Predicate<? super T> payloadPredicate) {\n\t\treturn event -> event.getPayload(payloadType).filter(payloadPredicate).isPresent();\n\t}\n\n\t/**\n\t * Create a {@link Predicate} for {@linkplain Event events} whose\n\t * {@linkplain EventType event types} match the supplied {@code type}.\n\t *\n\t * @param type the type to match against\n\t * @return the resulting {@code Predicate}\n\t */\n\tpublic static Predicate<Event> byType(EventType type) {\n\t\treturn event -> event.type.equals(type);\n\t}\n\n\t/**\n\t * Create a {@link Predicate} for {@linkplain Event events} whose\n\t * {@link TestDescriptor TestDescriptors} match the supplied\n\t * {@code testDescriptorPredicate}.\n\t *\n\t * @param testDescriptorPredicate a {@code Predicate} to match against test\n\t * descriptors\n\t * @return the resulting {@link Predicate}\n\t */\n\tpublic static Predicate<Event> byTestDescriptor(Predicate<? super TestDescriptor> testDescriptorPredicate) {\n\t\treturn where(Event::getTestDescriptor, testDescriptorPredicate);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate final Instant timestamp = Instant.now();\n\tprivate final EventType type;\n\tprivate final TestDescriptor testDescriptor;\n\n\tprivate final @Nullable Object payload;\n\n\t/**\n\t * Construct an {@code Event} with the supplied arguments.\n\t *\n\t * @param type the type of the event; never {@code null}\n\t * @param testDescriptor the {@code TestDescriptor} associated with the event;\n\t * never {@code null}\n\t * @param payload the generic payload associated with the event; may be {@code null}\n\t */\n\tprivate Event(EventType type, TestDescriptor testDescriptor, @Nullable Object payload) {\n\t\tthis.type = Preconditions.notNull(type, \"EventType must not be null\");\n\t\tthis.testDescriptor = Preconditions.notNull(testDescriptor, \"TestDescriptor must not be null\");\n\t\tthis.payload = payload;\n\t}\n\n\t/**\n\t * Get the type of this {@code Event}.\n\t *\n\t * @return the event type; never {@code null}\n\t * @see EventType\n\t */\n\tpublic EventType getType() {\n\t\treturn this.type;\n\t}\n\n\t/**\n\t * Get the {@link TestDescriptor} associated with this {@code Event}.\n\t *\n\t * @return the {@code TestDescriptor}; never {@code null}\n\t */\n\tpublic TestDescriptor getTestDescriptor() {\n\t\treturn this.testDescriptor;\n\t}\n\n\t/**\n\t * Get the {@link Instant} when this {@code Event} occurred.\n\t *\n\t * @return the {@code Instant} when this {@code Event} occurred;\n\t * never {@code null}\n\t */\n\tpublic Instant getTimestamp() {\n\t\treturn this.timestamp;\n\t}\n\n\t/**\n\t * Get the payload, if available.\n\t *\n\t * @return an {@code Optional} containing the payload; never {@code null}\n\t * but potentially empty\n\t * @see #getPayload(Class)\n\t * @see #getRequiredPayload(Class)\n\t */\n\tpublic Optional<Object> getPayload() {\n\t\treturn Optional.ofNullable(this.payload);\n\t}\n\n\t/**\n\t * Get the payload of the expected type, if available.\n\t *\n\t * <p>This is a convenience method that automatically casts the payload to\n\t * the expected type. If the payload is not present or is not of the expected\n\t * type, this method will return {@link Optional#empty()}.\n\t *\n\t * @param payloadType the expected payload type; never {@code null}\n\t * @return an {@code Optional} containing the payload; never {@code null}\n\t * but potentially empty\n\t * @see #getPayload()\n\t * @see #getRequiredPayload(Class)\n\t */\n\tpublic <T> Optional<T> getPayload(Class<T> payloadType) {\n\t\tPreconditions.notNull(payloadType, \"Payload type must not be null\");\n\t\treturn getPayload().filter(payloadType::isInstance).map(payloadType::cast);\n\t}\n\n\t/**\n\t * Get the payload of the required type.\n\t *\n\t * <p>This is a convenience method that automatically casts the payload to\n\t * the required type. If the payload is not present or is not of the expected\n\t * type, this method will throw an {@link IllegalArgumentException}.\n\t *\n\t * @param payloadType the required payload type; never {@code null}\n\t * @return the payload\n\t * @throws IllegalArgumentException if the payload is of a different type\n\t * or is not present\n\t * @see #getPayload()\n\t * @see #getPayload(Class)\n\t */\n\tpublic <T> T getRequiredPayload(Class<T> payloadType) throws IllegalArgumentException {\n\t\treturn getPayload(payloadType).orElseThrow(//\n\t\t\t() -> new IllegalArgumentException(\"Event does not contain a payload of type \" + payloadType.getName()));\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"type\", this.type)\n\t\t\t\t.append(\"testDescriptor\", this.testDescriptor)\n\t\t\t\t.append(\"timestamp\", this.timestamp)\n\t\t\t\t.append(\"payload\", this.payload)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventConditions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.apiguardian.api.API.Status.STABLE;\nimport static org.assertj.core.api.Assertions.allOf;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\nimport static org.junit.platform.testkit.engine.Event.byPayload;\nimport static org.junit.platform.testkit.engine.Event.byTestDescriptor;\nimport static org.junit.platform.testkit.engine.Event.byType;\nimport static org.junit.platform.testkit.engine.EventType.DYNAMIC_TEST_REGISTERED;\nimport static org.junit.platform.testkit.engine.EventType.FINISHED;\nimport static org.junit.platform.testkit.engine.EventType.SKIPPED;\nimport static org.junit.platform.testkit.engine.EventType.STARTED;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.assertj.core.api.Condition;\nimport org.assertj.core.description.Description;\nimport org.assertj.core.description.JoinDescription;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * Collection of AssertJ {@linkplain Condition conditions} for {@link Event}.\n *\n * @since 1.4\n * @see TestExecutionResultConditions\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic final class EventConditions {\n\n\tprivate EventConditions() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event} matches all the supplied conditions.\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic static Condition<Event> event(Condition<? super Event>... conditions) {\n\t\treturn allOf(conditions);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * an instance of {@link EngineDescriptor}.\n\t */\n\tpublic static Condition<Event> engine() {\n\t\treturn new Condition<>(byTestDescriptor(EngineDescriptor.class::isInstance), \"is an engine\");\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isTest() test} and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the supplied\n\t * {@link String}.\n\t *\n\t * @see #test()\n\t * @see #uniqueIdSubstring(String)\n\t */\n\tpublic static Condition<Event> test(String uniqueIdSubstring) {\n\t\treturn test(uniqueIdSubstring(uniqueIdSubstring));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isTest() test}, its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the supplied\n\t * {@link String}, and its {@linkplain TestDescriptor#getDisplayName()\n\t * display name} equals the supplied {@link String}.\n\t *\n\t * @see #test()\n\t * @see #test(Condition)\n\t * @see #uniqueIdSubstring(String)\n\t * @see #displayName(String)\n\t */\n\tpublic static Condition<Event> test(String uniqueIdSubstring, String displayName) {\n\t\treturn allOf(test(), uniqueIdSubstring(uniqueIdSubstring), displayName(displayName));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event} matches the supplied {@code Condition} and its\n\t * {@linkplain Event#getTestDescriptor() test descriptor} is a\n\t * {@linkplain TestDescriptor#isTest() test}.\n\t *\n\t * <p>For example, {@code test(displayName(\"my display name\"))} can be used\n\t * to match against a test with the given display name.\n\t *\n\t * @since 1.8\n\t * @see #test(String)\n\t * @see #test(String, String)\n\t * @see #displayName(String)\n\t */\n\t@API(status = MAINTAINED, since = \"1.8\")\n\tpublic static Condition<Event> test(Condition<Event> condition) {\n\t\treturn allOf(test(), condition);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isTest() test}.\n\t */\n\tpublic static Condition<Event> test() {\n\t\treturn new Condition<>(byTestDescriptor(TestDescriptor::isTest), \"is a test\");\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isContainer() container} and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the\n\t * fully qualified name of the supplied {@link Class}.\n\t */\n\tpublic static Condition<Event> container(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\treturn container(clazz.getName());\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isContainer() container} and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the supplied\n\t * {@link String}.\n\t */\n\tpublic static Condition<Event> container(String uniqueIdSubstring) {\n\t\treturn container(uniqueIdSubstring(uniqueIdSubstring));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event} matches the supplied {@code Condition} and its\n\t * {@linkplain Event#getTestDescriptor() test descriptor} is a\n\t * {@linkplain TestDescriptor#isContainer() container}.\n\t */\n\tpublic static Condition<Event> container(Condition<Event> condition) {\n\t\treturn allOf(container(), condition);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isContainer() container}.\n\t */\n\tpublic static Condition<Event> container() {\n\t\treturn new Condition<>(byTestDescriptor(TestDescriptor::isContainer), \"is a container\");\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event} matches the supplied {@code Condition}, its\n\t * {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isContainer() container}, and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the\n\t * simple names of the supplied {@link Class} and all of its\n\t * {@linkplain Class#getEnclosingClass() enclosing classes}.\n\t *\n\t * <p>For example, {@code nestedContainer(MyNestedTests.class, displayName(\"my display name\"))}\n\t * can be used to match against a nested container with the given display name.\n\t *\n\t * <p>Please note that this method does not differentiate between static\n\t * nested classes and non-static member classes (e.g., inner classes).\n\t *\n\t * @since 1.8\n\t * @see #nestedContainer(Class)\n\t */\n\t@API(status = MAINTAINED, since = \"1.8\")\n\tpublic static Condition<Event> nestedContainer(Class<?> clazz, Condition<Event> condition) {\n\t\treturn allOf(nestedContainer(clazz), condition);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor} is\n\t * a {@linkplain TestDescriptor#isContainer() container} and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the\n\t * simple names of the supplied {@link Class} and all of its\n\t * {@linkplain Class#getEnclosingClass() enclosing classes}.\n\t *\n\t * <p>Please note that this method does not differentiate between static\n\t * nested classes and non-static member classes (e.g., inner classes).\n\t *\n\t * @see #nestedContainer(Class, Condition)\n\t */\n\tpublic static Condition<Event> nestedContainer(Class<?> clazz) {\n\t\tPreconditions.notNull(clazz, \"Class must not be null\");\n\t\tPreconditions.notNull(clazz.getEnclosingClass(), () -> clazz.getName() + \" must be a nested class\");\n\n\t\tList<String> classNames = new ArrayList<>();\n\t\tfor (Class<?> current = clazz; current != null; current = current.getEnclosingClass()) {\n\t\t\tclassNames.add(0, current.getSimpleName());\n\t\t}\n\n\t\treturn allOf(container(), uniqueIdSubstrings(classNames));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#DYNAMIC_TEST_REGISTERED} and its\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} contains the\n\t * supplied {@link String}.\n\t */\n\tpublic static Condition<Event> dynamicTestRegistered(String uniqueIdSubstring) {\n\t\treturn dynamicTestRegistered(uniqueIdSubstring(uniqueIdSubstring));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#DYNAMIC_TEST_REGISTERED} and it matches the supplied\n\t * {@code Condition}.\n\t */\n\tpublic static Condition<Event> dynamicTestRegistered(Condition<Event> condition) {\n\t\treturn allOf(type(DYNAMIC_TEST_REGISTERED), condition);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an {@link Event}'s\n\t * {@linkplain Event#getTestDescriptor() test descriptor} is equal to the\n\t * {@link UniqueId} parsed from the supplied {@link String}.\n\t *\n\t * @since 1.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static Condition<Event> uniqueId(String uniqueId) {\n\t\treturn uniqueId(UniqueId.parse(uniqueId));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an {@link Event}'s\n\t * {@linkplain Event#getTestDescriptor() test descriptor} is equal to the\n\t * supplied {@link UniqueId}.\n\t *\n\t * @since 1.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static Condition<Event> uniqueId(UniqueId uniqueId) {\n\t\treturn uniqueId(new Condition<>(isEqual(uniqueId), \"equal to '%s'\", uniqueId));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor}\n\t * contains the supplied {@link String}.\n\t */\n\tpublic static Condition<Event> uniqueIdSubstring(String uniqueIdSubstring) {\n\t\tPredicate<UniqueId.Segment> predicate = segment -> {\n\t\t\tString text = segment.getType() + \":\" + segment.getValue();\n\t\t\treturn text.contains(uniqueIdSubstring);\n\t\t};\n\t\treturn uniqueId(new Condition<>(uniqueId -> uniqueId.getSegments().stream().anyMatch(predicate),\n\t\t\t\"substring '%s'\", uniqueIdSubstring));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an {@link Event}'s\n\t * {@linkplain Event#getTestDescriptor() test descriptor} matches the\n\t * supplied {@link Condition}.\n\t *\n\t * @since 1.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static Condition<Event> uniqueId(Condition<? super UniqueId> condition) {\n\t\treturn new Condition<>(byTestDescriptor(where(TestDescriptor::getUniqueId, condition::matches)),\n\t\t\t\"descriptor with uniqueId %s\", condition.description().value());\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor}\n\t * contains all of the supplied strings.\n\t *\n\t * @since 1.6\n\t */\n\tpublic static Condition<Event> uniqueIdSubstrings(String... uniqueIdSubstrings) {\n\t\treturn uniqueIdSubstrings(Arrays.asList(uniqueIdSubstrings));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getUniqueId() unique id} of an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor}\n\t * contains all of the supplied strings.\n\t *\n\t * @since 1.6\n\t */\n\tpublic static Condition<Event> uniqueIdSubstrings(List<String> uniqueIdSubstrings) {\n\t\t// The following worked with AssertJ 3.13.2\n\t\t// return allOf(uniqueIdSubstrings.stream().map(EventConditions::uniqueIdSubstring).toList());\n\n\t\t// Workaround for a regression in AssertJ 3.14.0 that loses the individual descriptions\n\t\t// when multiple conditions are supplied as an Iterable instead of as an array.\n\t\t// The underlying cause is that org.assertj.core.condition.Join.Join(Condition<? super T>...)\n\t\t// tracks all descriptions; whereas,\n\t\t// org.assertj.core.condition.Join.Join(Iterable<? extends Condition<? super T>>)\n\t\t// does not track all descriptions.\n\t\tList<Condition<Event>> conditions = uniqueIdSubstrings.stream()//\n\t\t\t\t.map(EventConditions::uniqueIdSubstring)//\n\t\t\t\t.toList();\n\t\tList<Description> descriptions = conditions.stream().map(Condition::description).toList();\n\t\treturn allOf(conditions).describedAs(new JoinDescription(\"all of :[\", \"]\", descriptions));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getDisplayName() display name} of an\n\t * {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor}\n\t * is equal to the supplied {@link String}.\n\t */\n\tpublic static Condition<Event> displayName(String displayName) {\n\t\treturn new Condition<>(byTestDescriptor(where(TestDescriptor::getDisplayName, isEqual(displayName))),\n\t\t\t\"descriptor with display name '%s'\", displayName);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if the\n\t * {@linkplain TestDescriptor#getLegacyReportingName()} () legacy reporting name}\n\t * of an {@link Event}'s {@linkplain Event#getTestDescriptor() test descriptor}\n\t * is equal to the supplied {@link String}.\n\t *\n\t * @since 1.13\n\t */\n\t@API(status = EXPERIMENTAL, since = \"6.0\")\n\tpublic static Condition<Event> legacyReportingName(String legacyReportingName) {\n\t\treturn new Condition<>(\n\t\t\tbyTestDescriptor(where(TestDescriptor::getLegacyReportingName, isEqual(legacyReportingName))),\n\t\t\t\"descriptor with legacy reporting name '%s'\", legacyReportingName);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#SKIPPED} and the\n\t * {@linkplain Event#getPayload() reason} is equal to the supplied\n\t * {@link String}.\n\t *\n\t * @see #reason(String)\n\t */\n\tpublic static Condition<Event> skippedWithReason(String expectedReason) {\n\t\treturn allOf(type(SKIPPED), reason(expectedReason));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#SKIPPED} and the\n\t * {@linkplain Event#getPayload() reason} matches the supplied\n\t * {@link Predicate}.\n\t *\n\t * @see #reason(Predicate)\n\t */\n\tpublic static Condition<Event> skippedWithReason(Predicate<String> predicate) {\n\t\treturn allOf(type(SKIPPED), reason(predicate));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#STARTED}.\n\t */\n\tpublic static Condition<Event> started() {\n\t\treturn type(STARTED);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#FINISHED} and its\n\t * {@linkplain Event#getPayload() result} has a\n\t * {@linkplain TestExecutionResult#getStatus() status} of\n\t * {@link TestExecutionResult.Status#ABORTED ABORTED} as well as a\n\t * {@linkplain TestExecutionResult#getThrowable() cause} that matches all of\n\t * the supplied conditions.\n\t */\n\t@SafeVarargs\n\tpublic static Condition<Event> abortedWithReason(Condition<Throwable>... conditions) {\n\t\treturn finishedWithCause(ABORTED, conditions);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#FINISHED} and its\n\t * {@linkplain Event#getPayload() result} has a\n\t * {@linkplain TestExecutionResult#getStatus() status} of\n\t * {@link TestExecutionResult.Status#FAILED FAILED} as well as a\n\t * {@linkplain TestExecutionResult#getThrowable() cause} that matches all of\n\t * the supplied {@code Conditions}.\n\t */\n\t@SafeVarargs\n\tpublic static Condition<Event> finishedWithFailure(Condition<Throwable>... conditions) {\n\t\treturn finishedWithCause(FAILED, conditions);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Condition<Event> finishedWithCause(Status expectedStatus, Condition<Throwable>... conditions) {\n\t\tList<Condition<TestExecutionResult>> list = Arrays.asList(TestExecutionResultConditions.status(expectedStatus),\n\t\t\tTestExecutionResultConditions.throwable(conditions));\n\n\t\treturn finished(allOf(list));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#FINISHED} and its\n\t * {@linkplain Event#getPayload() result} has a\n\t * {@linkplain TestExecutionResult#getStatus() status} of\n\t * {@link TestExecutionResult.Status#FAILED FAILED}.\n\t */\n\tpublic static Condition<Event> finishedWithFailure() {\n\t\treturn finished(TestExecutionResultConditions.status(FAILED));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#FINISHED} and its\n\t * {@linkplain Event#getPayload() result} has a\n\t * {@linkplain TestExecutionResult#getStatus() status} of\n\t * {@link TestExecutionResult.Status#SUCCESSFUL SUCCESSFUL}.\n\t */\n\tpublic static Condition<Event> finishedSuccessfully() {\n\t\treturn finished(TestExecutionResultConditions.status(SUCCESSFUL));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is\n\t * {@link EventType#FINISHED} and its\n\t * {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link TestExecutionResult} that matches the supplied {@code Condition}.\n\t */\n\tpublic static Condition<Event> finished(Condition<TestExecutionResult> resultCondition) {\n\t\treturn allOf(type(FINISHED), result(resultCondition));\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getType() type} is equal to the\n\t * supplied {@link EventType}.\n\t */\n\tpublic static Condition<Event> type(EventType expectedType) {\n\t\treturn new Condition<>(byType(expectedType), \"type is %s\", expectedType);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link TestExecutionResult} that matches the supplied {@code Condition}.\n\t */\n\tpublic static Condition<Event> result(Condition<TestExecutionResult> condition) {\n\t\treturn new Condition<>(byPayload(TestExecutionResult.class, condition::matches), \"event with result where %s\",\n\t\t\tcondition);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link String} that is equal to the supplied value.\n\t */\n\tpublic static Condition<Event> reason(String expectedReason) {\n\t\treturn new Condition<>(byPayload(String.class, isEqual(expectedReason)), \"event with reason '%s'\",\n\t\t\texpectedReason);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link String} that matches the supplied {@link Predicate}.\n\t */\n\tpublic static Condition<Event> reason(Predicate<String> predicate) {\n\t\treturn new Condition<>(byPayload(String.class, predicate), \"event with custom reason predicate\");\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link ReportEntry} that contains the supplied key-value pairs.\n\t */\n\t@API(status = STABLE, since = \"1.10\")\n\tpublic static Condition<Event> reportEntry(Map<String, String> keyValuePairs) {\n\t\treturn new Condition<>(byPayload(ReportEntry.class, it -> it.getKeyValuePairs().equals(keyValuePairs)),\n\t\t\t\"event for report entry with key-value pairs %s\", keyValuePairs);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if an\n\t * {@link Event}'s {@linkplain Event#getPayload() payload} is an instance of\n\t * {@link FileEntry} that contains a file that matches the supplied\n\t * {@link Predicate}.\n\t *\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic static Condition<Event> fileEntry(Predicate<FileEntry> predicate) {\n\t\treturn new Condition<>(byPayload(FileEntry.class, predicate), \"event for file entry with custom predicate\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventStatistics.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.testkit.engine.Assertions.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.testkit.engine.Assertions.Executable;\n\n/**\n * {@code EventStatistics} provides a fluent API for asserting statistics\n * for {@linkplain Event events}.\n *\n * <p>{@code EventStatistics} is used in conjunction with\n * {@link Events#assertStatistics(java.util.function.Consumer)} as in the\n * following example.\n *\n * <p>{@code events.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));}\n *\n * @since 1.4\n * @see Event\n * @see Events\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class EventStatistics {\n\n\tprivate final List<Executable> executables = new ArrayList<>();\n\tprivate final Events events;\n\n\tEventStatistics(Events events, String category) {\n\t\tthis.events = events;\n\t}\n\n\tvoid assertAll() {\n\t\tAssertions.assertAll(this.events.getCategory() + \" Event Statistics\", this.executables.stream());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t/**\n\t * Specify the number of expected <em>skipped</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics skipped(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.skipped().count(), \"skipped\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>started</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics started(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.started().count(), \"started\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>finished</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics finished(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.finished().count(), \"finished\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>aborted</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics aborted(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.aborted().count(), \"aborted\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>succeeded</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics succeeded(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.succeeded().count(), \"succeeded\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>failed</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics failed(long expected) {\n\t\tthis.executables.add(() -> assertEquals(expected, this.events.failed().count(), \"failed\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>reporting entry publication</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics reportingEntryPublished(long expected) {\n\t\tthis.executables.add(\n\t\t\t() -> assertEquals(expected, this.events.reportingEntryPublished().count(), \"reporting entry published\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>file entry publication</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic EventStatistics fileEntryPublished(long expected) {\n\t\tthis.executables.add(\n\t\t\t() -> assertEquals(expected, this.events.fileEntryPublished().count(), \"file entry published\"));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Specify the number of expected <em>dynamic registration</em> events.\n\t *\n\t * @param expected the expected number of events\n\t * @return this {@code EventStatistics} for method chaining\n\t */\n\tpublic EventStatistics dynamicallyRegistered(long expected) {\n\t\tthis.executables.add(\n\t\t\t() -> assertEquals(expected, this.events.dynamicallyRegistered().count(), \"dynamically registered\"));\n\t\treturn this;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/EventType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * Enumeration of the different possible {@link Event} types.\n *\n * @since 1.4\n * @see Event\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic enum EventType {\n\n\t/**\n\t * Signals that a {@link TestDescriptor} has been dynamically registered.\n\t *\n\t * @see org.junit.platform.engine.EngineExecutionListener#dynamicTestRegistered(TestDescriptor)\n\t */\n\tDYNAMIC_TEST_REGISTERED,\n\n\t/**\n\t * Signals that the execution of a {@link TestDescriptor} has been skipped.\n\t *\n\t * @see org.junit.platform.engine.EngineExecutionListener#executionSkipped(TestDescriptor, String)\n\t */\n\tSKIPPED,\n\n\t/**\n\t * Signals that the execution of a {@link TestDescriptor} has started.\n\t *\n\t * @see org.junit.platform.engine.EngineExecutionListener#executionStarted(TestDescriptor)\n\t */\n\tSTARTED,\n\n\t/**\n\t * Signals that the execution of a {@link TestDescriptor} has finished,\n\t * regardless of the outcome.\n\t *\n\t * @see org.junit.platform.engine.EngineExecutionListener#executionFinished(TestDescriptor, TestExecutionResult)\n\t */\n\tFINISHED,\n\n\t/**\n\t * Signals that a {@link TestDescriptor} published a reporting entry.\n\t *\n\t * @see org.junit.platform.engine.EngineExecutionListener#reportingEntryPublished(TestDescriptor, ReportEntry)\n\t */\n\tREPORTING_ENTRY_PUBLISHED,\n\n\t/**\n\t * Signals that a {@link TestDescriptor} published a file entry.\n\t *\n\t * @since 1.12\n\t * @see org.junit.platform.engine.EngineExecutionListener#fileEntryPublished(TestDescriptor, FileEntry)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tFILE_ENTRY_PUBLISHED\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Events.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static java.util.Collections.sort;\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.testkit.engine.Event.byPayload;\nimport static org.junit.platform.testkit.engine.Event.byType;\n\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.assertj.core.api.ListAssert;\nimport org.assertj.core.api.SoftAssertions;\nimport org.assertj.core.data.Index;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * {@code Events} is a facade that provides a fluent API for working with\n * {@linkplain Event events}.\n *\n * @since 1.4\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic final class Events {\n\n\tprivate final List<Event> events;\n\tprivate final String category;\n\n\tEvents(Stream<Event> events, String category) {\n\t\tthis(Preconditions.notNull(events, \"Event stream must not be null\").toList(), category);\n\t}\n\n\tEvents(List<Event> events, String category) {\n\t\tPreconditions.notNull(events, \"Event list must not be null\");\n\t\tPreconditions.containsNoNullElements(events, \"Event list must not contain null elements\");\n\n\t\tthis.events = List.copyOf(events);\n\t\tthis.category = category;\n\t}\n\n\tString getCategory() {\n\t\treturn this.category;\n\t}\n\n\t// --- Accessors -----------------------------------------------------------\n\n\t/**\n\t * Get the {@linkplain Event events} as a {@link List}.\n\t *\n\t * @return the list of events; never {@code null}\n\t * @see #stream()\n\t */\n\tpublic List<Event> list() {\n\t\treturn this.events;\n\t}\n\n\t/**\n\t * Get the {@linkplain Event events} as a {@link Stream}.\n\t *\n\t * @return the stream of events; never {@code null}\n\t * @see #list()\n\t */\n\tpublic Stream<Event> stream() {\n\t\treturn this.events.stream();\n\t}\n\n\t/**\n\t * Shortcut for {@code events.stream().map(mapper)}.\n\t *\n\t * @param mapper a {@code Function} to apply to each event; never {@code null}\n\t * @return the mapped stream of events; never {@code null}\n\t * @see #stream()\n\t * @see Stream#map(Function)\n\t */\n\tpublic <R> Stream<R> map(Function<? super Event, ? extends R> mapper) {\n\t\tPreconditions.notNull(mapper, \"Mapping function must not be null\");\n\t\treturn stream().map(mapper);\n\t}\n\n\t/**\n\t * Shortcut for {@code events.stream().filter(predicate)}.\n\t *\n\t * @param predicate a {@code Predicate} to apply to each event to decide if\n\t * it should be included in the filtered stream; never {@code null}\n\t * @return the filtered stream of events; never {@code null}\n\t * @see #stream()\n\t * @see Stream#filter(Predicate)\n\t */\n\tpublic Stream<Event> filter(Predicate<? super Event> predicate) {\n\t\tPreconditions.notNull(predicate, \"Filter predicate must not be null\");\n\t\treturn stream().filter(predicate);\n\t}\n\n\t/**\n\t * Get the {@link Executions} for the current set of {@linkplain Event events}.\n\t *\n\t * <p>The set of executions is derived from the current set of events by\n\t * taking single {@linkplain Event#executionSkipped(TestDescriptor, String)\n\t * execution skipped} events and pairs of {@linkplain Event#executionStarted(TestDescriptor)\n\t * execution started} and {@linkplain Event#executionFinished(TestDescriptor, TestExecutionResult)\n\t * execution finished} events. As a consequence, executions for which either\n\t * the started or finished event is not included in the current set of\n\t * events are not included in the {@code Executions} returned from this method.\n\t *\n\t * @return an instance of {@code Executions} for the current set of events;\n\t * never {@code null}\n\t */\n\tpublic Executions executions() {\n\t\treturn new Executions(this.events, this.category);\n\t}\n\n\t// --- Statistics ----------------------------------------------------------\n\n\t/**\n\t * Get the number of {@linkplain Event events} contained in this {@code Events}\n\t * object.\n\t */\n\tpublic long count() {\n\t\treturn this.events.size();\n\t}\n\n\t// --- Built-in Filters ----------------------------------------------------\n\n\t/**\n\t * Get the skipped {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events skipped() {\n\t\treturn new Events(eventsByType(EventType.SKIPPED), this.category + \" Skipped\");\n\t}\n\n\t/**\n\t * Get the started {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events started() {\n\t\treturn new Events(eventsByType(EventType.STARTED), this.category + \" Started\");\n\t}\n\n\t/**\n\t * Get the finished {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events finished() {\n\t\treturn new Events(eventsByType(EventType.FINISHED), this.category + \" Finished\");\n\t}\n\n\t/**\n\t * Get the aborted {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events aborted() {\n\t\treturn new Events(finishedEventsByStatus(Status.ABORTED), this.category + \" Aborted\");\n\t}\n\n\t/**\n\t * Get the succeeded {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events succeeded() {\n\t\treturn new Events(finishedEventsByStatus(Status.SUCCESSFUL), this.category + \" Successful\");\n\t}\n\n\t/**\n\t * Get the failed {@link Events} contained in this {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events failed() {\n\t\treturn new Events(finishedEventsByStatus(Status.FAILED), this.category + \" Failed\");\n\t}\n\n\t/**\n\t * Get the reporting entry publication {@link Events} contained in this\n\t * {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events reportingEntryPublished() {\n\t\treturn new Events(eventsByType(EventType.REPORTING_ENTRY_PUBLISHED),\n\t\t\tthis.category + \" Reporting Entry Published\");\n\t}\n\n\t/**\n\t * Get the file entry publication {@link Events} contained in this\n\t * {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\tpublic Events fileEntryPublished() {\n\t\treturn new Events(eventsByType(EventType.FILE_ENTRY_PUBLISHED), this.category + \" File Entry Published\");\n\t}\n\n\t/**\n\t * Get the dynamic registration {@link Events} contained in this\n\t * {@code Events} object.\n\t *\n\t * @return the filtered {@code Events}; never {@code null}\n\t */\n\tpublic Events dynamicallyRegistered() {\n\t\treturn new Events(eventsByType(EventType.DYNAMIC_TEST_REGISTERED), this.category + \" Dynamically Registered\");\n\t}\n\n\t// --- Assertions ----------------------------------------------------------\n\n\t/**\n\t * Assert statistics for the {@linkplain Event events} contained in this\n\t * {@code Events} object.\n\t *\n\t * <h4>Example</h4>\n\t *\n\t * <p>{@code events.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));}\n\t *\n\t * @param statisticsConsumer a {@link Consumer} of {@link EventStatistics};\n\t * never {@code null}\n\t * @return this {@code Events} object for method chaining; never {@code null}\n\t */\n\tpublic Events assertStatistics(Consumer<EventStatistics> statisticsConsumer) {\n\t\tPreconditions.notNull(statisticsConsumer, \"Consumer must not be null\");\n\t\tEventStatistics eventStatistics = new EventStatistics(this, this.category);\n\t\tstatisticsConsumer.accept(eventStatistics);\n\t\teventStatistics.assertAll();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Assert that all {@linkplain Event events} contained in this {@code Events}\n\t * object exactly match the provided conditions.\n\t *\n\t * <p>Conditions can be imported statically from {@link EventConditions}\n\t * and {@link TestExecutionResultConditions}.\n\t *\n\t * <h4>Example</h4>\n\t *\n\t * <pre class=\"code\">\n\t * executionResults.testEvents().assertEventsMatchExactly(\n\t *     event(test(\"exampleTestMethod\"), started()),\n\t *     event(test(\"exampleTestMethod\"), finishedSuccessfully())\n\t * );\n\t * </pre>\n\t *\n\t * @param conditions the conditions to match against; never {@code null}\n\t * @see #assertEventsMatchLoosely(Condition...)\n\t * @see #assertEventsMatchLooselyInOrder(Condition...)\n\t * @see EventConditions\n\t * @see TestExecutionResultConditions\n\t */\n\t@SafeVarargs\n\tpublic final void assertEventsMatchExactly(Condition<? super Event>... conditions) {\n\t\tPreconditions.notNull(conditions, \"conditions must not be null\");\n\t\tassertEventsMatchExactly(this.events, conditions);\n\t}\n\n\t/**\n\t * Assert that all provided conditions are matched by an {@linkplain Event event}\n\t * contained in this {@code Events} object, regardless of order.\n\t *\n\t * <p>Note that this method performs a partial match. Thus, some events may\n\t * not match any of the provided conditions.\n\t *\n\t * <p>Conditions can be imported statically from {@link EventConditions}\n\t * and {@link TestExecutionResultConditions}.\n\t *\n\t * <h4>Example</h4>\n\t *\n\t * <pre class=\"code\">\n\t * executionResults.testEvents().assertEventsMatchLoosely(\n\t *     event(test(\"exampleTestMethod\"), started()),\n\t *     event(test(\"exampleTestMethod\"), finishedSuccessfully())\n\t * );\n\t * </pre>\n\t *\n\t * @param conditions the conditions to match against; never {@code null}\n\t * @since 1.7\n\t * @see #assertEventsMatchExactly(Condition...)\n\t * @see #assertEventsMatchLooselyInOrder(Condition...)\n\t * @see EventConditions\n\t * @see TestExecutionResultConditions\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic final void assertEventsMatchLoosely(Condition<? super Event>... conditions) {\n\t\tPreconditions.notNull(conditions, \"conditions must not be null\");\n\t\tPreconditions.containsNoNullElements(conditions, \"conditions must not contain null elements\");\n\t\tassertEventsMatchLoosely(this.events, conditions);\n\t}\n\n\t/**\n\t * Assert that all provided conditions are matched by an {@linkplain Event event}\n\t * contained in this {@code Events} object.\n\t *\n\t * <p>Note that this method performs a partial match. Thus, some events may\n\t * not match any of the provided conditions; however, the conditions provided\n\t * must be in the correct order.\n\t *\n\t * <p>Conditions can be imported statically from {@link EventConditions}\n\t * and {@link TestExecutionResultConditions}.\n\t *\n\t * <h4>Example</h4>\n\t *\n\t * <pre class=\"code\">\n\t * executionResults.testEvents().assertEventsMatchLooselyInOrder(\n\t *     event(test(\"exampleTestMethod\"), started()),\n\t *     event(test(\"exampleTestMethod\"), finishedSuccessfully())\n\t * );\n\t * </pre>\n\t *\n\t * @param conditions the conditions to match against; never {@code null}\n\t * @since 1.7\n\t * @see #assertEventsMatchExactly(Condition...)\n\t * @see #assertEventsMatchLoosely(Condition...)\n\t * @see EventConditions\n\t * @see TestExecutionResultConditions\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic final void assertEventsMatchLooselyInOrder(Condition<? super Event>... conditions) {\n\t\tPreconditions.notNull(conditions, \"conditions must not be null\");\n\t\tPreconditions.containsNoNullElements(conditions, \"conditions must not contain null elements\");\n\t\tassertEventsMatchLooselyInOrder(this.events, conditions);\n\t}\n\n\t/**\n\t * Shortcut for {@code org.assertj.core.api.Assertions.assertThat(events.list())}.\n\t *\n\t * @return an instance of {@link ListAssert} for events; never {@code null}\n\t * @see org.assertj.core.api.Assertions#assertThat(List)\n\t * @see org.assertj.core.api.ListAssert\n\t */\n\tpublic ListAssert<Event> assertThatEvents() {\n\t\treturn org.assertj.core.api.Assertions.assertThat(list());\n\t}\n\n\t// --- Diagnostics ---------------------------------------------------------\n\n\t/**\n\t * Print all events to {@link System#out}.\n\t *\n\t * @return this {@code Events} object for method chaining; never {@code null}\n\t */\n\tpublic Events debug() {\n\t\tdebug(System.out);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Print all events to the supplied {@link OutputStream}.\n\t *\n\t * @param out the {@code OutputStream} to print to; never {@code null}\n\t * @return this {@code Events} object for method chaining; never {@code null}\n\t */\n\t@SuppressWarnings(\"DefaultCharset\")\n\tpublic Events debug(OutputStream out) {\n\t\tPreconditions.notNull(out, \"OutputStream must not be null\");\n\t\tdebug(new PrintWriter(out, true));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Print all events to the supplied {@link Writer}.\n\t *\n\t * @param writer the {@code Writer} to print to; never {@code null}\n\t * @return this {@code Events} object for method chaining; never {@code null}\n\t */\n\tpublic Events debug(Writer writer) {\n\t\tPreconditions.notNull(writer, \"Writer must not be null\");\n\t\tdebug(new PrintWriter(writer, true));\n\t\treturn this;\n\t}\n\n\tprivate Events debug(PrintWriter printWriter) {\n\t\tprintWriter.println(this.category + \" Events:\");\n\t\tthis.events.forEach(event -> printWriter.printf(\"\\t%s%n\", event));\n\t\treturn this;\n\t}\n\n\t// --- Internals -----------------------------------------------------------\n\n\tprivate Stream<Event> eventsByType(EventType type) {\n\t\tPreconditions.notNull(type, \"EventType must not be null\");\n\t\treturn stream().filter(byType(type));\n\t}\n\n\tprivate Stream<Event> finishedEventsByStatus(Status status) {\n\t\tPreconditions.notNull(status, \"Status must not be null\");\n\t\treturn eventsByType(EventType.FINISHED)//\n\t\t\t\t.filter(byPayload(TestExecutionResult.class, where(TestExecutionResult::getStatus, isEqual(status))));\n\t}\n\n\t@SafeVarargs\n\tprivate static void assertEventsMatchExactly(List<Event> events, Condition<? super Event>... conditions) {\n\t\tAssertions.assertThat(events).hasSize(conditions.length);\n\n\t\tSoftAssertions softly = new SoftAssertions();\n\t\tfor (int i = 0; i < conditions.length; i++) {\n\t\t\tsoftly.assertThat(events).has(conditions[i], Index.atIndex(i));\n\t\t}\n\t\tsoftly.assertAll();\n\t}\n\n\t@SafeVarargs\n\tprivate static void assertEventsMatchLoosely(List<Event> events, Condition<? super Event>... conditions) {\n\t\tSoftAssertions softly = new SoftAssertions();\n\t\tfor (Condition<? super Event> condition : conditions) {\n\t\t\tcheckCondition(events, softly, condition);\n\t\t}\n\t\tsoftly.assertAll();\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate static void assertEventsMatchLooselyInOrder(List<Event> events, Condition<? super Event>... conditions) {\n\t\tAssertions.assertThat(conditions).hasSizeLessThanOrEqualTo(events.size());\n\t\tSoftAssertions softly = new SoftAssertions();\n\n\t\t// @formatter:off\n\t\tList<Integer> indices = Arrays.stream(conditions)\n\t\t\t\t.map(condition -> findEvent(events, softly, condition))\n\t\t\t\t.filter(Objects::nonNull)\n\t\t\t\t.map(events::indexOf)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\n\t\tif (isNotInIncreasingOrder(indices)) {\n\t\t\tthrow new AssertionFailedError(\"Conditions are not in the correct order.\");\n\t\t}\n\n\t\tsoftly.assertAll();\n\t}\n\n\tprivate static boolean isNotInIncreasingOrder(List<Integer> indices) {\n\t\tList<Integer> copy = new ArrayList<>(indices);\n\t\tsort(copy);\n\n\t\treturn !indices.equals(copy);\n\t}\n\n\tprivate static void checkCondition(List<Event> events, SoftAssertions softly, Condition<? super Event> condition) {\n\t\tif (events.stream().noneMatch(condition::matches)) {\n\t\t\tsoftly.fail(\"Condition did not match any event: \" + condition);\n\t\t}\n\t}\n\n\tprivate static @Nullable Event findEvent(List<Event> events, SoftAssertions softly,\n\t\t\tCondition<? super Event> condition) {\n\t\t// @formatter:off\n\t\tOptional<Event> matchedEvent = events.stream()\n\t\t\t\t.filter(condition::matches)\n\t\t\t\t.findFirst();\n\t\t// @formatter:on\n\n\t\tif (matchedEvent.isEmpty()) {\n\t\t\tsoftly.fail(\"Condition did not match any event: \" + condition);\n\t\t}\n\n\t\treturn matchedEvent.orElse(null);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Execution.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.time.Duration;\nimport java.time.Instant;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * {@code Execution} encapsulates metadata for the execution of a single\n * {@link TestDescriptor}.\n *\n * <p>The execution either coincides with a single\n * {@linkplain org.junit.platform.engine.EngineExecutionListener#executionSkipped(TestDescriptor, String) skipped}\n * event or spans an\n * {@linkplain org.junit.platform.engine.EngineExecutionListener#executionStarted(TestDescriptor) execution started} and\n * {@linkplain org.junit.platform.engine.EngineExecutionListener#executionFinished(TestDescriptor, TestExecutionResult)\n * execution finished} event.\n *\n * @since 1.4\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class Execution {\n\n\t// --- Factories -----------------------------------------------------------\n\n\t/**\n\t * Create a new instance of an {@code Execution} that finished with the\n\t * provided {@link TestExecutionResult}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} that finished;\n\t * never {@code null}\n\t * @param startInstant the {@code Instant} that the {@code Execution} started;\n\t * never {@code null}\n\t * @param endInstant the {@code Instant} that the {@code Execution} completed;\n\t * never {@code null}\n\t * @param executionResult the {@code TestExecutionResult} of the finished\n\t * {@code TestDescriptor}; never {@code null}\n\t * @return the newly created {@code Execution} instance; never {@code null}\n\t */\n\tpublic static Execution finished(TestDescriptor testDescriptor, Instant startInstant, Instant endInstant,\n\t\t\tTestExecutionResult executionResult) {\n\n\t\treturn new Execution(testDescriptor, startInstant, endInstant, TerminationInfo.executed(executionResult));\n\t}\n\n\t/**\n\t * Create a new instance of an {@code Execution} that was skipped with the\n\t * provided {@code skipReason}.\n\t *\n\t * @param testDescriptor the {@code TestDescriptor} that finished;\n\t * never {@code null}\n\t * @param startInstant the {@code Instant} that the {@code Execution} started;\n\t * never {@code null}\n\t * @param endInstant the {@code Instant} that the {@code Execution} completed;\n\t * never {@code null}\n\t * @param skipReason the reason the {@code TestDescriptor} was skipped;\n\t * may be {@code null}\n\t * @return the newly created {@code Execution} instance; never {@code null}\n\t */\n\tpublic static Execution skipped(TestDescriptor testDescriptor, Instant startInstant, Instant endInstant,\n\t\t\tString skipReason) {\n\n\t\treturn new Execution(testDescriptor, startInstant, endInstant, TerminationInfo.skipped(skipReason));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate final TestDescriptor testDescriptor;\n\tprivate final Instant startInstant;\n\tprivate final Instant endInstant;\n\tprivate final Duration duration;\n\tprivate final TerminationInfo terminationInfo;\n\n\tprivate Execution(TestDescriptor testDescriptor, Instant startInstant, Instant endInstant,\n\t\t\tTerminationInfo terminationInfo) {\n\n\t\tPreconditions.notNull(testDescriptor, \"TestDescriptor must not be null\");\n\t\tPreconditions.notNull(startInstant, \"Start Instant must not be null\");\n\t\tPreconditions.notNull(endInstant, \"End Instant must not be null\");\n\t\tPreconditions.notNull(terminationInfo, \"TerminationInfo must not be null\");\n\n\t\tthis.testDescriptor = testDescriptor;\n\t\tthis.startInstant = startInstant;\n\t\tthis.endInstant = endInstant;\n\t\tthis.duration = Duration.between(startInstant, endInstant);\n\t\tthis.terminationInfo = terminationInfo;\n\t}\n\n\t/**\n\t * Get the {@link TestDescriptor} for this {@code Execution}.\n\t *\n\t * @return the {@code TestDescriptor} for this {@code Execution}\n\t */\n\tpublic TestDescriptor getTestDescriptor() {\n\t\treturn this.testDescriptor;\n\t}\n\n\t/**\n\t * Get the start {@link Instant} of this {@code Execution}.\n\t *\n\t * @return the start {@code Instant} of this {@code Execution}\n\t */\n\tpublic Instant getStartInstant() {\n\t\treturn this.startInstant;\n\t}\n\n\t/**\n\t * Get the end {@link Instant} of this {@code Execution}.\n\t *\n\t * @return the end {@code Instant} of this {@code Execution}\n\t */\n\tpublic Instant getEndInstant() {\n\t\treturn this.endInstant;\n\t}\n\n\t/**\n\t * Get the {@link Duration} of this {@code Execution}.\n\t *\n\t * @return the {@code Duration} of this {@code Execution}\n\t */\n\tpublic Duration getDuration() {\n\t\treturn this.duration;\n\t}\n\n\t/**\n\t * Get the {@link TerminationInfo} for this {@code Execution}.\n\t *\n\t * @return the {@code TerminationInfo} for this {@code Execution}\n\t */\n\tpublic TerminationInfo getTerminationInfo() {\n\t\treturn this.terminationInfo;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\t// @formatter:off\n\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"testDescriptor\", this.testDescriptor)\n\t\t\t\t.append(\"startInstant\", this.startInstant)\n\t\t\t\t.append(\"endInstant\", this.endInstant)\n\t\t\t\t.append(\"duration\", this.duration)\n\t\t\t\t.append(\"terminationInfo\", this.terminationInfo)\n\t\t\t\t.toString();\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/ExecutionRecorder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.util.List;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\n/**\n * {@code ExecutionRecorder} is an {@link EngineExecutionListener} that records\n * data from every event that occurs during the engine execution lifecycle and\n * provides functionality for retrieving execution state via\n * {@link EngineExecutionResults}.\n *\n * @since 1.4\n * @see EngineExecutionResults\n * @see Event\n * @see Execution\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class ExecutionRecorder implements EngineExecutionListener {\n\n\tprivate final List<Event> events = new CopyOnWriteArrayList<>();\n\n\tpublic ExecutionRecorder() {\n\t}\n\n\t/**\n\t * Record an {@link Event} for a dynamically registered container\n\t * or test.\n\t */\n\t@Override\n\tpublic void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\tthis.events.add(Event.dynamicTestRegistered(testDescriptor));\n\t}\n\n\t/**\n\t * Record an {@link Event} for a container or test that was skipped.\n\t */\n\t@Override\n\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\tthis.events.add(Event.executionSkipped(testDescriptor, reason));\n\t}\n\n\t/**\n\t * Record an {@link Event} for a container or test that started.\n\t */\n\t@Override\n\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\tthis.events.add(Event.executionStarted(testDescriptor));\n\t}\n\n\t/**\n\t * Record an {@link Event} for a container or test that completed\n\t * with the provided {@link TestExecutionResult}.\n\t */\n\t@Override\n\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\tthis.events.add(Event.executionFinished(testDescriptor, testExecutionResult));\n\t}\n\n\t/**\n\t * Record an {@link Event} for a published {@link ReportEntry}.\n\t */\n\t@Override\n\tpublic void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\tthis.events.add(Event.reportingEntryPublished(testDescriptor, entry));\n\t}\n\n\t/**\n\t * Record an {@link Event} for a published {@link FileEntry}.\n\t *\n\t * @since 1.12\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\t@Override\n\tpublic void fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\tthis.events.add(Event.fileEntryPublished(testDescriptor, file));\n\t}\n\n\t/**\n\t * Get the state of the engine's execution in the form of {@link EngineExecutionResults}.\n\t *\n\t * @return the {@code EngineExecutionResults} containing all current state information\n\t */\n\tpublic EngineExecutionResults getExecutionResults() {\n\t\treturn new EngineExecutionResults(this.events);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/Executions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport java.io.OutputStream;\nimport java.io.PrintWriter;\nimport java.io.Writer;\nimport java.time.Instant;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.apiguardian.api.API;\nimport org.assertj.core.api.ListAssert;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\n\n/**\n * {@code Executions} is a facade that provides a fluent API for working with\n * {@linkplain Execution executions}.\n *\n * @since 1.4\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic final class Executions {\n\n\tprivate final List<Execution> executions;\n\tprivate final String category;\n\n\tprivate Executions(Stream<Execution> executions, String category) {\n\t\tPreconditions.notNull(executions, \"Execution stream must not be null\");\n\t\tPreconditions.notNull(category, \"Category must not be null\");\n\n\t\tthis.executions = executions.toList();\n\t\tthis.category = category;\n\t}\n\n\tExecutions(List<Event> events, String category) {\n\t\tPreconditions.notNull(events, \"Event list must not be null\");\n\t\tPreconditions.containsNoNullElements(events, \"Event list must not contain null elements\");\n\t\tPreconditions.notNull(category, \"Category must not be null\");\n\n\t\tthis.executions = List.copyOf(createExecutions(events));\n\t\tthis.category = category;\n\t}\n\n\t// --- Accessors -----------------------------------------------------------\n\n\t/**\n\t * Get the {@linkplain Execution executions} as a {@link List}.\n\t *\n\t * @return the list of executions; never {@code null}\n\t * @see #stream()\n\t */\n\tpublic List<Execution> list() {\n\t\treturn this.executions;\n\t}\n\n\t/**\n\t * Get the {@linkplain Execution executions} as a {@link Stream}.\n\t *\n\t * @return the stream of executions; never {@code null}\n\t * @see #list()\n\t */\n\tpublic Stream<Execution> stream() {\n\t\treturn this.executions.stream();\n\t}\n\n\t/**\n\t * Shortcut for {@code executions.stream().map(mapper)}.\n\t *\n\t * @param mapper a {@code Function} to apply to each execution;\n\t * never {@code null}\n\t * @return the mapped stream of executions; never {@code null}\n\t * @see #stream()\n\t * @see Stream#map(Function)\n\t */\n\tpublic <R> Stream<R> map(Function<? super Execution, ? extends R> mapper) {\n\t\tPreconditions.notNull(mapper, \"Mapping function must not be null\");\n\t\treturn stream().map(mapper);\n\t}\n\n\t/**\n\t * Shortcut for {@code executions.stream().filter(predicate)}.\n\t *\n\t * @param predicate a {@code Predicate} to apply to each execution to decide\n\t * if it should be included in the filtered stream; never {@code null}\n\t * @return the filtered stream of executions; never {@code null}\n\t * @see #stream()\n\t * @see Stream#filter(Predicate)\n\t */\n\tpublic Stream<Execution> filter(Predicate<? super Execution> predicate) {\n\t\tPreconditions.notNull(predicate, \"Filter predicate must not be null\");\n\t\treturn stream().filter(predicate);\n\t}\n\n\t// --- Statistics ----------------------------------------------------------\n\n\t/**\n\t * Get the number of {@linkplain Execution executions} contained in this\n\t * {@code Executions} object.\n\t */\n\tpublic long count() {\n\t\treturn this.executions.size();\n\t}\n\n\t// --- Built-in Filters ----------------------------------------------------\n\n\t/**\n\t * Get the skipped {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>Executions that are not skipped are {@linkplain #finished() finished}.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t */\n\tpublic Executions skipped() {\n\t\treturn new Executions(executionsByTerminationInfo(TerminationInfo::skipped), this.category + \" Skipped\");\n\t}\n\n\t/**\n\t * Get the started {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>Executions that are not started are {@linkplain #skipped() skipped}.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t *\n\t * @deprecated by definition, all started executions are also finished executions.\n\t * Use {@link #finished()} instead.\n\t */\n\t@API(status = DEPRECATED, since = \"6.1\")\n\t@Deprecated(since = \"6.1\", forRemoval = true)\n\tpublic Executions started() {\n\t\treturn new Executions(executionsByTerminationInfo(TerminationInfo::notSkipped), this.category + \" Started\");\n\t}\n\n\t/**\n\t * Get the finished {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>Executions that are not finished are {@linkplain #skipped() skipped}.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t */\n\tpublic Executions finished() {\n\t\treturn new Executions(finishedExecutions(), this.category + \" Finished\");\n\t}\n\n\t/**\n\t * Get the aborted {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>The aborted executions are a subset of the {@linkplain #finished() finished}\n\t * executions.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t */\n\tpublic Executions aborted() {\n\t\treturn new Executions(finishedExecutionsByStatus(Status.ABORTED), this.category + \" Aborted\");\n\t}\n\n\t/**\n\t * Get the succeeded {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>The succeeded executions are a subset of the {@linkplain #finished() finished}\n\t * executions.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t */\n\tpublic Executions succeeded() {\n\t\treturn new Executions(finishedExecutionsByStatus(Status.SUCCESSFUL), this.category + \" Successful\");\n\t}\n\n\t/**\n\t * Get the failed {@link Executions} contained in this {@code Executions} object.\n\t *\n\t * <p>The failed executions are a subset of the {@linkplain #finished() finished}\n\t * executions.\n\t *\n\t * @return the filtered {@code Executions}; never {@code null}\n\t */\n\tpublic Executions failed() {\n\t\treturn new Executions(finishedExecutionsByStatus(Status.FAILED), this.category + \" Failed\");\n\t}\n\n\t// --- Assertions ----------------------------------------------------------\n\n\t/**\n\t * Shortcut for {@code org.assertj.core.api.Assertions.assertThat(executions.list())}.\n\t *\n\t * @return an instance of {@link ListAssert} for executions; never {@code null}\n\t * @see org.assertj.core.api.Assertions#assertThat(List)\n\t * @see org.assertj.core.api.ListAssert\n\t */\n\tpublic ListAssert<Execution> assertThatExecutions() {\n\t\treturn org.assertj.core.api.Assertions.assertThat(list());\n\t}\n\n\t// --- Diagnostics ---------------------------------------------------------\n\n\t/**\n\t * Print all executions to {@link System#out}.\n\t *\n\t * @return this {@code Executions} object for method chaining; never {@code null}\n\t */\n\tpublic Executions debug() {\n\t\tdebug(System.out);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Print all executions to the supplied {@link OutputStream}.\n\t *\n\t * @param out the {@code OutputStream} to print to; never {@code null}\n\t * @return this {@code Executions} object for method chaining; never {@code null}\n\t */\n\t@SuppressWarnings(\"DefaultCharset\")\n\tpublic Executions debug(OutputStream out) {\n\t\tPreconditions.notNull(out, \"OutputStream must not be null\");\n\t\tdebug(new PrintWriter(out, true));\n\t\treturn this;\n\t}\n\n\t/**\n\t * Print all executions to the supplied {@link Writer}.\n\t *\n\t * @param writer the {@code Writer} to print to; never {@code null}\n\t * @return this {@code Executions} object for method chaining; never {@code null}\n\t */\n\t@SuppressWarnings(\"DefaultCharset\")\n\tpublic Executions debug(Writer writer) {\n\t\tPreconditions.notNull(writer, \"Writer must not be null\");\n\t\tdebug(new PrintWriter(writer, true));\n\t\treturn this;\n\t}\n\n\tprivate Executions debug(PrintWriter printWriter) {\n\t\tprintWriter.println(this.category + \" Executions:\");\n\t\tthis.executions.forEach(execution -> printWriter.printf(\"\\t%s%n\", execution));\n\t\treturn this;\n\t}\n\n\t// --- Internals -----------------------------------------------------------\n\n\tprivate Stream<Execution> finishedExecutions() {\n\t\treturn executionsByTerminationInfo(TerminationInfo::executed);\n\t}\n\n\tprivate Stream<Execution> finishedExecutionsByStatus(Status status) {\n\t\tPreconditions.notNull(status, \"Status must not be null\");\n\t\treturn finishedExecutions()//\n\t\t\t\t.filter(execution -> execution.getTerminationInfo().getExecutionResult().getStatus() == status);\n\t}\n\n\tprivate Stream<Execution> executionsByTerminationInfo(Predicate<TerminationInfo> predicate) {\n\t\treturn filter(execution -> predicate.test(execution.getTerminationInfo()));\n\t}\n\n\t/**\n\t * Create executions from the supplied list of events.\n\t */\n\tprivate static List<Execution> createExecutions(List<Event> events) {\n\t\tList<Execution> executions = new ArrayList<>();\n\t\tMap<TestDescriptor, Instant> executionStarts = new HashMap<>();\n\n\t\tfor (Event event : events) {\n\t\t\tswitch (event.getType()) {\n\t\t\t\tcase STARTED -> executionStarts.put(event.getTestDescriptor(), event.getTimestamp());\n\t\t\t\tcase SKIPPED -> {\n\t\t\t\t\t// Based on the Javadoc for EngineExecutionListener.executionSkipped(...),\n\t\t\t\t\t// a skipped descriptor must never be reported as started or finished,\n\t\t\t\t\t// but just in case a TestEngine does not adhere to that contract, we\n\t\t\t\t\t// make an attempt to remove any tracked \"started\" event.\n\t\t\t\t\texecutionStarts.remove(event.getTestDescriptor());\n\n\t\t\t\t\t// Use the same timestamp for both the start and end Instant values.\n\t\t\t\t\tInstant timestamp = event.getTimestamp();\n\n\t\t\t\t\tExecution skippedEvent = Execution.skipped(event.getTestDescriptor(), timestamp, timestamp,\n\t\t\t\t\t\tevent.getRequiredPayload(String.class));\n\t\t\t\t\texecutions.add(skippedEvent);\n\t\t\t\t}\n\t\t\t\tcase FINISHED -> {\n\t\t\t\t\tInstant startInstant = executionStarts.remove(event.getTestDescriptor());\n\t\t\t\t\tInstant endInstant = event.getTimestamp();\n\n\t\t\t\t\t// If \"started\" events have already been filtered out, it is no\n\t\t\t\t\t// longer possible to create a legitimate Execution for the current\n\t\t\t\t\t// descriptor. So we effectively ignore it in that case.\n\t\t\t\t\tif (startInstant != null) {\n\t\t\t\t\t\tExecution finishedEvent = Execution.finished(event.getTestDescriptor(), startInstant,\n\t\t\t\t\t\t\tendInstant, event.getRequiredPayload(TestExecutionResult.class));\n\t\t\t\t\t\texecutions.add(finishedEvent);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdefault -> {\n\t\t\t\t\t// Ignore other events\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn executions;\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TerminationInfo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestExecutionResult;\n\n/**\n * {@code TerminationInfo} is a union type that allows propagation of terminated\n * container/test state, supporting either the <em>reason</em> if the container/test\n * was skipped or the {@link TestExecutionResult} if the container/test was executed.\n *\n * @since 1.4\n * @see Execution#getTerminationInfo()\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic class TerminationInfo {\n\n\t// --- Factories -----------------------------------------------------------\n\n\t/**\n\t * Create a <em>skipped</em> {@code TerminationInfo} instance for the\n\t * supplied reason.\n\t *\n\t * @param reason the reason the execution was skipped; may be {@code null}\n\t * @return the created {@code TerminationInfo}; never {@code null}\n\t * @see #executed(TestExecutionResult)\n\t */\n\tpublic static TerminationInfo skipped(@Nullable String reason) {\n\t\treturn new TerminationInfo(true, reason, null);\n\t}\n\n\t/**\n\t * Create an <em>executed</em> {@code TerminationInfo} instance for the\n\t * supplied {@link TestExecutionResult}.\n\t *\n\t * @param testExecutionResult the result of the execution; never {@code null}\n\t * @return the created {@code TerminationInfo}; never {@code null}\n\t * @see #skipped(String)\n\t */\n\tpublic static TerminationInfo executed(TestExecutionResult testExecutionResult) {\n\t\tPreconditions.notNull(testExecutionResult, \"TestExecutionResult must not be null\");\n\t\treturn new TerminationInfo(false, null, testExecutionResult);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate final boolean skipped;\n\n\tprivate final @Nullable String skipReason;\n\n\tprivate final @Nullable TestExecutionResult testExecutionResult;\n\n\tprivate TerminationInfo(boolean skipped, @Nullable String skipReason,\n\t\t\t@Nullable TestExecutionResult testExecutionResult) {\n\t\tboolean executed = (testExecutionResult != null);\n\t\tPreconditions.condition((skipped ^ executed),\n\t\t\t\"TerminationInfo must represent either a skipped execution or a TestExecutionResult but not both\");\n\n\t\tthis.skipped = skipped;\n\t\tthis.skipReason = skipReason;\n\t\tthis.testExecutionResult = testExecutionResult;\n\t}\n\n\t/**\n\t * Determine if this {@code TerminationInfo} represents a skipped execution.\n\t *\n\t * @return {@code true} if this this {@code TerminationInfo} represents a\n\t * skipped execution\n\t */\n\tpublic boolean skipped() {\n\t\treturn this.skipped;\n\t}\n\n\t/**\n\t * Determine if this {@code TerminationInfo} does not represent a skipped\n\t * execution.\n\t *\n\t * @return {@code true} if this this {@code TerminationInfo} does not\n\t * represent a skipped execution\n\t */\n\tpublic boolean notSkipped() {\n\t\treturn !skipped();\n\t}\n\n\t/**\n\t * Determine if this {@code TerminationInfo} represents a completed execution.\n\t *\n\t * @return {@code true} if this this {@code TerminationInfo} represents a\n\t * completed execution\n\t */\n\tpublic boolean executed() {\n\t\treturn (this.testExecutionResult != null);\n\t}\n\n\t/**\n\t * Get the reason the execution was skipped.\n\t *\n\t * @return the reason the execution was skipped\n\t * @throws UnsupportedOperationException if this {@code TerminationInfo}\n\t * does not represent a skipped execution\n\t */\n\tpublic @Nullable String getSkipReason() throws UnsupportedOperationException {\n\t\tif (skipped()) {\n\t\t\treturn this.skipReason;\n\t\t}\n\t\t// else\n\t\tthrow new UnsupportedOperationException(\"No skip reason contained in this TerminationInfo\");\n\t}\n\n\t/**\n\t * Get the {@link TestExecutionResult} for the completed execution.\n\t *\n\t * @return the result of the completed execution\n\t * @throws UnsupportedOperationException if this {@code TerminationInfo}\n\t * does not represent a completed execution\n\t */\n\tpublic TestExecutionResult getExecutionResult() throws UnsupportedOperationException {\n\t\tif (this.testExecutionResult != null) {\n\t\t\treturn this.testExecutionResult;\n\t\t}\n\t\t// else\n\t\tthrow new UnsupportedOperationException(\"No TestExecutionResult contained in this TerminationInfo\");\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tToStringBuilder builder = new ToStringBuilder(this);\n\t\tif (skipped()) {\n\t\t\tbuilder.append(\"skipped\", true).append(\"reason\", this.skipReason);\n\t\t}\n\t\telse {\n\t\t\tbuilder.append(\"executed\", true).append(\"result\", this.testExecutionResult);\n\t\t}\n\t\treturn builder.toString();\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/TestExecutionResultConditions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\nimport org.assertj.core.api.Assertions;\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.util.FunctionUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\n\n/**\n * Collection of AssertJ {@linkplain Condition conditions} for\n * {@link TestExecutionResult}.\n *\n * @since 1.4\n * @see EventConditions\n */\n@API(status = MAINTAINED, since = \"1.7\")\npublic final class TestExecutionResultConditions {\n\n\tprivate TestExecutionResultConditions() {\n\t\t/* no-op */\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link TestExecutionResult}'s {@linkplain TestExecutionResult#getStatus()\n\t * status} is equal to the supplied {@link Status Status}.\n\t */\n\tpublic static Condition<TestExecutionResult> status(Status expectedStatus) {\n\t\treturn new Condition<>(where(TestExecutionResult::getStatus, isEqual(expectedStatus)), \"status is %s\",\n\t\t\texpectedStatus);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link TestExecutionResult}'s\n\t * {@linkplain TestExecutionResult#getThrowable() throwable} matches all\n\t * supplied conditions.\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic static Condition<TestExecutionResult> throwable(Condition<Throwable>... conditions) {\n\t\tList<Condition<TestExecutionResult>> list = Arrays.stream(conditions)//\n\t\t\t\t.map(TestExecutionResultConditions::throwable)//\n\t\t\t\t.toList();\n\n\t\treturn Assertions.allOf(list);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable}'s {@linkplain Throwable#getCause() cause} matches all\n\t * supplied conditions.\n\t *\n\t * @see #rootCause(Condition...)\n\t * @see #suppressed(int, Condition...)\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic static Condition<Throwable> cause(Condition<Throwable>... conditions) {\n\t\tList<Condition<Throwable>> list = Arrays.stream(conditions)//\n\t\t\t\t.map(TestExecutionResultConditions::cause)//\n\t\t\t\t.toList();\n\n\t\treturn Assertions.allOf(list);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable}'s root {@linkplain Throwable#getCause() cause} matches\n\t * all supplied conditions.\n\t *\n\t * @since 1.11\n\t * @see #cause(Condition...)\n\t * @see #suppressed(int, Condition...)\n\t */\n\t@API(status = MAINTAINED, since = \"1.13.3\")\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic static Condition<Throwable> rootCause(Condition<Throwable>... conditions) {\n\t\tList<Condition<Throwable>> list = Arrays.stream(conditions)//\n\t\t\t\t.map(TestExecutionResultConditions::rootCause)//\n\t\t\t\t.toList();\n\n\t\treturn Assertions.allOf(list);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable}'s {@linkplain Throwable#getSuppressed() suppressed\n\t * throwable} at the supplied index matches all supplied conditions.\n\t *\n\t * @see #cause(Condition...)\n\t * @see #rootCause(Condition...)\n\t */\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tpublic static Condition<Throwable> suppressed(int index, Condition<Throwable>... conditions) {\n\t\tList<Condition<Throwable>> list = Arrays.stream(conditions)//\n\t\t\t\t.map(condition -> suppressed(index, condition))//\n\t\t\t\t.toList();\n\n\t\treturn Assertions.allOf(list);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable} is an {@linkplain Class#isInstance(Object) instance of}\n\t * the supplied {@link Class}.\n\t */\n\tpublic static Condition<Throwable> instanceOf(Class<? extends Throwable> expectedType) {\n\t\treturn new Condition<>(expectedType::isInstance, \"instance of %s\", expectedType.getName());\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable}'s {@linkplain Throwable#getMessage() message} is equal\n\t * to the supplied {@link String}.\n\t */\n\tpublic static Condition<Throwable> message(String expectedMessage) {\n\t\treturn new Condition<>(\n\t\t\tFunctionUtils.<Throwable, @Nullable String> where(Throwable::getMessage, isEqual(expectedMessage)),\n\t\t\t\"message is '%s'\", expectedMessage);\n\t}\n\n\t/**\n\t * Create a new {@link Condition} that matches if and only if a\n\t * {@link Throwable}'s {@linkplain Throwable#getMessage() message} matches\n\t * the supplied {@link Predicate}.\n\t */\n\tpublic static Condition<Throwable> message(Predicate<String> expectedMessagePredicate) {\n\t\treturn new Condition<>(\n\t\t\tFunctionUtils.<Throwable, @Nullable String> where(Throwable::getMessage, expectedMessagePredicate),\n\t\t\t\"message matches predicate\");\n\t}\n\n\tprivate static Condition<TestExecutionResult> throwable(Condition<? super Throwable> condition) {\n\t\treturn new Condition<>(\n\t\t\twhere(TestExecutionResult::getThrowable,\n\t\t\t\tthrowable -> throwable.isPresent() && condition.matches(throwable.get())),\n\t\t\t\"throwable matches %s\", condition);\n\t}\n\n\tprivate static Condition<Throwable> cause(Condition<Throwable> condition) {\n\t\treturn new Condition<>(throwable -> condition.matches(throwable.getCause()), \"throwable cause matches %s\",\n\t\t\tcondition);\n\t}\n\n\tprivate static Condition<Throwable> rootCause(Condition<Throwable> condition) {\n\t\tPredicate<Throwable> predicate = throwable -> {\n\t\t\tPreconditions.notNull(throwable, \"Throwable must not be null\");\n\t\t\tPreconditions.notNull(throwable.getCause(), \"Throwable does not have a cause\");\n\t\t\tThrowable rootCause = getRootCause(throwable, new ArrayList<>());\n\t\t\treturn condition.matches(rootCause);\n\t\t};\n\t\treturn new Condition<>(predicate, \"throwable root cause matches %s\", condition);\n\t}\n\n\t/**\n\t * Get the root cause of the supplied {@link Throwable}, or the supplied\n\t * {@link Throwable} if it has no cause.\n\t */\n\tprivate static Throwable getRootCause(Throwable throwable, List<Throwable> causeChain) {\n\t\t// If we have already seen the current Throwable, that means we have\n\t\t// encountered recursion in the cause chain and therefore return the last\n\t\t// Throwable in the cause chain, which was the root cause before the recursion.\n\t\tif (causeChain.contains(throwable)) {\n\t\t\treturn causeChain.get(causeChain.size() - 1);\n\t\t}\n\t\tThrowable cause = throwable.getCause();\n\t\tif (cause == null) {\n\t\t\treturn throwable;\n\t\t}\n\t\t// Track current Throwable before recursing.\n\t\tcauseChain.add(throwable);\n\t\treturn getRootCause(cause, causeChain);\n\t}\n\n\tprivate static Condition<Throwable> suppressed(int index, Condition<Throwable> condition) {\n\t\treturn new Condition<>(\n\t\t\tthrowable -> throwable.getSuppressed().length > index\n\t\t\t\t\t&& condition.matches(throwable.getSuppressed()[index]),\n\t\t\t\"suppressed throwable at index %d matches %s\", index, condition);\n\t}\n\n}\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/engine/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Test Kit for testing the execution of a {@link org.junit.platform.engine.TestEngine}\n * running on the JUnit Platform.\n */\n\n@NullMarked\npackage org.junit.platform.testkit.engine;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-testkit/src/main/java/org/junit/platform/testkit/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Test Kit for the JUnit Platform.\n */\n\n@NullMarked\npackage org.junit.platform.testkit;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-platform-testkit/src/test/README.md",
    "content": "For compatibility with the Eclipse IDE, the test for this module are in the `platform-tests` project.\n"
  },
  {
    "path": "junit-start/junit-start.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n}\n\ndescription = \"JUnit Start Module\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitJupiter)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\tcompileOnlyApi(projects.junitJupiterEngine)\n\n\timplementation(projects.junitPlatformLauncher)\n\timplementation(projects.junitPlatformConsole)\n}\n\nbackwardCompatibilityChecks {\n\tenabled = false // TODO enable after initial release\n}\n"
  },
  {
    "path": "junit-start/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/// Defines the API of the JUnit Start module for writing and running tests.\n///\n/// Usage example in a `HelloTests.java` compact source-file program:\n/// ```java\n/// import module org.junit.start;\n///\n/// void main() {\n///   JUnit.run();\n/// }\n///\n/// @Test\n/// void addition() {\n///   Assertions.assertEquals(2, 1 + 1, \"Addition error detected!\");\n/// }\n/// ```\n/// @since 6.1\nmodule org.junit.start {\n\trequires static transitive org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires transitive org.junit.jupiter;\n\trequires org.junit.platform.launcher;\n\trequires org.junit.platform.console;\n\n\texports org.junit.start;\n}\n"
  },
  {
    "path": "junit-start/src/main/java/org/junit/start/JUnit.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.start;\n\nimport static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;\nimport static org.apiguardian.api.API.Status.EXPERIMENTAL;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.io.PrintWriter;\nimport java.nio.charset.Charset;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.console.output.ColorPalette;\nimport org.junit.platform.console.output.Theme;\nimport org.junit.platform.console.output.TreePrintingListener;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\n\n/// This class provides simple helpers to discover and execute tests.\n///\n/// @since 6.1\n@API(status = EXPERIMENTAL, since = \"6.1\")\npublic final class JUnit {\n\t/// Run all tests defined in the caller class.\n\tpublic static void run() {\n\t\tvar walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);\n\t\trun(selectClass(walker.getCallerClass()));\n\t}\n\n\t/// Run all tests defined in the given test class.\n\t///\n\t/// @param testClass the class to discover and execute tests in\n\tpublic static void run(Class<?> testClass) {\n\t\trun(selectClass(testClass));\n\t}\n\n\t/// Run all tests defined in the given module.\n\t///\n\t/// @param testModule the module to discover and execute tests in\n\tpublic static void run(Module testModule) {\n\t\trun(selectModule(testModule));\n\t}\n\n\tprivate static void run(DiscoverySelector selector) {\n\t\tvar listener = new SummaryGeneratingListener();\n\t\tvar charset = Charset.defaultCharset();\n\t\tvar writer = new PrintWriter(System.out, true, charset);\n\t\tvar palette = System.getenv(\"NO_COLOR\") != null ? ColorPalette.NONE : ColorPalette.DEFAULT;\n\t\tvar theme = Theme.valueOf(charset);\n\t\tvar printer = new TreePrintingListener(writer, palette, theme);\n\t\tvar request = request().selectors(selector).forExecution() //\n\t\t\t\t.listeners(listener, printer) //\n\t\t\t\t.build();\n\t\tvar launcher = LauncherFactory.create();\n\t\tlauncher.execute(request);\n\t\tvar summary = listener.getSummary();\n\n\t\tif (summary.getTotalFailureCount() == 0)\n\t\t\treturn;\n\n\t\tsummary.printFailuresTo(new PrintWriter(System.err, true, charset));\n\t\tthrow new JUnitException(\"JUnit run finished with %d failure%s\".formatted( //\n\t\t\tsummary.getTotalFailureCount(), //\n\t\t\tsummary.getTotalFailureCount() == 1 ? \"\" : \"s\"));\n\t}\n\n\tprivate JUnit() {\n\t}\n}\n"
  },
  {
    "path": "junit-start/src/main/java/org/junit/start/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/// Contains JUnit Start API for writing and running tests.\n///\n/// @since 6.1\n\n@NullMarked\npackage org.junit.start;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/junit-vintage-engine.gradle.kts",
    "content": "plugins {\n\tid(\"junitbuild.java-library-conventions\")\n\tid(\"junitbuild.junit4-compatibility\")\n\tid(\"junitbuild.testing-conventions\")\n\t`java-test-fixtures`\n\tgroovy\n}\n\ndescription = \"JUnit Vintage Engine\"\n\ndependencies {\n\tapi(platform(projects.junitBom))\n\tapi(projects.junitPlatformEngine)\n\tapi(libs.junit4)\n\n\tcompileOnlyApi(libs.apiguardian)\n\tcompileOnlyApi(libs.jspecify)\n\n\ttestFixturesApi(platform(libs.groovy2.bom))\n\ttestFixturesApi(libs.spock1)\n\ttestFixturesImplementation(projects.junitPlatformSuiteApi)\n\n\ttestImplementation(projects.junitPlatformLauncher)\n\ttestImplementation(projects.junitPlatformSuiteEngine)\n\ttestImplementation(projects.junitPlatformTestkit)\n\ttestImplementation(testFixtures(projects.junitPlatformCommons))\n\ttestImplementation(testFixtures(projects.junitJupiterApi))\n\ttestImplementation(testFixtures(projects.junitPlatformLauncher))\n\ttestImplementation(testFixtures(projects.junitPlatformReporting))\n\n\tosgiVerification(projects.junitPlatformLauncher)\n}\n\ntasks {\n\tcompileJava {\n\t\toptions.compilerArgs.add(\"-Xlint:-requires-automatic\") // JUnit 4\n\t}\n\tcompileTestFixturesGroovy {\n\t\tjavaLauncher = project.javaToolchains.launcherFor {\n\t\t\t// Groovy 2.x (used for Spock tests) does not run on more recent JDKs\n\t\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t\t}\n\t}\n\tjar {\n\t\tbundle {\n\t\t\tval junit4Min = libs.versions.junit4Min.get()\n\t\t\tval version = project.version\n\t\t\tval importAPIGuardian: String by extra\n\t\t\tval importJSpecify: String by extra\n\t\t\tval importCommonsLogging: String by extra\n\t\t\tbnd(\"\"\"\n\t\t\t\t# Import JUnit4 packages with a version\n\t\t\t\tImport-Package: \\\n\t\t\t\t\t${importAPIGuardian},\\\n\t\t\t\t\t${importJSpecify},\\\n\t\t\t\t\t${importCommonsLogging},\\\n\t\t\t\t\tjunit.runner;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\torg.junit;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\torg.junit.experimental.categories;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\torg.junit.internal.builders;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\torg.junit.runner.*;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\torg.junit.runners.model;version=\"[${junit4Min},5)\",\\\n\t\t\t\t\t*\n\n\t\t\t\tProvide-Capability:\\\n\t\t\t\t\torg.junit.platform.engine;\\\n\t\t\t\t\t\torg.junit.platform.engine='junit-vintage';\\\n\t\t\t\t\t\tversion:Version=\"${'$'}{version_cleanup;$version}\"\n\t\t\t\tRequire-Capability:\\\n\t\t\t\t\torg.junit.platform.launcher;\\\n\t\t\t\t\t\tfilter:='(&(org.junit.platform.launcher=junit-platform-launcher)(version>=${'$'}{version_cleanup;$version})(!(version>=${'$'}{versionmask;+;${'$'}{version_cleanup;$version}})))';\\\n\t\t\t\t\t\teffective:=active\n\t\t\t\"\"\")\n\t\t}\n\t}\n\tval testWithoutJUnit4 by registering(Test::class) {\n\t\tval test by testing.suites.existing(JvmTestSuite::class)\n\t\t(options as JUnitPlatformOptions).apply {\n\t\t\tincludeTags(\"missing-junit4\")\n\t\t}\n\t\tsetIncludes(listOf(\"**/JUnit4VersionCheckTests.class\"))\n\t\ttestClassesDirs = files(test.map { it.sources.output.classesDirs })\n\t\tclasspath = files(test.map { it.sources.runtimeClasspath }).filter {\n\t\t\t!it.name.startsWith(\"junit-4\")\n\t\t}\n\t}\n\twithType<Test>().named { it != testWithoutJUnit4.name }.configureEach {\n\t\t(options as JUnitPlatformOptions).apply {\n\t\t\texcludeTags(\"missing-junit4\")\n\t\t}\n\t}\n\twithType<Test>().configureEach {\n\t\t// Workaround for Groovy 2.5\n\t\tjvmArgs(\"--add-opens\", \"java.base/java.lang=ALL-UNNAMED\")\n\t\tjvmArgs(\"--add-opens\", \"java.base/java.util=ALL-UNNAMED\")\n\t}\n\tcheck {\n\t\tdependsOn(testWithoutJUnit4)\n\t}\n}\n\neclipse {\n\tclasspath {\n\t\t// Avoid exposing test resources to dependent projects\n\t\tcontainsTestFixtures = false\n\t}\n\tproject {\n\t\t// Remove Groovy Nature, since we don't require a Groovy plugin for Eclipse\n\t\t// in order for developers to work with the code base.\n\t\tnatures.removeAll { it == \"org.eclipse.jdt.groovy.core.groovyNature\" }\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Provides a {@link org.junit.platform.engine.TestEngine} for running JUnit 3\n * and 4 based tests on the platform.\n *\n * @since 4.12\n * @provides org.junit.platform.engine.TestEngine The {@code VintageTestEngine}\n * runs JUnit 3 and 4 based tests on the platform.\n */\n@SuppressWarnings(\"deprecation\")\nmodule org.junit.vintage.engine {\n\n\trequires static org.apiguardian.api;\n\trequires static transitive org.jspecify;\n\n\trequires junit; // 4\n\trequires org.junit.platform.engine;\n\n\tprovides org.junit.platform.engine.TestEngine\n\t\t\twith org.junit.vintage.engine.VintageTestEngine;\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/Constants.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.apiguardian.api.API.Status.MAINTAINED;\n\nimport org.apiguardian.api.API;\n\n/**\n * Collection of constants related to the {@link VintageTestEngine}.\n *\n * @deprecated Should only be used temporarily while migrating tests to JUnit\n * Jupiter or another testing framework with native JUnit Platform support\n */\n@Deprecated(since = \"6.0\")\n@API(status = DEPRECATED, since = \"6.0\")\npublic final class Constants {\n\n\t/**\n\t * Property name used to indicate whether parallel execution is enabled for the JUnit Vintage engine: {@value}\n\t *\n\t * <p>Set this property to {@code true} to enable parallel execution of tests.\n\t * Defaults to {@code false}.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String PARALLEL_EXECUTION_ENABLED = \"junit.vintage.execution.parallel.enabled\";\n\n\t/**\n\t * Property name used to specify the size of the thread pool to be used for parallel execution: {@value}\n\t *\n\t * <p>Set this property to an integer value to specify the number of threads\n\t * to be used for parallel execution. Defaults to the number of available\n\t * processors.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String PARALLEL_POOL_SIZE = \"junit.vintage.execution.parallel.pool-size\";\n\n\t/**\n\t * Property name used to indicate whether parallel execution is enabled for test classes in the\n\t * JUnit Vintage engine: {@value}\n\t *\n\t * <p>Set this property to {@code true} to enable parallel execution of test\n\t * classes. Defaults to {@code false}.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String PARALLEL_CLASS_EXECUTION = \"junit.vintage.execution.parallel.classes\";\n\n\t/**\n\t * Property name used to indicate whether parallel execution is enabled for test methods in the\n\t * JUnit Vintage engine: {@value}\n\t *\n\t * <p>Set this property to {@code true} to enable parallel execution of test\n\t * methods. Defaults to {@code false}.\n\t *\n\t * @since 5.12\n\t */\n\t@API(status = MAINTAINED, since = \"5.13.3\")\n\tpublic static final String PARALLEL_METHOD_EXECUTION = \"junit.vintage.execution.parallel.methods\";\n\n\t/**\n\t * Property name used to configure whether the JUnit Vintage engine should\n\t * report discovery issues such as deprecation notices: {@value}\n\t *\n\t * <p>Set this property to {@code false} to disable reporting of discovery\n\t * issues. Defaults to {@code true}.\n\t *\n\t * @since 6.0.1\n\t */\n\t@API(status = MAINTAINED, since = \"6.0.1\")\n\tpublic static final String DISCOVERY_ISSUE_REPORTING_ENABLED_PROPERTY_NAME = \"junit.vintage.discovery.issue.reporting.enabled\";\n\n\tprivate Constants() {\n\t\t/* no-op */\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/JUnit4VersionCheck.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.ENGINE_ID;\n\nimport java.math.BigDecimal;\nimport java.util.function.Supplier;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport junit.runner.Version;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\n\n/**\n * @since 5.4\n */\nclass JUnit4VersionCheck {\n\n\tprivate static final Pattern versionPattern = Pattern.compile(\"^(\\\\d+\\\\.\\\\d+).*\");\n\tprivate static final BigDecimal minVersion = new BigDecimal(\"4.12\");\n\n\tstatic void checkSupported() {\n\t\ttry {\n\t\t\tcheckSupported(Version::id);\n\t\t}\n\t\tcatch (NoClassDefFoundError e) {\n\t\t\tthrow new JUnitException(\n\t\t\t\t\"Invalid class/module path: junit-vintage-engine is present but junit:junit is not. \"\n\t\t\t\t\t\t+ \"Please either remove junit-vintage-engine or add junit:junit, or \"\n\t\t\t\t\t\t+ \"alternatively use an excludeEngines(\\\"\" + ENGINE_ID + \"\\\") filter.\");\n\t\t}\n\t}\n\n\tstatic void checkSupported(Supplier<String> versionSupplier) {\n\t\tString versionString = readVersion(versionSupplier);\n\t\tBigDecimal version = parseVersion(versionString);\n\t\tif (version.compareTo(minVersion) < 0) {\n\t\t\tthrow new JUnitException(\"Unsupported version of junit:junit: \" + versionString\n\t\t\t\t\t+ \". Please upgrade to version \" + minVersion + \" or later.\");\n\t\t}\n\t}\n\n\tstatic BigDecimal parseVersion(String versionString) {\n\t\ttry {\n\t\t\tMatcher matcher = versionPattern.matcher(versionString);\n\t\t\tif (matcher.matches()) {\n\t\t\t\treturn new BigDecimal(matcher.group(1));\n\t\t\t}\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new JUnitException(\"Failed to parse version of junit:junit: \" + versionString, e);\n\t\t}\n\t\tthrow new JUnitException(\"Failed to parse version of junit:junit: \" + versionString);\n\t}\n\n\tprivate static String readVersion(Supplier<String> versionSupplier) {\n\t\ttry {\n\t\t\treturn versionSupplier.get();\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\tthrow new JUnitException(\"Failed to read version of junit:junit\", t);\n\t\t}\n\t}\n\n\tprivate JUnit4VersionCheck() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/VintageTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.ENGINE_ID;\n\nimport java.util.Optional;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.vintage.engine.descriptor.VintageEngineDescriptor;\nimport org.junit.vintage.engine.discovery.VintageDiscoverer;\nimport org.junit.vintage.engine.execution.VintageExecutor;\n\n/**\n * The JUnit Vintage {@link TestEngine}.\n *\n * @since 4.12\n * @deprecated Should only be used temporarily while migrating tests to JUnit\n * Jupiter or another testing framework with native JUnit Platform support\n */\n@Deprecated(since = \"6.0\")\n@API(status = DEPRECATED, since = \"6.0\")\npublic final class VintageTestEngine implements TestEngine {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn ENGINE_ID;\n\t}\n\n\t/**\n\t * Returns {@code org.junit.vintage} as the group ID.\n\t */\n\t@Override\n\tpublic Optional<String> getGroupId() {\n\t\treturn Optional.of(\"org.junit.vintage\");\n\t}\n\n\t/**\n\t * Returns {@code junit-vintage-engine} as the artifact ID.\n\t */\n\t@Override\n\tpublic Optional<String> getArtifactId() {\n\t\treturn Optional.of(\"junit-vintage-engine\");\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tJUnit4VersionCheck.checkSupported();\n\t\treturn new VintageDiscoverer().discover(discoveryRequest, uniqueId);\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\tEngineExecutionListener engineExecutionListener = request.getEngineExecutionListener();\n\t\tVintageEngineDescriptor engineDescriptor = (VintageEngineDescriptor) request.getRootTestDescriptor();\n\t\tengineExecutionListener.executionStarted(engineDescriptor);\n\t\tnew VintageExecutor(engineDescriptor, engineExecutionListener,\n\t\t\trequest.getConfigurationParameters()).executeAllChildren(request.getCancellationToken());\n\t\tengineExecutionListener.executionFinished(engineDescriptor, successful());\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/DescriptionUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.runner.Description;\n\n@API(status = API.Status.INTERNAL, since = \"5.8\")\npublic class DescriptionUtils {\n\n\tprivate DescriptionUtils() {\n\t}\n\n\tpublic static @Nullable String getMethodName(Description description) {\n\t\tString displayName = description.getDisplayName();\n\t\tint i = displayName.indexOf('(');\n\t\tif (i >= 0) {\n\t\t\tint j = displayName.lastIndexOf('(');\n\t\t\tif (i == j) {\n\t\t\t\tchar lastChar = displayName.charAt(displayName.length() - 1);\n\t\t\t\tif (lastChar == ')') {\n\t\t\t\t\treturn displayName.substring(0, i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn description.getMethodName();\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/OrFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static java.util.stream.Collectors.joining;\n\nimport java.util.Collection;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.runner.Description;\nimport org.junit.runner.manipulation.Filter;\n\n/**\n * @since 5.4\n */\nclass OrFilter extends Filter {\n\n\tprivate final Collection<? extends Filter> filters;\n\n\tOrFilter(Collection<? extends Filter> filters) {\n\t\tthis.filters = Preconditions.notEmpty(filters, \"filters must not be empty\");\n\t}\n\n\t@Override\n\tpublic boolean shouldRun(Description description) {\n\t\treturn filters.stream().anyMatch(filter -> filter.shouldRun(description));\n\t}\n\n\t@Override\n\tpublic String describe() {\n\t\treturn filters.stream().map(Filter::describe).collect(joining(\" OR \"));\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerDecorator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.runner.Runner;\n\n@API(status = INTERNAL, since = \"5.4\")\npublic interface RunnerDecorator {\n\n\tRunner getDecoratedRunner();\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerRequest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport org.junit.runner.Request;\nimport org.junit.runner.Runner;\n\n/**\n * @since 4.12\n */\nclass RunnerRequest extends Request {\n\n\tprivate final Runner runner;\n\n\tRunnerRequest(Runner runner) {\n\t\tthis.runner = runner;\n\t}\n\n\t@Override\n\tpublic Runner getRunner() {\n\t\treturn runner;\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/RunnerTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Consumer;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector;\nimport org.junit.platform.engine.support.hierarchical.ThrowableCollector;\nimport org.junit.runner.Description;\nimport org.junit.runner.Request;\nimport org.junit.runner.Runner;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.runner.manipulation.Filterable;\nimport org.junit.runner.manipulation.NoTestsRemainException;\nimport org.junit.runners.ParentRunner;\nimport org.junit.runners.model.RunnerScheduler;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class RunnerTestDescriptor extends VintageTestDescriptor {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(RunnerTestDescriptor.class);\n\n\tprivate final Set<Description> rejectedExclusions = new HashSet<>();\n\tprivate Runner runner;\n\tprivate final boolean ignored;\n\tprivate boolean wasFiltered;\n\n\tprivate @Nullable List<Filter> filters = new ArrayList<>();\n\n\tpublic RunnerTestDescriptor(UniqueId uniqueId, Class<?> testClass, Runner runner, boolean ignored) {\n\t\tsuper(uniqueId, runner.getDescription(), testClass.getSimpleName(), ClassSource.from(testClass));\n\t\tthis.runner = runner;\n\t\tthis.ignored = ignored;\n\t}\n\n\t@Override\n\tpublic String getLegacyReportingName() {\n\t\treturn getSource().map(source -> ((ClassSource) source).getClassName()) //\n\t\t\t\t.orElseThrow(() -> new JUnitException(\"source should have been present\"));\n\t}\n\n\tpublic Request toRequest() {\n\t\treturn new RunnerRequest(this.runner);\n\t}\n\n\tpublic Runner getRunner() {\n\t\treturn runner;\n\t}\n\n\t@Override\n\tprotected boolean tryToExcludeFromRunner(Description description) {\n\t\tboolean excluded = tryToFilterRunner(description);\n\t\tif (excluded) {\n\t\t\twasFiltered = true;\n\t\t}\n\t\telse {\n\t\t\trejectedExclusions.add(description);\n\t\t}\n\t\treturn excluded;\n\t}\n\n\tprivate boolean tryToFilterRunner(Description description) {\n\t\tif (runner instanceof Filterable filterable) {\n\t\t\tExcludeDescriptionFilter filter = new ExcludeDescriptionFilter(description);\n\t\t\ttry {\n\t\t\t\tfilterable.filter(filter);\n\t\t\t}\n\t\t\tcatch (NoTestsRemainException ignore) {\n\t\t\t\t// it's safe to ignore this exception because childless TestDescriptors will get pruned\n\t\t\t}\n\t\t\treturn filter.wasSuccessful();\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tprotected boolean canBeRemovedFromHierarchy() {\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic void prune() {\n\t\tif (wasFiltered) {\n\t\t\t// filtering the runner may render intermediate Descriptions obsolete\n\t\t\t// (e.g. test classes without any remaining children in a suite)\n\t\t\tpruneDescriptorsForObsoleteDescriptions(List.of(runner.getDescription()));\n\t\t}\n\t\tif (rejectedExclusions.isEmpty()) {\n\t\t\tsuper.prune();\n\t\t}\n\t\telse if (rejectedExclusions.containsAll(getDescription().getChildren())) {\n\t\t\t// since the Runner was asked to remove all of its direct children,\n\t\t\t// it's safe to remove it entirely\n\t\t\tremoveFromHierarchy();\n\t\t}\n\t\telse {\n\t\t\tlogIncompleteFiltering();\n\t\t}\n\t}\n\n\tprivate void logIncompleteFiltering() {\n\t\tif (runner instanceof Filterable) {\n\t\t\tlogger.warn(() -> \"Runner \" + getRunnerToReport().getClass().getName() //\n\t\t\t\t\t+ \" (used on class \" + getLegacyReportingName() + \") was not able to satisfy all filter requests.\");\n\t\t}\n\t\telse {\n\t\t\twarnAboutUnfilterableRunner();\n\t\t}\n\t}\n\n\tprivate void warnAboutUnfilterableRunner() {\n\t\tlogger.warn(() -> \"Runner \" + getRunnerToReport().getClass().getName() //\n\t\t\t\t+ \" (used on class \" + getLegacyReportingName() + \") does not support filtering\" //\n\t\t\t\t+ \" and will therefore be run completely.\");\n\t}\n\n\tpublic Optional<List<Filter>> getFilters() {\n\t\treturn Optional.ofNullable(filters);\n\t}\n\n\tpublic void clearFilters() {\n\t\tthis.filters = null;\n\t}\n\n\tpublic void applyFilters(Consumer<RunnerTestDescriptor> childrenCreator) {\n\t\tif (filters != null && !filters.isEmpty()) {\n\t\t\tif (runner instanceof Filterable) {\n\t\t\t\tthis.runner = toRequest().filterWith(new OrFilter(filters)).getRunner();\n\t\t\t\tthis.description = runner.getDescription();\n\t\t\t\tthis.children.clear();\n\t\t\t\tchildrenCreator.accept(this);\n\t\t\t}\n\t\t\telse {\n\t\t\t\twarnAboutUnfilterableRunner();\n\t\t\t}\n\t\t}\n\t\tclearFilters();\n\t}\n\n\tprivate Runner getRunnerToReport() {\n\t\treturn (runner instanceof RunnerDecorator decorator) ? decorator.getDecoratedRunner() : runner;\n\t}\n\n\tpublic boolean isIgnored() {\n\t\treturn ignored;\n\t}\n\n\tpublic void setExecutorService(ExecutorService executorService) {\n\t\tRunner runner = getRunnerToReport();\n\t\tif (runner instanceof ParentRunner<?> parentRunner) {\n\t\t\tparentRunner.setScheduler(new RunnerScheduler() {\n\n\t\t\t\tprivate final List<Future<?>> futures = new CopyOnWriteArrayList<>();\n\n\t\t\t\t@Override\n\t\t\t\tpublic void schedule(Runnable childStatement) {\n\t\t\t\t\tfutures.add(executorService.submit(childStatement));\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void finished() {\n\t\t\t\t\tThrowableCollector collector = new OpenTest4JAwareThrowableCollector();\n\t\t\t\t\tAtomicBoolean wasInterrupted = new AtomicBoolean(false);\n\t\t\t\t\tfor (Future<?> future : futures) {\n\t\t\t\t\t\tcollector.execute(() -> {\n\t\t\t\t\t\t\t// We're calling `Future.get()` individually to allow for work stealing\n\t\t\t\t\t\t\t// in case `ExecutorService` is a `ForkJoinPool`\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tfuture.get();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (ExecutionException e) {\n\t\t\t\t\t\t\t\tthrow e.getCause();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\t\t\t\twasInterrupted.set(true);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tcollector.assertEmpty();\n\t\t\t\t\tif (wasInterrupted.get()) {\n\t\t\t\t\t\tlogger.warn(() -> \"Interrupted while waiting for runner to finish\");\n\t\t\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static class ExcludeDescriptionFilter extends Filter {\n\n\t\tprivate final Description description;\n\t\tprivate boolean successful;\n\n\t\tExcludeDescriptionFilter(Description description) {\n\t\t\tthis.description = description;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean shouldRun(Description description) {\n\t\t\tif (this.description.equals(description)) {\n\t\t\t\tsuccessful = true;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic String describe() {\n\t\t\treturn \"exclude \" + description;\n\t\t}\n\n\t\tboolean wasSuccessful() {\n\t\t\treturn successful;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/TestSourceProvider.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static java.util.Collections.synchronizedMap;\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.util.LruCache;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.runner.Description;\n\n/**\n * @since 5.6\n */\n@API(status = INTERNAL, since = \"5.6\")\npublic class TestSourceProvider {\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static final TestSource NULL_SOURCE = new TestSource() {\n\t};\n\n\tprivate final Map<Description, TestSource> testSourceCache = new ConcurrentHashMap<>();\n\tprivate final Map<Class<?>, List<Method>> methodsCache = synchronizedMap(new LruCache<>(31));\n\n\tpublic @Nullable TestSource findTestSource(Description description) {\n\t\tTestSource testSource = testSourceCache.computeIfAbsent(description, this::computeTestSource);\n\t\treturn testSource == NULL_SOURCE ? null : testSource;\n\t}\n\n\tprivate TestSource computeTestSource(Description description) {\n\t\tClass<?> testClass = description.getTestClass();\n\t\tif (testClass != null) {\n\t\t\tString methodName = DescriptionUtils.getMethodName(description);\n\t\t\tif (methodName != null) {\n\t\t\t\tMethod method = findMethod(testClass, sanitizeMethodName(methodName));\n\t\t\t\tif (method != null) {\n\t\t\t\t\treturn MethodSource.from(testClass, method);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn ClassSource.from(testClass);\n\t\t}\n\t\treturn NULL_SOURCE;\n\t}\n\n\tprivate String sanitizeMethodName(String methodName) {\n\t\tif (methodName.contains(\"[\") && methodName.endsWith(\"]\")) {\n\t\t\t// special case for parameterized tests\n\t\t\treturn methodName.substring(0, methodName.indexOf(\"[\"));\n\t\t}\n\t\treturn methodName;\n\t}\n\n\tprivate @Nullable Method findMethod(Class<?> testClass, String methodName) {\n\t\tList<Method> methods = methodsCache.computeIfAbsent(testClass,\n\t\t\tclazz -> findMethods(clazz, m -> true, TOP_DOWN)).stream() //\n\t\t\t\t.filter(where(Method::getName, isEqual(methodName))) //\n\t\t\t\t.toList();\n\t\tif (methods.isEmpty()) {\n\t\t\treturn null;\n\t\t}\n\t\tif (methods.size() == 1) {\n\t\t\treturn methods.get(0);\n\t\t}\n\t\tmethods = methods.stream().filter(ModifierSupport::isPublic).toList();\n\t\tif (methods.size() == 1) {\n\t\t\treturn methods.get(0);\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageEngineDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * @since 5.6\n */\n@API(status = INTERNAL, since = \"5.6\")\npublic class VintageEngineDescriptor extends EngineDescriptor {\n\n\tpublic VintageEngineDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, \"JUnit Vintage\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/VintageTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static java.util.Arrays.stream;\nimport static java.util.function.Predicate.isEqual;\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.experimental.categories.Category;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.runner.Description;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class VintageTestDescriptor extends AbstractTestDescriptor {\n\n\tpublic static final String ENGINE_ID = \"junit-vintage\";\n\tpublic static final String SEGMENT_TYPE_RUNNER = \"runner\";\n\tpublic static final String SEGMENT_TYPE_TEST = \"test\";\n\tpublic static final String SEGMENT_TYPE_DYNAMIC = \"dynamic\";\n\n\tprotected Description description;\n\n\tpublic VintageTestDescriptor(UniqueId uniqueId, Description description, @Nullable TestSource source) {\n\t\tthis(uniqueId, description, generateDisplayName(description), source);\n\t}\n\n\tVintageTestDescriptor(UniqueId uniqueId, Description description, String displayName, @Nullable TestSource source) {\n\t\tsuper(uniqueId, displayName, source);\n\t\tthis.description = description;\n\t}\n\n\tprivate static String generateDisplayName(Description description) {\n\t\tString methodName = DescriptionUtils.getMethodName(description);\n\t\treturn isNotBlank(methodName) ? methodName : description.getDisplayName();\n\t}\n\n\tpublic Description getDescription() {\n\t\treturn description;\n\t}\n\n\t@Override\n\tpublic String getLegacyReportingName() {\n\t\tString methodName = DescriptionUtils.getMethodName(description);\n\t\tif (methodName == null) {\n\t\t\tString className = description.getClassName();\n\t\t\tif (isNotBlank(className)) {\n\t\t\t\treturn className;\n\t\t\t}\n\t\t}\n\t\treturn super.getLegacyReportingName();\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn description.isTest() ? Type.TEST : Type.CONTAINER;\n\t}\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\tSet<TestTag> tags = new LinkedHashSet<>();\n\t\taddTagsFromParent(tags);\n\t\taddCategoriesAsTags(tags);\n\t\treturn tags;\n\t}\n\n\t@Override\n\tpublic void removeFromHierarchy() {\n\t\tif (canBeRemovedFromHierarchy()) {\n\t\t\tsuper.removeFromHierarchy();\n\t\t}\n\t}\n\n\tprotected boolean canBeRemovedFromHierarchy() {\n\t\treturn tryToExcludeFromRunner(this.description);\n\t}\n\n\tprotected boolean tryToExcludeFromRunner(Description description) {\n\t\t// @formatter:off\n\t\treturn getParent().map(VintageTestDescriptor.class::cast)\n\t\t\t\t.map(parent -> parent.tryToExcludeFromRunner(description))\n\t\t\t\t.orElse(false);\n\t\t// @formatter:on\n\t}\n\n\tvoid pruneDescriptorsForObsoleteDescriptions(List<Description> newSiblingDescriptions) {\n\t\tOptional<Description> newDescription = newSiblingDescriptions.stream().filter(isEqual(description)).findAny();\n\t\tif (newDescription.isPresent()) {\n\t\t\tList<Description> newChildren = newDescription.get().getChildren();\n\t\t\tnew ArrayList<>(children).stream().map(VintageTestDescriptor.class::cast).forEach(\n\t\t\t\tchildDescriptor -> childDescriptor.pruneDescriptorsForObsoleteDescriptions(newChildren));\n\t\t}\n\t\telse {\n\t\t\tsuper.removeFromHierarchy();\n\t\t}\n\t}\n\n\tprivate void addTagsFromParent(Set<TestTag> tags) {\n\t\tgetParent().map(TestDescriptor::getTags).ifPresent(tags::addAll);\n\t}\n\n\tprivate void addCategoriesAsTags(Set<TestTag> tags) {\n\t\tCategory annotation = description.getAnnotation(Category.class);\n\t\tif (annotation != null) {\n\t\t\t// @formatter:off\n\t\t\tstream(annotation.value())\n\t\t\t\t\t.map(ReflectionUtils::getAllAssignmentCompatibleClasses)\n\t\t\t\t\t.flatMap(Collection::stream)\n\t\t\t\t\t.distinct()\n\t\t\t\t\t.map(Class::getName)\n\t\t\t\t\t.map(TestTag::create)\n\t\t\t\t\t.forEachOrdered(tags::add);\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/descriptor/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Test descriptors used within the JUnit Vintage test engine.\n */\n\n@NullMarked\npackage org.junit.vintage.engine.descriptor;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/ClassSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static java.util.Collections.emptySet;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_RUNNER;\n\nimport java.util.Optional;\n\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.UniqueId.Segment;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.support.discovery.SelectorResolver;\nimport org.junit.runner.Runner;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\n\n/**\n * @since 4.12\n */\nclass ClassSelectorResolver implements SelectorResolver {\n\n\tprivate static final DefensiveAllDefaultPossibilitiesBuilder RUNNER_BUILDER = new DefensiveAllDefaultPossibilitiesBuilder();\n\n\tprivate final ClassFilter classFilter;\n\n\tClassSelectorResolver(ClassFilter classFilter) {\n\t\tthis.classFilter = classFilter;\n\t}\n\n\t@Override\n\tpublic Resolution resolve(ClassSelector selector, Context context) {\n\t\tif (classFilter.match(selector.getClassName())) {\n\t\t\treturn resolveTestClassThatPassedNameFilter(selector.getJavaClass(), context);\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\t@Override\n\tpublic Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\tSegment lastSegment = selector.getUniqueId().getLastSegment();\n\t\tif (SEGMENT_TYPE_RUNNER.equals(lastSegment.getType())) {\n\t\t\tString testClassName = lastSegment.getValue();\n\t\t\tif (classFilter.match(testClassName)) {\n\t\t\t\tClass<?> testClass = ReflectionSupport.tryToLoadClass(testClassName)//\n\t\t\t\t\t\t.getNonNullOrThrow(cause -> new JUnitException(\"Unknown class: \" + testClassName, cause));\n\t\t\t\treturn resolveTestClassThatPassedNameFilter(testClass, context);\n\t\t\t}\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate Resolution resolveTestClassThatPassedNameFilter(Class<?> testClass, Context context) {\n\t\tif (!classFilter.match(testClass)) {\n\t\t\treturn unresolved();\n\t\t}\n\t\tRunner runner = RUNNER_BUILDER.safeRunnerForClass(testClass);\n\t\tif (runner == null) {\n\t\t\treturn unresolved();\n\t\t}\n\t\treturn context.addToParent(parent -> Optional.of(createRunnerTestDescriptor(parent, testClass, runner))).map(\n\t\t\trunnerTestDescriptor -> Match.exact(runnerTestDescriptor, () -> {\n\t\t\t\trunnerTestDescriptor.clearFilters();\n\t\t\t\treturn emptySet();\n\t\t\t})).map(Resolution::match).orElse(unresolved());\n\t}\n\n\tprivate RunnerTestDescriptor createRunnerTestDescriptor(TestDescriptor parent, Class<?> testClass, Runner runner) {\n\t\tUniqueId uniqueId = parent.getUniqueId().append(SEGMENT_TYPE_RUNNER, testClass.getName());\n\t\treturn new RunnerTestDescriptor(uniqueId, testClass, runner, RUNNER_BUILDER.isIgnored(runner));\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/DefensiveAllDefaultPossibilitiesBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.Ignore;\nimport org.junit.internal.builders.AllDefaultPossibilitiesBuilder;\nimport org.junit.internal.builders.AnnotatedBuilder;\nimport org.junit.internal.builders.IgnoredBuilder;\nimport org.junit.internal.builders.IgnoredClassRunner;\nimport org.junit.internal.builders.JUnit4Builder;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.runner.Runner;\nimport org.junit.runner.manipulation.Filterable;\nimport org.junit.runners.model.RunnerBuilder;\n\n/**\n * Customization of {@link AllDefaultPossibilitiesBuilder} from JUnit 4 to\n * ignore certain classes that would otherwise be reported as errors or cause\n * infinite recursion.\n *\n * @since 4.12\n * @see DefensiveAnnotatedBuilder\n * @see DefensiveJUnit4Builder\n * @see IgnoredClassRunner\n */\nclass DefensiveAllDefaultPossibilitiesBuilder extends AllDefaultPossibilitiesBuilder {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(DefensiveAllDefaultPossibilitiesBuilder.class);\n\n\tprivate final AnnotatedBuilder annotatedBuilder;\n\tprivate final JUnit4Builder junit4Builder;\n\tprivate final IgnoredBuilder ignoredBuilder;\n\n\t@SuppressWarnings(\"deprecation\")\n\tDefensiveAllDefaultPossibilitiesBuilder() {\n\t\tsuper(true);\n\t\tannotatedBuilder = new DefensiveAnnotatedBuilder(this);\n\t\tjunit4Builder = new DefensiveJUnit4Builder();\n\t\tignoredBuilder = new NullIgnoredBuilder();\n\t}\n\n\t@Override\n\tpublic Runner runnerForClass(Class<?> testClass) throws Throwable {\n\t\tRunner runner = super.runnerForClass(testClass);\n\t\tif (testClass.getAnnotation(Ignore.class) != null) {\n\t\t\tif (runner == null) {\n\t\t\t\treturn new IgnoredClassRunner(testClass);\n\t\t\t}\n\t\t\treturn decorateIgnoredTestClass(runner);\n\t\t}\n\t\treturn runner;\n\t}\n\n\tboolean isIgnored(Runner runner) {\n\t\treturn runner instanceof IgnoredClassRunner || runner instanceof IgnoringRunnerDecorator;\n\t}\n\n\t/**\n\t * Instead of checking for the {@link Ignore} annotation and returning an\n\t * {@link IgnoredClassRunner} from {@link IgnoredBuilder}, we've let the\n\t * super class determine the regular runner that would have been used if\n\t * {@link Ignore} hadn't been present. Now, we decorate the runner to\n\t * override its runtime behavior (i.e. skip execution) but return its\n\t * regular {@link org.junit.runner.Description}.\n\t */\n\tprivate IgnoringRunnerDecorator decorateIgnoredTestClass(Runner runner) {\n\t\tif (runner instanceof Filterable) {\n\t\t\treturn new FilterableIgnoringRunnerDecorator(runner);\n\t\t}\n\t\treturn new IgnoringRunnerDecorator(runner);\n\t}\n\n\t@Override\n\tprotected AnnotatedBuilder annotatedBuilder() {\n\t\treturn annotatedBuilder;\n\t}\n\n\t@Override\n\tprotected JUnit4Builder junit4Builder() {\n\t\treturn junit4Builder;\n\t}\n\n\t@Override\n\tprotected IgnoredBuilder ignoredBuilder() {\n\t\treturn ignoredBuilder;\n\t}\n\n\t/**\n\t * Customization of {@link AnnotatedBuilder} that ignores classes annotated\n\t * with {@code @RunWith(JUnitPlatform.class)} to avoid infinite recursion.\n\t */\n\tprivate static class DefensiveAnnotatedBuilder extends AnnotatedBuilder {\n\n\t\tDefensiveAnnotatedBuilder(RunnerBuilder suiteBuilder) {\n\t\t\tsuper(suiteBuilder);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Runner buildRunner(Class<? extends Runner> runnerClass, Class<?> testClass) throws Exception {\n\t\t\t// Referenced by name because it might not be available at runtime.\n\t\t\tif (\"org.junit.platform.runner.JUnitPlatform\".equals(runnerClass.getName())) {\n\t\t\t\tlogger.warn(() -> \"Ignoring test class using JUnitPlatform runner: \" + testClass.getName());\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn super.buildRunner(runnerClass, testClass);\n\t\t}\n\t}\n\n\t/**\n\t * Customization of {@link JUnit4Builder} that ignores classes that do not\n\t * contain any test methods in order not to report errors for them.\n\t */\n\tprivate static class DefensiveJUnit4Builder extends JUnit4Builder {\n\n\t\tprivate static final Predicate<Method> isPotentialJUnit4TestMethod = new IsPotentialJUnit4TestMethod();\n\n\t\t@Override\n\t\tpublic @Nullable Runner runnerForClass(Class<?> testClass) throws Throwable {\n\t\t\tif (containsTestMethods(testClass)) {\n\t\t\t\treturn super.runnerForClass(testClass);\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\tprivate boolean containsTestMethods(Class<?> testClass) {\n\t\t\treturn ReflectionUtils.isMethodPresent(testClass, isPotentialJUnit4TestMethod);\n\t\t}\n\t}\n\n\t/**\n\t * Customization of {@link IgnoredBuilder} that always returns {@code null}.\n\t *\n\t * @since 5.1\n\t */\n\tprivate static class NullIgnoredBuilder extends IgnoredBuilder {\n\t\t@Override\n\t\tpublic @Nullable Runner runnerForClass(Class<?> testClass) {\n\t\t\t// don't ignore entire test classes just yet\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/FilterableIgnoringRunnerDecorator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.runner.Runner;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.runner.manipulation.Filterable;\nimport org.junit.runner.manipulation.NoTestsRemainException;\n\n/**\n * {@link Filterable} {@link IgnoringRunnerDecorator}.\n *\n * @since 5.1\n */\nclass FilterableIgnoringRunnerDecorator extends IgnoringRunnerDecorator implements Filterable {\n\n\tFilterableIgnoringRunnerDecorator(Runner runner) {\n\t\tsuper(runner);\n\t\tPreconditions.condition(runner instanceof Filterable,\n\t\t\t() -> \"Runner must be an instance of Filterable: \" + runner.getClass().getName());\n\t}\n\n\t@Override\n\tpublic void filter(Filter filter) throws NoTestsRemainException {\n\t\t((Filterable) runner).filter(filter);\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IgnoringRunnerDecorator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.runner.Description;\nimport org.junit.runner.Runner;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.vintage.engine.descriptor.RunnerDecorator;\n\n/**\n * Decorator for Runners that will be ignored completely.\n *\n * <p>Contrary to {@link org.junit.internal.builders.IgnoredClassRunner}, this\n * runner returns a complete description including all children.\n *\n * @since 5.1\n */\nclass IgnoringRunnerDecorator extends Runner implements RunnerDecorator {\n\n\tprotected final Runner runner;\n\n\tIgnoringRunnerDecorator(Runner runner) {\n\t\tthis.runner = Preconditions.notNull(runner, \"Runner must not be null\");\n\t}\n\n\t@Override\n\tpublic Description getDescription() {\n\t\treturn runner.getDescription();\n\t}\n\n\t@Override\n\tpublic void run(RunNotifier notifier) {\n\t\tnotifier.fireTestIgnored(getDescription());\n\t}\n\n\t@Override\n\tpublic Runner getDecoratedRunner() {\n\t\treturn runner;\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.support.ModifierSupport.isAbstract;\nimport static org.junit.platform.commons.support.ModifierSupport.isPublic;\nimport static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;\n\nimport java.util.function.Predicate;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"5.8\", consumers = \"org.junit.vintage.**\")\npublic class IsPotentialJUnit4TestClass implements Predicate<Class<?>> {\n\n\t@Override\n\tpublic boolean test(Class<?> candidate) {\n\t\t// Do not collapse into a single return statement.\n\t\tif (!isPublic(candidate)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isAbstract(candidate)) {\n\t\t\treturn false;\n\t\t}\n\t\tif (isInnerClass(candidate)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Predicate;\n\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\nclass IsPotentialJUnit4TestMethod implements Predicate<Method> {\n\n\t@Override\n\tpublic boolean test(Method method) {\n\t\t// Don't use AnnotationUtils.isAnnotated since JUnit 4 does not support\n\t\t// meta-annotations\n\t\treturn method.isAnnotationPresent(Test.class);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/MethodSelectorResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_RUNNER;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.support.discovery.SelectorResolver;\nimport org.junit.runner.Description;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.vintage.engine.descriptor.DescriptionUtils;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\n\n/**\n * @since 4.12\n */\nclass MethodSelectorResolver implements SelectorResolver {\n\n\t@Override\n\tpublic Resolution resolve(MethodSelector selector, Context context) {\n\t\tClass<?> testClass = selector.getJavaClass();\n\t\treturn resolveParentAndAddFilter(context, selectClass(testClass), parent -> toMethodFilter(selector));\n\t}\n\n\t@Override\n\tpublic Resolution resolve(UniqueIdSelector selector, Context context) {\n\t\tfor (UniqueId current = selector.getUniqueId(); !current.getSegments().isEmpty(); current = current.removeLastSegment()) {\n\t\t\tif (SEGMENT_TYPE_RUNNER.equals(current.getLastSegment().getType())) {\n\t\t\t\treturn resolveParentAndAddFilter(context, selectUniqueId(current),\n\t\t\t\t\tparent -> toUniqueIdFilter(parent, selector.getUniqueId()));\n\t\t\t}\n\t\t}\n\t\treturn unresolved();\n\t}\n\n\tprivate Resolution resolveParentAndAddFilter(Context context, DiscoverySelector selector,\n\t\t\tFunction<RunnerTestDescriptor, Filter> filterCreator) {\n\t\treturn context.resolve(selector).flatMap(parent -> addFilter(parent, filterCreator)).map(\n\t\t\tthis::toResolution).orElse(unresolved());\n\t}\n\n\tprivate Optional<RunnerTestDescriptor> addFilter(TestDescriptor parent,\n\t\t\tFunction<RunnerTestDescriptor, Filter> filterCreator) {\n\t\tif (parent instanceof RunnerTestDescriptor runnerTestDescriptor) {\n\t\t\trunnerTestDescriptor.getFilters().ifPresent(\n\t\t\t\tfilters -> filters.add(filterCreator.apply(runnerTestDescriptor)));\n\t\t\treturn Optional.of(runnerTestDescriptor);\n\t\t}\n\t\treturn Optional.empty();\n\t}\n\n\tprivate Resolution toResolution(RunnerTestDescriptor parent) {\n\t\treturn Resolution.match(Match.partial(parent));\n\t}\n\n\tprivate Filter toMethodFilter(MethodSelector methodSelector) {\n\t\tClass<?> testClass = methodSelector.getJavaClass();\n\t\tString methodName = methodSelector.getMethodName();\n\t\treturn matchMethodDescription(Description.createTestDescription(testClass, methodName));\n\t}\n\n\tprivate Filter toUniqueIdFilter(RunnerTestDescriptor runnerTestDescriptor, UniqueId uniqueId) {\n\t\treturn new UniqueIdFilter(runnerTestDescriptor, uniqueId);\n\t}\n\n\t/**\n\t * The method {@link Filter#matchMethodDescription(Description)} returns a\n\t * filter that does not account for the case when the description is for a\n\t * {@link org.junit.runners.Parameterized} runner.\n\t */\n\tprivate static Filter matchMethodDescription(final Description desiredDescription) {\n\t\tString desiredMethodName = DescriptionUtils.getMethodName(desiredDescription);\n\t\treturn new Filter() {\n\n\t\t\t@Override\n\t\t\tpublic boolean shouldRun(Description description) {\n\t\t\t\tif (description.isTest()) {\n\t\t\t\t\treturn desiredDescription.equals(description) || isParameterizedMethod(description);\n\t\t\t\t}\n\n\t\t\t\t// explicitly check if any children want to run\n\t\t\t\tfor (Description each : description.getChildren()) {\n\t\t\t\t\tif (shouldRun(each)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tprivate boolean isParameterizedMethod(Description description) {\n\t\t\t\tString methodName = DescriptionUtils.getMethodName(description);\n\t\t\t\treturn methodName != null && methodName.startsWith(desiredMethodName + \"[\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String describe() {\n\t\t\t\treturn \"Method %s\".formatted(desiredDescription.getDisplayName());\n\t\t\t}\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static java.util.stream.Collectors.groupingBy;\nimport static java.util.stream.Collectors.toCollection;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.function.IntFunction;\n\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.runner.Description;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.TestSourceProvider;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\nimport org.junit.vintage.engine.support.UniqueIdReader;\nimport org.junit.vintage.engine.support.UniqueIdStringifier;\n\n/**\n * @since 5.5\n */\nclass RunnerTestDescriptorPostProcessor {\n\n\tprivate final UniqueIdReader uniqueIdReader = new UniqueIdReader();\n\tprivate final UniqueIdStringifier uniqueIdStringifier = new UniqueIdStringifier();\n\tprivate final TestSourceProvider testSourceProvider = new TestSourceProvider();\n\n\tvoid applyFiltersAndCreateDescendants(RunnerTestDescriptor runnerTestDescriptor) {\n\t\taddChildrenRecursively(runnerTestDescriptor);\n\t\trunnerTestDescriptor.applyFilters(this::addChildrenRecursively);\n\t}\n\n\tprivate void addChildrenRecursively(VintageTestDescriptor parent) {\n\t\tif (parent.getDescription().isTest()) {\n\t\t\treturn;\n\t\t}\n\t\tList<Description> children = parent.getDescription().getChildren();\n\t\t// Use LinkedHashMap to preserve order, ArrayList for fast access by index\n\t\tMap<String, List<Description>> childrenByUniqueId = children.stream().collect(\n\t\t\tgroupingBy(uniqueIdReader.andThen(uniqueIdStringifier), LinkedHashMap::new, toCollection(ArrayList::new)));\n\t\tfor (Entry<String, List<Description>> entry : childrenByUniqueId.entrySet()) {\n\t\t\tString uniqueId = entry.getKey();\n\t\t\tList<Description> childrenWithSameUniqueId = entry.getValue();\n\t\t\tIntFunction<String> uniqueIdGenerator = determineUniqueIdGenerator(uniqueId, childrenWithSameUniqueId);\n\t\t\tfor (int index = 0; index < childrenWithSameUniqueId.size(); index++) {\n\t\t\t\tString reallyUniqueId = uniqueIdGenerator.apply(index);\n\t\t\t\tDescription description = childrenWithSameUniqueId.get(index);\n\t\t\t\tUniqueId id = parent.getUniqueId().append(VintageTestDescriptor.SEGMENT_TYPE_TEST, reallyUniqueId);\n\t\t\t\tVintageTestDescriptor child = new VintageTestDescriptor(id, description,\n\t\t\t\t\ttestSourceProvider.findTestSource(description));\n\t\t\t\tparent.addChild(child);\n\t\t\t\taddChildrenRecursively(child);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate IntFunction<String> determineUniqueIdGenerator(String uniqueId,\n\t\t\tList<Description> childrenWithSameUniqueId) {\n\t\tif (childrenWithSameUniqueId.size() == 1) {\n\t\t\treturn index -> uniqueId;\n\t\t}\n\t\treturn index -> uniqueId + \"[\" + index + \"]\";\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/UniqueIdFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static java.util.stream.Collectors.toSet;\n\nimport java.util.ArrayDeque;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.runner.Description;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\n\n/**\n * @since 4.12\n */\nclass UniqueIdFilter extends Filter {\n\n\tprivate final RunnerTestDescriptor runnerTestDescriptor;\n\tprivate final UniqueId uniqueId;\n\n\t@SuppressWarnings({ \"NullAway.Init\", \"NotNullFieldNotInitialized\" })\n\tprivate Deque<Description> path;\n\n\t@SuppressWarnings({ \"NullAway.Init\", \"NotNullFieldNotInitialized\" })\n\tprivate Set<Description> descendants;\n\n\tUniqueIdFilter(RunnerTestDescriptor runnerTestDescriptor, UniqueId uniqueId) {\n\t\tthis.runnerTestDescriptor = runnerTestDescriptor;\n\t\tthis.uniqueId = uniqueId;\n\t}\n\n\t@SuppressWarnings(\"ConstantValue\")\n\tprivate void ensureInitialized() {\n\t\tif (descendants == null) {\n\t\t\tOptional<? extends TestDescriptor> identifiedTestDescriptor = runnerTestDescriptor.findByUniqueId(uniqueId);\n\t\t\tdescendants = determineDescendants(identifiedTestDescriptor);\n\t\t\tpath = determinePath(runnerTestDescriptor, identifiedTestDescriptor);\n\t\t}\n\t}\n\n\tprivate Deque<Description> determinePath(RunnerTestDescriptor runnerTestDescriptor,\n\t\t\tOptional<? extends TestDescriptor> identifiedTestDescriptor) {\n\t\tDeque<Description> path = new ArrayDeque<>();\n\t\tOptional<? extends TestDescriptor> current = identifiedTestDescriptor;\n\t\twhile (current.isPresent() && !current.get().equals(runnerTestDescriptor)) {\n\t\t\tpath.addFirst(((VintageTestDescriptor) current.get()).getDescription());\n\t\t\tcurrent = current.get().getParent();\n\t\t}\n\t\treturn path;\n\t}\n\n\tprivate Set<Description> determineDescendants(Optional<? extends TestDescriptor> identifiedTestDescriptor) {\n\t\t// @formatter:off\n\t\treturn identifiedTestDescriptor.map(\n\t\t\t\ttestDescriptor -> testDescriptor\n\t\t\t\t\t\t.getDescendants()\n\t\t\t\t\t\t.stream()\n\t\t\t\t\t\t.map(VintageTestDescriptor.class::cast)\n\t\t\t\t\t\t.map(VintageTestDescriptor::getDescription)\n\t\t\t\t\t\t.collect(toSet()))\n\t\t\t\t.orElseGet(Collections::emptySet);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic boolean shouldRun(Description description) {\n\t\tensureInitialized();\n\t\treturn path.contains(description) || descendants.contains(description);\n\t}\n\n\t@Override\n\tpublic String describe() {\n\t\treturn \"Unique ID \" + uniqueId;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/VintageDiscoverer.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver;\nimport org.junit.vintage.engine.Constants;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.VintageEngineDescriptor;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class VintageDiscoverer {\n\n\tprivate static final IsPotentialJUnit4TestClass isPotentialJUnit4TestClass = new IsPotentialJUnit4TestClass();\n\n\t// @formatter:off\n\tprivate static final EngineDiscoveryRequestResolver<TestDescriptor> resolver = EngineDiscoveryRequestResolver.builder()\n\t\t\t.addClassContainerSelectorResolver(isPotentialJUnit4TestClass)\n\t\t\t.addSelectorResolver(context -> new ClassSelectorResolver(ClassFilter.of(context.getClassNameFilter(), isPotentialJUnit4TestClass)))\n\t\t\t.addSelectorResolver(new MethodSelectorResolver())\n\t\t\t.build();\n\t// @formatter:on\n\n\tpublic VintageEngineDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tVintageEngineDescriptor engineDescriptor = new VintageEngineDescriptor(uniqueId);\n\t\tresolver.resolve(discoveryRequest, engineDescriptor);\n\t\tRunnerTestDescriptorPostProcessor postProcessor = new RunnerTestDescriptorPostProcessor();\n\t\tfor (TestDescriptor testDescriptor : engineDescriptor.getChildren()) {\n\t\t\tRunnerTestDescriptor runnerTestDescriptor = (RunnerTestDescriptor) testDescriptor;\n\t\t\tpostProcessor.applyFiltersAndCreateDescendants(runnerTestDescriptor);\n\t\t}\n\t\tif (isDiscoveryIssueReportingEnabled(discoveryRequest) && !engineDescriptor.getChildren().isEmpty()) {\n\t\t\tvar issue = DiscoveryIssue.create(DiscoveryIssue.Severity.INFO, //\n\t\t\t\t\"The JUnit Vintage engine is deprecated and should only be \" //\n\t\t\t\t\t\t+ \"used temporarily while migrating tests to JUnit Jupiter or another testing \" //\n\t\t\t\t\t\t+ \"framework with native JUnit Platform support.\");\n\t\t\tdiscoveryRequest.getDiscoveryListener().issueEncountered(uniqueId, issue);\n\t\t}\n\t\treturn engineDescriptor;\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static boolean isDiscoveryIssueReportingEnabled(EngineDiscoveryRequest discoveryRequest) {\n\t\treturn discoveryRequest.getConfigurationParameters() //\n\t\t\t\t.getBoolean(Constants.DISCOVERY_ISSUE_REPORTING_ENABLED_PROPERTY_NAME) //\n\t\t\t\t.orElse(true);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/discovery/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal classes for test discovery within the JUnit Vintage test engine.\n */\n\n@NullMarked\npackage org.junit.vintage.engine.discovery;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/CancellationTokenAwareRunNotifier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.runner.Description;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runner.notification.StoppedByUserException;\n\n/**\n * @since 6.0\n */\nclass CancellationTokenAwareRunNotifier extends RunNotifier {\n\n\tprivate final CancellationToken cancellationToken;\n\n\tCancellationTokenAwareRunNotifier(CancellationToken cancellationToken) {\n\t\tthis.cancellationToken = cancellationToken;\n\t}\n\n\t@Override\n\tpublic void fireTestStarted(Description description) throws StoppedByUserException {\n\t\tif (cancellationToken.isCancellationRequested()) {\n\t\t\tpleaseStop();\n\t\t}\n\t\tsuper.fireTestStarted(description);\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/EventType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\n/**\n * @since 5.4.1\n */\nenum EventType {\n\tREPORTED, SYNTHETIC\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunListenerAdapter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_DYNAMIC;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.Ignore;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.runner.Description;\nimport org.junit.runner.Result;\nimport org.junit.runner.notification.Failure;\nimport org.junit.runner.notification.RunListener;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.TestSourceProvider;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\nimport org.junit.vintage.engine.support.UniqueIdReader;\nimport org.junit.vintage.engine.support.UniqueIdStringifier;\n\n/**\n * @since 4.12\n */\nclass RunListenerAdapter extends RunListener {\n\n\tprivate final TestRun testRun;\n\tprivate final EngineExecutionListener listener;\n\tprivate final TestSourceProvider testSourceProvider;\n\tprivate final Function<Description, String> uniqueIdExtractor;\n\n\tRunListenerAdapter(TestRun testRun, EngineExecutionListener listener, TestSourceProvider testSourceProvider) {\n\t\tthis.testRun = testRun;\n\t\tthis.listener = listener;\n\t\tthis.testSourceProvider = testSourceProvider;\n\t\tthis.uniqueIdExtractor = new UniqueIdReader().andThen(new UniqueIdStringifier());\n\t}\n\n\t@Override\n\tpublic void testRunStarted(Description description) {\n\t\tif (description.isSuite() && !testRun.getRunnerTestDescriptor().isIgnored()) {\n\t\t\tfireExecutionStarted(testRun.getRunnerTestDescriptor(), EventType.REPORTED);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testSuiteStarted(Description description) {\n\t\tRunnerTestDescriptor runnerTestDescriptor = testRun.getRunnerTestDescriptor();\n\t\t// runnerTestDescriptor is reported in testRunStarted\n\t\tif (!runnerTestDescriptor.getDescription().equals(description)) {\n\t\t\ttestStarted(lookupOrRegisterNextTestDescriptor(description), EventType.REPORTED);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testIgnored(Description description) {\n\t\tTestDescriptor testDescriptor = lookupOrRegisterNextTestDescriptor(description);\n\t\tString reason = determineReasonForIgnoredTest(testDescriptor, description).orElse(\"<unknown>\");\n\t\ttestIgnored(testDescriptor, reason);\n\t}\n\n\t@Override\n\tpublic void testStarted(Description description) {\n\t\ttestStarted(lookupOrRegisterNextTestDescriptor(description), EventType.REPORTED);\n\t}\n\n\t@Override\n\tpublic void testAssumptionFailure(Failure failure) {\n\t\thandleFailure(failure, TestExecutionResult::aborted);\n\t}\n\n\t@Override\n\tpublic void testFailure(Failure failure) {\n\t\thandleFailure(failure, TestExecutionResult::failed);\n\t}\n\n\t@Override\n\tpublic void testFinished(Description description) {\n\t\ttestFinished(lookupOrRegisterCurrentTestDescriptor(description));\n\t}\n\n\t@Override\n\tpublic void testSuiteFinished(Description description) {\n\t\tRunnerTestDescriptor runnerTestDescriptor = testRun.getRunnerTestDescriptor();\n\t\t// runnerTestDescriptor is reported in testRunFinished\n\t\tif (!runnerTestDescriptor.getDescription().equals(description)) {\n\t\t\treportContainerFinished(lookupOrRegisterCurrentTestDescriptor(description));\n\t\t}\n\t}\n\n\t@Override\n\tpublic void testRunFinished(Result result) {\n\t\ttestRunFinished();\n\t}\n\n\tvoid testRunFinished() {\n\t\treportContainerFinished(testRun.getRunnerTestDescriptor());\n\t}\n\n\tprivate void reportContainerFinished(TestDescriptor containerTestDescriptor) {\n\t\tif (testRun.isNotSkipped(containerTestDescriptor)) {\n\t\t\tif (testRun.isNotStarted(containerTestDescriptor)) {\n\t\t\t\tfireExecutionStarted(containerTestDescriptor, EventType.SYNTHETIC);\n\t\t\t}\n\t\t\ttestRun.getInProgressTestDescriptorsWithSyntheticStartEvents().stream() //\n\t\t\t\t\t.filter(this::canFinish) //\n\t\t\t\t\t.forEach(this::fireExecutionFinished);\n\t\t\tif (testRun.isNotFinished(containerTestDescriptor)) {\n\t\t\t\tfireExecutionFinished(containerTestDescriptor);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate TestDescriptor lookupOrRegisterNextTestDescriptor(Description description) {\n\t\treturn lookupOrRegisterTestDescriptor(description, testRun::lookupNextTestDescriptor);\n\t}\n\n\tprivate TestDescriptor lookupOrRegisterCurrentTestDescriptor(Description description) {\n\t\treturn lookupOrRegisterTestDescriptor(description, testRun::lookupCurrentTestDescriptor);\n\t}\n\n\tprivate TestDescriptor lookupOrRegisterTestDescriptor(Description description,\n\t\t\tFunction<Description, Optional<VintageTestDescriptor>> lookup) {\n\t\treturn lookup.apply(description).orElseGet(() -> registerDynamicTestDescriptor(description, lookup));\n\t}\n\n\tprivate VintageTestDescriptor registerDynamicTestDescriptor(Description description,\n\t\t\tFunction<Description, Optional<VintageTestDescriptor>> lookup) {\n\t\t// workaround for dynamic children as used by Spock's Runner\n\t\tTestDescriptor parent = findParent(description, lookup);\n\t\tUniqueId uniqueId = parent.getUniqueId().append(SEGMENT_TYPE_DYNAMIC, uniqueIdExtractor.apply(description));\n\t\tVintageTestDescriptor dynamicDescriptor = new VintageTestDescriptor(uniqueId, description,\n\t\t\ttestSourceProvider.findTestSource(description));\n\t\tparent.addChild(dynamicDescriptor);\n\t\ttestRun.registerDynamicTest(dynamicDescriptor);\n\t\tdynamicTestRegistered(dynamicDescriptor);\n\t\treturn dynamicDescriptor;\n\t}\n\n\tprivate TestDescriptor findParent(Description description,\n\t\t\tFunction<Description, Optional<VintageTestDescriptor>> lookup) {\n\t\t// @formatter:off\n\t\treturn Optional.ofNullable(description.getTestClass())\n\t\t\t\t.map(Description::createSuiteDescription)\n\t\t\t\t.flatMap(lookup)\n\t\t\t\t.orElseGet(testRun::getRunnerTestDescriptor);\n\t\t// @formatter:on\n\t}\n\n\tprivate void handleFailure(Failure failure, Function<Throwable, TestExecutionResult> resultCreator) {\n\t\thandleFailure(failure, resultCreator, lookupOrRegisterCurrentTestDescriptor(failure.getDescription()));\n\t}\n\n\tprivate void handleFailure(Failure failure, Function<Throwable, TestExecutionResult> resultCreator,\n\t\t\tTestDescriptor testDescriptor) {\n\t\tTestExecutionResult result = resultCreator.apply(failure.getException());\n\t\ttestRun.storeResult(testDescriptor, result);\n\t\tif (testRun.isNotStarted(testDescriptor)) {\n\t\t\ttestStarted(testDescriptor, EventType.SYNTHETIC);\n\t\t}\n\t\tif (testRun.isNotFinished(testDescriptor) && testDescriptor.isContainer()\n\t\t\t\t&& testRun.hasSyntheticStartEvent(testDescriptor)\n\t\t\t\t&& testRun.isDescendantOfRunnerTestDescriptor(testDescriptor)) {\n\t\t\ttestFinished(testDescriptor);\n\t\t}\n\t}\n\n\tprivate void testIgnored(TestDescriptor testDescriptor, String reason) {\n\t\tfireExecutionFinishedForInProgressNonAncestorTestDescriptorsWithSyntheticStartEvents(testDescriptor);\n\t\tfireExecutionStartedIncludingUnstartedAncestors(testDescriptor.getParent());\n\t\tfireExecutionSkipped(testDescriptor, reason);\n\t}\n\n\tprivate Optional<String> determineReasonForIgnoredTest(TestDescriptor testDescriptor, Description description) {\n\t\tOptional<String> reason = getReason(description.getAnnotation(Ignore.class));\n\t\tif (reason.isPresent()) {\n\t\t\treturn reason;\n\t\t}\n\t\t// Workaround for some runners (e.g. JUnit38ClassRunner) don't include the @Ignore annotation\n\t\t// in the description, so we read it from the test class directly\n\t\treturn testDescriptor.getSource() //\n\t\t\t\t.filter(ClassSource.class::isInstance) //\n\t\t\t\t.map(source -> ((ClassSource) source).getJavaClass()) //\n\t\t\t\t.flatMap(testClass -> getReason(testClass.getAnnotation(Ignore.class)));\n\t}\n\n\tprivate static Optional<String> getReason(@Nullable Ignore annotation) {\n\t\treturn Optional.ofNullable(annotation).map(Ignore::value);\n\t}\n\n\tprivate void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\tfireExecutionStartedIncludingUnstartedAncestors(testDescriptor.getParent());\n\t\tlistener.dynamicTestRegistered(testDescriptor);\n\t}\n\n\tprivate void testStarted(TestDescriptor testDescriptor, EventType eventType) {\n\t\tfireExecutionFinishedForInProgressNonAncestorTestDescriptorsWithSyntheticStartEvents(testDescriptor);\n\t\tfireExecutionStartedIncludingUnstartedAncestors(testDescriptor.getParent());\n\t\tfireExecutionStarted(testDescriptor, eventType);\n\t}\n\n\tprivate void fireExecutionFinishedForInProgressNonAncestorTestDescriptorsWithSyntheticStartEvents(\n\t\t\tTestDescriptor testDescriptor) {\n\t\ttestRun.getInProgressTestDescriptorsWithSyntheticStartEvents().stream() //\n\t\t\t\t.filter(it -> !isAncestor(it, testDescriptor) && canFinish(it)) //\n\t\t\t\t.forEach(this::fireExecutionFinished);\n\t}\n\n\tprivate boolean isAncestor(TestDescriptor candidate, TestDescriptor testDescriptor) {\n\t\tOptional<TestDescriptor> parent = testDescriptor.getParent();\n\t\tif (parent.isEmpty()) {\n\t\t\treturn false;\n\t\t}\n\t\tif (parent.get().equals(candidate)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn isAncestor(candidate, parent.get());\n\t}\n\n\tprivate void testFinished(TestDescriptor descriptor) {\n\t\tfireExecutionFinished(descriptor);\n\t}\n\n\tprivate void fireExecutionStartedIncludingUnstartedAncestors(Optional<TestDescriptor> parent) {\n\t\tif (parent.isPresent() && canStart(parent.get())) {\n\t\t\tfireExecutionStartedIncludingUnstartedAncestors(parent.get().getParent());\n\t\t\tfireExecutionStarted(parent.get(), EventType.SYNTHETIC);\n\t\t}\n\t}\n\n\tprivate boolean canStart(TestDescriptor testDescriptor) {\n\t\treturn testRun.isNotStarted(testDescriptor) //\n\t\t\t\t&& (testDescriptor.equals(testRun.getRunnerTestDescriptor())\n\t\t\t\t\t\t|| testRun.isDescendantOfRunnerTestDescriptor(testDescriptor));\n\t}\n\n\tprivate boolean canFinish(TestDescriptor testDescriptor) {\n\t\treturn testRun.isNotFinished(testDescriptor) //\n\t\t\t\t&& testRun.isDescendantOfRunnerTestDescriptor(testDescriptor)\n\t\t\t\t&& testRun.areAllFinishedOrSkipped(testDescriptor.getChildren());\n\t}\n\n\tprivate void fireExecutionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\ttestRun.markSkipped(testDescriptor);\n\t\tlistener.executionSkipped(testDescriptor, reason);\n\t}\n\n\tprivate void fireExecutionStarted(TestDescriptor testDescriptor, EventType eventType) {\n\t\ttestRun.markStarted(testDescriptor, eventType);\n\t\tlistener.executionStarted(testDescriptor);\n\t}\n\n\tprivate void fireExecutionFinished(TestDescriptor testDescriptor) {\n\t\ttestRun.markFinished(testDescriptor);\n\t\tlistener.executionFinished(testDescriptor, testRun.getStoredResultOrSuccessful(testDescriptor));\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/RunnerExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.util.UnrecoverableExceptions;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runner.notification.StoppedByUserException;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.TestSourceProvider;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class RunnerExecutor {\n\n\tprivate final EngineExecutionListener engineExecutionListener;\n\tprivate final CancellationToken cancellationToken;\n\tprivate final TestSourceProvider testSourceProvider = new TestSourceProvider();\n\n\tpublic RunnerExecutor(EngineExecutionListener engineExecutionListener, CancellationToken cancellationToken) {\n\t\tthis.engineExecutionListener = engineExecutionListener;\n\t\tthis.cancellationToken = cancellationToken;\n\t}\n\n\tpublic void execute(RunnerTestDescriptor runnerTestDescriptor) {\n\t\tif (cancellationToken.isCancellationRequested()) {\n\t\t\tengineExecutionListener.executionSkipped(runnerTestDescriptor, \"Execution cancelled\");\n\t\t\treturn;\n\t\t}\n\t\tRunNotifier notifier = new CancellationTokenAwareRunNotifier(cancellationToken);\n\t\tvar testRun = new TestRun(runnerTestDescriptor);\n\t\tvar listener = new RunListenerAdapter(testRun, engineExecutionListener, testSourceProvider);\n\t\tnotifier.addListener(listener);\n\t\ttry {\n\t\t\tlistener.testRunStarted(runnerTestDescriptor.getDescription());\n\t\t\trunnerTestDescriptor.getRunner().run(notifier);\n\t\t\tlistener.testRunFinished();\n\t\t}\n\t\tcatch (StoppedByUserException e) {\n\t\t\treportEventsForCancellation(e, testRun);\n\t\t}\n\t\tcatch (Throwable t) {\n\t\t\tUnrecoverableExceptions.rethrowIfUnrecoverable(t);\n\t\t\treportUnexpectedFailure(testRun, runnerTestDescriptor, failed(t));\n\t\t}\n\t}\n\n\tprivate void reportEventsForCancellation(StoppedByUserException exception, TestRun testRun) {\n\t\ttestRun.getInProgressTestDescriptors().forEach(startedDescriptor -> {\n\t\t\tstartedDescriptor.getChildren().forEach(child -> {\n\t\t\t\tif (!testRun.isFinishedOrSkipped(child)) {\n\t\t\t\t\tengineExecutionListener.executionSkipped(child, \"Execution cancelled\");\n\t\t\t\t\ttestRun.markSkipped(child);\n\t\t\t\t}\n\t\t\t});\n\t\t\tengineExecutionListener.executionFinished(startedDescriptor, TestExecutionResult.aborted(exception));\n\t\t\ttestRun.markFinished(startedDescriptor);\n\t\t});\n\t}\n\n\tprivate void reportUnexpectedFailure(TestRun testRun, RunnerTestDescriptor runnerTestDescriptor,\n\t\t\tTestExecutionResult result) {\n\t\tif (testRun.isNotStarted(runnerTestDescriptor)) {\n\t\t\tengineExecutionListener.executionStarted(runnerTestDescriptor);\n\t\t}\n\t\tengineExecutionListener.executionFinished(runnerTestDescriptor, result);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/TestRun.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Collectors.toCollection;\nimport static java.util.stream.Collectors.toMap;\nimport static java.util.stream.Stream.concat;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\n\nimport java.util.ArrayDeque;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Deque;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.runner.Description;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * @since 4.12\n */\nclass TestRun {\n\n\tprivate final RunnerTestDescriptor runnerTestDescriptor;\n\tprivate final Set<TestDescriptor> runnerDescendants;\n\tprivate final Map<Description, VintageDescriptors> descriptionToDescriptors;\n\tprivate final Map<TestDescriptor, List<TestExecutionResult>> executionResults = new LinkedHashMap<>();\n\tprivate final Set<TestDescriptor> skippedDescriptors = new LinkedHashSet<>();\n\tprivate final Set<TestDescriptor> startedDescriptors = new HashSet<>();\n\tprivate final Map<TestDescriptor, EventType> inProgressDescriptors = new LinkedHashMap<>();\n\tprivate final Set<TestDescriptor> finishedDescriptors = new LinkedHashSet<>();\n\tprivate final ThreadLocal<Deque<VintageTestDescriptor>> inProgressDescriptorsByStartingThread = ThreadLocal.withInitial(\n\t\tArrayDeque::new);\n\n\tTestRun(RunnerTestDescriptor runnerTestDescriptor) {\n\t\tthis.runnerTestDescriptor = runnerTestDescriptor;\n\t\trunnerDescendants = new LinkedHashSet<>(runnerTestDescriptor.getDescendants());\n\t\t// @formatter:off\n\t\tdescriptionToDescriptors = concat(Stream.of(runnerTestDescriptor), runnerDescendants.stream())\n\t\t\t\t.map(VintageTestDescriptor.class::cast)\n\t\t\t\t.collect(toMap(VintageTestDescriptor::getDescription, VintageDescriptors::new, VintageDescriptors::merge, HashMap::new));\n\t\t// @formatter:on\n\t}\n\n\tvoid registerDynamicTest(VintageTestDescriptor testDescriptor) {\n\t\tdescriptionToDescriptors.computeIfAbsent(testDescriptor.getDescription(), __ -> new VintageDescriptors()).add(\n\t\t\ttestDescriptor);\n\t\trunnerDescendants.add(testDescriptor);\n\t}\n\n\tRunnerTestDescriptor getRunnerTestDescriptor() {\n\t\treturn runnerTestDescriptor;\n\t}\n\n\tCollection<TestDescriptor> getInProgressTestDescriptorsWithSyntheticStartEvents() {\n\t\tList<TestDescriptor> result = inProgressDescriptors.entrySet().stream() //\n\t\t\t\t.filter(entry -> entry.getValue().equals(EventType.SYNTHETIC)) //\n\t\t\t\t.map(Entry::getKey) //\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\t\tCollections.reverse(result);\n\t\treturn result;\n\t}\n\n\tCollection<TestDescriptor> getInProgressTestDescriptors() {\n\t\tList<TestDescriptor> result = new ArrayList<>(inProgressDescriptors.keySet());\n\t\tCollections.reverse(result);\n\t\treturn result;\n\t}\n\n\tboolean isDescendantOfRunnerTestDescriptor(TestDescriptor testDescriptor) {\n\t\treturn runnerDescendants.contains(testDescriptor);\n\t}\n\n\tboolean hasSyntheticStartEvent(TestDescriptor testDescriptor) {\n\t\treturn inProgressDescriptors.get(testDescriptor) == EventType.SYNTHETIC;\n\t}\n\n\tOptional<VintageTestDescriptor> lookupNextTestDescriptor(Description description) {\n\t\treturn lookupUnambiguouslyOrApplyFallback(description, VintageDescriptors::getNextUnstarted);\n\t}\n\n\tOptional<VintageTestDescriptor> lookupCurrentTestDescriptor(Description description) {\n\t\treturn lookupUnambiguouslyOrApplyFallback(description, __ -> {\n\t\t\tVintageTestDescriptor lastStarted = inProgressDescriptorsByStartingThread.get().peekLast();\n\t\t\tif (lastStarted != null && description.equals(lastStarted.getDescription())) {\n\t\t\t\treturn Optional.of(lastStarted);\n\t\t\t}\n\t\t\treturn Optional.empty();\n\t\t});\n\t}\n\n\tprivate Optional<VintageTestDescriptor> lookupUnambiguouslyOrApplyFallback(Description description,\n\t\t\tFunction<VintageDescriptors, Optional<VintageTestDescriptor>> fallback) {\n\t\tVintageDescriptors vintageDescriptors = descriptionToDescriptors.getOrDefault(description,\n\t\t\tVintageDescriptors.NONE);\n\t\tOptional<VintageTestDescriptor> result = vintageDescriptors.getUnambiguously(description);\n\t\tif (result.isEmpty()) {\n\t\t\tresult = fallback.apply(vintageDescriptors);\n\t\t}\n\t\treturn result;\n\t}\n\n\tvoid markSkipped(TestDescriptor testDescriptor) {\n\t\tskippedDescriptors.add(testDescriptor);\n\t\tif (testDescriptor instanceof VintageTestDescriptor vintageDescriptor) {\n\t\t\tgetVintageDescriptors(vintageDescriptor).incrementSkippedOrStarted();\n\t\t}\n\t}\n\n\tboolean isNotSkipped(TestDescriptor testDescriptor) {\n\t\treturn !isSkipped(testDescriptor);\n\t}\n\n\tboolean isSkipped(TestDescriptor testDescriptor) {\n\t\treturn skippedDescriptors.contains(testDescriptor);\n\t}\n\n\tvoid markStarted(TestDescriptor testDescriptor, EventType eventType) {\n\t\tinProgressDescriptors.put(testDescriptor, eventType);\n\t\tstartedDescriptors.add(testDescriptor);\n\t\tif (testDescriptor instanceof VintageTestDescriptor vintageDescriptor) {\n\t\t\tinProgressDescriptorsByStartingThread.get().addLast(vintageDescriptor);\n\t\t\tgetVintageDescriptors(vintageDescriptor).incrementSkippedOrStarted();\n\t\t}\n\t}\n\n\tprivate VintageDescriptors getVintageDescriptors(VintageTestDescriptor vintageDescriptor) {\n\t\treturn requireNonNull(descriptionToDescriptors.get(vintageDescriptor.getDescription()),\n\t\t\t() -> \"No descriptors for \" + vintageDescriptor);\n\t}\n\n\tboolean isNotStarted(TestDescriptor testDescriptor) {\n\t\treturn !startedDescriptors.contains(testDescriptor);\n\t}\n\n\tvoid markFinished(TestDescriptor testDescriptor) {\n\t\tinProgressDescriptors.remove(testDescriptor);\n\t\tfinishedDescriptors.add(testDescriptor);\n\t\tif (testDescriptor instanceof VintageTestDescriptor descriptor) {\n\t\t\tinProgressDescriptorsByStartingThread.get().removeLastOccurrence(descriptor);\n\t\t}\n\t}\n\n\tboolean isNotFinished(TestDescriptor testDescriptor) {\n\t\treturn !isFinished(testDescriptor);\n\t}\n\n\tboolean isFinished(TestDescriptor testDescriptor) {\n\t\treturn finishedDescriptors.contains(testDescriptor);\n\t}\n\n\tboolean areAllFinishedOrSkipped(Set<? extends TestDescriptor> testDescriptors) {\n\t\treturn testDescriptors.stream().allMatch(this::isFinishedOrSkipped);\n\t}\n\n\tboolean isFinishedOrSkipped(TestDescriptor testDescriptor) {\n\t\treturn isFinished(testDescriptor) || isSkipped(testDescriptor);\n\t}\n\n\tvoid storeResult(TestDescriptor testDescriptor, TestExecutionResult result) {\n\t\tList<TestExecutionResult> testExecutionResults = executionResults.computeIfAbsent(testDescriptor,\n\t\t\tkey -> new ArrayList<>());\n\t\ttestExecutionResults.add(result);\n\t}\n\n\tTestExecutionResult getStoredResultOrSuccessful(TestDescriptor testDescriptor) {\n\t\tList<TestExecutionResult> testExecutionResults = executionResults.get(testDescriptor);\n\n\t\tif (testExecutionResults == null) {\n\t\t\treturn successful();\n\t\t}\n\t\tif (testExecutionResults.size() == 1) {\n\t\t\treturn testExecutionResults.get(0);\n\t\t}\n\t\t// @formatter:off\n\t\tList<Throwable> failures = testExecutionResults\n\t\t\t\t.stream()\n\t\t\t\t.map(TestExecutionResult::getThrowable)\n\t\t\t\t.map(Optional::orElseThrow)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t\tMultipleFailuresError multipleFailuresError = new MultipleFailuresError(\"\", failures);\n\t\tfailures.forEach(multipleFailuresError::addSuppressed);\n\t\treturn failed(multipleFailuresError);\n\t}\n\n\tprivate static class VintageDescriptors {\n\n\t\tprivate static final VintageDescriptors NONE = new VintageDescriptors(emptyList());\n\n\t\tprivate final List<VintageTestDescriptor> descriptors;\n\t\tprivate int skippedOrStartedCount;\n\n\t\tstatic VintageDescriptors merge(VintageDescriptors a, VintageDescriptors b) {\n\t\t\tList<VintageTestDescriptor> mergedDescriptors = new ArrayList<>(\n\t\t\t\ta.descriptors.size() + b.descriptors.size());\n\t\t\tmergedDescriptors.addAll(a.descriptors);\n\t\t\tmergedDescriptors.addAll(b.descriptors);\n\t\t\treturn new VintageDescriptors(mergedDescriptors);\n\t\t}\n\n\t\tVintageDescriptors(VintageTestDescriptor vintageTestDescriptor) {\n\t\t\tthis();\n\t\t\tadd(vintageTestDescriptor);\n\t\t}\n\n\t\tVintageDescriptors() {\n\t\t\tthis(new ArrayList<>(1));\n\t\t}\n\n\t\tVintageDescriptors(List<VintageTestDescriptor> descriptors) {\n\t\t\tthis.descriptors = descriptors;\n\t\t}\n\n\t\tvoid add(VintageTestDescriptor descriptor) {\n\t\t\tdescriptors.add(descriptor);\n\t\t}\n\n\t\t/**\n\t\t * Returns the {@link TestDescriptor} that represents the specified\n\t\t * {@link Description}.\n\t\t *\n\t\t * <p>There are edge cases where multiple {@link Description Descriptions}\n\t\t * with the same {@code uniqueId} exist, e.g. when using overloaded methods\n\t\t * to define {@linkplain org.junit.experimental.theories.Theory theories}.\n\t\t * In this case, we try to find the correct {@link TestDescriptor} by\n\t\t * checking for object identity on the {@link Description} it represents.\n\t\t *\n\t\t * @param description the {@code Description} to look up\n\t\t */\n\t\t@SuppressWarnings(\"ReferenceEquality\")\n\t\tOptional<VintageTestDescriptor> getUnambiguously(Description description) {\n\t\t\tif (descriptors.isEmpty()) {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t\tif (descriptors.size() == 1) {\n\t\t\t\treturn Optional.of(descriptors.get(0));\n\t\t\t}\n\t\t\t// @formatter:off\n\t\t\treturn descriptors.stream()\n\t\t\t\t\t.filter(testDescriptor -> description == testDescriptor.getDescription())\n\t\t\t\t\t.findFirst();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate void incrementSkippedOrStarted() {\n\t\t\tskippedOrStartedCount++;\n\t\t}\n\n\t\tprivate Optional<VintageTestDescriptor> getNextUnstarted() {\n\t\t\tif (skippedOrStartedCount < descriptors.size()) {\n\t\t\t\treturn Optional.of(descriptors.get(skippedOrStartedCount));\n\t\t\t}\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/VintageExecutor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static java.util.Objects.requireNonNullElse;\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.vintage.engine.Constants;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.VintageEngineDescriptor;\n\n/**\n * @since 5.12\n */\n@SuppressWarnings({ \"deprecation\", \"RedundantSuppression\" })\n@API(status = INTERNAL, since = \"5.12\")\npublic class VintageExecutor {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(VintageExecutor.class);\n\n\tprivate static final int DEFAULT_THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors();\n\tprivate static final int SHUTDOWN_TIMEOUT_SECONDS = 30;\n\n\tprivate final VintageEngineDescriptor engineDescriptor;\n\tprivate final EngineExecutionListener engineExecutionListener;\n\tprivate final ConfigurationParameters configurationParameters;\n\n\tprivate final boolean parallelExecutionEnabled;\n\tprivate final boolean classes;\n\tprivate final boolean methods;\n\n\tpublic VintageExecutor(VintageEngineDescriptor engineDescriptor, EngineExecutionListener engineExecutionListener,\n\t\t\tConfigurationParameters configurationParameters) {\n\t\tthis.engineDescriptor = engineDescriptor;\n\t\tthis.engineExecutionListener = engineExecutionListener;\n\t\tthis.configurationParameters = configurationParameters;\n\t\tthis.parallelExecutionEnabled = configurationParameters.getBoolean(Constants.PARALLEL_EXECUTION_ENABLED).orElse(\n\t\t\tfalse);\n\t\tthis.classes = configurationParameters.getBoolean(Constants.PARALLEL_CLASS_EXECUTION).orElse(false);\n\t\tthis.methods = configurationParameters.getBoolean(Constants.PARALLEL_METHOD_EXECUTION).orElse(false);\n\t}\n\n\tpublic void executeAllChildren(CancellationToken cancellationToken) {\n\n\t\tif (!parallelExecutionEnabled) {\n\t\t\texecuteClassesAndMethodsSequentially(cancellationToken);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!classes && !methods) {\n\t\t\tlogger.warn(() -> \"Parallel execution is enabled but no scope is defined. \"\n\t\t\t\t\t+ \"Falling back to sequential execution.\");\n\t\t\texecuteClassesAndMethodsSequentially(cancellationToken);\n\t\t\treturn;\n\t\t}\n\n\t\tboolean wasInterrupted = executeInParallel(cancellationToken);\n\t\tif (wasInterrupted) {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\t}\n\n\tprivate void executeClassesAndMethodsSequentially(CancellationToken cancellationToken) {\n\t\tRunnerExecutor runnerExecutor = new RunnerExecutor(engineExecutionListener, cancellationToken);\n\t\t// Create a mutable copy so test descriptors can be made available for\n\t\t// GC immediately after execution.\n\t\tvar children = new LinkedHashSet<>(engineDescriptor.getChildren());\n\t\tfor (var iterator = children.iterator(); iterator.hasNext();) {\n\t\t\tvar testDescriptor = (RunnerTestDescriptor) iterator.next();\n\t\t\trunnerExecutor.execute(testDescriptor);\n\t\t\t// Remove the test descriptor from the engine and iterable to allow GC.\n\t\t\tengineDescriptor.removeChild(testDescriptor);\n\t\t\titerator.remove();\n\t\t}\n\t}\n\n\tprivate boolean executeInParallel(CancellationToken cancellationToken) {\n\t\tExecutorService executorService = Executors.newWorkStealingPool(getThreadPoolSize());\n\t\tRunnerExecutor runnerExecutor = new RunnerExecutor(engineExecutionListener, cancellationToken);\n\n\t\tList<RunnerTestDescriptor> runnerTestDescriptors = collectRunnerTestDescriptors(executorService);\n\n\t\tif (!classes) {\n\t\t\texecuteClassesSequentially(runnerTestDescriptors, runnerExecutor);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn executeClassesInParallel(runnerTestDescriptors, runnerExecutor, executorService);\n\t}\n\n\tprivate int getThreadPoolSize() {\n\t\tOptional<String> optionalPoolSize = configurationParameters.get(Constants.PARALLEL_POOL_SIZE);\n\t\tif (optionalPoolSize.isPresent()) {\n\t\t\ttry {\n\t\t\t\tint poolSize = Integer.parseInt(optionalPoolSize.get());\n\t\t\t\tif (poolSize > 0) {\n\t\t\t\t\treturn poolSize;\n\t\t\t\t}\n\t\t\t\tlogger.warn(() -> \"Invalid value for parallel pool size: \" + poolSize);\n\t\t\t}\n\t\t\tcatch (NumberFormatException e) {\n\t\t\t\tlogger.warn(() -> \"Invalid value for parallel pool size: \" + optionalPoolSize.get());\n\t\t\t}\n\t\t}\n\t\treturn DEFAULT_THREAD_POOL_SIZE;\n\t}\n\n\tprivate List<RunnerTestDescriptor> collectRunnerTestDescriptors(ExecutorService executorService) {\n\t\treturn engineDescriptor.getChildren().stream() //\n\t\t\t\t.map(RunnerTestDescriptor.class::cast) //\n\t\t\t\t.map(it -> methods ? parallelMethodExecutor(it, executorService) : it) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate RunnerTestDescriptor parallelMethodExecutor(RunnerTestDescriptor runnerTestDescriptor,\n\t\t\tExecutorService executorService) {\n\t\trunnerTestDescriptor.setExecutorService(executorService);\n\t\treturn runnerTestDescriptor;\n\t}\n\n\tprivate void executeClassesSequentially(List<RunnerTestDescriptor> runnerTestDescriptors,\n\t\t\tRunnerExecutor runnerExecutor) {\n\t\tfor (RunnerTestDescriptor runnerTestDescriptor : runnerTestDescriptors) {\n\t\t\trunnerExecutor.execute(runnerTestDescriptor);\n\t\t}\n\t}\n\n\tprivate boolean executeClassesInParallel(List<RunnerTestDescriptor> runnerTestDescriptors,\n\t\t\tRunnerExecutor runnerExecutor, ExecutorService executorService) {\n\t\tList<CompletableFuture<Void>> futures = new ArrayList<>();\n\t\tfor (RunnerTestDescriptor runnerTestDescriptor : runnerTestDescriptors) {\n\t\t\tCompletableFuture<Void> future = CompletableFuture.runAsync(\n\t\t\t\t() -> runnerExecutor.execute(runnerTestDescriptor), executorService);\n\t\t\tfutures.add(future);\n\t\t}\n\n\t\tCompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]));\n\t\tboolean wasInterrupted = false;\n\t\ttry {\n\t\t\tallOf.get();\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tlogger.warn(e, () -> \"Interruption while waiting for parallel test execution to finish\");\n\t\t\twasInterrupted = true;\n\t\t}\n\t\tcatch (ExecutionException e) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(requireNonNullElse(e.getCause(), e));\n\t\t}\n\t\tfinally {\n\t\t\tshutdownExecutorService(executorService);\n\t\t}\n\t\treturn wasInterrupted;\n\t}\n\n\tprivate void shutdownExecutorService(ExecutorService executorService) {\n\t\ttry {\n\t\t\texecutorService.shutdown();\n\t\t\tif (!executorService.awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {\n\t\t\t\tlogger.warn(() -> \"Executor service did not terminate within the specified timeout\");\n\t\t\t\texecutorService.shutdownNow();\n\t\t\t}\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tlogger.warn(e, () -> \"Interruption while waiting for executor service to shut down\");\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/execution/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal classes for test execution within the JUnit Vintage test engine.\n */\n\n@NullMarked\npackage org.junit.vintage.engine.execution;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Core package for the JUnit Vintage test engine.\n */\n\n@NullMarked\npackage org.junit.vintage.engine;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdReader.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.support;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\nimport static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue;\n\nimport java.io.Serializable;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\nimport org.junit.platform.commons.logging.Logger;\nimport org.junit.platform.commons.logging.LoggerFactory;\nimport org.junit.runner.Description;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class UniqueIdReader implements Function<Description, Serializable> {\n\n\tprivate static final Logger logger = LoggerFactory.getLogger(UniqueIdReader.class);\n\n\tprivate final String fieldName;\n\n\tpublic UniqueIdReader() {\n\t\tthis(\"fUniqueId\");\n\t}\n\n\t// For tests only\n\tUniqueIdReader(String fieldName) {\n\t\tthis.fieldName = fieldName;\n\t}\n\n\t@Override\n\tpublic Serializable apply(Description description) {\n\t\t// @formatter:off\n\t\treturn tryToReadFieldValue(Description.class, fieldName, description)\n\t\t\t\t.andThenTry(Serializable.class::cast)\n\t\t\t\t.ifFailure(cause -> logger.warn(cause, () ->\n\t\t\t\t\t\t\"Could not read unique ID for Description; using display name instead: %s\".formatted(description)))\n\t\t\t\t.toOptional()\n\t\t\t\t.orElseGet(description::getDisplayName);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/UniqueIdStringifier.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.support;\n\nimport static org.apiguardian.api.API.Status.INTERNAL;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.text.NumberFormat;\nimport java.util.Base64;\nimport java.util.Locale;\nimport java.util.function.Function;\n\nimport org.apiguardian.api.API;\n\n/**\n * @since 4.12\n */\n@API(status = INTERNAL, since = \"4.12\")\npublic class UniqueIdStringifier implements Function<Serializable, String> {\n\n\tstatic final Charset CHARSET = StandardCharsets.UTF_8;\n\n\t@Override\n\tpublic String apply(Serializable uniqueId) {\n\t\tif (uniqueId instanceof CharSequence) {\n\t\t\treturn uniqueId.toString();\n\t\t}\n\t\tif (uniqueId instanceof Number) {\n\t\t\treturn NumberFormat.getInstance(Locale.US).format(uniqueId);\n\t\t}\n\t\treturn encodeBase64(serialize(uniqueId));\n\t}\n\n\tprivate byte[] serialize(Serializable uniqueId) {\n\t\tByteArrayOutputStream byteStream = new ByteArrayOutputStream();\n\t\ttry (ObjectOutputStream out = new ObjectOutputStream(byteStream)) {\n\t\t\tout.writeObject(uniqueId);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\treturn uniqueId.toString().getBytes(CHARSET);\n\t\t}\n\t\treturn byteStream.toByteArray();\n\t}\n\n\tprivate String encodeBase64(byte[] bytes) {\n\t\treturn new String(Base64.getEncoder().encode(bytes), CHARSET);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/main/java/org/junit/vintage/engine/support/package-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n/**\n * Internal support classes for test discovery and execution within the JUnit\n * Vintage test engine.\n */\n\n@NullMarked\npackage org.junit.vintage.engine.support;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "junit-vintage-engine/src/main/resources/META-INF/services/org.junit.platform.engine.TestEngine",
    "content": "org.junit.vintage.engine.VintageTestEngine"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4ParameterizedTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.vintage.engine.samples.junit4.JUnit4ParameterizedTestCase;\n\n/**\n * @since 4.12\n */\nclass JUnit4ParameterizedTests {\n\n\tprivate final Map<TestExecutionResult.Status, Integer> callCounts = new HashMap<>();\n\n\t@Test\n\tvoid selectingWholeParameterizedClassRunsTestsWithAllValues() {\n\t\texecuteTests(selectClass(JUnit4ParameterizedTestCase.class));\n\n\t\tMap<TestExecutionResult.Status, Integer> expectedCallCounts = new HashMap<>();\n\t\texpectedCallCounts.put(SUCCESSFUL, 3);\n\t\texpectedCallCounts.put(FAILED, 9);\n\n\t\tassertEquals(expectedCallCounts, callCounts);\n\t}\n\n\t@Test\n\tvoid selectingOneTestFromParameterizedClassRunsWithAllValues() {\n\t\texecuteTests(selectMethod(JUnit4ParameterizedTestCase.class, \"test1\"));\n\n\t\tassertEquals(Map.of(FAILED, 3), callCounts);\n\t}\n\n\tprivate void executeTests(DiscoverySelector selector) {\n\t\tvar launcher = LauncherFactory.create();\n\t\tlauncher.registerTestExecutionListeners(new StatusTrackingListener());\n\n\t\t// @formatter:off\n\t\tlauncher.execute(\n\t\t\trequest()\n\t\t\t\t.selectors(selector)\n\t\t\t\t.filters(includeEngines(\"junit-vintage\"))\n\t\t\t\t.enableImplicitConfigurationParameters(false)\n\t\t\t\t.forExecution()\n\t\t\t\t.build()\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate class StatusTrackingListener implements TestExecutionListener {\n\n\t\t@Override\n\t\tpublic void executionFinished(TestIdentifier identifier, TestExecutionResult result) {\n\t\t\tif (identifier.isTest()) {\n\t\t\t\tcallCounts.merge(result.getStatus(), 1, Integer::sum);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/JUnit4VersionCheckTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * @since 5.4\n */\nclass JUnit4VersionCheckTests {\n\n\t/**\n\t * @since 5.7\n\t */\n\t@Test\n\tvoid handlesParsingSupportedVersionIdWithStandardVersionFormat() {\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.12\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.13\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.13.1\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.13.2\"));\n\t}\n\n\t/**\n\t * @since 5.7\n\t */\n\t@Test\n\tvoid handlesParsingSupportedVersionIdWithCustomizedVersionFormat() {\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.12-patch_1\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.12.0\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.12.0.1\"));\n\t\tassertDoesNotThrow(() -> JUnit4VersionCheck.checkSupported(() -> \"4.12.0.patch-042\"));\n\t}\n\n\t@Test\n\tvoid throwsExceptionForUnsupportedVersion() {\n\t\tvar exception = assertThrows(JUnitException.class, () -> JUnit4VersionCheck.checkSupported(() -> \"4.11\"));\n\n\t\tassertEquals(\"Unsupported version of junit:junit: 4.11. Please upgrade to version 4.12 or later.\",\n\t\t\texception.getMessage());\n\t}\n\n\t@Test\n\tvoid handlesErrorsReadingVersion() {\n\t\tError error = new NoClassDefFoundError();\n\n\t\tvar exception = assertThrows(JUnitException.class, () -> JUnit4VersionCheck.checkSupported(() -> {\n\t\t\tthrow error;\n\t\t}));\n\n\t\tassertEquals(\"Failed to read version of junit:junit\", exception.getMessage());\n\t\tassertSame(error, exception.getCause());\n\t}\n\n\t@Test\n\tvoid handlesErrorsParsingVersion() {\n\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t() -> JUnit4VersionCheck.checkSupported(() -> \"not a version\"));\n\n\t\tassertEquals(\"Failed to parse version of junit:junit: not a version\", exception.getMessage());\n\t}\n\n\t@Test\n\t@Tag(\"missing-junit4\")\n\tvoid handlesMissingJUnit() {\n\t\tvar exception = assertThrows(JUnitException.class, JUnit4VersionCheck::checkSupported);\n\n\t\tassertEquals(\"Invalid class/module path: junit-vintage-engine is present but junit:junit is not. \"\n\t\t\t\t+ \"Please either remove junit-vintage-engine or add junit:junit, or alternatively use \"\n\t\t\t\t+ \"an excludeEngines(\\\"junit-vintage\\\") filter.\",\n\t\t\texception.getMessage());\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageLauncherIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.includedIf;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.TagFilter.excludeTags;\nimport static org.junit.platform.launcher.TagFilter.includeTags;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.ENGINE_ID;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.internal.runners.SuiteMethod;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.runners.Suite;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.samples.junit3.JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit3.PlainJUnit3TestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.Categories;\nimport org.junit.vintage.engine.samples.junit4.EnclosedJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteOfSuiteWithFilterableChildRunner;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithTwoTestCases;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithNotFilterableRunner;\nimport org.junit.vintage.engine.samples.junit4.NotFilterableRunner;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedTestCase;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithFiveTestMethods;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithTwoTestMethods;\n\n/**\n * @since 5.1\n */\nclass VintageLauncherIntegrationTests {\n\n\t@Test\n\tvoid executesOnlyTaggedMethodOfRegularTestClass() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(includeTags(Categories.Failing.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(2);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", testClass.getSimpleName(), \"failingTest\");\n\t}\n\n\t@Test\n\tvoid executesIncludedTaggedMethodOfNestedTestClass() {\n\t\tClass<?> testClass = EnclosedJUnit4TestCase.class;\n\t\tClass<?> nestedTestClass = EnclosedJUnit4TestCase.NestedClass.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(includeTags(Categories.Failing.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(3);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", testClass.getSimpleName(), nestedTestClass.getName(),\n\t\t\t\t\t\"failingTest\");\n\t}\n\n\t@Test\n\tvoid executesOnlyNotExcludedTaggedMethodOfNestedTestClass() {\n\t\tClass<?> testClass = EnclosedJUnit4TestCase.class;\n\t\tClass<?> nestedTestClass = EnclosedJUnit4TestCase.NestedClass.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(excludeTags(Categories.Failing.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(3);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", testClass.getSimpleName(), nestedTestClass.getName(),\n\t\t\t\t\t\"successfulTest\");\n\t}\n\n\t@Test\n\tvoid removesWholeSubtree() {\n\t\tClass<?> testClass = EnclosedJUnit4TestCase.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(excludeTags(Categories.Plain.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).isEmpty();\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\");\n\t}\n\n\t@Test\n\tvoid removesCompleteClassIfNoMethodHasMatchingTags() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(includeTags(\"wrong-tag\"));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).isEmpty();\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactly(\"JUnit Vintage\");\n\t}\n\n\t@Test\n\tvoid removesCompleteClassIfItHasExcludedTag() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(excludeTags(Categories.Plain.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).isEmpty();\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactly(\"JUnit Vintage\");\n\t}\n\n\t@Test\n\tvoid executesAllTestsForNotFilterableRunner(@TrackLogRecords LogRecordListener logRecordListener) {\n\t\tClass<?> testClass = JUnit4TestCaseWithNotFilterableRunner.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters((PostDiscoveryFilter) descriptor -> includedIf(descriptor.getDisplayName().contains(\"#1\")));\n\n\t\tvar testPlan = discover(request);\n\t\tlogRecordListener.clear();\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(3);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", testClass.getSimpleName(), \"Test #0\", \"Test #1\");\n\t\tassertThat(logRecordListener.stream(RunnerTestDescriptor.class, Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"Runner \" + NotFilterableRunner.class.getName() + \" (used on class \" + testClass.getName() + \")\" //\n\t\t\t\t\t\t\t+ \" does not support filtering and will therefore be run completely.\");\n\t}\n\n\t@Test\n\tvoid executesAllTestsForNotFilterableChildRunnerOfSuite(@TrackLogRecords LogRecordListener logRecordListener) {\n\t\tClass<?> suiteClass = JUnit4SuiteOfSuiteWithFilterableChildRunner.class;\n\t\tClass<?> testClass = JUnit4TestCaseWithNotFilterableRunner.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(suiteClass)) //\n\t\t\t\t.filters((PostDiscoveryFilter) descriptor -> includedIf(descriptor.getDisplayName().contains(\"#1\")));\n\n\t\tvar testPlan = discover(request);\n\t\tlogRecordListener.clear();\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(4);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", suiteClass.getSimpleName(), testClass.getName(), \"Test #0\",\n\t\t\t\t\t\"Test #1\");\n\t\tassertThat(logRecordListener.stream(RunnerTestDescriptor.class, Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t.containsExactly(\"Runner \" + Suite.class.getName() + \" (used on class \" + suiteClass.getName() + \")\" //\n\t\t\t\t\t\t+ \" was not able to satisfy all filter requests.\");\n\t}\n\n\t@Test\n\tvoid executesAllTestsWhenFilterDidNotExcludeTestForJUnit3Suite(\n\t\t\t@TrackLogRecords LogRecordListener logRecordListener) {\n\t\tClass<?> suiteClass = JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.class;\n\t\tClass<?> testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(suiteClass)) //\n\t\t\t\t.filters((PostDiscoveryFilter) descriptor -> excluded(\"not today\"));\n\n\t\tvar testPlan = discover(request);\n\t\tlogRecordListener.clear();\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(3);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", suiteClass.getSimpleName(), testClass.getName(), \"test\");\n\t\tassertThat(logRecordListener.stream(RunnerTestDescriptor.class, Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"Runner \" + SuiteMethod.class.getName() + \" (used on class \" + suiteClass.getName() + \")\" //\n\t\t\t\t\t\t\t+ \" was not able to satisfy all filter requests.\");\n\t}\n\n\t@Test\n\tvoid executesOnlyTaggedMethodsForSuite() {\n\t\tClass<?> suiteClass = JUnit4SuiteWithTwoTestCases.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithTwoTestMethods.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(suiteClass)) //\n\t\t\t\t.filters(includeTags(Categories.Successful.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).hasSize(3);\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactlyInAnyOrder(\"JUnit Vintage\", suiteClass.getSimpleName(), testClass.getName(),\n\t\t\t\t\t\"successfulTest\");\n\t}\n\n\t@Test\n\tvoid removesCompleteClassWithNotFilterableRunnerIfItHasExcludedTag() {\n\t\tClass<?> testClass = JUnit4TestCaseWithNotFilterableRunner.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters(excludeTags(Categories.Successful.class.getName()));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).isEmpty();\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactly(\"JUnit Vintage\");\n\t}\n\n\t@Test\n\tvoid filtersOutAllDescendantsOfParameterizedTestCase() {\n\t\tClass<?> testClass = ParameterizedTestCase.class;\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.filters((PostDiscoveryFilter) descriptor -> excluded(\"excluded\"));\n\n\t\tvar testPlan = discover(request);\n\t\tassertThat(testPlan.getDescendants(getOnlyElement(testPlan.getRoots()))).isEmpty();\n\n\t\tvar results = execute(request);\n\t\tassertThat(results.keySet().stream().map(TestIdentifier::getDisplayName)) //\n\t\t\t\t.containsExactly(\"JUnit Vintage\");\n\t}\n\n\tprivate TestPlan discover(LauncherDiscoveryRequestBuilder requestBuilder) {\n\t\tvar launcher = LauncherFactory.create();\n\t\treturn launcher.discover(toDiscoveryRequest(requestBuilder).build());\n\t}\n\n\tprivate Map<TestIdentifier, TestExecutionResult> execute(LauncherDiscoveryRequestBuilder requestBuilder) {\n\t\tMap<TestIdentifier, TestExecutionResult> results = new LinkedHashMap<>();\n\t\tvar launcher = LauncherFactory.create();\n\t\tvar listener = new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\t\tresults.put(testIdentifier, testExecutionResult);\n\t\t\t}\n\t\t};\n\t\tvar executionRequest = toDiscoveryRequest(requestBuilder) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\t\tlauncher.execute(executionRequest);\n\t\treturn results;\n\t}\n\n\tprivate LauncherDiscoveryRequestBuilder toDiscoveryRequest(LauncherDiscoveryRequestBuilder requestBuilder) {\n\t\treturn requestBuilder //\n\t\t\t\t.filters(includeEngines(ENGINE_ID)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineBasicTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Basic assertions regarding {@link org.junit.platform.engine.TestEngine}\n * functionality in JUnit Vintage.\n *\n * @since 4.12\n */\n@SuppressWarnings(\"deprecation\")\nclass VintageTestEngineBasicTests {\n\n\tprivate final VintageTestEngine vintage = new VintageTestEngine();\n\n\t@Test\n\tvoid id() {\n\t\tassertEquals(\"junit-vintage\", vintage.getId());\n\t}\n\n\t@Test\n\tvoid groupId() {\n\t\tassertEquals(\"org.junit.vintage\", vintage.getGroupId().get());\n\t}\n\n\t@Test\n\tvoid artifactId() {\n\t\tassertEquals(\"junit-vintage-engine\", vintage.getArtifactId().get());\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineDiscoveryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static java.text.MessageFormat.format;\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathRoots;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.includePackageNames;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Base64;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineDiscoveryResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.vintage.engine.samples.PlainOldJavaClassWithoutAnyTestsTestCase;\nimport org.junit.vintage.engine.samples.junit3.JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit3.PlainJUnit3TestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.Categories.Failing;\nimport org.junit.vintage.engine.samples.junit4.Categories.Plain;\nimport org.junit.vintage.engine.samples.junit4.Categories.Skipped;\nimport org.junit.vintage.engine.samples.junit4.Categories.SkippedWithReason;\nimport org.junit.vintage.engine.samples.junit4.EmptyIgnoredTestCase;\nimport org.junit.vintage.engine.samples.junit4.IgnoredJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithTwoTestCases;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithDistinguishableOverloadedMethod;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithIndistinguishableOverloadedMethod;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithNotFilterableRunner;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedTestCase;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithFiveTestMethods;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleInheritedTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleTestWhichIsIgnored;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithTwoTestMethods;\nimport org.junit.vintage.engine.samples.junit4.SingleFailingTheoryTestCase;\nimport org.junit.vintage.engine.samples.junit4.TestCaseRunWithJUnitPlatformRunner;\n\n/**\n * @since 4.12\n */\nclass VintageTestEngineDiscoveryTests {\n\n\t@Test\n\tvoid resolvesSimpleJUnit4TestClass() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesIgnoredJUnit4TestClass() throws Exception {\n\t\tClass<?> testClass = IgnoredJUnit4TestCase.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tassertThat(runnerDescriptor.getChildren()).hasSize(2);\n\t\tList<? extends TestDescriptor> children = new ArrayList<>(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(children.get(0), testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t\tassertTestMethodDescriptor(children.get(1), testClass, \"succeedingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesEmptyIgnoredTestClass() {\n\t\tClass<?> testClass = EmptyIgnoredTestCase.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertFalse(runnerDescriptor.isContainer());\n\t\tassertTrue(runnerDescriptor.isTest());\n\t\tassertEquals(testClass.getSimpleName(), runnerDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForClass(testClass), runnerDescriptor.getUniqueId());\n\t\tassertThat(runnerDescriptor.getChildren()).isEmpty();\n\t}\n\n\t@Test\n\tvoid resolvesJUnit4TestClassWithCustomRunner() throws Exception {\n\t\tClass<?> testClass = SingleFailingTheoryTestCase.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"theory\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesJUnit3TestCase() throws Exception {\n\t\tClass<?> testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"test\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesJUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails() throws Exception {\n\t\tClass<?> suiteClass = JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.class;\n\t\tClass<?> testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(suiteClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar suiteDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(suiteDescriptor, suiteClass);\n\t\tassertThat(suiteDescriptor.getDisplayName()).describedAs(\"display name\") //\n\t\t\t\t.startsWith(suiteClass.getSimpleName());\n\t\tassertThat(suiteDescriptor.getLegacyReportingName()).describedAs(\"legacy reporting name\") //\n\t\t\t\t.isEqualTo(suiteClass.getName());\n\n\t\tvar testClassDescriptor = getOnlyElement(suiteDescriptor.getChildren());\n\t\tassertContainerTestDescriptor(testClassDescriptor, suiteClass, testClass);\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(testMethodDescriptor, testClass, \"test\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass));\n\t}\n\n\t@Test\n\tvoid resolvesJUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored() throws Exception {\n\t\tClass<?> suiteClass = JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(suiteClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar suiteDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(suiteDescriptor, suiteClass);\n\n\t\tvar testClassDescriptor = getOnlyElement(suiteDescriptor.getChildren());\n\t\tassertContainerTestDescriptor(testClassDescriptor, suiteClass, testClass);\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(testMethodDescriptor, testClass, \"ignoredTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass));\n\t}\n\n\t@Test\n\tvoid resolvesJUnit4TestCaseWithIndistinguishableOverloadedMethod() {\n\t\tClass<?> testClass = JUnit4TestCaseWithIndistinguishableOverloadedMethod.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tList<TestDescriptor> testMethodDescriptors = new ArrayList<>(runnerDescriptor.getChildren());\n\t\tassertThat(testMethodDescriptors).hasSize(2);\n\n\t\tvar testMethodDescriptor = testMethodDescriptors.getFirst();\n\t\tassertEquals(\"theory\", testMethodDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"theory\", \"0\"),\n\t\t\ttestMethodDescriptor.getUniqueId());\n\t\tassertClassSource(testClass, testMethodDescriptor);\n\n\t\ttestMethodDescriptor = testMethodDescriptors.get(1);\n\t\tassertEquals(\"theory\", testMethodDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"theory\", \"1\"),\n\t\t\ttestMethodDescriptor.getUniqueId());\n\t\tassertClassSource(testClass, testMethodDescriptor);\n\t}\n\n\t@Test\n\tvoid resolvesJUnit4TestCaseWithDistinguishableOverloadedMethod() throws Exception {\n\t\tClass<?> testClass = JUnit4TestCaseWithDistinguishableOverloadedMethod.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tList<TestDescriptor> testMethodDescriptors = new ArrayList<>(runnerDescriptor.getChildren());\n\n\t\tvar testMethodDescriptor = getOnlyElement(testMethodDescriptors);\n\t\tassertEquals(\"test\", testMethodDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"test\"), testMethodDescriptor.getUniqueId());\n\t\tassertMethodSource(testClass.getMethod(\"test\"), testMethodDescriptor);\n\t}\n\n\t@Test\n\tvoid doesNotResolvePlainOldJavaClassesWithoutAnyTest() {\n\t\tassertYieldsNoDescriptors(PlainOldJavaClassWithoutAnyTestsTestCase.class);\n\t}\n\n\t@Test\n\tvoid doesNotResolveClassRunWithJUnitPlatform() {\n\t\tassertYieldsNoDescriptors(TestCaseRunWithJUnitPlatformRunner.class);\n\t}\n\n\t@Test\n\tvoid resolvesClasspathSelector() throws Exception {\n\t\tvar root = getClasspathRoot(PlainJUnit4TestCaseWithSingleTestWhichFails.class);\n\t\tvar discoveryRequest = request().selectors(selectClasspathRoots(Set.of(root))).build();\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t.contains(PlainJUnit4TestCaseWithSingleTestWhichFails.class.getSimpleName())\n\t\t\t.contains(PlainJUnit3TestCaseWithSingleTestWhichFails.class.getSimpleName())\n\t\t\t.doesNotContain(PlainOldJavaClassWithoutAnyTestsTestCase.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid resolvesClasspathSelectorForJarFile() throws Exception {\n\t\tvar jarUrl = getClass().getResource(\"/vintage-testjar.jar\");\n\t\tvar jarFile = Path.of(jarUrl.toURI());\n\n\t\tvar originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarUrl })) {\n\t\t\tThread.currentThread().setContextClassLoader(classLoader);\n\n\t\t\tvar discoveryRequest = request().selectors(selectClasspathRoots(Set.of(jarFile))).build();\n\t\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t\t.containsExactly(\"JUnit4Test\");\n\t\t\t// @formatter:on\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\t@Test\n\tvoid resolvesApplyingClassNameFilters() throws Exception {\n\t\tvar root = getClasspathRoot(PlainJUnit4TestCaseWithSingleTestWhichFails.class);\n\n\t\tvar discoveryRequest = request().selectors(selectClasspathRoots(Set.of(root))).filters(\n\t\t\tincludeClassNamePatterns(\".*JUnit4.*\"), includeClassNamePatterns(\".*Plain.*\")).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t.contains(PlainJUnit4TestCaseWithSingleTestWhichFails.class.getSimpleName())\n\t\t\t.doesNotContain(JUnit4TestCaseWithIndistinguishableOverloadedMethod.class.getSimpleName())\n\t\t\t.doesNotContain(PlainJUnit3TestCaseWithSingleTestWhichFails.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid resolvesApplyingPackageNameFilters() throws Exception {\n\t\tvar root = getClasspathRoot(PlainJUnit4TestCaseWithSingleTestWhichFails.class);\n\n\t\tvar discoveryRequest = request().selectors(selectClasspathRoots(Set.of(root))).filters(\n\t\t\tincludePackageNames(\"org\"), includePackageNames(\"org.junit\")).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t.contains(PlainJUnit4TestCaseWithSingleTestWhichFails.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid resolvesPackageSelectorForJUnit4SamplesPackage() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\n\t\tvar discoveryRequest = request().selectors(selectPackage(testClass.getPackage().getName())).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t.contains(testClass.getSimpleName())\n\t\t\t.doesNotContain(PlainJUnit3TestCaseWithSingleTestWhichFails.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid resolvesPackageSelectorForJUnit3SamplesPackage() {\n\t\tClass<?> testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class;\n\n\t\tvar discoveryRequest = request().selectors(selectPackage(testClass.getPackage().getName())).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t.contains(testClass.getSimpleName())\n\t\t\t.doesNotContain(PlainJUnit4TestCaseWithSingleTestWhichFails.class.getSimpleName());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid resolvesClassesWithInheritedMethods() throws Exception {\n\t\tClass<?> superclass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertEquals(testClass.getSimpleName(), runnerDescriptor.getDisplayName());\n\t\tassertClassSource(testClass, runnerDescriptor);\n\n\t\tvar testDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertEquals(\"failingTest\", testDescriptor.getDisplayName());\n\t\tassertMethodSource(testClass, superclass.getMethod(\"failingTest\"), testDescriptor);\n\t}\n\n\t@Test\n\tvoid resolvesCategoriesIntoTags() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = discoveryRequestForClass(testClass);\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(runnerDescriptor.getTags()).containsOnly(TestTag.create(Plain.class.getName()));\n\n\t\tvar failingTest = findChildByDisplayName(runnerDescriptor, \"failingTest\");\n\t\tassertThat(failingTest.getTags()).containsOnly(//\n\t\t\tTestTag.create(Plain.class.getName()), //\n\t\t\tTestTag.create(Failing.class.getName()));\n\n\t\tvar ignoredWithoutReason = findChildByDisplayName(runnerDescriptor, \"ignoredTest1_withoutReason\");\n\t\tassertThat(ignoredWithoutReason.getTags()).containsOnly(//\n\t\t\tTestTag.create(Plain.class.getName()), //\n\t\t\tTestTag.create(Skipped.class.getName()));\n\n\t\tvar ignoredWithReason = findChildByDisplayName(runnerDescriptor, \"ignoredTest2_withReason\");\n\t\tassertThat(ignoredWithReason.getTags()).containsOnly(//\n\t\t\tTestTag.create(Plain.class.getName()), //\n\t\t\tTestTag.create(Skipped.class.getName()), //\n\t\t\tTestTag.create(SkippedWithReason.class.getName()));\n\t}\n\n\t@Test\n\tvoid resolvesMethodSelectorForSingleMethod() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(selectMethod(testClass, testClass.getMethod(\"failingTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesMethodOfIgnoredJUnit4TestClass() throws Exception {\n\t\tClass<?> testClass = IgnoredJUnit4TestCase.class;\n\t\tvar discoveryRequest = request().selectors(selectMethod(testClass, testClass.getMethod(\"failingTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesMethodSelectorForTwoMethodsOfSameClass() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(selectMethod(testClass, testClass.getMethod(\"failingTest\")),\n\t\t\tselectMethod(testClass, testClass.getMethod(\"successfulTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tList<TestDescriptor> testMethodDescriptors = new ArrayList<>(runnerDescriptor.getChildren());\n\t\tassertThat(testMethodDescriptors).hasSize(2);\n\n\t\tvar failingTest = testMethodDescriptors.get(0);\n\t\tassertTestMethodDescriptor(failingTest, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\n\t\tvar successfulTest = testMethodDescriptors.get(1);\n\t\tassertTestMethodDescriptor(successfulTest, testClass, \"successfulTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesUniqueIdSelectorForSingleMethod() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"failingTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar childDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(childDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid resolvesUniqueIdSelectorForSingleClass() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForClass(testClass))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tassertThat(runnerDescriptor.getChildren()).hasSize(5);\n\t}\n\n\t@Test\n\tvoid resolvesUniqueIdSelectorOfSingleClassWithinSuite() throws Exception {\n\t\tClass<?> suiteClass = JUnit4SuiteWithTwoTestCases.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\t\tvar discoveryRequest = request().selectors(\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar suiteDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(suiteDescriptor, suiteClass);\n\n\t\tvar testClassDescriptor = getOnlyElement(suiteDescriptor.getChildren());\n\t\tassertContainerTestDescriptor(testClassDescriptor, suiteClass, testClass);\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(testMethodDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass));\n\t}\n\n\t@Test\n\tvoid resolvesUniqueIdSelectorOfSingleMethodWithinSuite() throws Exception {\n\t\tClass<?> suiteClass = JUnit4SuiteWithTwoTestCases.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithTwoTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(selectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass), testClass, \"successfulTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar suiteDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(suiteDescriptor, suiteClass);\n\n\t\tvar testClassDescriptor = getOnlyElement(suiteDescriptor.getChildren());\n\t\tassertContainerTestDescriptor(testClassDescriptor, suiteClass, testClass);\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(testMethodDescriptor, testClass, \"successfulTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass));\n\t}\n\n\t@Test\n\tvoid resolvesMultipleUniqueIdSelectorsForMethodsOfSameClass() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithTwoTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"successfulTest\")),\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"failingTest\"))).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tList<TestDescriptor> testMethodDescriptors = new ArrayList<>(runnerDescriptor.getChildren());\n\t\tassertThat(testMethodDescriptors).hasSize(2);\n\t\tassertTestMethodDescriptor(testMethodDescriptors.get(0), testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t\tassertTestMethodDescriptor(testMethodDescriptors.get(1), testClass, \"successfulTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid doesNotResolveMissingUniqueIdSelectorForSingleClass() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors(\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForClass(testClass) + \"/[test:doesNotExist]\")).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar testDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertInitializationError(testDescriptor, Filter.class, testClass);\n\t}\n\n\t@Test\n\tvoid ignoresMoreFineGrainedSelectorsWhenClassIsSelectedAsWell() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors( //\n\t\t\tselectMethod(testClass, testClass.getMethod(\"failingTest\")), //\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"abortedTest\")), selectClass(testClass) //\n\t\t).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tassertThat(runnerDescriptor.getChildren()).hasSize(5);\n\t}\n\n\t@Test\n\tvoid resolvesCombinationOfMethodAndUniqueIdSelector() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors( //\n\t\t\tselectMethod(testClass, testClass.getMethod(\"failingTest\")), //\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"abortedTest\") //\n\t\t\t)).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tList<TestDescriptor> testMethodDescriptors = new ArrayList<>(runnerDescriptor.getChildren());\n\t\tassertThat(testMethodDescriptors).hasSize(2);\n\t\tassertTestMethodDescriptor(testMethodDescriptors.get(0), testClass, \"abortedTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t\tassertTestMethodDescriptor(testMethodDescriptors.get(1), testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid ignoresRedundantSelector() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\tvar discoveryRequest = request().selectors( //\n\t\t\tselectMethod(testClass, testClass.getMethod(\"failingTest\")), //\n\t\t\tselectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"failingTest\") //\n\t\t\t)).build();\n\n\t\tvar engineDescriptor = discoverTests(discoveryRequest);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar testMethodDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertTestMethodDescriptor(testMethodDescriptor, testClass, \"failingTest\",\n\t\t\tVintageUniqueIdBuilder.uniqueIdForClass(testClass));\n\t}\n\n\t@Test\n\tvoid doesNotResolveMethodOfClassNotAcceptedByClassNameFilter() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\t// @formatter:off\n\t\tvar request = request()\n\t\t\t\t.selectors(selectMethod(testClass, testClass.getMethod(\"failingTest\")))\n\t\t\t\t.filters(includeClassNamePatterns(\"Foo\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertYieldsNoDescriptors(request);\n\t}\n\n\t@Test\n\tvoid doesNotResolveMethodOfClassNotAcceptedByPackageNameFilter() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\t\t// @formatter:off\n\t\tvar request = request()\n\t\t\t\t.selectors(selectMethod(testClass, testClass.getMethod(\"failingTest\")))\n\t\t\t\t.filters(includePackageNames(\"com.acme\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tassertYieldsNoDescriptors(request);\n\t}\n\n\t@Test\n\tvoid resolvesClassForMethodSelectorForClassWithNonFilterableRunner() {\n\t\tClass<?> testClass = JUnit4TestCaseWithNotFilterableRunner.class;\n\t\t// @formatter:off\n\t\tvar request = request()\n\t\t\t\t.selectors(selectUniqueId(VintageUniqueIdBuilder.uniqueIdForMethod(testClass, \"Test #0\")))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tvar engineDescriptor = discoverTests(request);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertEquals(testClass.getSimpleName(), runnerDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForClass(testClass), runnerDescriptor.getUniqueId());\n\t\tassertThat(runnerDescriptor.getChildren()).isNotEmpty();\n\t}\n\n\t@Test\n\tvoid usesCustomUniqueIdsAndDisplayNamesWhenPresent() {\n\t\tClass<?> suiteClass = JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.class;\n\t\tvar request = request().selectors(selectClass(suiteClass)).build();\n\n\t\tvar engineDescriptor = discoverTests(request);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, suiteClass);\n\n\t\tvar testClassDescriptor = getOnlyElement(runnerDescriptor.getChildren());\n\t\tassertEquals(\"(TestClass)\", testClassDescriptor.getDisplayName());\n\n\t\tvar childDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\n\t\tvar prefix = VintageUniqueIdBuilder.uniqueIdForClass(suiteClass);\n\t\tassertThat(childDescriptor.getUniqueId().toString()).startsWith(prefix.toString());\n\t\tassertEquals(\"(TestMethod)\", childDescriptor.getDisplayName());\n\n\t\tvar customUniqueIdValue = childDescriptor.getUniqueId().getSegments().get(2).getType();\n\t\tassertNotNull(Base64.getDecoder().decode(customUniqueIdValue.getBytes(StandardCharsets.UTF_8)),\n\t\t\t\"is a valid Base64 encoding scheme\");\n\t}\n\n\t@Test\n\tvoid resolvesTestSourceForParameterizedTests() throws Exception {\n\t\tClass<?> testClass = ParameterizedTestCase.class;\n\t\tvar request = request().selectors(selectClass(testClass)).build();\n\n\t\tvar engineDescriptor = discoverTests(request);\n\n\t\tvar runnerDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertRunnerTestDescriptor(runnerDescriptor, testClass);\n\n\t\tvar fooParentDescriptor = findChildByDisplayName(runnerDescriptor, \"[foo]\");\n\t\tassertTrue(fooParentDescriptor.isContainer());\n\t\tassertFalse(fooParentDescriptor.isTest());\n\t\tassertThat(fooParentDescriptor.getSource()).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(fooParentDescriptor.getChildren());\n\t\tassertEquals(\"test[foo]\", testMethodDescriptor.getDisplayName());\n\t\tassertTrue(testMethodDescriptor.isTest());\n\t\tassertFalse(testMethodDescriptor.isContainer());\n\t\tassertMethodSource(testClass.getMethod(\"test\"), testMethodDescriptor);\n\t}\n\n\t@Test\n\tvoid reportsNoDiscoveryIssuesWhenNoTestsAreFound() {\n\t\tvar request = discoveryRequestForClass(PlainOldJavaClassWithoutAnyTestsTestCase.class);\n\n\t\tvar results = discover(request);\n\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\t}\n\n\t@Test\n\tvoid reportDiscoveryIssueWhenTestsAreFoundByDefault() {\n\t\tvar request = discoveryRequestForClass(PlainJUnit4TestCaseWithSingleTestWhichFails.class);\n\n\t\tvar results = discover(request);\n\n\t\tassertThat(results.getDiscoveryIssues()).hasSize(1);\n\n\t\tvar issue = results.getDiscoveryIssues().getFirst();\n\t\tassertThat(issue.severity()).isEqualTo(Severity.INFO);\n\t\tassertThat(issue.message()).contains(\"JUnit Vintage engine is deprecated\");\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\tvoid reportNoDiscoveryIssueWhenTestsAreFoundButConfigurationParameterIsSet() {\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(PlainJUnit4TestCaseWithSingleTestWhichFails.class)) //\n\t\t\t\t.configurationParameter(Constants.DISCOVERY_ISSUE_REPORTING_ENABLED_PROPERTY_NAME, \"false\").build();\n\n\t\tvar results = discover(request);\n\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\t}\n\n\tprivate TestDescriptor findChildByDisplayName(TestDescriptor runnerDescriptor, String displayName) {\n\t\t// @formatter:off\n\t\tvar children = runnerDescriptor.getChildren();\n\t\treturn children\n\t\t\t\t.stream()\n\t\t\t\t.filter(where(TestDescriptor::getDisplayName, isEqual(displayName)))\n\t\t\t\t.findAny()\n\t\t\t\t.orElseThrow(() ->\n\t\t\t\t\tnew AssertionError(format(\"No child with display name \\\"{0}\\\" in {1}\", displayName, children)));\n\t\t// @formatter:on\n\t}\n\n\tprivate TestDescriptor discoverTests(LauncherDiscoveryRequest discoveryRequest) {\n\t\treturn discover(discoveryRequest).getEngineDescriptor();\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static EngineDiscoveryResults discover(LauncherDiscoveryRequest discoveryRequest) {\n\t\treturn EngineTestKit.discover(new VintageTestEngine(), discoveryRequest);\n\t}\n\n\tprivate Path getClasspathRoot(Class<?> testClass) throws Exception {\n\t\tvar location = testClass.getProtectionDomain().getCodeSource().getLocation();\n\t\treturn Path.of(location.toURI());\n\t}\n\n\tprivate void assertYieldsNoDescriptors(Class<?> testClass) {\n\t\tvar request = discoveryRequestForClass(testClass);\n\n\t\tassertYieldsNoDescriptors(request);\n\t}\n\n\tprivate void assertYieldsNoDescriptors(LauncherDiscoveryRequest request) {\n\t\tvar engineDescriptor = discoverTests(request);\n\n\t\tassertThat(engineDescriptor.getChildren()).isEmpty();\n\t}\n\n\tprivate static void assertRunnerTestDescriptor(TestDescriptor runnerDescriptor, Class<?> testClass) {\n\t\tassertTrue(runnerDescriptor.isContainer());\n\t\tassertFalse(runnerDescriptor.isTest());\n\t\tassertEquals(testClass.getSimpleName(), runnerDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForClass(testClass), runnerDescriptor.getUniqueId());\n\t\tassertClassSource(testClass, runnerDescriptor);\n\t}\n\n\tprivate static void assertTestMethodDescriptor(TestDescriptor testMethodDescriptor, Class<?> testClass,\n\t\t\tString methodName, UniqueId uniqueContainerId) throws Exception {\n\t\tassertTrue(testMethodDescriptor.isTest());\n\t\tassertFalse(testMethodDescriptor.isContainer());\n\t\tassertEquals(methodName, testMethodDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForMethod(uniqueContainerId, testClass, methodName),\n\t\t\ttestMethodDescriptor.getUniqueId());\n\t\tassertThat(testMethodDescriptor.getChildren()).isEmpty();\n\t\tassertMethodSource(testClass.getMethod(methodName), testMethodDescriptor);\n\t}\n\n\tprivate static void assertContainerTestDescriptor(TestDescriptor containerDescriptor, Class<?> suiteClass,\n\t\t\tClass<?> testClass) {\n\t\tassertTrue(containerDescriptor.isContainer());\n\t\tassertFalse(containerDescriptor.isTest());\n\t\tassertEquals(testClass.getName(), containerDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForClasses(suiteClass, testClass),\n\t\t\tcontainerDescriptor.getUniqueId());\n\t\tassertClassSource(testClass, containerDescriptor);\n\t}\n\n\tprivate static void assertInitializationError(TestDescriptor testDescriptor, Class<?> failingClass,\n\t\t\tClass<?> testClass) {\n\t\tassertTrue(testDescriptor.isTest());\n\t\tassertFalse(testDescriptor.isContainer());\n\t\tassertEquals(\"initializationError\", testDescriptor.getDisplayName());\n\t\tassertEquals(VintageUniqueIdBuilder.uniqueIdForErrorInClass(testClass, failingClass),\n\t\t\ttestDescriptor.getUniqueId());\n\t\tassertThat(testDescriptor.getChildren()).isEmpty();\n\t\tassertClassSource(failingClass, testDescriptor);\n\t}\n\n\tprivate static void assertClassSource(Class<?> expectedClass, TestDescriptor testDescriptor) {\n\t\tassertThat(testDescriptor.getSource()).containsInstanceOf(ClassSource.class);\n\t\tvar classSource = (ClassSource) testDescriptor.getSource().get();\n\t\tassertThat(classSource.getJavaClass()).isEqualTo(expectedClass);\n\t}\n\n\tprivate static void assertMethodSource(Method expectedMethod, TestDescriptor testDescriptor) {\n\t\tassertMethodSource(expectedMethod.getDeclaringClass(), expectedMethod, testDescriptor);\n\t}\n\n\tprivate static void assertMethodSource(Class<?> expectedClass, Method expectedMethod,\n\t\t\tTestDescriptor testDescriptor) {\n\t\tassertThat(testDescriptor.getSource()).containsInstanceOf(MethodSource.class);\n\t\tvar methodSource = (MethodSource) testDescriptor.getSource().get();\n\t\tassertThat(methodSource.getClassName()).isEqualTo(expectedClass.getName());\n\t\tassertThat(methodSource.getMethodName()).isEqualTo(expectedMethod.getName());\n\t\tassertThat(methodSource.getMethodParameterTypes()).isEqualTo(\n\t\t\tClassUtils.nullSafeToString(expectedMethod.getParameterTypes()));\n\t}\n\n\tprivate static LauncherDiscoveryRequest discoveryRequestForClass(Class<?> testClass) {\n\t\treturn request().selectors(selectClass(testClass)).build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineExecutionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.testkit.engine.EventConditions.abortedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.EventConditions.uniqueIdSubstring;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.runner.Description.createSuiteDescription;\nimport static org.junit.runner.Description.createTestDescription;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.math.BigDecimal;\n\nimport junit.runner.Version;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.AssumptionViolatedException;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.DisabledInEclipse;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.runner.Description;\nimport org.junit.runner.RunWith;\nimport org.junit.runner.Runner;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runner.notification.StoppedByUserException;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\nimport org.junit.vintage.engine.samples.junit3.IgnoredJUnit3TestCase;\nimport org.junit.vintage.engine.samples.junit3.JUnit3ParallelSuiteWithSubsuites;\nimport org.junit.vintage.engine.samples.junit3.JUnit3SuiteWithSubsuites;\nimport org.junit.vintage.engine.samples.junit3.JUnit4SuiteWithIgnoredJUnit3TestCase;\nimport org.junit.vintage.engine.samples.junit3.PlainJUnit3TestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.CancellingTestCase;\nimport org.junit.vintage.engine.samples.junit4.CompletelyDynamicTestCase;\nimport org.junit.vintage.engine.samples.junit4.EmptyIgnoredTestCase;\nimport org.junit.vintage.engine.samples.junit4.EnclosedJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.EnclosedWithParameterizedChildrenJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.IgnoredJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.IgnoredParameterizedTestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithExceptionThrowingRunner;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithIgnoredJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithJUnit3SuiteWithSingleTestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished;\nimport org.junit.vintage.engine.samples.junit4.JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithAssumptionFailureInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithErrorCollectorStoringMultipleFailures;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithErrorInAfterClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithErrorInBeforeClass;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithExceptionThrowingRunner;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithIndistinguishableOverloadedMethod;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames;\nimport org.junit.vintage.engine.samples.junit4.JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions;\nimport org.junit.vintage.engine.samples.junit4.MalformedJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedTestCase;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedTimingTestCase;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedWithAfterParamFailureTestCase;\nimport org.junit.vintage.engine.samples.junit4.ParameterizedWithBeforeParamFailureTestCase;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithFiveTestMethods;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithLifecycleMethods;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleTestWhichFails;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleTestWhichIsIgnored;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithTwoTestMethods;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * @since 4.12\n */\nclass VintageTestEngineExecutionTests {\n\n\t@Test\n\tvoid executesPlainJUnit4TestCaseWithSingleTestWhichFails() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"this test should fail\"))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesPlainJUnit4TestCaseWithTwoTests() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithTwoTestMethods.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"this test should fail\"))), //\n\t\t\tevent(test(\"successfulTest\"), started()), //\n\t\t\tevent(test(\"successfulTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesPlainJUnit4TestCaseWithFiveTests() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithFiveTestMethods.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"abortedTest\"), started()), //\n\t\t\tevent(test(\"abortedTest\"),\n\t\t\t\tabortedWithReason(instanceOf(AssumptionViolatedException.class),\n\t\t\t\t\tmessage(\"this test should be aborted\"))), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"this test should fail\"))), //\n\t\t\tevent(test(\"ignoredTest1_withoutReason\"), skippedWithReason(\"\")), //\n\t\t\tevent(test(\"ignoredTest2_withReason\"), skippedWithReason(\"a custom reason\")), //\n\t\t\tevent(test(\"successfulTest\"), started()), //\n\t\t\tevent(test(\"successfulTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesMultiplePlainJUnit4TestCases() {\n\t\tLauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(DiscoverySelectors.selectClass(PlainJUnit4TestCaseWithTwoTestMethods.class),\n\t\t\t\t\tDiscoverySelectors.selectClass(PlainJUnit4TestCaseWithFiveTestMethods.class)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\n\t\texecute(request).allEvents().assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithTwoTestMethods.class), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithTwoTestMethods.class), finishedSuccessfully()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithFiveTestMethods.class), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithFiveTestMethods.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesMultiplePlainJUnit4TestCasesWithMemoryCleanupEnabled() {\n\t\tLauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.configurationParameter(LauncherConstants.MEMORY_CLEANUP_ENABLED_PROPERTY_NAME, \"true\") //\n\t\t\t\t.configurationParameter(LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, \"false\") //\n\t\t\t\t.selectors( //\n\t\t\t\t\tDiscoverySelectors.selectClass(PlainJUnit4TestCaseWithTwoTestMethods.class), //\n\t\t\t\t\tDiscoverySelectors.selectClass(PlainJUnit4TestCaseWithFiveTestMethods.class)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\n\t\tEngineExecutionResults execute = execute(request);\n\n\t\texecute.allEvents().assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithTwoTestMethods.class), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithTwoTestMethods.class), finishedSuccessfully()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithFiveTestMethods.class), started()), //\n\t\t\tevent(container(PlainJUnit4TestCaseWithFiveTestMethods.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesEnclosedJUnit4TestCase() {\n\t\tClass<?> testClass = EnclosedJUnit4TestCase.class;\n\t\tClass<?> nestedClass = EnclosedJUnit4TestCase.NestedClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(nestedClass), started()), //\n\t\t\tevent(test(\"successfulTest\"), started()), //\n\t\t\tevent(test(\"successfulTest\"), finishedSuccessfully()), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"this test should fail\"))), //\n\t\t\tevent(container(nestedClass), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesEnclosedWithParameterizedChildrenJUnit4TestCase() {\n\t\tClass<?> testClass = EnclosedWithParameterizedChildrenJUnit4TestCase.class;\n\t\tString commonNestedClassPrefix = EnclosedWithParameterizedChildrenJUnit4TestCase.class.getName()\n\t\t\t\t+ \"$NestedTestCase\";\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(commonNestedClassPrefix), started()), //\n\t\t\tevent(container(\"[0]\"), started()), //\n\t\t\tevent(test(\"test[0]\"), started()), //\n\t\t\tevent(test(\"test[0]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[0]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[1]\"), started()), //\n\t\t\tevent(test(\"test[1]\"), started()), //\n\t\t\tevent(test(\"test[1]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[1]\"), finishedSuccessfully()), //\n\t\t\tevent(container(commonNestedClassPrefix), finishedSuccessfully()), //\n\t\t\tevent(container(commonNestedClassPrefix), started()), //\n\t\t\tevent(container(\"[0]\"), started()), //\n\t\t\tevent(test(\"test[0]\"), started()), //\n\t\t\tevent(test(\"test[0]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[0]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[1]\"), started()), //\n\t\t\tevent(test(\"test[1]\"), started()), //\n\t\t\tevent(test(\"test[1]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[1]\"), finishedSuccessfully()), //\n\t\t\tevent(container(commonNestedClassPrefix), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithJUnit3SuiteWithSingleTestCase() {\n\t\tClass<?> junit4SuiteClass = JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.class;\n\t\tClass<?> testClass = PlainJUnit3TestCaseWithSingleTestWhichFails.class;\n\n\t\texecute(junit4SuiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(junit4SuiteClass), started()), //\n\t\t\tevent(container(\"TestSuite with 1 tests\"), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"test\"), started()), //\n\t\t\tevent(test(\"test\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"this test should fail\"))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(container(\"TestSuite with 1 tests\"), finishedSuccessfully()), //\n\t\t\tevent(container(junit4SuiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesMalformedJUnit4TestCase() {\n\t\tClass<?> testClass = MalformedJUnit4TestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"initializationError\"), started()), //\n\t\t\tevent(test(\"initializationError\"),\n\t\t\t\tfinishedWithFailure(message(it -> it.contains(\"Method nonPublicTest() should be public\")))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithErrorInBeforeClass() {\n\t\tClass<?> testClass = JUnit4TestCaseWithErrorInBeforeClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"something went wrong\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass() {\n\t\tClass<?> suiteClass = JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.class;\n\t\tClass<?> testClass = JUnit4TestCaseWithErrorInBeforeClass.class;\n\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"something went wrong\"))), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass() {\n\t\tClass<?> suiteOfSuiteClass = JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.class;\n\t\tClass<?> suiteClass = JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.class;\n\t\tClass<?> testClass = JUnit4TestCaseWithErrorInBeforeClass.class;\n\n\t\texecute(suiteOfSuiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteOfSuiteClass), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"something went wrong\"))), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(container(suiteOfSuiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithAssumptionFailureInBeforeClass() {\n\t\tClass<?> testClass = JUnit4TestCaseWithAssumptionFailureInBeforeClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tabortedWithReason(instanceOf(AssumptionViolatedException.class), message(\"assumption violated\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass() {\n\t\tClass<?> suiteOfSuiteClass = JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.class;\n\t\tClass<?> suiteClass = JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.class;\n\t\tClass<?> testClass = JUnit4TestCaseWithAssumptionFailureInBeforeClass.class;\n\n\t\texecute(suiteOfSuiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteOfSuiteClass), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tabortedWithReason(instanceOf(AssumptionViolatedException.class), message(\"assumption violated\"))), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(container(suiteOfSuiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithErrorInAfterClass() {\n\t\tClass<?> testClass = JUnit4TestCaseWithErrorInAfterClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"expected to fail\"))), //\n\t\t\tevent(test(\"succeedingTest\"), started()), //\n\t\t\tevent(test(\"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"error in @AfterClass\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithOverloadedMethod() {\n\t\tClass<?> testClass = JUnit4TestCaseWithIndistinguishableOverloadedMethod.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"theory(\" + JUnit4TestCaseWithIndistinguishableOverloadedMethod.class.getName() + \")[0]\"),\n\t\t\t\tstarted()), //\n\t\t\tevent(test(\"theory(\" + JUnit4TestCaseWithIndistinguishableOverloadedMethod.class.getName() + \")[0]\"),\n\t\t\t\tfinishedWithFailure()), //\n\t\t\tevent(test(\"theory(\" + JUnit4TestCaseWithIndistinguishableOverloadedMethod.class.getName() + \")[1]\"),\n\t\t\t\tstarted()), //\n\t\t\tevent(test(\"theory(\" + JUnit4TestCaseWithIndistinguishableOverloadedMethod.class.getName() + \")[1]\"),\n\t\t\t\tfinishedWithFailure()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesIgnoredJUnit4TestCase() {\n\t\tClass<?> testClass = IgnoredJUnit4TestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), skippedWithReason(\"complete class is ignored\")), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesEmptyIgnoredTestClass() {\n\t\tClass<?> testClass = EmptyIgnoredTestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(test(testClass.getName()), skippedWithReason(\"empty\")), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid reportsExecutionEventsAroundLifecycleMethods() {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithLifecycleMethods.class;\n\t\tPlainJUnit4TestCaseWithLifecycleMethods.EVENTS.clear();\n\n\t\tvar listener = new EngineExecutionListener() {\n\n\t\t\t@Override\n\t\t\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\t\t\tPlainJUnit4TestCaseWithLifecycleMethods.EVENTS.add(\n\t\t\t\t\t\"executionStarted:\" + testDescriptor.getDisplayName());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\t\t\tPlainJUnit4TestCaseWithLifecycleMethods.EVENTS.add(\n\t\t\t\t\t\"executionFinished:\" + testDescriptor.getDisplayName());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\t\t\tPlainJUnit4TestCaseWithLifecycleMethods.EVENTS.add(\n\t\t\t\t\t\"executionSkipped:\" + testDescriptor.getDisplayName());\n\t\t\t}\n\t\t};\n\n\t\texecute(testClass, listener);\n\n\t\t// @formatter:off\n\t\tassertThat(PlainJUnit4TestCaseWithLifecycleMethods.EVENTS).containsExactly(\n\t\t\t\"executionStarted:JUnit Vintage\",\n\t\t\t\t\"executionStarted:\" + testClass.getSimpleName(),\n\t\t\t\t\t\"beforeClass\",\n\t\t\t\t\t\t\"executionStarted:failingTest\",\n\t\t\t\t\t\t\t\"before\",\n\t\t\t\t\t\t\t\t\"failingTest\",\n\t\t\t\t\t\t\t\"after\",\n\t\t\t\t\t\t\"executionFinished:failingTest\",\n\t\t\t\t\t\t\"executionSkipped:skippedTest\",\n\t\t\t\t\t\t\"executionStarted:succeedingTest\",\n\t\t\t\t\t\t\t\"before\",\n\t\t\t\t\t\t\t\t\"succeedingTest\",\n\t\t\t\t\t\t\t\"after\",\n\t\t\t\t\t\t\"executionFinished:succeedingTest\",\n\t\t\t\t\t\"afterClass\",\n\t\t\t\t\"executionFinished:\" + testClass.getSimpleName(),\n\t\t\t\"executionFinished:JUnit Vintage\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored() {\n\t\tClass<?> suiteClass = JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.class;\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.class;\n\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"ignoredTest\"), skippedWithReason(\"ignored test\")), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase() {\n\t\tClass<?> suiteOfSuiteClass = JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.class;\n\t\tClass<?> suiteClass = JUnit4SuiteWithIgnoredJUnit4TestCase.class;\n\t\tClass<?> testClass = IgnoredJUnit4TestCase.class;\n\n\t\texecute(suiteOfSuiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteOfSuiteClass), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), skippedWithReason(\"complete class is ignored\")), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(container(suiteOfSuiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesParameterizedTestCase() {\n\t\tClass<?> testClass = ParameterizedTestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"[foo]\"), started()), //\n\t\t\tevent(test(\"test[foo]\"), started()), //\n\t\t\tevent(test(\"test[foo]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[foo]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[bar]\"), started()), //\n\t\t\tevent(test(\"test[bar]\"), started()), //\n\t\t\tevent(test(\"test[bar]\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"expected:<[foo]> but was:<[bar]>\"))), //\n\t\t\tevent(container(\"[bar]\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesIgnoredParameterizedTestCase() {\n\t\tClass<?> testClass = IgnoredParameterizedTestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"[foo]\"), started()), //\n\t\t\tevent(test(\"test[foo]\"), skippedWithReason(\"\")), //\n\t\t\tevent(container(\"[foo]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[bar]\"), started()), //\n\t\t\tevent(test(\"test[bar]\"), skippedWithReason(\"\")), //\n\t\t\tevent(container(\"[bar]\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesParameterizedTimingTestCase() {\n\t\tassumeTrue(atLeastJUnit4_13(), \"@BeforeParam and @AfterParam were introduced in JUnit 4.13\");\n\n\t\tClass<?> testClass = ParameterizedTimingTestCase.class;\n\n\t\tvar events = execute(testClass).allEvents();\n\n\t\tvar firstParamStartedEvent = events.filter(event(container(\"[foo]\"), started())::matches).findFirst() //\n\t\t\t\t.orElseThrow(() -> new AssertionError(\"No start event for [foo]\"));\n\t\tvar firstParamFinishedEvent = events.filter(\n\t\t\tevent(container(\"[foo]\"), finishedSuccessfully())::matches).findFirst() //\n\t\t\t\t.orElseThrow(() -> new AssertionError(\"No finish event for [foo]\"));\n\t\tvar secondParamStartedEvent = events.filter(event(container(\"[bar]\"), started())::matches).findFirst() //\n\t\t\t\t.orElseThrow(() -> new AssertionError(\"No start event for [bar]\"));\n\t\tvar secondParamFinishedEvent = events.filter(\n\t\t\tevent(container(\"[bar]\"), finishedSuccessfully())::matches).findFirst() //\n\t\t\t\t.orElseThrow(() -> new AssertionError(\"No finish event for [bar]\"));\n\n\t\tassertThat(ParameterizedTimingTestCase.EVENTS.get(\"beforeParam(foo)\")).isAfterOrEqualTo(\n\t\t\tfirstParamStartedEvent.getTimestamp());\n\t\tassertThat(ParameterizedTimingTestCase.EVENTS.get(\"afterParam(foo)\")).isBeforeOrEqualTo(\n\t\t\tfirstParamFinishedEvent.getTimestamp());\n\t\tassertThat(ParameterizedTimingTestCase.EVENTS.get(\"beforeParam(bar)\")).isAfterOrEqualTo(\n\t\t\tsecondParamStartedEvent.getTimestamp());\n\t\tassertThat(ParameterizedTimingTestCase.EVENTS.get(\"afterParam(bar)\")).isBeforeOrEqualTo(\n\t\t\tsecondParamFinishedEvent.getTimestamp());\n\t}\n\n\t@Test\n\tvoid executesParameterizedWithAfterParamFailureTestCase() {\n\t\tassumeTrue(atLeastJUnit4_13(), \"@AfterParam was introduced in JUnit 4.13\");\n\n\t\tClass<?> testClass = ParameterizedWithAfterParamFailureTestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"[foo]\"), started()), //\n\t\t\tevent(test(\"test[foo]\"), started()), //\n\t\t\tevent(test(\"test[foo]\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"[foo]\"), finishedWithFailure(instanceOf(AssertionError.class))), //\n\t\t\tevent(container(\"[bar]\"), started()), //\n\t\t\tevent(test(\"test[bar]\"), started()), //\n\t\t\tevent(test(\"test[bar]\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), message(\"expected:<[foo]> but was:<[bar]>\"))), //\n\t\t\tevent(container(\"[bar]\"), finishedWithFailure(instanceOf(AssertionError.class))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesParameterizedWithBeforeParamFailureTestCase() {\n\t\tassumeTrue(atLeastJUnit4_13(), \"@BeforeParam was introduced in JUnit 4.13\");\n\n\t\tClass<?> testClass = ParameterizedWithBeforeParamFailureTestCase.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"[foo]\"), started()), //\n\t\t\tevent(container(\"[foo]\"), finishedWithFailure(instanceOf(AssertionError.class))), //\n\t\t\tevent(container(\"[bar]\"), started()), //\n\t\t\tevent(container(\"[bar]\"), finishedWithFailure(instanceOf(AssertionError.class))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithExceptionThrowingRunner() {\n\t\tClass<?> testClass = JUnit4TestCaseWithExceptionThrowingRunner.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(test(testClass.getName()), started()), //\n\t\t\tevent(test(testClass.getName()), finishedWithFailure()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithExceptionThrowingRunner() {\n\t\tClass<?> testClass = JUnit4SuiteWithExceptionThrowingRunner.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), finishedWithFailure()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\tpublic static class DynamicSuiteRunner extends Runner {\n\n\t\tprivate final Class<?> testClass;\n\n\t\t@SuppressWarnings(\"RedundantModifier\")\n\t\tpublic DynamicSuiteRunner(Class<?> testClass) {\n\t\t\tthis.testClass = testClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic Description getDescription() {\n\t\t\treturn createSuiteDescription(testClass);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run(RunNotifier notifier) {\n\t\t\tvar dynamicDescription = createTestDescription(testClass, \"dynamicTest\");\n\t\t\tnotifier.fireTestStarted(dynamicDescription);\n\t\t\tnotifier.fireTestFinished(dynamicDescription);\n\t\t}\n\n\t}\n\n\t@RunWith(DynamicSuiteRunner.class)\n\tpublic static class DynamicTestClass {\n\t}\n\n\t@Test\n\tvoid reportsDynamicTestsForUnknownDescriptions() {\n\t\tClass<?> testClass = DynamicTestClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(test(testClass.getName()), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamicTest\")), //\n\t\t\tevent(test(\"dynamicTest\"), started()), //\n\t\t\tevent(test(\"dynamicTest\"), finishedSuccessfully()), //\n\t\t\tevent(test(testClass.getName()), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\tpublic static class DynamicAndStaticChildrenRunner extends Runner {\n\n\t\tprivate final Class<?> testClass;\n\n\t\t@SuppressWarnings(\"RedundantModifier\")\n\t\tpublic DynamicAndStaticChildrenRunner(Class<?> testClass) {\n\t\t\tthis.testClass = testClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic Description getDescription() {\n\t\t\tvar suiteDescription = createSuiteDescription(testClass);\n\t\t\tsuiteDescription.addChild(createTestDescription(testClass, \"staticTest\"));\n\t\t\treturn suiteDescription;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run(RunNotifier notifier) {\n\t\t\tvar staticDescription = getDescription().getChildren().getFirst();\n\t\t\tnotifier.fireTestStarted(staticDescription);\n\t\t\tnotifier.fireTestFinished(staticDescription);\n\t\t\tvar dynamicDescription = createTestDescription(testClass, \"dynamicTest\");\n\t\t\tnotifier.fireTestStarted(dynamicDescription);\n\t\t\tnotifier.fireTestFinished(dynamicDescription);\n\t\t}\n\n\t}\n\n\t@RunWith(DynamicAndStaticChildrenRunner.class)\n\tpublic static class DynamicAndStaticTestClass {\n\t}\n\n\t@RunWith(Suite.class)\n\t@SuiteClasses(DynamicAndStaticTestClass.class)\n\tpublic static class SuiteWithDynamicAndStaticTestClass {\n\t}\n\n\t@Test\n\tvoid reportsIntermediateContainersFinishedAfterTheirDynamicChildren() {\n\t\tClass<?> suiteClass = SuiteWithDynamicAndStaticTestClass.class;\n\t\tClass<?> testClass = DynamicAndStaticTestClass.class;\n\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass.getName()), started()), //\n\t\t\tevent(container(testClass.getName()), started()), //\n\t\t\tevent(test(\"staticTest\"), started()), //\n\t\t\tevent(test(\"staticTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamicTest\")), //\n\t\t\tevent(test(\"dynamicTest\"), started()), //\n\t\t\tevent(test(\"dynamicTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass.getName()), finishedSuccessfully()), //\n\t\t\tevent(container(suiteClass.getName()), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\tpublic static class MisbehavingChildlessRunner extends Runner {\n\n\t\tprivate final Class<?> testClass;\n\n\t\t@SuppressWarnings(\"RedundantModifier\")\n\t\tpublic MisbehavingChildlessRunner(Class<?> testClass) {\n\t\t\tthis.testClass = testClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic Description getDescription() {\n\t\t\treturn createSuiteDescription(testClass);\n\t\t}\n\n\t\t@Override\n\t\tpublic void run(RunNotifier notifier) {\n\t\t\tnotifier.fireTestStarted(createTestDescription(testClass, \"doesNotExist\"));\n\t\t}\n\n\t}\n\n\t@RunWith(MisbehavingChildlessRunner.class)\n\tpublic static class MisbehavingChildTestClass {\n\n\t}\n\n\t@Test\n\tvoid ignoreEventsForUnknownDescriptionsByMisbehavingChildlessRunner() {\n\t\tClass<?> testClass = MisbehavingChildTestClass.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(test(testClass.getName()), started()), //\n\t\t\tevent(dynamicTestRegistered(\"doesNotExist\")), //\n\t\t\tevent(test(\"doesNotExist\"), started()), //\n\t\t\tevent(test(testClass.getName()), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithRunnerWithCustomUniqueIds() {\n\t\tClass<?> testClass = JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(uniqueIdSubstring(testClass.getName()), started()), //\n\t\t\tevent(uniqueIdSubstring(testClass.getName()), finishedWithFailure()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithErrorCollectorStoringMultipleFailures() {\n\t\tClass<?> testClass = JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"example\"), started()), //\n\t\t\tevent(test(\"example\"), //\n\t\t\t\tfinishedWithFailure(//\n\t\t\t\t\tinstanceOf(MultipleFailuresError.class), //\n\t\t\t\t\tnew Condition<>(throwable -> ((MultipleFailuresError) throwable).getFailures().size() == 3,\n\t\t\t\t\t\t\"MultipleFailuresError must contain 3 failures\"), //\n\t\t\t\t\tnew Condition<>(throwable -> throwable.getSuppressed().length == 3,\n\t\t\t\t\t\t\"MultipleFailuresError must contain 3 suppressed exceptions\")//\n\t\t\t\t)), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished() {\n\t\tClass<?> testClass = JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.class;\n\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"testWithMissingEvents\"), started()), //\n\t\t\tevent(test(\"testWithMissingEvents\"), finishedWithFailure()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished() {\n\t\tClass<?> suiteClass = JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.class;\n\t\tClass<?> firstTestClass = JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.class;\n\t\tClass<?> secondTestClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(firstTestClass), started()), //\n\t\t\tevent(test(\"testWithMissingEvents\"), started()), //\n\t\t\tevent(test(\"testWithMissingEvents\"), finishedWithFailure()), //\n\t\t\tevent(container(firstTestClass), finishedSuccessfully()), //\n\t\t\tevent(container(secondTestClass), started()), //\n\t\t\tevent(test(\"failingTest\"), started()), //\n\t\t\tevent(test(\"failingTest\"), finishedWithFailure()), //\n\t\t\tevent(container(secondTestClass), finishedSuccessfully()), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesCompletelyDynamicTestCaseDiscoveredByUniqueId() {\n\t\tClass<?> testClass = CompletelyDynamicTestCase.class;\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectUniqueId(VintageUniqueIdBuilder.uniqueIdForClass(testClass))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\n\t\texecute(request).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(displayName(testClass.getSimpleName()), started()), //\n\t\t\tevent(dynamicTestRegistered(\"Test #0\")), //\n\t\t\tevent(test(\"Test #0\"), started()), //\n\t\t\tevent(test(\"Test #0\"), finishedSuccessfully()), //\n\t\t\tevent(displayName(testClass.getSimpleName()), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit3ParallelSuiteWithSubsuites() {\n\t\tvar suiteClass = JUnit3ParallelSuiteWithSubsuites.class;\n\t\tvar results = execute(suiteClass);\n\t\tresults.containerEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.started(4).dynamicallyRegistered(0).finished(4).succeeded(4)) //\n\t\t\t\t.assertEventsMatchExactly( //\n\t\t\t\t\tevent(engine(), started()), //\n\t\t\t\t\tevent(container(suiteClass), started()), //\n\t\t\t\t\tevent(container(\"Case\"), started()), //\n\t\t\t\t\tevent(container(\"Case\")), //\n\t\t\t\t\tevent(container(\"Case\")), //\n\t\t\t\t\tevent(container(\"Case\"), finishedSuccessfully()), //\n\t\t\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\t\t\tevent(engine(), finishedSuccessfully()));\n\t\tresults.testEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.started(2).dynamicallyRegistered(0).finished(2).succeeded(2)) //\n\t\t\t\t.assertEventsMatchExactly( //\n\t\t\t\t\tevent(test(\"hello\"), started()), //\n\t\t\t\t\tevent(test(\"hello\")), //\n\t\t\t\t\tevent(test(\"hello\")), //\n\t\t\t\t\tevent(test(\"hello\"), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit3SuiteWithSubsuites() {\n\t\tvar suiteClass = JUnit3SuiteWithSubsuites.class;\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(\"Case1\"), started()), //\n\t\t\tevent(test(\"hello\"), started()), //\n\t\t\tevent(test(\"hello\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"Case1\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"Case2\"), started()), //\n\t\t\tevent(test(\"hello\"), started()), //\n\t\t\tevent(test(\"hello\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"Case2\"), finishedSuccessfully()), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions() {\n\t\tClass<?> testClass = JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.class;\n\t\texecute(testClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"1st\"), started()), //\n\t\t\tevent(test(\"0\"), skippedWithReason(__ -> true)), //\n\t\t\tevent(test(\"1\"), started()), //\n\t\t\tevent(test(\"1\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"1st\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"2nd\"), started()), //\n\t\t\tevent(test(\"0\"), skippedWithReason(__ -> true)), //\n\t\t\tevent(test(\"1\"), started()), //\n\t\t\tevent(test(\"1\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"2nd\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\t@DisabledInEclipse\n\tvoid executesUnrolledSpockFeatureMethod() {\n\t\t// Load Groovy class via reflection to avoid compilation errors in Eclipse IDE.\n\t\tString testClassName = \"org.junit.vintage.engine.samples.spock.SpockTestCaseWithUnrolledAndRegularFeatureMethods\";\n\t\tClass<?> testClass = ReflectionUtils.loadRequiredClass(testClassName, getClass().getClassLoader());\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectMethod(testClass, \"unrolled feature for #input\"))//\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\texecute(request).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(uniqueIdSubstring(testClassName), started()), //\n\t\t\tevent(dynamicTestRegistered(\"unrolled feature for 23\")), //\n\t\t\tevent(test(\"unrolled feature for 23\"), started()), //\n\t\t\tevent(test(\"unrolled feature for 23\"), finishedWithFailure()), //\n\t\t\tevent(dynamicTestRegistered(\"unrolled feature for 42\")), //\n\t\t\tevent(test(\"unrolled feature for 42\"), started()), //\n\t\t\tevent(test(\"unrolled feature for 42\"), finishedSuccessfully()), //\n\t\t\tevent(uniqueIdSubstring(testClass.getName()), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\t@DisabledInEclipse\n\tvoid executesRegularSpockFeatureMethod() {\n\t\t// Load Groovy class via reflection to avoid compilation errors in Eclipse IDE.\n\t\tString testClassName = \"org.junit.vintage.engine.samples.spock.SpockTestCaseWithUnrolledAndRegularFeatureMethods\";\n\t\tClass<?> testClass = ReflectionUtils.loadRequiredClass(testClassName, getClass().getClassLoader());\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectMethod(testClass, \"regular\")) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\texecute(request).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"regular\"), started()), //\n\t\t\tevent(test(\"regular\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesIgnoredJUnit3TestCase() {\n\t\tvar suiteClass = IgnoredJUnit3TestCase.class;\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), skippedWithReason(isEqual(\"testing\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesJUnit4SuiteWithIgnoredJUnit3TestCase() {\n\t\tvar suiteClass = JUnit4SuiteWithIgnoredJUnit3TestCase.class;\n\t\tvar testClass = IgnoredJUnit3TestCase.class;\n\t\texecute(suiteClass).allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(suiteClass), started()), //\n\t\t\tevent(container(testClass), skippedWithReason(isEqual(\"testing\"))), //\n\t\t\tevent(container(suiteClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid supportsCancellation() {\n\t\tCancellingTestCase.cancellationToken = CancellationToken.create();\n\t\ttry {\n\t\t\tvar results = vintageTestEngine() //\n\t\t\t\t\t.selectors(selectClass(CancellingTestCase.class),\n\t\t\t\t\t\tselectClass(PlainJUnit4TestCaseWithSingleTestWhichFails.class)) //\n\t\t\t\t\t.cancellationToken(CancellingTestCase.cancellationToken) //\n\t\t\t\t\t.execute();\n\n\t\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\t\tevent(engine(), started()), //\n\t\t\t\tevent(container(CancellingTestCase.class), started()), //\n\t\t\t\tevent(test(), started()), //\n\t\t\t\tevent(test(), finishedWithFailure()), //\n\t\t\t\tevent(test(), skippedWithReason(\"Execution cancelled\")), //\n\t\t\t\tevent(container(CancellingTestCase.class), abortedWithReason(instanceOf(StoppedByUserException.class))), //\n\t\t\t\tevent(container(PlainJUnit4TestCaseWithSingleTestWhichFails.class),\n\t\t\t\t\tskippedWithReason(\"Execution cancelled\")), //\n\t\t\t\tevent(engine(), finishedSuccessfully()));\n\t\t}\n\t\tfinally {\n\t\t\tCancellingTestCase.cancellationToken = null;\n\t\t}\n\t}\n\n\tprivate static EngineExecutionResults execute(Class<?> testClass) {\n\t\treturn execute(request(testClass));\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static EngineExecutionResults execute(LauncherDiscoveryRequest request) {\n\t\treturn EngineTestKit.execute(new VintageTestEngine(), request);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static EngineTestKit.Builder vintageTestEngine() {\n\t\treturn EngineTestKit.engine(new VintageTestEngine()) //\n\t\t\t\t.enableImplicitConfigurationParameters(false);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static void execute(Class<?> testClass, EngineExecutionListener listener) {\n\t\tvar testEngine = new VintageTestEngine();\n\t\tvar engineTestDescriptor = testEngine.discover(request(testClass), UniqueId.forEngine(testEngine.getId()));\n\t\tExecutionRequest executionRequest = mock();\n\t\twhen(executionRequest.getRootTestDescriptor()).thenReturn(engineTestDescriptor);\n\t\twhen(executionRequest.getEngineExecutionListener()).thenReturn(listener);\n\t\twhen(executionRequest.getConfigurationParameters()).thenReturn(mock());\n\t\twhen(executionRequest.getCancellationToken()).thenReturn(CancellationToken.disabled());\n\t\ttestEngine.execute(executionRequest);\n\t}\n\n\tprivate static LauncherDiscoveryRequest request(Class<?> testClass) {\n\t\treturn LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static boolean atLeastJUnit4_13() {\n\t\treturn JUnit4VersionCheck.parseVersion(Version.id()).compareTo(new BigDecimal(\"4.13\")) >= 0;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageTestEngineTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport org.junit.platform.suite.api.ExcludeTags;\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * Test suite for the {@link VintageTestEngine}.\n *\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 4.12\n */\n@Suite\n@SelectPackages(\"org.junit.vintage.engine\")\n@IncludeClassNamePatterns(\".*Tests?\")\n@IncludeEngines(\"junit-jupiter\")\n@ExcludeTags(\"missing-junit4\")\nclass VintageTestEngineTestSuite {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/VintageUniqueIdBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine;\n\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\n\n/**\n * Test data builder for building unique IDs for the {@link VintageTestEngine}.\n *\n * Used to decouple tests from concrete unique ID strings.\n *\n * @since 4.12\n */\npublic class VintageUniqueIdBuilder {\n\n\tpublic static UniqueId uniqueIdForErrorInClass(Class<?> clazz, Class<?> failingClass) {\n\t\treturn uniqueIdForClasses(clazz).append(VintageTestDescriptor.SEGMENT_TYPE_TEST,\n\t\t\t\"initializationError(\" + failingClass.getName() + \")\");\n\t}\n\n\tpublic static UniqueId uniqueIdForClass(Class<?> clazz) {\n\t\treturn uniqueIdForClasses(clazz);\n\t}\n\n\tpublic static UniqueId uniqueIdForClasses(Class<?> clazz, Class<?>... clazzes) {\n\t\tvar uniqueId = uniqueIdForClass(clazz.getName());\n\t\tfor (var each : clazzes) {\n\t\t\tuniqueId = uniqueId.append(VintageTestDescriptor.SEGMENT_TYPE_TEST, each.getName());\n\t\t}\n\t\treturn uniqueId;\n\t}\n\n\tpublic static UniqueId uniqueIdForClass(String fullyQualifiedClassName) {\n\t\tvar containerId = engineId();\n\t\treturn containerId.append(VintageTestDescriptor.SEGMENT_TYPE_RUNNER, fullyQualifiedClassName);\n\t}\n\n\tpublic static UniqueId uniqueIdForMethod(Class<?> testClass, String methodName) {\n\t\treturn uniqueIdForClass(testClass).append(VintageTestDescriptor.SEGMENT_TYPE_TEST,\n\t\t\tmethodValue(testClass, methodName));\n\t}\n\n\tprivate static String methodValue(Class<?> testClass, String methodName) {\n\t\treturn methodName + \"(\" + testClass.getName() + \")\";\n\t}\n\n\tpublic static UniqueId uniqueIdForMethod(Class<?> testClass, String methodName, String index) {\n\t\treturn uniqueIdForClass(testClass).append(VintageTestDescriptor.SEGMENT_TYPE_TEST,\n\t\t\tmethodValue(testClass, methodName) + \"[\" + index + \"]\");\n\t}\n\n\tpublic static UniqueId uniqueIdForMethod(UniqueId containerId, Class<?> testClass, String methodName) {\n\t\treturn containerId.append(VintageTestDescriptor.SEGMENT_TYPE_TEST, methodValue(testClass, methodName));\n\t}\n\n\tpublic static UniqueId engineId() {\n\t\treturn UniqueId.forEngine(VintageTestDescriptor.ENGINE_ID);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/DescriptionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.stream.Stream;\n\nimport org.junit.internal.builders.AllDefaultPossibilitiesBuilder;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.runner.Description;\nimport org.junit.vintage.engine.discovery.IsPotentialJUnit4TestClass;\n\nclass DescriptionUtilsTests {\n\n\t@SuppressWarnings(\"deprecation\")\n\tAllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);\n\n\t@TestFactory\n\tStream<DynamicNode> computedMethodNameCorrectly() {\n\t\tvar testClasses = ReflectionSupport.findAllClassesInPackage(\"org.junit.vintage.engine.samples\",\n\t\t\tnew IsPotentialJUnit4TestClass(), name -> true);\n\t\treturn testClasses.stream().flatMap(this::toDynamicTests);\n\t}\n\n\tprivate Stream<DynamicNode> toDynamicTests(Class<?> testClass) {\n\t\ttry {\n\t\t\tvar runner = builder.runnerForClass(testClass);\n\t\t\treturn toDynamicTests(Stream.of(runner.getDescription()));\n\t\t}\n\t\tcatch (Throwable throwable) {\n\t\t\tthrow new RuntimeException(throwable);\n\t\t}\n\t}\n\n\tStream<DynamicNode> toDynamicTests(Stream<Description> children) {\n\t\treturn children.map(description -> description.isTest() //\n\t\t\t\t? toDynamicTest(description, \"child: \" + description) //\n\t\t\t\t: dynamicContainer(\"class: \" + description, Stream.concat( //\n\t\t\t\t\tStream.of(toDynamicTest(description, \"self\")), //\n\t\t\t\t\ttoDynamicTests(description.getChildren().stream()))));\n\t}\n\n\tprivate DynamicTest toDynamicTest(Description description, String displayName) {\n\t\treturn dynamicTest(displayName,\n\t\t\t() -> assertEquals(description.getMethodName(), DescriptionUtils.getMethodName(description)));\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/OrFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotEmptyFor;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.runner.Description;\nimport org.junit.runner.manipulation.Filter;\n\n/**\n * @since 5.5\n */\nclass OrFilterTests {\n\n\t@Test\n\tvoid exceptionWithoutAnyFilters() {\n\t\tassertPreconditionViolationNotEmptyFor(\"filters\", () -> new OrFilter(Set.of()));\n\t}\n\n\t@Test\n\tvoid evaluatesSingleFilter() {\n\t\tvar filter = mockFilter(\"foo\", true);\n\n\t\tvar orFilter = new OrFilter(Set.of(filter));\n\n\t\tassertEquals(\"foo\", orFilter.describe());\n\n\t\tvar description = Description.createTestDescription(getClass(), \"evaluatesSingleFilter\");\n\t\tassertTrue(orFilter.shouldRun(description));\n\n\t\tverify(filter).shouldRun(same(description));\n\t}\n\n\t@Test\n\tvoid evaluatesMultipleFilters() {\n\t\tvar filter1 = mockFilter(\"foo\", false);\n\t\tvar filter2 = mockFilter(\"bar\", true);\n\n\t\tvar orFilter = new OrFilter(List.of(filter1, filter2));\n\n\t\tassertEquals(\"foo OR bar\", orFilter.describe());\n\n\t\tvar description = Description.createTestDescription(getClass(), \"evaluatesMultipleFilters\");\n\t\tassertTrue(orFilter.shouldRun(description));\n\n\t\tverify(filter1).shouldRun(same(description));\n\t\tverify(filter2).shouldRun(same(description));\n\t}\n\n\tprivate Filter mockFilter(String description, boolean result) {\n\t\tvar filter = mock(Filter.class);\n\t\twhen(filter.describe()).thenReturn(description);\n\t\twhen(filter.shouldRun(any())).thenReturn(result);\n\t\treturn filter;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/TestSourceProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.runner.Description;\nimport org.junit.vintage.engine.samples.junit4.ConcreteJUnit4TestCase;\n\n/**\n * @since 5.6\n */\nclass TestSourceProviderTests {\n\n\t@Test\n\tvoid findsInheritedMethod() {\n\t\tvar description = Description.createTestDescription(ConcreteJUnit4TestCase.class, \"theTest\");\n\n\t\tvar source = new TestSourceProvider().findTestSource(description);\n\t\tassertThat(source).isInstanceOf(MethodSource.class);\n\n\t\tvar methodSource = (MethodSource) source;\n\t\tassertEquals(ConcreteJUnit4TestCase.class.getName(), methodSource.getClassName());\n\t\tassertEquals(\"theTest\", methodSource.getMethodName());\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/descriptor/VintageTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.runner.Description;\nimport org.junit.vintage.engine.samples.junit4.ConcreteJUnit4TestCase;\n\nclass VintageTestDescriptorTests {\n\n\tprivate static final UniqueId uniqueId = UniqueId.forEngine(\"vintage\");\n\n\t@Test\n\tvoid legacyReportingNameUsesClassName() {\n\t\tvar description = Description.createSuiteDescription(ConcreteJUnit4TestCase.class);\n\t\tvar testDescriptor = new VintageTestDescriptor(uniqueId, description, null);\n\n\t\tassertEquals(\"org.junit.vintage.engine.samples.junit4.ConcreteJUnit4TestCase\",\n\t\t\ttestDescriptor.getLegacyReportingName());\n\t}\n\n\t@Test\n\tvoid legacyReportingNameUsesMethodName() {\n\t\tvar description = Description.createTestDescription(ConcreteJUnit4TestCase.class, \"legacyTest\");\n\t\tvar testDescriptor = new VintageTestDescriptor(uniqueId, description, null);\n\n\t\tassertEquals(\"legacyTest\", testDescriptor.getLegacyReportingName());\n\t}\n\n\t@Test\n\tvoid legacyReportingNameFallbackToDisplayName() {\n\t\tvar suiteName = \"Legacy Suite\";\n\t\tvar description = Description.createSuiteDescription(suiteName);\n\t\tvar testDescriptor = new VintageTestDescriptor(uniqueId, description, null);\n\n\t\tassertEquals(testDescriptor.getDisplayName(), testDescriptor.getLegacyReportingName());\n\t\tassertEquals(suiteName, testDescriptor.getLegacyReportingName());\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/IsPotentialJUnit4TestClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.Test;\n\nclass IsPotentialJUnit4TestClassTests {\n\n\tprivate final IsPotentialJUnit4TestClass isPotentialJUnit4TestClass = new IsPotentialJUnit4TestClass();\n\n\t@Test\n\tvoid staticMemberClass() {\n\t\tassertTrue(isPotentialJUnit4TestClass.test(Foo.class));\n\t}\n\n\tpublic static class Foo {\n\t}\n\n\t@Test\n\tvoid nonPublicClass() {\n\t\tassertFalse(isPotentialJUnit4TestClass.test(Bar.class));\n\t}\n\n\tstatic class Bar {\n\t}\n\n\t@Test\n\tvoid abstractClass() {\n\t\tassertFalse(isPotentialJUnit4TestClass.test(Baz.class));\n\t}\n\n\tpublic static abstract class Baz {\n\t}\n\n\t@Test\n\tvoid anonymousClass() {\n\t\tvar foo = new Foo() {\n\t\t};\n\n\t\tassertFalse(isPotentialJUnit4TestClass.test(foo.getClass()));\n\t}\n\n\tpublic class FooBaz {\n\t}\n\n\t@Test\n\tvoid publicInnerClass() {\n\t\tassertFalse(isPotentialJUnit4TestClass.test(FooBaz.class));\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/RunnerTestDescriptorPostProcessorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.vintage.engine.VintageUniqueIdBuilder;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.samples.junit4.IgnoredJUnit4TestCase;\nimport org.junit.vintage.engine.samples.junit4.IgnoredJUnit4TestCaseWithNotFilterableRunner;\nimport org.junit.vintage.engine.samples.junit4.NotFilterableRunner;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithFiveTestMethods;\n\n/**\n * Tests for {@link RunnerTestDescriptorPostProcessor}.\n *\n * @since 5.5\n */\n@TrackLogRecords\nclass RunnerTestDescriptorPostProcessorTests {\n\n\t@Test\n\tvoid doesNotLogAnythingForFilterableRunner(LogRecordListener listener) {\n\t\tresolve(selectMethod(PlainJUnit4TestCaseWithFiveTestMethods.class, \"successfulTest\"));\n\n\t\tassertThat(listener.stream(RunnerTestDescriptor.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid doesNotLogAnythingForNonFilterableRunnerIfNoFiltersAreToBeApplied(LogRecordListener listener) {\n\t\tresolve(selectClass(IgnoredJUnit4TestCase.class));\n\n\t\tassertThat(listener.stream(RunnerTestDescriptor.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid logsWarningOnNonFilterableRunner(LogRecordListener listener) {\n\t\tClass<?> testClass = IgnoredJUnit4TestCaseWithNotFilterableRunner.class;\n\n\t\tresolve(selectMethod(testClass, \"someTest\"));\n\n\t\t// @formatter:off\n\t\tassertThat(listener.stream(RunnerTestDescriptor.class, Level.WARNING).map(LogRecord::getMessage))\n\t\t\t\t.containsOnlyOnce(\"Runner \" + NotFilterableRunner.class.getName()\n\t\t\t\t\t\t+ \" (used on class \" + testClass.getName() + \") does not support filtering\"\n\t\t\t\t\t\t+ \" and will therefore be run completely.\");\n\t\t// @formatter:on\n\t}\n\n\tprivate void resolve(DiscoverySelector selector) {\n\t\tvar request = LauncherDiscoveryRequestBuilder.request().selectors(selector).listeners(\n\t\t\tmock(LauncherDiscoveryListener.class)).build();\n\t\tTestDescriptor engineDescriptor = new VintageDiscoverer().discover(request, VintageUniqueIdBuilder.engineId());\n\t\tvar runnerTestDescriptor = (RunnerTestDescriptor) getOnlyElement(engineDescriptor.getChildren());\n\t\tnew RunnerTestDescriptorPostProcessor().applyFiltersAndCreateDescendants(runnerTestDescriptor);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/discovery/VintageDiscovererTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.FAILED;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.UNRESOLVED;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.vintage.engine.VintageUniqueIdBuilder.engineId;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.function.Consumer;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.PackageNameFilter;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.vintage.engine.VintageUniqueIdBuilder;\nimport org.junit.vintage.engine.samples.junit3.AbstractJUnit3TestCase;\nimport org.junit.vintage.engine.samples.junit4.AbstractJunit4TestCaseWithConstructorParameter;\nimport org.mockito.ArgumentCaptor;\n\n/**\n * Tests for {@link VintageDiscoverer}.\n *\n * @since 4.12\n */\nclass VintageDiscovererTests {\n\n\t@Test\n\tvoid classNameFilterExcludesClass() {\n\t\t// @formatter:off\n\t\tEngineDiscoveryRequest request = request()\n\t\t\t\t.selectors(selectClass(Foo.class), selectClass(Bar.class))\n\t\t\t\t.filters(ClassNameFilter.includeClassNamePatterns(\".*Foo\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tvar testDescriptor = discover(request);\n\n\t\tassertThat(testDescriptor.getChildren()).hasSize(1);\n\t\tassertThat(getOnlyElement(testDescriptor.getChildren()).getUniqueId().toString()).contains(Foo.class.getName());\n\t}\n\n\t@Test\n\tvoid packageNameFilterExcludesClasses() {\n\t\t// @formatter:off\n\t\tEngineDiscoveryRequest request = request()\n\t\t\t\t.selectors(selectClass(Foo.class), selectClass(Bar.class))\n\t\t\t\t.filters(PackageNameFilter.excludePackageNames(\"org.junit.vintage.engine.discovery\"))\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tvar testDescriptor = discover(request);\n\n\t\tassertThat(testDescriptor.getChildren()).isEmpty();\n\t}\n\n\t@Test\n\tvoid doesNotResolveAbstractJUnit3Classes() {\n\t\tdoesNotResolve(selectClass(AbstractJUnit3TestCase.class));\n\t}\n\n\t@Test\n\tvoid doesNotResolveAbstractJUnit4Classes() {\n\t\tdoesNotResolve(selectClass(AbstractJunit4TestCaseWithConstructorParameter.class));\n\t}\n\n\t@Test\n\tvoid failsToResolveUnloadableTestClass() {\n\t\tvar uniqueId = VintageUniqueIdBuilder.uniqueIdForClass(\"foo.bar.UnknownClass\");\n\n\t\tdoesNotResolve(selectUniqueId(uniqueId), result -> {\n\t\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\t\tassertThat(result.getThrowable().orElseThrow()).hasMessageContaining(\"Unknown class\");\n\t\t});\n\t}\n\n\t@Test\n\tvoid ignoresUniqueIdsOfOtherEngines() {\n\t\tdoesNotResolve(selectUniqueId(UniqueId.forEngine(\"someEngine\")));\n\t}\n\n\tprivate void doesNotResolve(DiscoverySelector selector) {\n\t\tdoesNotResolve(selector, result -> assertThat(result.getStatus()).isEqualTo(UNRESOLVED));\n\t}\n\n\tprivate void doesNotResolve(DiscoverySelector selector, Consumer<SelectorResolutionResult> resultCheck) {\n\t\tvar discoveryListener = mock(LauncherDiscoveryListener.class);\n\t\tvar request = request() //\n\t\t\t\t.selectors(selector) //\n\t\t\t\t.listeners(discoveryListener) //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.build();\n\n\t\tvar testDescriptor = discover(request);\n\n\t\tassertThat(testDescriptor.getChildren()).isEmpty();\n\t\tvar resultCaptor = ArgumentCaptor.forClass(SelectorResolutionResult.class);\n\t\tverify(discoveryListener).selectorProcessed(eq(UniqueId.forEngine(\"junit-vintage\")), eq(selector),\n\t\t\tresultCaptor.capture());\n\t\tresultCheck.accept(resultCaptor.getValue());\n\t}\n\n\tprivate TestDescriptor discover(EngineDiscoveryRequest request) {\n\t\treturn new VintageDiscoverer().discover(request, engineId());\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class Foo {\n\n\t\t@org.junit.Test\n\t\tpublic void test() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class Bar {\n\n\t\t@org.junit.Test\n\t\tpublic void test() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/ParallelExecutionIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_RUNNER;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_TEST;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelClassesTestCase.FirstClassTestCase;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelClassesTestCase.SecondClassTestCase;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelClassesTestCase.ThirdClassTestCase;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelMethodsTestCase.FirstMethodTestCase;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelMethodsTestCase.SecondMethodTestCase;\nimport static org.junit.vintage.engine.samples.junit4.JUnit4ParallelMethodsTestCase.ThirdMethodTestCase;\n\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.Events;\nimport org.junit.vintage.engine.Constants;\nimport org.junit.vintage.engine.VintageTestEngine;\nimport org.junit.vintage.engine.samples.junit4.JUnit4ParallelClassesTestCase;\nimport org.junit.vintage.engine.samples.junit4.JUnit4ParallelMethodsTestCase;\n\nclass ParallelExecutionIntegrationTests {\n\n\t@Test\n\tvoid executesTestClassesInParallel(TestReporter reporter) {\n\t\tJUnit4ParallelClassesTestCase.AbstractBlockingTestCase.threadNames.clear();\n\t\tJUnit4ParallelClassesTestCase.AbstractBlockingTestCase.countDownLatch = new CountDownLatch(3);\n\n\t\tvar events = executeInParallelSuccessfully(3, true, false, FirstClassTestCase.class, SecondClassTestCase.class,\n\t\t\tThirdClassTestCase.class).list();\n\n\t\tvar startedTimestamps = getTimestampsFor(events, event(container(SEGMENT_TYPE_RUNNER), started()));\n\t\tvar finishedTimestamps = getTimestampsFor(events,\n\t\t\tevent(container(SEGMENT_TYPE_RUNNER), finishedSuccessfully()));\n\t\tvar threadNames = new HashSet<>(JUnit4ParallelClassesTestCase.AbstractBlockingTestCase.threadNames);\n\n\t\treporter.publishEntry(\"startedTimestamps\", startedTimestamps.toString());\n\t\treporter.publishEntry(\"finishedTimestamps\", finishedTimestamps.toString());\n\n\t\tassertThat(startedTimestamps).hasSize(3);\n\t\tassertThat(finishedTimestamps).hasSize(3);\n\t\tassertThat(startedTimestamps).allMatch(startTimestamp -> finishedTimestamps.stream().noneMatch(\n\t\t\tfinishedTimestamp -> finishedTimestamp.isBefore(startTimestamp)));\n\t\tassertThat(threadNames).hasSize(3);\n\t}\n\n\t@Test\n\tvoid executesTestMethodsInParallel(TestReporter reporter) {\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames.clear();\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.countDownLatch = new CountDownLatch(3);\n\n\t\tvar events = executeInParallelSuccessfully(3, false, true, FirstMethodTestCase.class).list();\n\n\t\tvar startedTimestamps = getTimestampsFor(events, event(test(SEGMENT_TYPE_TEST), started()));\n\t\tvar finishedTimestamps = getTimestampsFor(events, event(test(SEGMENT_TYPE_TEST), finishedSuccessfully()));\n\t\tvar threadNames = new HashSet<>(JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames);\n\n\t\treporter.publishEntry(\"startedTimestamps\", startedTimestamps.toString());\n\t\treporter.publishEntry(\"finishedTimestamps\", finishedTimestamps.toString());\n\n\t\tassertAll( //\n\t\t\t() -> assertThat(startedTimestamps).hasSize(3), //\n\t\t\t() -> assertThat(finishedTimestamps).hasSize(3), //\n\t\t\t() -> assertThat(startedTimestamps).allMatch(startTimestamp -> finishedTimestamps.stream().noneMatch( //\n\t\t\t\tfinishedTimestamp -> finishedTimestamp.isBefore(startTimestamp))), //\n\t\t\t() -> assertThat(threadNames).hasSize(3));\n\t}\n\n\t@Test\n\tvoid executesTestClassesAndMethodsInParallel(TestReporter reporter) {\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames.clear();\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.countDownLatch = new CountDownLatch(9);\n\n\t\tvar events = executeInParallelSuccessfully(3, true, true, FirstMethodTestCase.class, SecondMethodTestCase.class,\n\t\t\tThirdMethodTestCase.class).list();\n\n\t\tvar startedClassesTimestamps = getTimestampsFor(events, event(container(SEGMENT_TYPE_RUNNER), started()));\n\t\tvar finishedClassesTimestamps = getTimestampsFor(events,\n\t\t\tevent(container(SEGMENT_TYPE_RUNNER), finishedSuccessfully()));\n\t\tvar startedMethodsTimestamps = getTimestampsFor(events, event(test(SEGMENT_TYPE_TEST), started()));\n\t\tvar finishedMethodsTimestamps = getTimestampsFor(events,\n\t\t\tevent(test(SEGMENT_TYPE_TEST), finishedSuccessfully()));\n\n\t\tvar threadNames = new HashSet<>(JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames);\n\n\t\treporter.publishEntry(\"startedClassesTimestamps\", startedClassesTimestamps.toString());\n\t\treporter.publishEntry(\"finishedClassesTimestamps\", finishedClassesTimestamps.toString());\n\t\treporter.publishEntry(\"startedMethodsTimestamps\", startedMethodsTimestamps.toString());\n\t\treporter.publishEntry(\"finishedMethodsTimestamps\", finishedMethodsTimestamps.toString());\n\n\t\tassertThat(startedClassesTimestamps).hasSize(3);\n\t\tassertThat(finishedClassesTimestamps).hasSize(3);\n\t\tassertThat(startedMethodsTimestamps).hasSize(9);\n\t\tassertThat(finishedMethodsTimestamps).hasSize(9);\n\n\t\tassertThat(threadNames).hasSize(3);\n\t}\n\n\t@Test\n\tvoid executesInParallelWhenNoScopeIsDefined(@TrackLogRecords LogRecordListener listener) {\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames.clear();\n\t\tJUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.countDownLatch = new CountDownLatch(9);\n\t\texecute(3, false, false, FirstMethodTestCase.class, SecondMethodTestCase.class, ThirdMethodTestCase.class);\n\n\t\t// @formatter:off\n\t\tassertTrue(listener.stream(Level.WARNING)\n\t\t\t\t.map(LogRecord::getMessage)\n\t\t\t\t.anyMatch(m -> m.startsWith(\n\t\t\t\t\t\"Parallel execution is enabled but no scope is defined. Falling back to sequential execution.\")));\n\t\t// @formatter:on\n\n\t\tvar threadNames = new HashSet<>(JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase.threadNames);\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@Test\n\tvoid executesInParallelWhenInvalidPoolSizeIsDefined(@TrackLogRecords LogRecordListener listener) {\n\t\texecute(-1, true, true, FirstMethodTestCase.class, SecondMethodTestCase.class, ThirdMethodTestCase.class);\n\n\t\t// @formatter:off\n\t\tassertTrue(listener.stream(Level.WARNING)\n\t\t\t\t.map(LogRecord::getMessage)\n\t\t\t\t.anyMatch(m -> m.startsWith(\"Invalid value for parallel pool size: -1\")));\n\t\t// @formatter:on\n\t}\n\n\tprivate List<Instant> getTimestampsFor(List<Event> events, Condition<Event> condition) {\n\t\t// @formatter:off\n\t\treturn events.stream()\n\t\t\t\t.filter(condition::matches)\n\t\t\t\t.map(Event::getTimestamp)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate Events executeInParallelSuccessfully(int poolSize, boolean parallelClasses, boolean parallelMethods,\n\t\t\tClass<?>... testClasses) {\n\t\tvar events = execute(poolSize, parallelClasses, parallelMethods, testClasses).allEvents();\n\t\ttry {\n\t\t\treturn events.assertStatistics(it -> it.failed(0));\n\t\t}\n\t\tcatch (AssertionError error) {\n\t\t\tevents.debug();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static EngineExecutionResults execute(int poolSize, boolean parallelClasses, boolean parallelMethods,\n\t\t\tClass<?>... testClass) {\n\t\treturn EngineTestKit.execute(new VintageTestEngine(),\n\t\t\trequest(poolSize, parallelClasses, parallelMethods, testClass));\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static LauncherDiscoveryRequest request(int poolSize, boolean parallelClasses, boolean parallelMethods,\n\t\t\tClass<?>... testClasses) {\n\t\tvar classSelectors = Arrays.stream(testClasses) //\n\t\t\t\t.map(DiscoverySelectors::selectClass) //\n\t\t\t\t.toArray(ClassSelector[]::new);\n\n\t\treturn LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(classSelectors) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_EXECUTION_ENABLED, String.valueOf(true)) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_POOL_SIZE, String.valueOf(poolSize)) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CLASS_EXECUTION, String.valueOf(parallelClasses)) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_METHOD_EXECUTION, String.valueOf(parallelMethods)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/execution/TestRunTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.runner.Description.createTestDescription;\nimport static org.junit.vintage.engine.VintageUniqueIdBuilder.engineId;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_DYNAMIC;\nimport static org.junit.vintage.engine.descriptor.VintageTestDescriptor.SEGMENT_TYPE_RUNNER;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.junit.vintage.engine.descriptor.RunnerTestDescriptor;\nimport org.junit.vintage.engine.descriptor.VintageTestDescriptor;\nimport org.junit.vintage.engine.samples.junit4.PlainJUnit4TestCaseWithSingleTestWhichFails;\n\n/**\n * @since 4.12\n */\nclass TestRunTests {\n\n\t@Test\n\tvoid returnsEmptyOptionalForUnknownDescriptions() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\t\tvar runnerId = engineId().append(SEGMENT_TYPE_RUNNER, testClass.getName());\n\t\tvar runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass),\n\t\t\tfalse);\n\t\tvar unknownDescription = createTestDescription(testClass, \"dynamicTest\");\n\n\t\tvar testRun = new TestRun(runnerTestDescriptor);\n\t\tvar testDescriptor = testRun.lookupNextTestDescriptor(unknownDescription);\n\n\t\tassertThat(testDescriptor).isEmpty();\n\t}\n\n\t@Test\n\tvoid registersDynamicTestDescriptors() throws Exception {\n\t\tClass<?> testClass = PlainJUnit4TestCaseWithSingleTestWhichFails.class;\n\t\tvar runnerId = engineId().append(SEGMENT_TYPE_RUNNER, testClass.getName());\n\t\tvar runnerTestDescriptor = new RunnerTestDescriptor(runnerId, testClass, new BlockJUnit4ClassRunner(testClass),\n\t\t\tfalse);\n\t\tvar dynamicTestId = runnerId.append(SEGMENT_TYPE_DYNAMIC, \"dynamicTest\");\n\t\tvar dynamicDescription = createTestDescription(testClass, \"dynamicTest\");\n\t\tvar dynamicTestDescriptor = new VintageTestDescriptor(dynamicTestId, dynamicDescription, null);\n\n\t\tvar testRun = new TestRun(runnerTestDescriptor);\n\t\ttestRun.registerDynamicTest(dynamicTestDescriptor);\n\n\t\tassertThat(testRun.lookupNextTestDescriptor(dynamicDescription)).contains(dynamicTestDescriptor);\n\t\tassertTrue(testRun.isDescendantOfRunnerTestDescriptor(dynamicTestDescriptor));\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdReaderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.runner.Description.createTestDescription;\n\nimport java.util.logging.Level;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\n\n/**\n * Tests for {@link UniqueIdReader}.\n *\n * @since 4.12\n */\n@TrackLogRecords\nclass UniqueIdReaderTests {\n\n\t@Test\n\tvoid readsUniqueId(LogRecordListener listener) {\n\t\tvar description = createTestDescription(\"ClassName\", \"methodName\", \"uniqueId\");\n\n\t\tvar uniqueId = new UniqueIdReader().apply(description);\n\n\t\tassertEquals(\"uniqueId\", uniqueId);\n\t\tassertThat(listener.stream(UniqueIdReader.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid returnsDisplayNameWhenUniqueIdCannotBeRead(LogRecordListener listener) {\n\t\tvar description = createTestDescription(\"ClassName\", \"methodName\", \"uniqueId\");\n\t\tassertEquals(\"methodName(ClassName)\", description.getDisplayName());\n\n\t\tvar uniqueId = new UniqueIdReader(\"wrongFieldName\").apply(description);\n\n\t\tassertEquals(description.getDisplayName(), uniqueId);\n\n\t\tvar logRecord = listener.stream(UniqueIdReader.class, Level.WARNING).findFirst();\n\t\tassertThat(logRecord).isPresent();\n\t\tassertThat(logRecord.get().getMessage()).isEqualTo(\n\t\t\t\"Could not read unique ID for Description; using display name instead: \" + description.getDisplayName());\n\t\tassertThat(logRecord.get().getThrown()).isInstanceOf(NoSuchFieldException.class);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/java/org/junit/vintage/engine/support/UniqueIdStringifierTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InvalidObjectException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectStreamException;\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Base64;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 4.12\n */\nclass UniqueIdStringifierTests {\n\n\t@Test\n\tvoid returnsReadableStringForKnownTypes() {\n\t\tvar stringifier = new UniqueIdStringifier();\n\n\t\tassertEquals(\"foo\", stringifier.apply(\"foo\"));\n\t\tassertEquals(\"42\", stringifier.apply(42));\n\t\tassertEquals(\"42\", stringifier.apply(42L));\n\t\tassertEquals(\"42.23\", stringifier.apply(42.23d));\n\t}\n\n\t@Test\n\tvoid serializesUnknownTypes() throws Exception {\n\t\tvar stringifier = new UniqueIdStringifier();\n\n\t\tvar serialized = stringifier.apply(new MyCustomId(42));\n\n\t\tvar deserializedObject = deserialize(decodeBase64(serialized));\n\t\tassertThat(deserializedObject).isInstanceOf(MyCustomId.class);\n\t\tassertEquals(42, ((MyCustomId) deserializedObject).value());\n\t}\n\n\t@Test\n\tvoid usesToStringWhenSerializationFails() {\n\t\tvar stringifier = new UniqueIdStringifier();\n\t\tvar serialized = stringifier.apply(new ClassWithErroneousSerialization());\n\n\t\tvar deserializedString = new String(decodeBase64(serialized), UniqueIdStringifier.CHARSET);\n\n\t\tassertEquals(\"value from toString()\", deserializedString);\n\t}\n\n\tprivate byte[] decodeBase64(String value) {\n\t\treturn Base64.getDecoder().decode(value.getBytes(UniqueIdStringifier.CHARSET));\n\t}\n\n\tprivate Object deserialize(byte[] bytes) throws Exception {\n\t\ttry (var inputStream = new ObjectInputStream(new ByteArrayInputStream(bytes))) {\n\t\t\treturn inputStream.readObject();\n\t\t}\n\t}\n\n\tprivate record MyCustomId(int value) implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t}\n\n\tprivate static class ClassWithErroneousSerialization implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t@Serial\n\t\tObject writeReplace() throws ObjectStreamException {\n\t\t\tthrow new InvalidObjectException(\"failed on purpose\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"value from toString()\";\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.extensions.autodetection.enabled=true\n"
  },
  {
    "path": "junit-vintage-engine/src/test/resources/log4j2-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\"\n\t\t\t   xmlns=\"https://logging.apache.org/xml/ns\"\n\t\t\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t   xsi:schemaLocation=\"https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd\">\n\t<Appenders>\n\t\t<Console name=\"Console\" target=\"SYSTEM_OUT\">\n\t\t\t<PatternLayout pattern=\"%d{HH:mm:ss.SSSSSS} [%-18t] %-5level %logger{1.} - %msg%n\"/>\n\t\t</Console>\n\t</Appenders>\n\t<Loggers>\n\t\t<Logger name=\"org.junit\" level=\"WARN\"/>\n\t\t<Logger name=\"org.junit.vintage.engine\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.listeners.discovery.LoggingLauncherDiscoveryListener\" level=\"OFF\"/>\n\t\t<Root level=\"ERROR\">\n\t\t\t<AppenderRef ref=\"Console\"/>\n\t\t</Root>\n\t</Loggers>\n</Configuration>\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/groovy/org/junit/vintage/engine/samples/spock/SpockTestCaseWithUnrolledAndRegularFeatureMethods.groovy",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.spock\n\nimport spock.lang.Specification\nimport spock.lang.Unroll\n\nclass SpockTestCaseWithUnrolledAndRegularFeatureMethods extends Specification {\n\n    @Unroll\n    def \"unrolled feature for #input\"() {\n        expect:\n        input == 42\n        where:\n        input << [23, 42]\n    }\n\n    def \"regular\"() {\n        expect:\n        true\n    }\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/platform/runner/JUnitPlatform.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.runner;\n\nimport java.util.List;\n\nimport org.junit.runner.Description;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runners.ParentRunner;\nimport org.junit.runners.model.InitializationError;\n\n/**\n * Dummy Runner class mimicking the one from the discontinued\n * {@code junit-platform-runner} module.\n */\npublic class JUnitPlatform extends ParentRunner<Void> {\n\n\tpublic JUnitPlatform(Class<?> testClass) throws InitializationError {\n\t\tsuper(testClass);\n\t}\n\n\t@Override\n\tprotected List<Void> getChildren() {\n\t\treturn List.of();\n\t}\n\n\t@Override\n\tprotected Description describeChild(Void child) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tprotected void runChild(Void child, RunNotifier notifier) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/PlainOldJavaClassWithoutAnyTestsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples;\n\n/**\n * @since 4.12\n */\npublic class PlainOldJavaClassWithoutAnyTestsTestCase {\n\n\tpublic void doSomething() {\n\t\t// no-op\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/AbstractJUnit3TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Assert;\n\n/**\n * @since 4.12\n */\npublic abstract class AbstractJUnit3TestCase extends TestCase {\n\n\tpublic void test() {\n\t\tAssert.fail(\"this test should not be run\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/IgnoredJUnit3TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\n\n/**\n * @since 4.12\n */\n@Ignore(\"testing\")\npublic class IgnoredJUnit3TestCase extends TestCase {\n\n\tpublic void test() {\n\t\tAssert.fail(\"this test should be ignored\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3ParallelSuiteWithSubsuites.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.extensions.ActiveTestSuite;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n@SuppressWarnings(\"JUnitMalformedDeclaration\")\npublic class JUnit3ParallelSuiteWithSubsuites extends TestCase {\n\tprivate final String arg;\n\n\tpublic JUnit3ParallelSuiteWithSubsuites(String name, String arg) {\n\t\tsuper(name);\n\t\tthis.arg = arg;\n\t}\n\n\tpublic void hello() {\n\t\tassertNotNull(arg);\n\t}\n\n\tpublic static TestSuite suite() {\n\t\tTestSuite root = new ActiveTestSuite(\"allTests\");\n\t\tvar case1 = new TestSuite(\"Case1\");\n\t\tcase1.addTest(new JUnit3ParallelSuiteWithSubsuites(\"hello\", \"world\"));\n\t\troot.addTest(case1);\n\t\tvar case2 = new TestSuite(\"Case2\");\n\t\tcase2.addTest(new JUnit3ParallelSuiteWithSubsuites(\"hello\", \"WORLD\"));\n\t\troot.addTest(case2);\n\t\treturn root;\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * @since 4.12\n */\npublic class JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails extends TestCase {\n\n\tpublic static junit.framework.Test suite() {\n\t\tvar suite = new TestSuite();\n\t\tsuite.addTestSuite(PlainJUnit3TestCaseWithSingleTestWhichFails.class);\n\t\treturn suite;\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit3SuiteWithSubsuites.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n@SuppressWarnings(\"JUnitMalformedDeclaration\")\npublic class JUnit3SuiteWithSubsuites extends TestCase {\n\tprivate final String arg;\n\n\tpublic JUnit3SuiteWithSubsuites(String name, String arg) {\n\t\tsuper(name);\n\t\tthis.arg = arg;\n\t}\n\n\tpublic void hello() {\n\t\tassertNotNull(arg);\n\t}\n\n\tpublic static TestSuite suite() {\n\t\tvar root = new TestSuite(\"allTests\");\n\t\tvar case1 = new TestSuite(\"Case1\");\n\t\tcase1.addTest(new JUnit3SuiteWithSubsuites(\"hello\", \"world\"));\n\t\troot.addTest(case1);\n\t\tvar case2 = new TestSuite(\"Case2\");\n\t\tcase2.addTest(new JUnit3SuiteWithSubsuites(\"hello\", \"WORLD\"));\n\t\troot.addTest(case2);\n\t\treturn root;\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/JUnit4SuiteWithIgnoredJUnit3TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n@RunWith(Suite.class)\n@SuiteClasses({ IgnoredJUnit3TestCase.class })\npublic class JUnit4SuiteWithIgnoredJUnit3TestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit3/PlainJUnit3TestCaseWithSingleTestWhichFails.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit3;\n\nimport junit.framework.TestCase;\n\nimport org.junit.Assert;\n\n/**\n * @since 4.12\n */\npublic class PlainJUnit3TestCaseWithSingleTestWhichFails extends TestCase {\n\n\tpublic void test() {\n\t\tAssert.fail(\"this test should fail\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Test;\n\npublic abstract class AbstractJUnit4TestCase {\n\n\t@Test\n\tpublic void theTest() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/AbstractJunit4TestCaseWithConstructorParameter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Test;\n\npublic abstract class AbstractJunit4TestCaseWithConstructorParameter {\n\n\tpublic AbstractJunit4TestCaseWithConstructorParameter(int parameter) {\n\n\t}\n\n\t@Test\n\tpublic void test() {\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CancellingTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.Assert.fail;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.platform.engine.CancellationToken;\n\npublic class CancellingTestCase {\n\n\tpublic static CancellationToken cancellationToken;\n\n\t@Before\n\tpublic void cancelExecution() {\n\t\trequireNonNull(cancellationToken).cancel();\n\t}\n\n\t@Test\n\tpublic void first() {\n\t\tfail();\n\t}\n\n\t@Test\n\tpublic void second() {\n\t\tfail();\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Categories.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\n/**\n * @since 4.12\n */\npublic class Categories {\n\n\tpublic interface Plain {\n\t}\n\n\tpublic interface Failing {\n\t}\n\n\tpublic interface Skipped {\n\t}\n\n\tpublic interface SkippedWithReason extends Skipped {\n\t}\n\n\tpublic interface Successful {\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/CompletelyDynamicTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.vintage.engine.samples.junit4.ConfigurableRunner.ChildCount;\n\n/**\n * Simulates a Spock 1.x test with only {@code @Unroll} feature methods.\n */\n@RunWith(DynamicRunner.class)\n@ChildCount(1)\npublic class CompletelyDynamicTestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConcreteJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\npublic class ConcreteJUnit4TestCase extends AbstractJUnit4TestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ConfigurableRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static java.util.stream.IntStream.range;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.runner.Description;\nimport org.junit.runner.Runner;\nimport org.junit.runner.notification.RunNotifier;\n\n/**\n * @since 5.1\n */\nabstract class ConfigurableRunner extends Runner {\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.TYPE)\n\tpublic @interface ChildCount {\n\n\t\tint value();\n\n\t}\n\n\tprotected final Class<?> testClass;\n\tprotected final List<Description> filteredChildren = new ArrayList<>();\n\n\tConfigurableRunner(Class<?> testClass) {\n\t\tthis.testClass = testClass;\n\t\tvar childCountAnnotation = testClass.getAnnotation(ChildCount.class);\n\t\tint childCount = Optional.ofNullable(childCountAnnotation).map(ChildCount::value).orElse(0);\n\t\t// @formatter:off\n\t\trange(0, childCount)\n\t\t\t\t.mapToObj(index -> Description.createTestDescription(testClass, \"Test #\" + index))\n\t\t\t\t.forEach(filteredChildren::add);\n\t\t// @formatter:on\n\t}\n\n\t@Override\n\tpublic Description getDescription() {\n\t\tvar suiteDescription = Description.createSuiteDescription(testClass);\n\t\tfilteredChildren.forEach(suiteDescription::addChild);\n\t\treturn suiteDescription;\n\t}\n\n\t@Override\n\tpublic void run(RunNotifier notifier) {\n\t\tfilteredChildren.forEach(child -> {\n\t\t\tnotifier.fireTestStarted(child);\n\t\t\tnotifier.fireTestFinished(child);\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/DynamicRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.Description;\nimport org.junit.runner.manipulation.Filter;\nimport org.junit.runner.manipulation.Filterable;\nimport org.junit.runner.manipulation.NoTestsRemainException;\n\npublic class DynamicRunner extends ConfigurableRunner implements Filterable {\n\n\tpublic DynamicRunner(Class<?> testClass) {\n\t\tsuper(testClass);\n\t}\n\n\t@Override\n\tpublic Description getDescription() {\n\t\treturn Description.createSuiteDescription(testClass);\n\t}\n\n\t@Override\n\tpublic void filter(Filter filter) throws NoTestsRemainException {\n\t\tfilteredChildren.removeIf(each -> !filter.shouldRun(each));\n\t\tif (filteredChildren.isEmpty()) {\n\t\t\tthrow new NoTestsRemainException();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EmptyIgnoredTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Ignore;\n\n@Ignore(\"empty\")\npublic class EmptyIgnoredTestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\nimport org.junit.experimental.categories.Category;\nimport org.junit.experimental.runners.Enclosed;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 4.12\n */\n@RunWith(Enclosed.class)\npublic class EnclosedJUnit4TestCase {\n\n\t@Category(Categories.Plain.class)\n\tpublic static class NestedClass {\n\n\t\t@Test\n\t\t@Category(Categories.Failing.class)\n\t\tpublic void failingTest() {\n\t\t\tfail(\"this test should fail\");\n\t\t}\n\n\t\t@Test\n\t\tpublic void successfulTest() {\n\t\t\tassertEquals(3, 1 + 2);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/EnclosedWithParameterizedChildrenJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport java.util.Arrays;\nimport java.util.Collection;\n\nimport org.junit.Test;\nimport org.junit.experimental.runners.Enclosed;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.Parameters;\n\n// Source: https://github.com/junit-team/junit-framework/issues/3083\n@RunWith(Enclosed.class)\npublic class EnclosedWithParameterizedChildrenJUnit4TestCase {\n\n\t@RunWith(Parameterized.class)\n\tpublic static class NestedTestCase1 {\n\n\t\t@Parameters\n\t\tpublic static Collection<Object[]> data() {\n\t\t\treturn Arrays.asList(new Object[] { 1, 2 }, new Object[] { 3, 4 });\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic NestedTestCase1(final int a, final int b) {\n\t\t}\n\n\t\t@Test\n\t\tpublic void test() {\n\t\t}\n\t}\n\n\t@RunWith(Parameterized.class)\n\tpublic static class NestedTestCase2 {\n\n\t\t@Parameters\n\t\tpublic static Collection<Object[]> data() {\n\t\t\treturn Arrays.asList(new Object[] { 1, 2 }, new Object[] { 3, 4 });\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tpublic NestedTestCase2(final int a, final int b) {\n\t\t}\n\n\t\t@Test\n\t\tpublic void test() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ExceptionThrowingRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.notification.RunNotifier;\n\n/**\n * @since 4.12\n */\npublic class ExceptionThrowingRunner extends ConfigurableRunner {\n\n\tpublic ExceptionThrowingRunner(Class<?> testClass) {\n\t\tsuper(testClass);\n\t}\n\n\t@Override\n\tpublic void run(RunNotifier notifier) {\n\t\tthrow new RuntimeException(\"Simulated exception in custom runner for \" + testClass.getName());\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\nimport static org.junit.runners.MethodSorters.NAME_ASCENDING;\n\nimport org.junit.FixMethodOrder;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\n@Ignore(\"complete class is ignored\")\n@FixMethodOrder(NAME_ASCENDING)\npublic class IgnoredJUnit4TestCase {\n\n\t@Test\n\tpublic void failingTest() {\n\t\tfail(\"this test is discovered, but skipped\");\n\t}\n\n\t@Test\n\tpublic void succeedingTest() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredJUnit4TestCaseWithNotFilterableRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Ignore;\n\n/**\n * @since 5.1\n */\n@Ignore\npublic class IgnoredJUnit4TestCaseWithNotFilterableRunner extends JUnit4TestCaseWithNotFilterableRunner {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/IgnoredParameterizedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport java.util.List;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.Parameter;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * @since 5.4.1\n */\n@RunWith(Parameterized.class)\npublic class IgnoredParameterizedTestCase {\n\n\t@Parameters(name = \"{0}\")\n\tpublic static Iterable<String> parameters() {\n\t\treturn List.of(\"foo\", \"bar\");\n\t}\n\n\t@Parameter\n\tpublic String value;\n\n\t@Test\n\t@Ignore\n\tpublic void test() {\n\t\t// never called\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelClassesTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.experimental.runners.Enclosed;\nimport org.junit.rules.TestWatcher;\nimport org.junit.runner.Description;\nimport org.junit.runner.RunWith;\n\n@RunWith(Enclosed.class)\npublic class JUnit4ParallelClassesTestCase {\n\n\tpublic static class AbstractBlockingTestCase {\n\n\t\tpublic static final Set<String> threadNames = ConcurrentHashMap.newKeySet();\n\t\tpublic static CountDownLatch countDownLatch;\n\n\t\t@Rule\n\t\tpublic final TestWatcher testWatcher = new TestWatcher() {\n\t\t\t@Override\n\t\t\tprotected void starting(Description description) {\n\t\t\t\tAbstractBlockingTestCase.threadNames.add(Thread.currentThread().getName());\n\t\t\t}\n\t\t};\n\n\t\t@Test\n\t\tpublic void test() throws Exception {\n\t\t\tcountDownAndBlock(countDownLatch);\n\t\t}\n\n\t\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\t\tprivate static void countDownAndBlock(CountDownLatch countDownLatch) throws InterruptedException {\n\t\t\tcountDownLatch.countDown();\n\t\t\tcountDownLatch.await(estimateSimulatedTestDurationInMilliseconds(), MILLISECONDS);\n\t\t}\n\n\t\tprivate static long estimateSimulatedTestDurationInMilliseconds() {\n\t\t\tvar runningInCi = Boolean.parseBoolean(System.getenv(\"CI\"));\n\t\t\treturn runningInCi ? 1000 : 100;\n\t\t}\n\t}\n\n\tpublic static class FirstClassTestCase extends AbstractBlockingTestCase {\n\t}\n\n\tpublic static class SecondClassTestCase extends AbstractBlockingTestCase {\n\t}\n\n\tpublic static class ThirdClassTestCase extends AbstractBlockingTestCase {\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParallelMethodsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.experimental.runners.Enclosed;\nimport org.junit.rules.TestWatcher;\nimport org.junit.runner.Description;\nimport org.junit.runner.RunWith;\n\n@RunWith(Enclosed.class)\npublic class JUnit4ParallelMethodsTestCase {\n\n\tpublic static class AbstractBlockingTestCase {\n\n\t\tpublic static final Set<String> threadNames = ConcurrentHashMap.newKeySet();\n\t\tpublic static CountDownLatch countDownLatch;\n\n\t\t@Rule\n\t\tpublic final TestWatcher testWatcher = new TestWatcher() {\n\t\t\t@Override\n\t\t\tprotected void starting(Description description) {\n\t\t\t\tAbstractBlockingTestCase.threadNames.add(Thread.currentThread().getName());\n\t\t\t}\n\t\t};\n\n\t\t@Test\n\t\tpublic void fistTest() throws Exception {\n\t\t\tcountDownAndBlock(countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tpublic void secondTest() throws Exception {\n\t\t\tcountDownAndBlock(countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tpublic void thirdTest() throws Exception {\n\t\t\tcountDownAndBlock(countDownLatch);\n\t\t}\n\n\t\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\t\tprivate static void countDownAndBlock(CountDownLatch countDownLatch) throws InterruptedException {\n\t\t\tcountDownLatch.countDown();\n\t\t\tcountDownLatch.await(estimateSimulatedTestDurationInMilliseconds(), MILLISECONDS);\n\t\t}\n\n\t\tprivate static long estimateSimulatedTestDurationInMilliseconds() {\n\t\t\tvar runningInCi = Boolean.parseBoolean(System.getenv(\"CI\"));\n\t\t\treturn runningInCi ? 1000 : 100;\n\t\t}\n\t}\n\n\tpublic static class FirstMethodTestCase extends JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase {\n\t}\n\n\tpublic static class SecondMethodTestCase extends JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase {\n\t}\n\n\tpublic static class ThirdMethodTestCase extends JUnit4ParallelMethodsTestCase.AbstractBlockingTestCase {\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4ParameterizedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * Test case used in {@link JUnit4ParameterizedTests}.\n *\n * @since 4.12\n */\n@RunWith(Parameterized.class)\npublic class JUnit4ParameterizedTestCase {\n\n\t@Parameters\n\tpublic static Object[] data() {\n\t\treturn new Object[] { 1, 2, 3 };\n\t}\n\n\tpublic JUnit4ParameterizedTestCase(int i) {\n\t}\n\n\t@Test\n\tpublic void test1() {\n\t\tfail(\"this test should fail\");\n\t}\n\n\t@Test\n\tpublic void endingIn_test1() {\n\t\tfail(\"this test should fail\");\n\t}\n\n\t@Test\n\tpublic void test1_atTheBeginning() {\n\t\tfail(\"this test should fail\");\n\t}\n\n\t@Test\n\tpublic void test2() {\n\t\t/* always succeeds */\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithFilterableChildRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 5.1\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4TestCaseWithNotFilterableRunner.class)\npublic class JUnit4SuiteOfSuiteWithFilterableChildRunner {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4SuiteWithIgnoredJUnit4TestCase.class)\npublic class JUnit4SuiteOfSuiteWithIgnoredJUnit4TestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.class)\npublic class JUnit4SuiteOfSuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.class)\npublic class JUnit4SuiteOfSuiteWithJUnit4TestCaseWithErrorInBeforeClass {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithExceptionThrowingRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.vintage.engine.samples.junit4.ConfigurableRunner.ChildCount;\n\n/**\n * @since 4.12\n */\n@RunWith(ExceptionThrowingRunner.class)\n@ChildCount(1)\npublic class JUnit4SuiteWithExceptionThrowingRunner {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithIgnoredJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(IgnoredJUnit4TestCase.class)\npublic class JUnit4SuiteWithIgnoredJUnit4TestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit3SuiteWithSingleTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\nimport org.junit.vintage.engine.samples.junit3.JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit3SuiteWithSingleTestCaseWithSingleTestWhichFails.class)\npublic class JUnit4SuiteWithJUnit3SuiteWithSingleTestCase {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4TestCaseWithAssumptionFailureInBeforeClass.class)\npublic class JUnit4SuiteWithJUnit4TestCaseWithAssumptionFailureInBeforeClass {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4TestCaseWithErrorInBeforeClass.class)\npublic class JUnit4SuiteWithJUnit4TestCaseWithErrorInBeforeClass {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n@RunWith(Suite.class)\n@SuiteClasses({ JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.class,\n\t\tPlainJUnit4TestCaseWithSingleTestWhichFails.class })\npublic class JUnit4SuiteWithJUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 5.6.2\n */\n@RunWith(Suite.class)\n@SuiteClasses(JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.class)\npublic class JUnit4SuiteWithJUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses(PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.class)\npublic class JUnit4SuiteWithPlainJUnit4TestCaseWithSingleTestWhichIsIgnored {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4SuiteWithTwoTestCases.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Suite;\nimport org.junit.runners.Suite.SuiteClasses;\n\n/**\n * @since 4.12\n */\n@RunWith(Suite.class)\n@SuiteClasses({ PlainJUnit4TestCaseWithTwoTestMethods.class, PlainJUnit4TestCaseWithSingleTestWhichFails.class })\npublic class JUnit4SuiteWithTwoTestCases {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithAssumptionFailureInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.AssumptionViolatedException;\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\npublic class JUnit4TestCaseWithAssumptionFailureInBeforeClass {\n\n\t@BeforeClass\n\tpublic static void failingBeforeClass() {\n\t\tthrow new AssumptionViolatedException(\"assumption violated\");\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tfail(\"this should never be called\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithDistinguishableOverloadedMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\nimport org.junit.experimental.theories.Theories;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 5.5\n */\n@RunWith(Theories.class)\npublic class JUnit4TestCaseWithDistinguishableOverloadedMethod {\n\n\t@Test\n\tpublic void test() {\n\t\ttest(\"foo\");\n\t}\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tprivate void test(String message) {\n\t\tfail(message);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorCollectorStoringMultipleFailures.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.hamcrest.core.IsNot.not;\nimport static org.hamcrest.core.StringContains.containsString;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ErrorCollector;\n\npublic class JUnit4TestCaseWithErrorCollectorStoringMultipleFailures {\n\t@Rule\n\tpublic ErrorCollector collector = new ErrorCollector();\n\n\t@Test\n\tpublic void example() {\n\t\tcollector.addError(new Throwable(\"first thing went wrong\"));\n\t\tcollector.addError(new Throwable(\"second thing went wrong\"));\n\t\tcollector.checkThat(getResult(), not(containsString(\"ERROR!\")));\n\t\t// all lines will run, and then a combined failure logged at the end.\n\t}\n\n\tprivate String getResult() {\n\t\treturn \"This is an ERROR!\";\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInAfterClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\nimport static org.junit.runners.MethodSorters.NAME_ASCENDING;\n\nimport org.junit.AfterClass;\nimport org.junit.FixMethodOrder;\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\n@FixMethodOrder(NAME_ASCENDING)\npublic class JUnit4TestCaseWithErrorInAfterClass {\n\n\t@AfterClass\n\tpublic static void failingAfterClass() {\n\t\tfail(\"error in @AfterClass\");\n\t}\n\n\t@Test\n\tpublic void failingTest() {\n\t\tfail(\"expected to fail\");\n\t}\n\n\t@Test\n\tpublic void succeedingTest() {\n\t\t// no-op\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithErrorInBeforeClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\npublic class JUnit4TestCaseWithErrorInBeforeClass {\n\n\t@BeforeClass\n\tpublic static void failingBeforeClass() {\n\t\tfail(\"something went wrong\");\n\t}\n\n\t@Test\n\tpublic void test() {\n\t\tfail(\"this should never be called\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithExceptionThrowingRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.RunWith;\nimport org.junit.vintage.engine.samples.junit4.ConfigurableRunner.ChildCount;\n\n/**\n * @since 4.12\n */\n@RunWith(ExceptionThrowingRunner.class)\n@ChildCount(0)\npublic class JUnit4TestCaseWithExceptionThrowingRunner {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n@RunWith(RunnerThatOnlyReportsFailures.class)\npublic class JUnit4TestCaseWithFailingDescriptionThatIsNotReportedAsFinished {\n\t@Test\n\tpublic void testWithMissingEvents() {\n\t\tfail(\"boom\");\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithIndistinguishableOverloadedMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.experimental.theories.DataPoint;\nimport org.junit.experimental.theories.Theories;\nimport org.junit.experimental.theories.Theory;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 4.12\n */\n@RunWith(Theories.class)\npublic class JUnit4TestCaseWithIndistinguishableOverloadedMethod {\n\n\t@DataPoint\n\tpublic static int MAGIC_NUMBER = 42;\n\n\t@Theory\n\tpublic void theory(int i) {\n\t\tfail(\"failing theory with single parameter\");\n\t}\n\n\t@Theory\n\tpublic void theory(int i, int j) {\n\t\tfail(\"failing theory with two parameters\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithNotFilterableRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Test;\nimport org.junit.experimental.categories.Category;\nimport org.junit.runner.RunWith;\nimport org.junit.vintage.engine.samples.junit4.ConfigurableRunner.ChildCount;\n\n/**\n * @since 5.1\n */\n@RunWith(NotFilterableRunner.class)\n@ChildCount(2)\n@Category(Categories.Successful.class)\npublic class JUnit4TestCaseWithNotFilterableRunner {\n\n\t@Test\n\tpublic void someTest() {\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 4.12\n */\n@Label(\"(TestClass)\")\n@RunWith(RunnerWithCustomUniqueIdsAndDisplayNames.class)\npublic class JUnit4TestCaseWithRunnerWithCustomUniqueIdsAndDisplayNames {\n\n\t@Test\n\t@Label(\"(TestMethod)\")\n\tpublic void test() {\n\t\tAssert.fail();\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.Description;\nimport org.junit.runner.RunWith;\nimport org.junit.runner.notification.RunNotifier;\n\n@RunWith(JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions.Runner.class)\npublic class JUnit4TestCaseWithRunnerWithDuplicateChangingChildDescriptions {\n\tpublic static class Runner extends org.junit.runner.Runner {\n\n\t\tprivate final Class<?> testClass;\n\n\t\tpublic Runner(Class<?> testClass) {\n\t\t\tthis.testClass = testClass;\n\t\t}\n\n\t\t@Override\n\t\tpublic Description getDescription() {\n\t\t\tvar suiteDescription = Description.createSuiteDescription(testClass);\n\t\t\tsuiteDescription.addChild(getContainerDescription(\"1st\"));\n\t\t\tsuiteDescription.addChild(getContainerDescription(\"2nd\"));\n\t\t\treturn suiteDescription;\n\t\t}\n\n\t\tprivate Description getContainerDescription(String name) {\n\t\t\tvar parent = Description.createSuiteDescription(name);\n\t\t\tparent.addChild(getLeafDescription());\n\t\t\tparent.addChild(getLeafDescription());\n\t\t\treturn parent;\n\t\t}\n\n\t\tprivate Description getLeafDescription() {\n\t\t\treturn Description.createTestDescription(testClass, \"leaf\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void run(RunNotifier notifier) {\n\t\t\tfor (var i = 0; i < 2; i++) {\n\t\t\t\tnotifier.fireTestIgnored(getLeafDescription());\n\t\t\t\tnotifier.fireTestStarted(getLeafDescription());\n\t\t\t\tnotifier.fireTestFinished(getLeafDescription());\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/Label.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\n\nimport java.lang.annotation.Retention;\n\n@Retention(RUNTIME)\n@interface Label {\n\tString value();\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/MalformedJUnit4TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\npublic class MalformedJUnit4TestCase {\n\n\t@Test\n\t@SuppressWarnings(\"TestMethodWithIncorrectSignature\") // intentionally not public\n\tvoid nonPublicTest() {\n\t\tfail(\"this should never be called\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/NotFilterableRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\n/**\n * @since 5.1\n */\npublic class NotFilterableRunner extends ConfigurableRunner {\n\n\tpublic NotFilterableRunner(Class<?> testClass) {\n\t\tsuper(testClass);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.Parameter;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * @since 4.12\n */\n@RunWith(Parameterized.class)\npublic class ParameterizedTestCase {\n\n\t@Parameters(name = \"{0}\")\n\tpublic static Iterable<String> parameters() {\n\t\treturn List.of(\"foo\", \"bar\");\n\t}\n\n\t@Parameter\n\tpublic String value;\n\n\t@Test\n\tpublic void test() {\n\t\tassertEquals(\"foo\", value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedTimingTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\n\nimport java.time.Instant;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.BeforeClass;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.AfterParam;\nimport org.junit.runners.Parameterized.BeforeParam;\nimport org.junit.runners.Parameterized.Parameter;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * @since 5.9\n */\n@RunWith(Parameterized.class)\npublic class ParameterizedTimingTestCase {\n\n\tpublic static Map<String, Instant> EVENTS = new LinkedHashMap<>();\n\n\t@BeforeClass\n\tpublic static void beforeClass() throws Exception {\n\t\tEVENTS.clear();\n\t}\n\n\t@BeforeParam\n\tpublic static void beforeParam(String param) throws Exception {\n\t\tEVENTS.put(\"beforeParam(\" + param + \")\", Instant.now());\n\t\tThread.sleep(100);\n\t}\n\n\t@AfterParam\n\tpublic static void afterParam(String param) throws Exception {\n\t\tThread.sleep(100);\n\t\tSystem.out.println(\"ParameterizedTimingTestCase.afterParam\");\n\t\tEVENTS.put(\"afterParam(\" + param + \")\", Instant.now());\n\t}\n\n\t@Parameters(name = \"{0}\")\n\tpublic static Iterable<String> parameters() {\n\t\treturn List.of(\"foo\", \"bar\");\n\t}\n\n\t@Parameter\n\tpublic String value;\n\n\t@Test\n\tpublic void test() {\n\t\tassertEquals(\"foo\", value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithAfterParamFailureTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.AfterParam;\nimport org.junit.runners.Parameterized.Parameter;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * @since 5.9\n */\n@RunWith(Parameterized.class)\npublic class ParameterizedWithAfterParamFailureTestCase {\n\n\t@AfterParam\n\tpublic static void afterParam() {\n\t\tfail();\n\t}\n\n\t@Parameters(name = \"{0}\")\n\tpublic static Iterable<String> parameters() {\n\t\treturn List.of(\"foo\", \"bar\");\n\t}\n\n\t@Parameter\n\tpublic String value;\n\n\t@Test\n\tpublic void test() {\n\t\tassertEquals(\"foo\", value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/ParameterizedWithBeforeParamFailureTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport java.util.List;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.Parameterized;\nimport org.junit.runners.Parameterized.BeforeParam;\nimport org.junit.runners.Parameterized.Parameter;\nimport org.junit.runners.Parameterized.Parameters;\n\n/**\n * @since 5.9\n */\n@RunWith(Parameterized.class)\npublic class ParameterizedWithBeforeParamFailureTestCase {\n\n\t@BeforeParam\n\tpublic static void beforeParam() {\n\t\tfail();\n\t}\n\n\t@Parameters(name = \"{0}\")\n\tpublic static Iterable<String> parameters() {\n\t\treturn List.of(\"foo\", \"bar\");\n\t}\n\n\t@Parameter\n\tpublic String value;\n\n\t@Test\n\tpublic void test() {\n\t\tassertEquals(\"foo\", value);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithFiveTestMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\nimport static org.junit.Assume.assumeFalse;\nimport static org.junit.runners.MethodSorters.NAME_ASCENDING;\n\nimport org.junit.FixMethodOrder;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.junit.experimental.categories.Category;\nimport org.junit.vintage.engine.samples.junit4.Categories.Failing;\nimport org.junit.vintage.engine.samples.junit4.Categories.Plain;\nimport org.junit.vintage.engine.samples.junit4.Categories.Skipped;\nimport org.junit.vintage.engine.samples.junit4.Categories.SkippedWithReason;\n\n/**\n * @since 4.12\n */\n@FixMethodOrder(NAME_ASCENDING)\n@Category(Plain.class)\npublic class PlainJUnit4TestCaseWithFiveTestMethods {\n\n\t@Test\n\tpublic void abortedTest() {\n\t\tassumeFalse(\"this test should be aborted\", true);\n\t}\n\n\t@Test\n\t@Category(Failing.class)\n\tpublic void failingTest() {\n\t\tfail(\"this test should fail\");\n\t}\n\n\t@Test\n\t@Ignore\n\t@Category(Skipped.class)\n\tpublic void ignoredTest1_withoutReason() {\n\t\tfail(\"this should never be called\");\n\t}\n\n\t@Test\n\t@Ignore(\"a custom reason\")\n\t@Category(SkippedWithReason.class)\n\tpublic void ignoredTest2_withReason() {\n\t\tfail(\"this should never be called\");\n\t}\n\n\t@Test\n\tpublic void successfulTest() {\n\t\tassertEquals(3, 1 + 2);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithLifecycleMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.After;\nimport org.junit.AfterClass;\nimport org.junit.Before;\nimport org.junit.BeforeClass;\nimport org.junit.FixMethodOrder;\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.junit.runners.MethodSorters;\n\n@FixMethodOrder(MethodSorters.NAME_ASCENDING)\npublic class PlainJUnit4TestCaseWithLifecycleMethods {\n\n\tpublic static final List<String> EVENTS = new ArrayList<>();\n\n\t@BeforeClass\n\tpublic static void beforeClass() {\n\t\tEVENTS.add(\"beforeClass\");\n\t}\n\n\t@Before\n\tpublic void before() {\n\t\tEVENTS.add(\"before\");\n\t}\n\n\t@Test\n\tpublic void failingTest() {\n\t\tEVENTS.add(\"failingTest\");\n\t\tfail();\n\t}\n\n\t@Test\n\t@Ignore(\"skipped\")\n\tpublic void skippedTest() {\n\t\tEVENTS.add(\"this should never ever be executed because the test is skipped\");\n\t}\n\n\t@Test\n\tpublic void succeedingTest() {\n\t\tEVENTS.add(\"succeedingTest\");\n\t}\n\n\t@After\n\tpublic void after() {\n\t\tEVENTS.add(\"after\");\n\t}\n\n\t@AfterClass\n\tpublic static void afterClass() {\n\t\tEVENTS.add(\"afterClass\");\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleInheritedTestWhichFails.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\n/**\n * @since 4.12\n */\npublic class PlainJUnit4TestCaseWithSingleInheritedTestWhichFails extends PlainJUnit4TestCaseWithSingleTestWhichFails {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichFails.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.fail;\n\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\npublic class PlainJUnit4TestCaseWithSingleTestWhichFails {\n\n\t@Test\n\tpublic void failingTest() {\n\t\tfail(\"this test should fail\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithSingleTestWhichIsIgnored.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Assert;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\n/**\n * @since 4.12\n */\npublic class PlainJUnit4TestCaseWithSingleTestWhichIsIgnored {\n\n\t@Test\n\t@Ignore(\"ignored test\")\n\tpublic void ignoredTest() {\n\t\tAssert.fail(\"this should not be called\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/PlainJUnit4TestCaseWithTwoTestMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\nimport static org.junit.runners.MethodSorters.NAME_ASCENDING;\n\nimport org.junit.FixMethodOrder;\nimport org.junit.Test;\nimport org.junit.experimental.categories.Category;\n\n/**\n * @since 4.12\n */\n@FixMethodOrder(NAME_ASCENDING)\npublic class PlainJUnit4TestCaseWithTwoTestMethods {\n\n\t@Test\n\tpublic void failingTest() {\n\t\tfail(\"this test should fail\");\n\t}\n\n\t@Test\n\t@Category(Categories.Successful.class)\n\tpublic void successfulTest() {\n\t\tassertEquals(3, 1 + 2);\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerThatOnlyReportsFailures.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.runner.notification.Failure;\nimport org.junit.runner.notification.RunNotifier;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.junit.runners.model.FrameworkMethod;\nimport org.junit.runners.model.InitializationError;\n\npublic class RunnerThatOnlyReportsFailures extends BlockJUnit4ClassRunner {\n\tpublic RunnerThatOnlyReportsFailures(Class<?> klass) throws InitializationError {\n\t\tsuper(klass);\n\t}\n\n\t@Override\n\tprotected void runChild(FrameworkMethod method, RunNotifier notifier) {\n\t\tvar statement = methodBlock(method);\n\t\ttry {\n\t\t\tstatement.evaluate();\n\t\t}\n\t\tcatch (Throwable e) {\n\t\t\tnotifier.fireTestFailure(new Failure(describeChild(method), e));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/RunnerWithCustomUniqueIdsAndDisplayNames.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport static org.junit.runner.Description.createTestDescription;\n\nimport java.io.Serial;\nimport java.io.Serializable;\nimport java.util.Objects;\nimport java.util.function.Supplier;\n\nimport org.junit.runner.Description;\nimport org.junit.runners.BlockJUnit4ClassRunner;\nimport org.junit.runners.model.Annotatable;\nimport org.junit.runners.model.FrameworkMethod;\nimport org.junit.runners.model.InitializationError;\n\n/**\n * @since 4.12\n */\npublic class RunnerWithCustomUniqueIdsAndDisplayNames extends BlockJUnit4ClassRunner {\n\n\tpublic RunnerWithCustomUniqueIdsAndDisplayNames(Class<?> klass) throws InitializationError {\n\t\tsuper(klass);\n\t}\n\n\t@Override\n\tprotected String getName() {\n\t\treturn getLabel(getTestClass(), super::getName);\n\t}\n\n\t@Override\n\tprotected Description describeChild(FrameworkMethod method) {\n\t\tvar testName = testName(method);\n\t\treturn createTestDescription(getTestClass().getJavaClass().getName(), testName, new CustomUniqueId(testName));\n\t}\n\n\t@Override\n\tprotected String testName(FrameworkMethod method) {\n\t\treturn getLabel(method, () -> super.testName(method));\n\t}\n\n\tprivate String getLabel(Annotatable element, Supplier<String> fallback) {\n\t\tvar label = element.getAnnotation(Label.class);\n\t\treturn label == null ? fallback.get() : label.value();\n\t}\n\n\tprivate record CustomUniqueId(String testName) implements Serializable {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj instanceof CustomUniqueId(String name)) {\n\t\t\t\treturn Objects.equals(this.testName, name);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/SingleFailingTheoryTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.Assert;\nimport org.junit.experimental.theories.Theories;\nimport org.junit.experimental.theories.Theory;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 4.12\n */\n@RunWith(Theories.class)\npublic class SingleFailingTheoryTestCase {\n\n\t@Theory\n\tpublic void theory() {\n\t\tAssert.fail(\"this theory should fail\");\n\t}\n\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/TestCaseRunWithJUnitPlatformRunner.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.junit.platform.runner.JUnitPlatform;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.runner.RunWith;\n\n/**\n * @since 4.12\n */\n@RunWith(JUnitPlatform.class)\n@SelectClasses(PlainJUnit4TestCaseWithSingleTestWhichFails.class)\npublic class TestCaseRunWithJUnitPlatformRunner {\n}\n"
  },
  {
    "path": "junit-vintage-engine/src/testFixtures/java/org/junit/vintage/engine/samples/junit4/package-info.java",
    "content": "\n@NullUnmarked\npackage org.junit.vintage.engine.samples.junit4;\n\nimport org.jspecify.annotations.NullUnmarked;\n"
  },
  {
    "path": "jupiter-tests/jupiter-tests.gradle.kts",
    "content": "import org.gradle.api.tasks.PathSensitivity.RELATIVE\nimport org.gradle.plugins.ide.eclipse.model.Classpath\nimport org.gradle.plugins.ide.eclipse.model.SourceFolder\n\nplugins {\n\tid(\"junitbuild.code-generator\")\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.junit4-compatibility\")\n\tid(\"junitbuild.testing-conventions\")\n\tgroovy\n}\n\ndependencies {\n\ttestImplementation(projects.junitJupiter)\n\ttestImplementation(projects.junitJupiterMigrationsupport)\n\ttestImplementation(projects.junitPlatformLauncher)\n\ttestImplementation(projects.junitPlatformSuiteEngine)\n\ttestImplementation(projects.junitPlatformTestkit)\n\ttestImplementation(testFixtures(projects.junitPlatformCommons))\n\ttestImplementation(kotlin(\"stdlib\"))\n\ttestImplementation(libs.jimfs)\n\ttestImplementation(libs.junit4)\n\ttestImplementation(libs.kotlinx.coroutines.core)\n\ttestImplementation(libs.groovy)\n\ttestImplementation(libs.memoryfilesystem)\n\ttestImplementation(testFixtures(projects.junitJupiterApi))\n\ttestImplementation(testFixtures(projects.junitJupiterEngine))\n\ttestImplementation(testFixtures(projects.junitPlatformLauncher))\n\ttestImplementation(testFixtures(projects.junitPlatformReporting))\n\n\ttestRuntimeOnly(kotlin(\"reflect\"))\n}\n\ntasks {\n\ttest {\n\t\tinputs.dir(\"src/test/resources\").withPathSensitivity(RELATIVE)\n\t\tsystemProperty(\"developmentVersion\", version)\n\t}\n\ttest_4_12 {\n\t\tfilter {\n\t\t\tincludeTestsMatching(\"org.junit.jupiter.migrationsupport.*\")\n\t\t}\n\t}\n}\n\neclipse {\n\tclasspath.file.whenMerged {\n\t\tthis as Classpath\n\t\tentries.filterIsInstance<SourceFolder>().forEach {\n\t\t\tif (it.path == \"src/test/java\") {\n\t\t\t\t// Exclude test classes that depend on compiled Kotlin code.\n\t\t\t\tit.excludes.add(\"**/AtypicalJvmMethodNameTests.java\")\n\t\t\t\tit.excludes.add(\"**/TestInstanceLifecycleKotlinTests.java\")\n\t\t\t}\n\t\t}\n\t}\n\tproject {\n\t\t// Remove Groovy Nature, since we don't require a Groovy plugin for Eclipse\n\t\t// in order for developers to work with the code base.\n\t\tnatures.removeAll { it == \"org.eclipse.jdt.groovy.core.groovyNature\" }\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreConditionTests.java.jte",
    "content": "@import java.util.List\n@import junitbuild.generator.model.JRE\n\n@param List<JRE> supportedJres\n@param List<JRE> supportedJresSortedByStringValue\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\n@for(var jre : supportedJresSortedByStringValue)<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()};\n@endfor<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link DisabledOnJreCondition}, generated from\n * {@code DisabledOnJreConditionTests.java.jte}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledOnJreIntegrationTests}.\n *\n * @since 5.1\n */\nclass DisabledOnJreConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String JAVA_VERSION = System.getProperty(\"java.version\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new DisabledOnJreCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledOnJreIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@DisabledOnJre is not present\");\n\t}\n\n\t/**\n\t * @see DisabledOnJreIntegrationTests#missingVersionDeclaration()\n\t */\n\t@Test\n\tvoid missingVersionDeclaration() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"You must declare at least one JRE or version in @DisabledOnJre\");\n\t}\n\n\t/**\n\t * @see DisabledOnJreIntegrationTests#jreUndefined()\n\t */\n\t@Test\n\tvoid jreUndefined() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"JRE.UNDEFINED is not supported in @DisabledOnJre\");\n\t}\n\n\t/**\n\t * @see DisabledOnJreIntegrationTests#version7()\n\t */\n\t@Test\n\tvoid version7() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"Version [7] in @DisabledOnJre must be greater than or equal to 8\");\n\t}\n\n\t/**\n\t * @see DisabledOnJreIntegrationTests#disabledOnAllJavaVersions()\n\t */\n\t@Test\n\tvoid disabledOnAllJavaVersions() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(true);\n\t\tassertCustomDisabledReasonIs(\"Disabled on every JRE\");\n\t}\n@for(var jre : supportedJres)\n\t/**\n\t * @see DisabledOnJreIntegrationTests#jre${jre.getVersion()}()\n\t */\n\t@Test\n\tvoid jre${jre.getVersion()}() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(onJava${jre.getVersion()}());\n\t}\n@endfor<%--\n--%>@for(var jre : supportedJres)\n\t/**\n\t * @see DisabledOnJreIntegrationTests#version${jre.getVersion()}()\n\t */\n\t@Test\n\tvoid version${jre.getVersion()}() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(onJava${jre.getVersion()}());\n\t}\n@endfor\n\t/**\n\t * @see DisabledOnJreIntegrationTests#other()\n\t */\n\t@Test\n\tvoid other() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(!onKnownVersion());\n\t}\n\n\tprivate void assertDisabledOnCurrentJreIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t\telse {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/DisabledOnJreIntegrationTests.java.jte",
    "content": "@import java.util.List\n@import junitbuild.generator.model.JRE\n\n@param List<JRE> allJres\n@param List<JRE> supportedJres\n@param List<JRE> supportedJresSortedByStringValue\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n@for(var jre : supportedJresSortedByStringValue)<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()};\n@endfor<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link DisabledOnJre @DisabledOnJre}, generated from\n * {@code DisabledOnJreIntegrationTests.java.jte}.\n *\n * @since 5.1\n */\nclass DisabledOnJreIntegrationTests {\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledOnJre\n\tvoid missingVersionDeclaration() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledOnJre(JRE.UNDEFINED)\n\tvoid jreUndefined() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledOnJre(value = JRE.JAVA_17, versions = { 21, 7 })\n\tvoid version7() {\n\t}\n\n\t@SuppressWarnings({ \"removal\", \"deprecation\" })\n\t@Test\n\t@DisabledOnJre(disabledReason = \"Disabled on every JRE\", value = { //\n@for(var jre : allJres)<%--\n--%>\t\t\tJRE.JAVA_${jre.getVersion()}, //\n@endfor<%--\n--%>\t\t\tJRE.OTHER //\n\t})\n\tvoid disabledOnAllJavaVersions() {\n\t\tfail(\"should be disabled\");\n\t}\n@for(var jre : supportedJres)\n\t@Test\n\t@DisabledOnJre(JRE.JAVA_${jre.getVersion()})\n\tvoid jre${jre.getVersion()}() {\n\t\tassertFalse(onJava${jre.getVersion()}());\n\t}\n@endfor<%--\n--%>@for(var jre : supportedJres)\n\t@Test\n\t@DisabledOnJre(versions = ${jre.getVersion()})\n\tvoid version${jre.getVersion()}() {\n\t\tassertFalse(onJava${jre.getVersion()}());\n\t}\n@endfor\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\t@DisabledOnJre(JRE.OTHER)\n\tvoid other() {\n\t\tassertTrue(onKnownVersion());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreConditionTests.java.jte",
    "content": "@import java.util.List\n@import junitbuild.generator.model.JRE\n\n@param List<JRE> supportedJres\n@param List<JRE> supportedJresSortedByStringValue\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\n@for(var jre : supportedJresSortedByStringValue)<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()};\n@endfor<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link EnabledOnJreCondition}, generated from\n * {@code EnabledOnJreConditionTests.java.jte}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledOnJreIntegrationTests}.\n *\n * @since 5.1\n */\nclass EnabledOnJreConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String JAVA_VERSION = System.getProperty(\"java.version\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new EnabledOnJreCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledOnJreIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledOnJreIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@EnabledOnJre is not present\");\n\t}\n\n\t/**\n\t * @see EnabledOnJreIntegrationTests#missingVersionDeclaration()\n\t */\n\t@Test\n\tvoid missingVersionDeclaration() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"You must declare at least one JRE or version in @EnabledOnJre\");\n\t}\n\n\t/**\n\t * @see EnabledOnJreIntegrationTests#jreUndefined()\n\t */\n\t@Test\n\tvoid jreUndefined() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"JRE.UNDEFINED is not supported in @EnabledOnJre\");\n\t}\n\n\t/**\n\t * @see EnabledOnJreIntegrationTests#version7()\n\t */\n\t@Test\n\tvoid version7() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"Version [7] in @EnabledOnJre must be greater than or equal to 8\");\n\t}\n\n\t/**\n\t * @see EnabledOnJreIntegrationTests#enabledOnAllJavaVersions()\n\t */\n\t@Test\n\tvoid enabledOnAllJavaVersions() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(true);\n\t}\n@for(var jre : supportedJres)\n\t/**\n\t * @see EnabledOnJreIntegrationTests#jre${jre.getVersion()}()\n\t */\n\t@Test\n\tvoid jre${jre.getVersion()}() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava${jre.getVersion()}());\n\t}\n@endfor<%--\n--%>@for(var jre : supportedJres)\n\t/**\n\t * @see EnabledOnJreIntegrationTests#version${jre.getVersion()}()\n\t */\n\t@Test\n\tvoid version${jre.getVersion()}() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava${jre.getVersion()}());\n\t}\n@endfor\n\t/**\n\t * @see EnabledOnJreIntegrationTests#other()\n\t */\n\t@Test\n\tvoid other() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(!onKnownVersion());\n\t\tassertCustomDisabledReasonIs(\"Disabled on almost every JRE\");\n\t}\n\n\tprivate void assertEnabledOnCurrentJreIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t\telse {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/templates/resources/test/org/junit/jupiter/api/condition/EnabledOnJreIntegrationTests.java.jte",
    "content": "@import java.util.List\n@import junitbuild.generator.model.JRE\n\n@param List<JRE> allJres\n@param List<JRE> supportedJres\n@param List<JRE> supportedJresSortedByStringValue\n@param String licenseHeader\n${licenseHeader}\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n@for(var jre : supportedJresSortedByStringValue)<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava${jre.getVersion()};\n@endfor<%--\n--%>import static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link EnabledOnJre @EnabledOnJre}, generated from\n * {@code EnabledOnJreIntegrationTests.java.jte}.\n *\n * @since 5.1\n */\nclass EnabledOnJreIntegrationTests {\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledOnJre\n\tvoid missingVersionDeclaration() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledOnJre(JRE.UNDEFINED)\n\tvoid jreUndefined() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledOnJre(value = JRE.JAVA_17, versions = { 21, 7 })\n\tvoid version7() {\n\t}\n\n\t@SuppressWarnings({ \"removal\", \"deprecation\" })\n\t@Test\n\t@EnabledOnJre({ //\n@for(var jre : allJres)<%--\n--%>\t\t\tJRE.JAVA_${jre.getVersion()}, //\n@endfor<%--\n--%>\t\t\tJRE.OTHER //\n\t})\n\tvoid enabledOnAllJavaVersions() {\n\t}\n@for(var jre : supportedJres)\n\t@Test\n\t@EnabledOnJre(JRE.JAVA_${jre.getVersion()})\n\tvoid jre${jre.getVersion()}() {\n\t\tassertTrue(onJava${jre.getVersion()}());\n\t}\n@endfor<%--\n--%>@for(var jre : supportedJres)\n\t@Test\n\t@EnabledOnJre(versions = ${jre.getVersion()})\n\tvoid version${jre.getVersion()}() {\n\t\tassertTrue(onJava${jre.getVersion()}());\n\t}\n@endfor\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\t@EnabledOnJre(value = JRE.OTHER, disabledReason = \"Disabled on almost every JRE\")\n\tvoid other() {\n\t\tassertFalse(onKnownVersion());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertEqualsTests.groovy",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api\n\nimport java.util.function.Supplier\nimport static org.junit.jupiter.api.Assertions.assertEquals\nimport static org.junit.jupiter.api.PrimitiveAndWrapperTypeHelpers.*\n\nclass GroovyAssertEqualsTests {\n\n    Supplier<String> supplier = { '' }\n\n    @Test\n    void \"null references can be passed to assertEquals\"() {\n        Object null1 = null\n        Object null2 = null\n\n        assertEquals(null1, null)\n        assertEquals(null, null2)\n        assertEquals(null1, null2)\n    }\n\n    @Test\n    void \"integers can be passed to assertEquals\"() {\n        assertEquals(i(42), i(42))\n        assertEquals(i(42), I(42))\n        assertEquals(I(42), i(42))\n        assertEquals(I(42), I(42))\n\n        assertEquals(i(42), i(42), '')\n        assertEquals(i(42), I(42), '')\n        assertEquals(I(42), i(42), '')\n        assertEquals(I(42), I(42), '')\n\n        assertEquals(i(42), i(42), supplier)\n        assertEquals(i(42), I(42), supplier)\n        assertEquals(I(42), i(42), supplier)\n        assertEquals(I(42), I(42), supplier)\n    }\n\n    @Test\n    void \"floats can be passed to assertEquals\"() {\n        assertEquals(f(42), f(42))\n        assertEquals(f(42), F(42))\n        assertEquals(F(42), f(42))\n        assertEquals(F(42), F(42))\n\n        assertEquals(f(42), f(42), '')\n        assertEquals(f(42), F(42), '')\n        assertEquals(F(42), f(42), '')\n        assertEquals(F(42), F(42), '')\n\n        assertEquals(f(42), f(42), supplier)\n        assertEquals(f(42), F(42), supplier)\n        assertEquals(F(42), f(42), supplier)\n        assertEquals(F(42), F(42), supplier)\n    }\n\n    @Test\n    void \"floats can be passed to assertEquals with delta\"() {\n        assertEquals(f(42), f(42), 0.01f)\n        assertEquals(f(42), F(42), 0.01f)\n        assertEquals(F(42), f(42), 0.01f)\n        assertEquals(F(42), F(42), 0.01f)\n\n        assertEquals(f(42), f(42), 0.01f, '')\n        assertEquals(f(42), F(42), 0.01f, '')\n        assertEquals(F(42), f(42), 0.01f, '')\n        assertEquals(F(42), F(42), 0.01f, '')\n\n        assertEquals(f(42), f(42), 0.01f, supplier)\n        assertEquals(f(42), F(42), 0.01f, supplier)\n        assertEquals(F(42), f(42), 0.01f, supplier)\n        assertEquals(F(42), F(42), 0.01f, supplier)\n    }\n\n    @Test\n    void \"bytes can be passed to assertEquals\"() {\n        assertEquals(b(42), b(42))\n        assertEquals(b(42), B(42))\n        assertEquals(B(42), b(42))\n        assertEquals(B(42), B(42))\n\n        assertEquals(b(42), b(42), '')\n        assertEquals(b(42), B(42), '')\n        assertEquals(B(42), b(42), '')\n        assertEquals(B(42), B(42), '')\n\n        assertEquals(b(42), b(42), supplier)\n        assertEquals(b(42), B(42), supplier)\n        assertEquals(B(42), b(42), supplier)\n        assertEquals(B(42), B(42), supplier)\n    }\n\n    @Test\n    void \"doubles can be passed to assertEquals\"() {\n        assertEquals(d(42), d(42))\n        assertEquals(d(42), D(42))\n        assertEquals(D(42), d(42))\n        assertEquals(D(42), D(42))\n\n        assertEquals(d(42), d(42), '')\n        assertEquals(d(42), D(42), '')\n        assertEquals(D(42), d(42), '')\n        assertEquals(D(42), D(42), '')\n\n        assertEquals(d(42), d(42), supplier)\n        assertEquals(d(42), D(42), supplier)\n        assertEquals(D(42), d(42), supplier)\n        assertEquals(D(42), D(42), supplier)\n    }\n\n    @Test\n    void \"doubles can be passed to assertEquals with delta\"() {\n        assertEquals(d(42), d(42), 0.01d)\n        assertEquals(d(42), D(42), 0.01d)\n        assertEquals(D(42), d(42), 0.01d)\n        assertEquals(D(42), D(42), 0.01d)\n\n        assertEquals(d(42), d(42), 0.01d, '')\n        assertEquals(d(42), D(42), 0.01d, '')\n        assertEquals(D(42), d(42), 0.01d, '')\n        assertEquals(D(42), D(42), 0.01d, '')\n\n        assertEquals(d(42), d(42), 0.01d, supplier)\n        assertEquals(d(42), D(42), 0.01d, supplier)\n        assertEquals(D(42), d(42), 0.01d, supplier)\n        assertEquals(D(42), D(42), 0.01d, supplier)\n    }\n\n    @Test\n    void \"chars can be passed to assertEquals\"() {\n        assertEquals(c(42), c(42))\n        assertEquals(c(42), C(42))\n        assertEquals(C(42), c(42))\n        assertEquals(C(42), C(42))\n\n        assertEquals(c(42), c(42), '')\n        assertEquals(c(42), C(42), '')\n        assertEquals(C(42), c(42), '')\n        assertEquals(C(42), C(42), '')\n\n        assertEquals(c(42), c(42), supplier)\n        assertEquals(c(42), C(42), supplier)\n        assertEquals(C(42), c(42), supplier)\n        assertEquals(C(42), C(42), supplier)\n    }\n\n    @Test\n    void \"longs can be passed to assertEquals\"() {\n        assertEquals(l(42), l(42))\n        assertEquals(l(42), L(42))\n        assertEquals(L(42), l(42))\n        assertEquals(L(42), L(42))\n\n        assertEquals(l(42), l(42), '')\n        assertEquals(l(42), L(42), '')\n        assertEquals(L(42), l(42), '')\n        assertEquals(L(42), L(42), '')\n\n        assertEquals(l(42), l(42), supplier)\n        assertEquals(l(42), L(42), supplier)\n        assertEquals(L(42), l(42), supplier)\n        assertEquals(L(42), L(42), supplier)\n    }\n\n    @Test\n    void \"shorts can be passed to assertEquals\"() {\n        assertEquals(s(42), s(42))\n        assertEquals(s(42), S(42))\n        assertEquals(S(42), s(42))\n        assertEquals(S(42), S(42))\n\n        assertEquals(s(42), s(42), '')\n        assertEquals(s(42), S(42), '')\n        assertEquals(S(42), s(42), '')\n        assertEquals(S(42), S(42), '')\n\n        assertEquals(s(42), s(42), supplier)\n        assertEquals(s(42), S(42), supplier)\n        assertEquals(S(42), s(42), supplier)\n        assertEquals(S(42), S(42), supplier)\n    }\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/groovy/org/junit/jupiter/api/GroovyAssertNotEqualsTests.groovy",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api\n\nimport org.opentest4j.AssertionFailedError\n\nimport java.util.function.Supplier\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals\nimport static org.junit.jupiter.api.Assertions.assertThrows\nimport static org.junit.jupiter.api.PrimitiveAndWrapperTypeHelpers.*\n\nclass GroovyAssertNotEqualsTests {\n\n    Supplier<String> supplier = { '' }\n\n    @Test\n    void \"null references can be passed to assertNotEquals\"() {\n        Object null1 = null\n        Object null2 = null\n\n        assertThrows(AssertionFailedError, { assertNotEquals(null1, null) } )\n        assertThrows(AssertionFailedError, { assertNotEquals(null, null2) } )\n        assertThrows(AssertionFailedError, { assertNotEquals(null1, null2) } )\n    }\n\n    @Test\n    void \"integers can be passed to assertNotEquals\"() {\n        assertNotEquals(i(42), i(2))\n        assertNotEquals(i(42), I(2))\n        assertNotEquals(I(42), i(2))\n        assertNotEquals(I(42), I(2))\n\n        assertNotEquals(i(42), i(2), '')\n        assertNotEquals(i(42), I(2), '')\n        assertNotEquals(I(42), i(2), '')\n        assertNotEquals(I(42), I(2), '')\n\n        assertNotEquals(i(42), i(2), supplier)\n        assertNotEquals(i(42), I(2), supplier)\n        assertNotEquals(I(42), i(2), supplier)\n        assertNotEquals(I(42), I(2), supplier)\n    }\n\n    @Test\n    void \"floats can be passed to assertNotEquals\"() {\n        assertNotEquals(f(42), f(2))\n        assertNotEquals(f(42), F(2))\n        assertNotEquals(F(42), f(2))\n        assertNotEquals(F(42), F(2))\n\n        assertNotEquals(f(42), f(2), '')\n        assertNotEquals(f(42), F(2), '')\n        assertNotEquals(F(42), f(2), '')\n        assertNotEquals(F(42), F(2), '')\n\n        assertNotEquals(f(42), f(2), supplier)\n        assertNotEquals(f(42), F(2), supplier)\n        assertNotEquals(F(42), f(2), supplier)\n        assertNotEquals(F(42), F(2), supplier)\n    }\n\n    @Test\n    void \"floats can be passed to assertNotEquals with delta\"() {\n        assertNotEquals(f(42), f(2), 0.01f)\n        assertNotEquals(f(42), F(2), 0.01f)\n        assertNotEquals(F(42), f(2), 0.01f)\n        assertNotEquals(F(42), F(2), 0.01f)\n\n        assertNotEquals(f(42), f(2), 0.01f, '')\n        assertNotEquals(f(42), F(2), 0.01f, '')\n        assertNotEquals(F(42), f(2), 0.01f, '')\n        assertNotEquals(F(42), F(2), 0.01f, '')\n\n        assertNotEquals(f(42), f(2), 0.01f, supplier)\n        assertNotEquals(f(42), F(2), 0.01f, supplier)\n        assertNotEquals(F(42), f(2), 0.01f, supplier)\n        assertNotEquals(F(42), F(2), 0.01f, supplier)\n    }\n\n    @Test\n    void \"bytes can be passed to assertNotEquals\"() {\n        assertNotEquals(b(42), b(2))\n        assertNotEquals(b(42), B(2))\n        assertNotEquals(B(42), b(2))\n        assertNotEquals(B(42), B(2))\n\n        assertNotEquals(b(42), b(2), '')\n        assertNotEquals(b(42), B(2), '')\n        assertNotEquals(B(42), b(2), '')\n        assertNotEquals(B(42), B(2), '')\n\n        assertNotEquals(b(42), b(2), supplier)\n        assertNotEquals(b(42), B(2), supplier)\n        assertNotEquals(B(42), b(2), supplier)\n        assertNotEquals(B(42), B(2), supplier)\n    }\n\n    @Test\n    void \"doubles can be passed to assertNotEquals\"() {\n        assertNotEquals(d(42), d(2))\n        assertNotEquals(d(42), D(2))\n        assertNotEquals(D(42), d(2))\n        assertNotEquals(D(42), D(2))\n\n        assertNotEquals(d(42), d(2), '')\n        assertNotEquals(d(42), D(2), '')\n        assertNotEquals(D(42), d(2), '')\n        assertNotEquals(D(42), D(2), '')\n\n        assertNotEquals(d(42), d(2), supplier)\n        assertNotEquals(d(42), D(2), supplier)\n        assertNotEquals(D(42), d(2), supplier)\n        assertNotEquals(D(42), D(2), supplier)\n    }\n\n    @Test\n    void \"doubles can be passed to assertNotEquals with delta\"() {\n        assertNotEquals(d(42), d(2), 0.01d)\n        assertNotEquals(d(42), D(2), 0.01d)\n        assertNotEquals(D(42), d(2), 0.01d)\n        assertNotEquals(D(42), D(2), 0.01d)\n\n        assertNotEquals(d(42), d(2), 0.01d, '')\n        assertNotEquals(d(42), D(2), 0.01d, '')\n        assertNotEquals(D(42), d(2), 0.01d, '')\n        assertNotEquals(D(42), D(2), 0.01d, '')\n\n        assertNotEquals(d(42), d(2), 0.01d, supplier)\n        assertNotEquals(d(42), D(2), 0.01d, supplier)\n        assertNotEquals(D(42), d(2), 0.01d, supplier)\n        assertNotEquals(D(42), D(2), 0.01d, supplier)\n    }\n\n    @Test\n    void \"chars can be passed to assertNotEquals\"() {\n        assertNotEquals(c(42), c(2))\n        assertNotEquals(c(42), C(2))\n        assertNotEquals(C(42), c(2))\n        assertNotEquals(C(42), C(2))\n\n        assertNotEquals(c(42), c(2), '')\n        assertNotEquals(c(42), C(2), '')\n        assertNotEquals(C(42), c(2), '')\n        assertNotEquals(C(42), C(2), '')\n\n        assertNotEquals(c(42), c(2), supplier)\n        assertNotEquals(c(42), C(2), supplier)\n        assertNotEquals(C(42), c(2), supplier)\n        assertNotEquals(C(42), C(2), supplier)\n    }\n\n    @Test\n    void \"longs can be passed to assertNotEquals\"() {\n        assertNotEquals(l(42), l(2))\n        assertNotEquals(l(42), L(2))\n        assertNotEquals(L(42), l(2))\n        assertNotEquals(L(42), L(2))\n\n        assertNotEquals(l(42), l(2), '')\n        assertNotEquals(l(42), L(2), '')\n        assertNotEquals(L(42), l(2), '')\n        assertNotEquals(L(42), L(2), '')\n\n        assertNotEquals(l(42), l(2), supplier)\n        assertNotEquals(l(42), L(2), supplier)\n        assertNotEquals(L(42), l(2), supplier)\n        assertNotEquals(L(42), L(2), supplier)\n    }\n\n    @Test\n    void \"shorts can be passed to assertNotEquals\"() {\n        assertNotEquals(s(42), s(2))\n        assertNotEquals(s(42), S(2))\n        assertNotEquals(S(42), s(2))\n        assertNotEquals(S(42), S(2))\n\n        assertNotEquals(s(42), s(2), '')\n        assertNotEquals(s(42), S(2), '')\n        assertNotEquals(S(42), s(2), '')\n        assertNotEquals(S(42), S(2), '')\n\n        assertNotEquals(s(42), s(2), supplier)\n        assertNotEquals(s(42), S(2), supplier)\n        assertNotEquals(S(42), s(2), supplier)\n        assertNotEquals(S(42), S(2), supplier)\n    }\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/groovy/org/junit/jupiter/api/PrimitiveAndWrapperTypeHelpers.groovy",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api\n\nclass PrimitiveAndWrapperTypeHelpers {\n\n    static char c(int number) {\n        return (char) number\n    }\n\n    static Character C(int number) {\n        return Character.valueOf((char) number)\n    }\n\n    static byte b(int number) {\n        return (byte) number\n    }\n\n    static Byte B(int number) {\n        return Byte.valueOf((byte) number)\n    }\n\n    static double d(int number) {\n        return (double) number\n    }\n\n    static Double D(int number) {\n        return Double.valueOf((double) number)\n    }\n\n    static float f(int number) {\n        return (float) number\n    }\n\n    static Float F(int number) {\n        return Float.valueOf((float) number)\n    }\n\n    static long l(int number) {\n        return (long) number\n    }\n\n    static Long L(int number) {\n        return Long.valueOf( (long) number)\n    }\n\n    static short s(int number) {\n        return (short) number\n    }\n\n    static Short S(int number) {\n        return Short.valueOf( (short) number)\n    }\n\n    static int i(int number) {\n        return  number\n    }\n\n    static Integer I(int number) {\n        return Integer.valueOf( number)\n    }\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/DefaultPackageTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Simple test case that is used to verify proper support for classpath scanning\n * within the <em>default</em> package.\n *\n * @since 5.0\n */\n@Disabled(\"Only used reflectively by other tests\")\nclass DefaultPackageTestCase {\n\n\t@Test\n\tvoid test() {\n\t\t// do nothing\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/example/B_TestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage example;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\n\n/**\n * @since 5.8\n */\npublic class B_TestCase {\n\n\tpublic static List<String> callSequence;\n\n\t@BeforeEach\n\tvoid trackInvocations(TestInfo testInfo) {\n\t\tif (callSequence != null) {\n\t\t\tcallSequence.add(testInfo.getTestClass().get().getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid a() {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/JupiterTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * Test suite for the JUnit Jupiter programming model, extension model, and\n * {@code TestEngine} implementation.\n *\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 5.0\n */\n@Suite\n@SelectPackages(\"org.junit.jupiter\")\n@IncludeClassNamePatterns(\".*Tests?\")\n@IncludeEngines(\"junit-jupiter\")\nclass JupiterTestSuite {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedExceptionTypes;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\n\nimport java.io.IOException;\nimport java.util.Collection;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.MultipleFailuresError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertAllAssertionsTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid assertAllWithNullExecutableArray() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"executables array\", () -> assertAll((Executable[]) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid assertAllWithNullExecutableCollection() {\n\t\tassertPreconditionViolationNotNullFor(\"executables collection\", () -> assertAll((Collection<Executable>) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid assertAllWithNullExecutableStream() {\n\t\tassertPreconditionViolationNotNullFor(\"executables stream\", () -> assertAll((Stream<Executable>) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid assertAllWithNullInExecutableArray() {\n\t\tassertPreconditionViolationNotNullFor(\"individual executables\", () -> assertAll((Executable) null));\n\t}\n\n\t@Test\n\tvoid assertAllWithNullInExecutableCollection() {\n\t\tassertPreconditionViolationNotNullFor(\"individual executables\", () -> assertAll(asList((Executable) null)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid assertAllWithNullInExecutableStream() {\n\t\tassertPreconditionViolationNotNullFor(\"individual executables\", () -> assertAll(Stream.of((Executable) null)));\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutablesThatDoNotThrowExceptions() {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertTrue(true),\n\t\t\t() -> assertFalse(false)\n\t\t);\n\t\tassertAll(\"heading\",\n\t\t\t() -> assertTrue(true),\n\t\t\t() -> assertFalse(false)\n\t\t);\n\t\tassertAll(asList(\n\t\t\t() -> assertTrue(true),\n\t\t\t() -> assertFalse(false)\n\t\t));\n\t\tassertAll(\"heading\", asList(\n\t\t\t() -> assertTrue(true),\n\t\t\t() -> assertFalse(false)\n\t\t));\n\t\tassertAll(Stream.of(\n\t\t\t\t() -> assertTrue(true),\n\t\t\t\t() -> assertFalse(false)\n\t\t));\n\t\tassertAll(\"heading\", Stream.of(\n\t\t\t\t() -> assertTrue(true),\n\t\t\t\t() -> assertFalse(false)\n\t\t));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutablesThatThrowAssertionErrors() {\n\t\t// @formatter:off\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () ->\n\t\t\tassertAll(\n\t\t\t\tAssertions::fail,\n\t\t\t\tAssertions::fail\n\t\t\t)\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError.class, AssertionFailedError.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithCollectionOfExecutablesThatThrowAssertionErrors() {\n\t\t// @formatter:off\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () ->\n\t\t\tassertAll(asList(\n\t\t\t\tAssertions::fail,\n\t\t\t\tAssertions::fail\n\t\t\t))\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError.class, AssertionFailedError.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithStreamOfExecutablesThatThrowAssertionErrors() {\n\t\t// @formatter:off\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () ->\n\t\t\tassertAll(Stream.of(\n\t\t\t\tAssertions::fail,\n\t\t\t\tAssertions::fail\n\t\t\t))\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError.class, AssertionFailedError.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutableThatThrowsThrowable() {\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll(() -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}));\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, EnigmaThrowable.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutableThatThrowsCheckedException() {\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll(() -> {\n\t\t\tthrow new IOException();\n\t\t}));\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, IOException.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutableThatThrowsRuntimeException() {\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class, () -> assertAll(() -> {\n\t\t\tthrow new IllegalStateException();\n\t\t}));\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, IllegalStateException.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutableThatThrowsError() {\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class,\n\t\t\t() -> assertAll(AssertionTestUtils::recurseIndefinitely));\n\n\t\tassertExpectedExceptionTypes(multipleFailuresError, StackOverflowError.class);\n\t}\n\n\t@Test\n\tvoid assertAllWithExecutableThatThrowsUnrecoverableException() {\n\t\tOutOfMemoryError outOfMemoryError = assertThrows(OutOfMemoryError.class,\n\t\t\t() -> assertAll(AssertionTestUtils::runOutOfMemory));\n\n\t\tassertEquals(\"boom\", outOfMemoryError.getMessage());\n\t}\n\n\t@Test\n\tvoid assertAllWithParallelStream() {\n\t\tExecutable executable = () -> {\n\t\t\tthrow new RuntimeException();\n\t\t};\n\t\tMultipleFailuresError multipleFailuresError = assertThrows(MultipleFailuresError.class,\n\t\t\t() -> assertAll(Stream.generate(() -> executable).parallel().limit(100)));\n\n\t\tassertThat(multipleFailuresError.getFailures()).hasSize(100).doesNotContainNull();\n\t}\n\n\t@SuppressWarnings(\"serial\")\n\tprivate static class EnigmaThrowable extends Throwable {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertArrayEqualsAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEndsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertArrayEqualsAssertionsTests {\n\n\t@Test\n\tvoid assertArrayEqualsWithNulls() {\n\t\tassertArrayEquals(null, (boolean[]) null);\n\t\tassertArrayEquals(null, (char[]) null);\n\t\tassertArrayEquals(null, (byte[]) null);\n\t\tassertArrayEquals(null, (int[]) null);\n\t\tassertArrayEquals(null, (long[]) null);\n\t\tassertArrayEquals(null, (float[]) null);\n\t\tassertArrayEquals(null, (double[]) null);\n\t\tassertArrayEquals(null, (Object[]) null);\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArrays() {\n\t\tboolean[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new boolean[] {}, new boolean[] {});\n\t\tassertArrayEquals(new boolean[] {}, new boolean[] {}, \"message\");\n\t\tassertArrayEquals(new boolean[] {}, new boolean[] {}, () -> \"message\");\n\t\tassertArrayEquals(new boolean[] { true }, new boolean[] { true });\n\t\tassertArrayEquals(new boolean[] { false, true, false, false }, new boolean[] { false, true, false, false });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new boolean[] { true, false });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new boolean[] { true, false }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new boolean[] { true, false }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false }, new boolean[] { true, false, true });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false, false }, new boolean[] { true }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <1>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsBooleanArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true }, new boolean[] { true, false }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <1> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentBooleanArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, false, false }, new boolean[] { true, false, true });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <false> but was: <true>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentBooleanArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { true, true }, new boolean[] { false, true }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [0], expected: <true> but was: <false>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentBooleanArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new boolean[] { false, false, false }, new boolean[] { false, true, true },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <false> but was: <true>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArrays() {\n\t\tchar[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new char[] {}, new char[] {});\n\t\tassertArrayEquals(new char[] {}, new char[] {}, \"message\");\n\t\tassertArrayEquals(new char[] {}, new char[] {}, () -> \"message\");\n\t\tassertArrayEquals(new char[] { 'a' }, new char[] { 'a' });\n\t\tassertArrayEquals(new char[] { 'j', 'u', 'n', 'i', 't' }, new char[] { 'j', 'u', 'n', 'i', 't' });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new char[] { 'a', 'z' });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'a', 'z' }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new char[] { 'a', 'b', 'z' }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'a', 'b', 'z' }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new char[] { 'z', 'x', 'y' }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'z', 'x', 'y' }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'q', 'w', 'e' }, new char[] { 'q', 'w' });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <3> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'a', 's', 'd' }, new char[] { 'd' }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <1>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsCharArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'q' }, new char[] { 't', 'u' }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <1> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentCharArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'a', 'b', 'c' }, new char[] { 'a', 'b', 'a' });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <c> but was: <a>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentCharArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'z', 'x', 'c', 'v' }, new char[] { 'x', 'x', 'c', 'v' }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [0], expected: <z> but was: <x>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentCharArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new char[] { 'r', 't', 'y' }, new char[] { 'r', 'y', 'u' }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <t> but was: <y>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArrays() {\n\t\tbyte[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new byte[] {}, new byte[] {});\n\t\tassertArrayEquals(new byte[] {}, new byte[] {}, \"message\");\n\t\tassertArrayEquals(new byte[] {}, new byte[] {}, () -> \"message\");\n\t\tassertArrayEquals(new byte[] { 42 }, new byte[] { 42 });\n\t\tassertArrayEquals(new byte[] { 1, 2, 3, 42 }, new byte[] { 1, 2, 3, 42 });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new byte[] { 7, 8, 9 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 7, 8, 9 }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new byte[] { 9, 8, 7 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 9, 8, 7 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new byte[] { 10, 20, 30 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 10, 20, 30 }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 1, 2, 100 }, new byte[] { 1, 2, 100, 101 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 1, 2 }, new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <9>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsByteArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 88, 99 }, new byte[] { 99, 88, 77 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentByteArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 12, 13, 12, 13 }, new byte[] { 12, 13, 12, 14 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3], expected: <13> but was: <14>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentByteArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 1, 2, 3, 4, 5 }, new byte[] { 1, 2, 3, 5, 5 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3], expected: <4> but was: <5>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentByteArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new byte[] { 127, 126, -128, +127 }, new byte[] { 127, 126, -128, -127 },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3], expected: <127> but was: <-127>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArrays() {\n\t\tshort[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new short[] {}, new short[] {});\n\t\tassertArrayEquals(new short[] {}, new short[] {}, \"message\");\n\t\tassertArrayEquals(new short[] {}, new short[] {}, () -> \"message\");\n\t\tassertArrayEquals(new short[] { 999 }, new short[] { 999 });\n\t\tassertArrayEquals(new short[] { 111, 222, 333, 444, 999 }, new short[] { 111, 222, 333, 444, 999 });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new short[] { 5, 10, 12 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 5, 10, 12 }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new short[] { 128, 129, 130 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { -129, -130, -131 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new short[] { 1, 2, 3, 4 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { -2000, 1, 2, 3, 4 }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 1, 2, 3, 4, 5, 6 }, new short[] { 1, 2, 3 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <6> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 1, 2, 3, 10_000 }, new short[] { 10_000, 1, 2, 3, 4, 5, 6, 7 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <4> but was: <8>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsShortArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 150, 151 }, new short[] { 150, 151, 152 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentShortArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 10, 100, 1000, 10000 }, new short[] { 1, 10, 100, 1000 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [0], expected: <10> but was: <1>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentShortArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 1, 2, 100, -200 }, new short[] { 1, 2, 100, -500 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3], expected: <-200> but was: <-500>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentShortArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new short[] { 1000, 2000, +3000, 42 }, new short[] { 1000, 2000, -3000, 42 },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [2], expected: <3000> but was: <-3000>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArrays() {\n\t\tint[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new int[] {}, new int[] {});\n\t\tassertArrayEquals(new int[] {}, new int[] {}, \"message\");\n\t\tassertArrayEquals(new int[] {}, new int[] {}, () -> \"message\");\n\t\tassertArrayEquals(new int[] { Integer.MAX_VALUE }, new int[] { Integer.MAX_VALUE });\n\t\tassertArrayEquals(new int[] { 1, 2, 3, 4, 5, 99_999 }, new int[] { 1, 2, 3, 4, 5, 99_999 });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new int[] { Integer.MIN_VALUE, 2, 10 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { Integer.MIN_VALUE, 2, 10 }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new int[] { 99_999, 88_888, 1 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 99_999, 77_7777, 2 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new int[] { 1, 10, 100, 1000, 10000, 100000 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 100000, 10000, 1000, 100, 10, 1 }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 1, 2, 3, Integer.MIN_VALUE, 4 }, new int[] { 1, Integer.MAX_VALUE, 2 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <5> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 100_000, 200_000, 1, 2 }, new int[] { 1, 2, 3 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <4> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsIntArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { Integer.MAX_VALUE, Integer.MIN_VALUE }, new int[] { 1, 2, 3 },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentIntArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { Integer.MIN_VALUE, 1, 2, 10 }, new int[] { Integer.MIN_VALUE, 1, 10, 10 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <2> but was: <10>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentIntArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 9, 10, 100, 100_000, 7 }, new int[] { 9, 10, 100, 100_000, 200_000 },\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [4], expected: <7> but was: <200000>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentIntArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new int[] { 1, Integer.MIN_VALUE, 2 }, new int[] { 1, Integer.MAX_VALUE, 2 },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex,\n\t\t\t\t\"array contents differ at index [1], expected: <-2147483648> but was: <2147483647>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArrays() {\n\t\tlong[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new long[] {}, new long[] {});\n\t\tassertArrayEquals(new long[] {}, new long[] {}, \"message\");\n\t\tassertArrayEquals(new long[] {}, new long[] {}, () -> \"message\");\n\t\tassertArrayEquals(new long[] { Long.MAX_VALUE }, new long[] { Long.MAX_VALUE });\n\t\tassertArrayEquals(new long[] { Long.MIN_VALUE, 10, 20, 30 }, new long[] { Long.MIN_VALUE, 10, 20, 30 });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new long[] { Long.MAX_VALUE, 2, 10 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { Long.MAX_VALUE, 2, 10 }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new long[] { 42, 4242, 424242, 4242424242L }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 4242424242L, 424242, 4242, 42 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new long[] { 12345678910L, 10, 9, 8 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 8, 9, 10, 12345678910L }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 1, 2, 3, Long.MIN_VALUE, 4 }, new long[] { 1, Long.MAX_VALUE, 2 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <5> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 100_000L, 200_000L, 1L, 2L }, new long[] { 1L, 2L, 3L }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <4> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsLongArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { Long.MAX_VALUE, Long.MIN_VALUE }, new long[] { 1L, 2L, 42L },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentLongArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { Long.MIN_VALUE, 17, 18L, 19 }, new long[] { Long.MIN_VALUE, 17, 18, 20 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3], expected: <19> but was: <20>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentLongArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 6, 5, 4, 3, 2, Long.MIN_VALUE }, new long[] { 6, 5, 4, 3, 2, 1 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex,\n\t\t\t\t\"array contents differ at index [5], expected: <-9223372036854775808> but was: <1>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentLongArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new long[] { 42, -9999L, 2 }, new long[] { 42L, +9999L, 2L }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <-9999> but was: <9999>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArrays() {\n\t\tfloat[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new float[] {}, new float[] {});\n\t\tassertArrayEquals(new float[] {}, new float[] {}, \"message\");\n\t\tassertArrayEquals(new float[] {}, new float[] {}, () -> \"message\");\n\t\tassertArrayEquals(new float[] { Float.MAX_VALUE }, new float[] { Float.MAX_VALUE });\n\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 5F, 5.5F, 1.00F },\n\t\t\tnew float[] { Float.MIN_VALUE, 5F, 5.5F, 1.00F });\n\n\t\tassertArrayEquals(new float[] { Float.NaN }, new float[] { Float.NaN });\n\t\tassertArrayEquals(new float[] { 10.18F, Float.NaN, 42.9F }, new float[] { 10.18F, Float.NaN, 42.9F });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { Float.MAX_VALUE, 4.2F, 9.0F });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 2.3F, 10.10F }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { 42.42F, 42.4242F, 19.20F }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 11.101F, 12.101F, 99.9F }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { 5F, 6F, 7.77F, 8.88F }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1F, 1.1F, 1.11F, 1.111F }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 1F, 2F, 3F }, new float[] { Float.MAX_VALUE, 7.1F });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <4> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 19.1F, 12.77F, 18.F }, new float[] { .9F, .8F, 5.123F, .10F }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsFloatArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.1F, 1.2F, 1.3F }, new float[] { 1F, 2F, 3F, 4F, 5F, 6F },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <6>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentFloatArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 5.5F, 6.5F, 7.5F, 8.5F }, new float[] { 5.5F, 6.5F, 7.4F, 8.5F });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <7.5> but was: <7.4>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.0F, 2.0F, 3.0F, Float.NaN }, new float[] { 1.0F, 2.0F, 3.0F, 4.0F });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3], expected: <NaN> but was: <4.0>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentFloatArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.9F, 0.5F, 0.4F, 0.3F }, new float[] { 1.9F, 0.5F, 0.4F, -0.333F },\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3], expected: <0.3> but was: <-0.333>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentFloatArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 0.3F, 0.9F, 8F }, new float[] { 0.3F, Float.MIN_VALUE, 8F },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <0.9> but was: <1.4E-45>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArrays() {\n\t\tfloat[] array = {};\n\t\tassertArrayEquals(array, array, 0.001F);\n\t\tassertArrayEquals(new float[] {}, new float[] {}, 0.001F);\n\t\tassertArrayEquals(new float[] {}, new float[] {}, 0.001F, \"message\");\n\t\tassertArrayEquals(new float[] {}, new float[] {}, 0.001F, () -> \"message\");\n\t\tassertArrayEquals(new float[] { Float.MAX_VALUE }, new float[] { Float.MAX_VALUE }, 0.0001F);\n\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 2.111F, 2.521F, 1.01F },\n\t\t\tnew float[] { Float.MIN_VALUE, 2.119F, 2.523F, 1.01001F }, 0.01F);\n\n\t\tassertArrayEquals(new float[] { Float.NaN }, new float[] { Float.NaN }, 0.1F);\n\t\tassertArrayEquals(new float[] { 10.18F, Float.NaN, 42.9F }, new float[] { 10.98F, Float.NaN, 43.9F }, 1F);\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArraysThrowsForIllegalDelta() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] {}, new float[] {}, -0.5F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <-0.5>\");\n\t\t}\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] {}, new float[] {}, Float.NaN);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <NaN>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 12.9F, 7F, 13F }, new float[] { 12.9F, 7F, 13F }, -0.5F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <-0.5>\");\n\t\t}\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.11F, 1.11F, 9F }, new float[] { 1.11F, 1.11F, 9F, 10F }, Float.NaN);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <NaN>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { Float.MAX_VALUE, 4.2F, 9.0F }, 0.001F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 2.3F, 10.10F }, null, 0.01F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { 42.42F, 42.4242F, 19.20F }, 0.0001F, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 11.101F, 12.101F, 99.9F }, null, 0.01F, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new float[] { 5F, 6F, 7.77F, 8.88F }, 0.1F, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1F, 1.1F, 1.11F, 1.111F }, null, 0.1F, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { Float.MIN_VALUE, 1F, 2F, 3F }, new float[] { Float.MAX_VALUE, 7.1F }, 0.1F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <4> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 19.1F, 12.77F }, new float[] { .9F, .8F, 5.123F }, 0.1F, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaFloatArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.1F, 1.2F, 1.3F }, new float[] { 1F, 2F, 3F, 4F }, 0.1F, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentFloatArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 5.6F, 3.2F, 9.1F, 0.5F }, new float[] { 5.55F, 3.3F, 9.201F, 0.51F }, 0.1F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <9.1> but was: <9.201>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.0F, 2.0F, 3.0F, Float.NaN }, new float[] { 1.5F, 1.5F, 2.9F, 4.0F },\n\t\t\t\t0.5F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3], expected: <NaN> but was: <4.0>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentFloatArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 1.91F, 0.5F, .4F, 0.3F }, new float[] { 2F, 0.509F, .499F, -0.333F }, 0.1F,\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3], expected: <0.3> but was: <-0.333>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentFloatArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new float[] { 0.3F, 0.9F, 8F }, new float[] { 0.6F, Float.MIN_VALUE, 8.4F }, 0.5F,\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <0.9> but was: <1.4E-45>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArrays() {\n\t\tdouble[] array = {};\n\t\tassertArrayEquals(array, array);\n\t\tassertArrayEquals(new double[] {}, new double[] {});\n\t\tassertArrayEquals(new double[] {}, new double[] {}, \"message\");\n\t\tassertArrayEquals(new double[] {}, new double[] {}, () -> \"message\");\n\t\tassertArrayEquals(new double[] { Double.MAX_VALUE }, new double[] { Double.MAX_VALUE });\n\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 2.1, 5.5, 1.0 },\n\t\t\tnew double[] { Double.MIN_VALUE, 2.1, 5.5, 1.0 });\n\n\t\tassertArrayEquals(new double[] { Double.NaN }, new double[] { Double.NaN });\n\t\tassertArrayEquals(new double[] { 1.2, 10.8, Double.NaN, 42.9 }, new double[] { 1.2, 10.8, Double.NaN, 42.9 });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { Double.MAX_VALUE, 17.4, 98.7654321 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 93.0, 92.000001 }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { 33.3, 34.9, 20.1, 11.0011 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 44.4, 20.19, 11.3, 0.11 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { 1.2, 1.3, 1.4, 2.2002 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 13.13, 43.33, 100 }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 1.0, 2.0, 3.0 },\n\t\t\t\tnew double[] { Double.MAX_VALUE, 1.1, 1.0 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <4> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 11.1, 99.1, 2 }, new double[] { .9, .1, .0, .1, .3 }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <5>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDoubleArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.15D, 2.2, 2.3 }, new double[] { 1.15D, 1.15D }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentDoubleArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.17, 1.19, 1.21, 5 }, new double[] { 1.17, 1.00019, 1.21, 5 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [1], expected: <1.19> but was: <1.00019>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 0.1, 0.2, 0.3, 0.4, 0.5 },\n\t\t\t\tnew double[] { 0.1, 0.2, 0.3, 0.4, Double.NaN });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [4], expected: <0.5> but was: <NaN>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentDoubleArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.01, 9.031, .123, 4.23 }, new double[] { 1.01, 9.099, .123, 4.23 },\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <9.031> but was: <9.099>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentDoubleArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 0.7, .1, 8 }, new double[] { 0.7, Double.MIN_VALUE, 8 }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <0.1> but was: <4.9E-324>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArrays() {\n\t\tdouble[] array = {};\n\t\tassertArrayEquals(array, array, 0.5);\n\t\tassertArrayEquals(new double[] {}, new double[] {}, 0.5);\n\t\tassertArrayEquals(new double[] {}, new double[] {}, 0.5, \"message\");\n\t\tassertArrayEquals(new double[] {}, new double[] {}, 0.5, () -> \"message\");\n\t\tassertArrayEquals(new double[] { Double.MAX_VALUE, 0.1 }, new double[] { Double.MAX_VALUE, 0.2 }, 0.2);\n\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 3.1, 1.3, 2.7 },\n\t\t\tnew double[] { Double.MIN_VALUE, 3.4, 1.7, 2.4 }, 0.5);\n\n\t\tassertArrayEquals(new double[] { Double.NaN }, new double[] { Double.NaN }, 0.01);\n\t\tassertArrayEquals(new double[] { 1.2, 1.8, Double.NaN, 4.9 }, new double[] { 1.25, 1.7, Double.NaN, 4.8 }, 0.2);\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArraysThrowsForIllegalDelta() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] {}, new double[] {}, -0.5F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <-0.5>\");\n\t\t}\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] {}, new double[] {}, Float.NaN);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <NaN>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.2, 1.3, 10 }, new double[] { 1.2, 1.3, 10 }, -0.5F);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <-0.5>\");\n\t\t}\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 0.1, 10 }, new double[] { 0.1, 10, 11 }, Float.NaN);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"positive delta expected but was: <NaN>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { Double.MAX_VALUE, 11.1, 12.12 }, 0.5);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 90, 91.9 }, null, 0.1);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { 33.3, 34.9, 20.1, 11.0011 }, 0.1, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 44.4, 20.19, 11.3, 0.11 }, null, 0.5, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new double[] { 1.2, 1.3, 1.4, 2.2002 }, 1, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 13.13, 43.33, 100 }, null, 1.5, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { Double.MIN_VALUE, 2.0, 3.0, 4.0 },\n\t\t\t\tnew double[] { Double.MAX_VALUE, 2.1, 3.1 }, 0.001);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <4> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.1, 99.1, 3.1 }, new double[] { .9, .1, .0, .1, .3 }, 0.1, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <5>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDoubleArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.77D, 2.1, 3 }, new double[] { 8.8, 0.11 }, 1, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentDoubleArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.12, 2.92, 1.201 }, new double[] { 1.1201, 2.94, 1.201 }, 0.01);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [1], expected: <2.92> but was: <2.94>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 0.6, 0.12, 19.9, 5.5 }, new double[] { 1.0, 0.42, 20, Double.NaN }, 0.5);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3], expected: <5.5> but was: <NaN>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentDoubleArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 1.01, 9.031, .123, 4.23 }, new double[] { 1.1, 9.231, .13, 4.3 }, 0.1,\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <9.031> but was: <9.231>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDeltaDifferentDoubleArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new double[] { 0.7, 0.3001, 8 }, new double[] { 0.7, 0.4002, 8 }, 0.1, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [1], expected: <0.3001> but was: <0.4002>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArrays() {\n\t\tObject[] array = { \"a\", 'b', 1, 2 };\n\t\tassertArrayEquals(array, array);\n\n\t\tassertArrayEquals(new Object[] {}, new Object[] {});\n\t\tassertArrayEquals(new Object[] {}, new Object[] {}, \"message\");\n\t\tassertArrayEquals(new Object[] {}, new Object[] {}, () -> \"message\");\n\t\tassertArrayEquals(new Object[] { \"abc\" }, new Object[] { \"abc\" });\n\t\tassertArrayEquals(new Object[] { \"abc\", 1, 2L, 3D }, new Object[] { \"abc\", 1, 2L, 3D });\n\n\t\tassertArrayEquals(new Object[] { new Object[] { new Object[] {} } },\n\t\t\tnew Object[] { new Object[] { new Object[] {} } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { null, new Object[] { null, new Object[] { null, null } }, null, new Object[] { null } },\n\t\t\tnew Object[] { null, new Object[] { null, new Object[] { null, null } }, null, new Object[] { null } });\n\n\t\tassertArrayEquals(new Object[] { \"a\", new Object[] { new Object[] { \"b\", new Object[] { \"c\", \"d\" } } }, \"e\" },\n\t\t\tnew Object[] { \"a\", new Object[] { new Object[] { \"b\", new Object[] { \"c\", \"d\" } } }, \"e\" });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { new Object[] { 1 }, new Object[] { 2 },\n\t\t\t\t\tnew Object[] { new Object[] { 3, new Object[] { 4 } } } },\n\t\t\tnew Object[] { new Object[] { 1 }, new Object[] { 2 },\n\t\t\t\t\tnew Object[] { new Object[] { 3, new Object[] { 4 } } } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { new Object[] {\n\t\t\t\t\tnew Object[] { new Object[] { new Object[] { new Object[] { new Object[] { \"abc\" } } } } } } },\n\t\t\tnew Object[] { new Object[] {\n\t\t\t\t\tnew Object[] { new Object[] { new Object[] { new Object[] { new Object[] { \"abc\" } } } } } } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { null, new Object[] { null, Double.NaN, new Object[] { Float.NaN, null, new Object[] {} } } },\n\t\t\tnew Object[] { null,\n\t\t\t\t\tnew Object[] { null, Double.NaN, new Object[] { Float.NaN, null, new Object[] {} } } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { new String(\"a\"), Integer.valueOf(1), new Object[] { Double.parseDouble(\"1.1\"), \"b\" } },\n\t\t\tnew Object[] { new String(\"a\"), Integer.valueOf(1), new Object[] { Double.parseDouble(\"1.1\"), \"b\" } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { 1, 2,\n\t\t\t\t\tnew Object[] { 3, new int[] { 4, 5 }, new long[] { 6 },\n\t\t\t\t\t\t\tnew Object[] { new Object[] { new int[] { 7 } } } },\n\t\t\t\t\tnew int[] { 8 }, new Object[] { new long[] { 9 } } },\n\t\t\tnew Object[] { 1, 2,\n\t\t\t\t\tnew Object[] { 3, new int[] { 4, 5 }, new long[] { 6 },\n\t\t\t\t\t\t\tnew Object[] { new Object[] { new int[] { 7 } } } },\n\t\t\t\t\tnew int[] { 8 }, new Object[] { new long[] { 9 } } });\n\n\t\tassertArrayEquals(\n\t\t\tnew Object[] { \"a\", new byte[] { 42 }, new short[] { 42 }, new char[] { 'b', 'c' }, new float[] { 42.23f },\n\t\t\t\t\tnew double[] { 42.23 }, new boolean[] { true },\n\t\t\t\t\tnew Object[] { new Object[] { new String[] { \"ef\" }, new Object[] { new String[] { \"ghi\" } } } } },\n\t\t\tnew Object[] { \"a\", new byte[] { 42 }, new short[] { 42 }, new char[] { 'b', 'c' }, new float[] { 42.23f },\n\t\t\t\t\tnew double[] { 42.23 }, new boolean[] { true },\n\t\t\t\t\tnew Object[] { new Object[] { new String[] { \"ef\" }, new Object[] { new String[] { \"ghi\" } } } } });\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new Object[] { \"a\", \"b\", 1, new Object() });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 'a', 1, new Object(), 10L }, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArrayVsNull() {\n\t\ttry {\n\t\t\tassertArrayEquals(//\n\t\t\t\tnew Object[] { new Object[] {}, 1, \"2\", new Object[] { '3', new Object[] { null } } }, //\n\t\t\t\tnew Object[] { new Object[] {}, 1, \"2\", new Object[] { '3', new Object[] { new Object[] { \"4\" } } } });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected array was <null> at index [3][1][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { 1, 2, new Object[] { 3, new Object[] { \"4\", new Object[] { 5, new Object[] { 6 } } } },\n\t\t\t\t\t\t\"7\" },\n\t\t\t\tnew Object[] { 1, 2, new Object[] { 3, new Object[] { \"4\", new Object[] { 5, null } } }, \"7\" });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual array was <null> at index [2][1][1][1]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new Object[] { 'a', \"b\", 10, 20D }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { \"hello\", 42 }, null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArrayVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 1, new Object[] { 2, 3, new Object[] { 4, 5, new Object[] { null } } } },\n\t\t\t\tnew Object[] { 1, new Object[] { 2, 3, new Object[] { 4, 5, new Object[] { new Object[] { 6 } } } } },\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null> at index [1][2][2][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { 1, new Object[] { 2, new Object[] { 3, new Object[] { new Object[] { 4 } } } } },\n\t\t\t\tnew Object[] { 1, new Object[] { 2, new Object[] { 3, new Object[] { null } } } }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null> at index [1][1][1][0]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(null, new Object[] { 42, \"42\", new float[] { 42F }, 42D }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { new Object[] { \"a\" }, new Object() }, null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArrayVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { \"1\", \"2\", \"3\", new Object[] { \"4\", new Object[] { null } } },\n\t\t\t\tnew Object[] { \"1\", \"2\", \"3\", new Object[] { \"4\", new Object[] { new int[] { 5 } } } },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected array was <null> at index [3][1][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { 1, 2, new Object[] { \"3\", new Object[] { '4', new Object[] { 5, 6, new long[] {} } } } },\n\t\t\t\tnew Object[] { 1, 2, new Object[] { \"3\", new Object[] { '4', new Object[] { 5, 6, null } } } },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual array was <null> at index [2][1][1][2]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 'a', \"b\", 'c' }, new Object[] { 'a', \"b\", 'c', 1 });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ, expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArraysOfDifferentLength() {\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { \"a\", new Object[] { \"b\", new Object[] { \"c\", \"d\", new Object[] { \"e\", 1, 2, 3 } } } },\n\t\t\t\tnew Object[] { \"a\",\n\t\t\t\t\t\tnew Object[] { \"b\", new Object[] { \"c\", \"d\", new Object[] { \"e\", 1, 2, 3, 4, 5 } } } });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ at index [1][1][2], expected: <4> but was: <6>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { new Object[] {\n\t\t\t\t\t\tnew Object[] { new Object[] { new Object[] { new Object[] { new char[] { 'a' } } } } } } },\n\t\t\t\tnew Object[] { new Object[] { new Object[] {\n\t\t\t\t\t\tnew Object[] { new Object[] { new Object[] { new char[] { 'a', 'b' } } } } } } });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array lengths differ at index [0][0][0][0][0][0], expected: <1> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 'a', 1 }, new Object[] { 'a', 1, new Object() }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArraysOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(//\n\t\t\t\tnew Object[] { 'a', 1, new Object[] { 2, 3 } }, //\n\t\t\t\tnew Object[] { 'a', 1, new Object[] { 2, 3, 4, 5 } }, //\n\t\t\t\t\"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ at index [2], expected: <2> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsObjectArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { \"a\", \"b\", \"c\" }, new Object[] { \"a\", \"b\", \"c\", \"d\", \"e\", \"f\" },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ, expected: <3> but was: <6>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsNestedObjectArraysOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(//\n\t\t\t\tnew Object[] { \"a\", new Object[] { 1, 2, 3, new double[] { 4.0, 5.1, 6.1 }, 7 } }, //\n\t\t\t\tnew Object[] { \"a\", new Object[] { 1, 2, 3, new double[] { 4.0, 5.1, 6.1, 7.0 }, 8 } }, //\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array lengths differ at index [1][3], expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentObjectArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 1L, \"2\", '3', 4, 5D }, new Object[] { 1L, \"2\", '9', 4, 5D });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2], expected: <3> but was: <9>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { \"a\", 10, 11, 12, Double.NaN }, new Object[] { \"a\", 10, 11, 12, 13.55D });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [4], expected: <NaN> but was: <13.55>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentNestedObjectArrays() {\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { 1, 2, new Object[] { 3, new Object[] { 4, new boolean[] { false, true } } } },\n\t\t\t\tnew Object[] { 1, 2, new Object[] { 3, new Object[] { 4, new boolean[] { true, false } } } });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [2][1][1][0], expected: <false> but was: <true>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 1, 2, 3, new Object[] { new Object[] { 4, new Object[] { 5 } } } },\n\t\t\t\tnew Object[] { 1, 2, 3, new Object[] { new Object[] { 4, new Object[] { new Object[] {} } } } });\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"array contents differ at index [3][0][1][0], expected: <5> but was: <[]>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentObjectArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 1.1D, 2L, \"3\" }, new Object[] { 1D, 2L, \"3\" }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [0], expected: <1.1> but was: <1.0>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentNestedObjectArraysAndMessage() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 9, 8, '6', new Object[] { 5, 4, \"3\", new Object[] { \"2\", '1' } } },\n\t\t\t\tnew Object[] { 9, 8, '6', new Object[] { 5, 4, \"3\", new Object[] { \"99\", '1' } } }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3][3][0], expected: <2> but was: <99>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { 9, 8, '6', new Object[] { 5, 4, \"3\", new String[] { \"2\", \"1\" } } },\n\t\t\t\tnew Object[] { 9, 8, '6', new Object[] { 5, 4, \"3\", new String[] { \"99\", \"1\" } } }, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [3][3][0], expected: <2> but was: <99>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentObjectArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(new Object[] { \"one\", 1L, Double.MIN_VALUE, \"abc\" },\n\t\t\t\tnew Object[] { \"one\", 1L, 42.42, \"abc\" }, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [2], expected: <4.9E-324> but was: <42.42>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertArrayEqualsDifferentNestedObjectArraysAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { \"one\", 1L, new Object[] { \"a\", 'b', new Object[] { 1, new Object[] { 2, 3 } } }, \"abc\" },\n\t\t\t\tnew Object[] { \"one\", 1L, new Object[] { \"a\", 'b', new Object[] { 1, new Object[] { 2, 4 } } }, \"abc\" },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [2][2][1][1], expected: <3> but was: <4>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertArrayEquals(\n\t\t\t\tnew Object[] { \"j\", new String[] { \"a\" }, new int[] { 42 }, \"ab\", new Object[] { 1, new int[] { 3 } } },\n\t\t\t\tnew Object[] { \"j\", new String[] { \"a\" }, new int[] { 42 }, \"ab\", new Object[] { 1, new int[] { 5 } } },\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"array contents differ at index [4][1][0], expected: <3> but was: <5>\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertDoesNotThrowAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.io.IOException;\nimport java.util.concurrent.FutureTask;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingSupplier;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.2\n */\nclass AssertDoesNotThrowAssertionsTests {\n\n\tprivate static final Executable nix = () -> {\n\t};\n\n\tprivate static final ThrowingSupplier<String> something = () -> \"enigma\";\n\n\t@Test\n\tvoid assertDoesNotThrowWithMethodReferenceForNonVoidReturnType() {\n\t\tFutureTask<String> future = new FutureTask<>(() -> \"foo\");\n\t\tfuture.run();\n\n\t\tString result;\n\n\t\t// Current compiler's type inference: does NOT compile since the compiler\n\t\t// cannot figure out which overloaded variant of assertDoesNotThrow() to\n\t\t// invoke (i.e., Executable vs. ThrowingSupplier).\n\t\t//\n\t\t// result = assertDoesNotThrow(future::get);\n\n\t\t// Explicitly as an Executable\n\t\tassertDoesNotThrow((Executable) future::get);\n\n\t\t// Explicitly as a ThrowingSupplier\n\t\tresult = assertDoesNotThrow((ThrowingSupplier<String>) future::get);\n\t\tassertEquals(\"foo\", result);\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithMethodReferenceForVoidReturnType() {\n\t\tvar foo = new Foo();\n\n\t\t// Note: the following does not compile since the compiler cannot properly\n\t\t// perform type inference for a method reference for an overloaded method\n\t\t// that has a void return type such as Foo.overloaded(...), IFF the\n\t\t// compiler is simultaneously trying to pick which overloaded variant\n\t\t// of assertDoesNotThrow() to invoke.\n\t\t//\n\t\t// assertDoesNotThrow(foo::overloaded);\n\n\t\t// Current compiler's type inference\n\t\tassertDoesNotThrow(foo::normalMethod);\n\n\t\t// Explicitly as an Executable\n\t\tassertDoesNotThrow(foo::normalMethod);\n\t\tassertDoesNotThrow((Executable) foo::overloaded);\n\t}\n\n\t// --- executable ----------------------------------------------------------\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithExecutable() {\n\t\tassertDoesNotThrow(nix);\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithExecutableAndMessage() {\n\t\tassertDoesNotThrow(nix, \"message\");\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithExecutableAndMessageSupplier() {\n\t\tassertDoesNotThrow(nix, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsACheckedException() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IOException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + IOException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsACheckedExceptionWithMessage() {\n\t\tString message = \"Checked exception message\";\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IOException(message);\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + IOException.class.getName() + \": \" + message);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsARuntimeException() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsARuntimeExceptionWithMessage() {\n\t\tString message = \"Runtime exception message\";\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException(message);\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Unexpected exception thrown: \" + IllegalStateException.class.getName() + \": \" + message);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsAnError() {\n\t\ttry {\n\t\t\tassertDoesNotThrow(AssertionTestUtils::recurseIndefinitely);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + StackOverflowError.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsAnExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t}, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsAnExceptionWithMessageWithMessageString() {\n\t\tString message = \"Runtime exception message\";\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException(message);\n\t\t\t}, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Custom message ==> Unexpected exception thrown: \"\n\t\t\t\t\t+ IllegalStateException.class.getName() + \": \" + message);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsAnExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t}, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithExecutableThatThrowsAnExceptionWithMessageWithMessageSupplier() {\n\t\tString message = \"Runtime exception message\";\n\t\ttry {\n\t\t\tassertDoesNotThrow((Executable) () -> {\n\t\t\t\tthrow new IllegalStateException(message);\n\t\t\t}, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Custom message ==> Unexpected exception thrown: \"\n\t\t\t\t\t+ IllegalStateException.class.getName() + \": \" + message);\n\t\t}\n\t}\n\n\t// --- supplier ------------------------------------------------------------\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithSupplier() {\n\t\tassertEquals(\"enigma\", assertDoesNotThrow(something));\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithSupplierAndMessage() {\n\t\tassertEquals(\"enigma\", assertDoesNotThrow(something, \"message\"));\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowAnythingWithSupplierAndMessageSupplier() {\n\t\tassertEquals(\"enigma\", assertDoesNotThrow(something, () -> \"message\"));\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithSupplierThatThrowsACheckedException() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((ThrowingSupplier<?>) () -> {\n\t\t\t\tthrow new IOException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + IOException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithSupplierThatThrowsARuntimeException() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((ThrowingSupplier<?>) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithSupplierThatThrowsAnError() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((ThrowingSupplier<?>) () -> {\n\t\t\t\tthrow new StackOverflowError();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Unexpected exception thrown: \" + StackOverflowError.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithSupplierThatThrowsAnExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((ThrowingSupplier<?>) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t}, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertDoesNotThrowWithSupplierThatThrowsAnExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertDoesNotThrow((ThrowingSupplier<?>) () -> {\n\t\t\t\tthrow new IllegalStateException();\n\t\t\t}, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Unexpected exception thrown: \" + IllegalStateException.class.getName());\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static class Foo {\n\n\t\tvoid normalMethod() {\n\t\t}\n\n\t\tvoid overloaded() {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid overloaded(int i) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertEqualsAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedAndActualValues;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEndsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertEqualsAssertionsTests {\n\n\t@Test\n\tvoid assertEqualsByte() {\n\t\tbyte expected = 1;\n\t\tbyte actual = 1;\n\t\tassertEquals(expected, actual);\n\t\tassertEquals(expected, actual, \"message\");\n\t\tassertEquals(expected, actual, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsByteWithUnequalValues() {\n\t\tbyte expected = 1;\n\t\tbyte actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsByteWithUnequalValuesAndMessage() {\n\t\tbyte expected = 1;\n\t\tbyte actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsByteWithUnequalValuesAndMessageSupplier() {\n\t\tbyte expected = 1;\n\t\tbyte actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsShort() {\n\t\tshort expected = 1;\n\t\tshort actual = 1;\n\t\tassertEquals(expected, actual);\n\t\tassertEquals(expected, actual, \"message\");\n\t\tassertEquals(expected, actual, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsShortWithUnequalValues() {\n\t\tshort expected = 1;\n\t\tshort actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsShortWithUnequalValuesAndMessage() {\n\t\tshort expected = 1;\n\t\tshort actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsShortWithUnequalValuesAndMessageSupplier() {\n\t\tshort expected = 1;\n\t\tshort actual = 2;\n\t\ttry {\n\t\t\tassertEquals(expected, actual, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsInt() {\n\t\tassertEquals(1, 1);\n\t\tassertEquals(1, 1, \"message\");\n\t\tassertEquals(1, 1, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsIntWithUnequalValues() {\n\t\ttry {\n\t\t\tassertEquals(1, 2);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1, 2);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsIntWithUnequalValuesAndMessage() {\n\t\ttry {\n\t\t\tassertEquals(1, 2, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1, 2);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsIntWithUnequalValuesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(1, 2, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1, 2);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsLong() {\n\t\tassertEquals(1L, 1L);\n\t\tassertEquals(1L, 1L, \"message\");\n\t\tassertEquals(1L, 1L, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsLongWithUnequalValues() {\n\t\ttry {\n\t\t\tassertEquals(1L, 2L);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1L, 2L);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsLongWithUnequalValuesAndMessage() {\n\t\ttry {\n\t\t\tassertEquals(1L, 2L, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1L, 2L);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsLongWithUnequalValuesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(1L, 2L, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1> but was: <2>\");\n\t\t\tassertExpectedAndActualValues(ex, 1L, 2L);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsChar() {\n\t\tassertEquals('a', 'a');\n\t\tassertEquals('a', 'a', \"message\");\n\t\tassertEquals('a', 'a', () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsCharWithUnequalValues() {\n\t\ttry {\n\t\t\tassertEquals('a', 'b');\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <a> but was: <b>\");\n\t\t\tassertExpectedAndActualValues(ex, 'a', 'b');\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsCharWithUnequalValuesAndMessage() {\n\t\ttry {\n\t\t\tassertEquals('a', 'b', \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <a> but was: <b>\");\n\t\t\tassertExpectedAndActualValues(ex, 'a', 'b');\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsCharWithUnequalValuesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals('a', 'b', () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <a> but was: <b>\");\n\t\t\tassertExpectedAndActualValues(ex, 'a', 'b');\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsFloat() {\n\t\tassertEquals(1.0f, 1.0f);\n\t\tassertEquals(1.0f, 1.0f, \"message\");\n\t\tassertEquals(1.0f, 1.0f, () -> \"message\");\n\t\tassertEquals(Float.NaN, Float.NaN);\n\t\tassertEquals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);\n\t\tassertEquals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);\n\t\tassertEquals(Float.MIN_VALUE, Float.MIN_VALUE);\n\t\tassertEquals(Float.MAX_VALUE, Float.MAX_VALUE);\n\t\tassertEquals(Float.MIN_NORMAL, Float.MIN_NORMAL);\n\t\tassertEquals(Double.NaN, Float.NaN);\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithUnequalValues() {\n\t\ttry {\n\t\t\tassertEquals(1.0f, 1.1f);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0f, 1.1f);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithUnequalValuesAndMessage() {\n\t\ttry {\n\t\t\tassertEquals(1.0f, 1.1f, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0f, 1.1f);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithUnequalValuesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(1.0f, 1.1f, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0f, 1.1f);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithDelta() {\n\t\tassertEquals(0.0f, 0.0f, 0.1f);\n\t\tassertEquals(0.0f, 0.0f, 0.1f, \"message\");\n\t\tassertEquals(0.0f, 0.0f, 0.1f, () -> \"message\");\n\t\tassertEquals(0.56f, 0.6f, 0.05f);\n\t\tassertEquals(0.01f, 0.011f, 0.002f);\n\t\tassertEquals(Float.NaN, Float.NaN, 0.5f);\n\t\tassertEquals(0.1f, 0.1f, 0.0f);\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithIllegalDelta() {\n\t\tAssertionFailedError e1 = assertThrows(AssertionFailedError.class, () -> assertEquals(0.1f, 0.2f, -0.9f));\n\t\tassertMessageEndsWith(e1, \"positive delta expected but was: <-0.9>\");\n\n\t\tAssertionFailedError e2 = assertThrows(AssertionFailedError.class, () -> assertEquals(.0f, .0f, -10.5f));\n\t\tassertMessageEndsWith(e2, \"positive delta expected but was: <-10.5>\");\n\n\t\tAssertionFailedError e3 = assertThrows(AssertionFailedError.class, () -> assertEquals(4.5f, 4.6f, Float.NaN));\n\t\tassertMessageEndsWith(e3, \"positive delta expected but was: <NaN>\");\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithDeltaWithUnequalValues() {\n\t\tAssertionFailedError e1 = assertThrows(AssertionFailedError.class, () -> assertEquals(0.5f, 0.2f, 0.2f));\n\t\tassertMessageEndsWith(e1, \"expected: <0.5> but was: <0.2>\");\n\n\t\tAssertionFailedError e2 = assertThrows(AssertionFailedError.class, () -> assertEquals(0.1f, 0.2f, 0.000001f));\n\t\tassertMessageEndsWith(e2, \"expected: <0.1> but was: <0.2>\");\n\n\t\tAssertionFailedError e3 = assertThrows(AssertionFailedError.class, () -> assertEquals(100.0f, 50.0f, 10.0f));\n\t\tassertMessageEndsWith(e3, \"expected: <100.0> but was: <50.0>\");\n\n\t\tAssertionFailedError e4 = assertThrows(AssertionFailedError.class, () -> assertEquals(-3.5f, -3.3f, 0.01f));\n\t\tassertMessageEndsWith(e4, \"expected: <-3.5> but was: <-3.3>\");\n\n\t\tAssertionFailedError e5 = assertThrows(AssertionFailedError.class, () -> assertEquals(+0.0f, -0.001f, .00001f));\n\t\tassertMessageEndsWith(e5, \"expected: <0.0> but was: <-0.001>\");\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithDeltaWithUnequalValuesAndMessage() {\n\t\tExecutable assertion = () -> assertEquals(0.5f, 0.45f, 0.03f, \"message\");\n\n\t\tAssertionFailedError e = assertThrows(AssertionFailedError.class, assertion);\n\n\t\tassertMessageStartsWith(e, \"message\");\n\t\tassertMessageEndsWith(e, \"expected: <0.5> but was: <0.45>\");\n\t\tassertExpectedAndActualValues(e, 0.5f, 0.45f);\n\t}\n\n\t@Test\n\tvoid assertEqualsFloatWithDeltaWithUnequalValuesAndMessageSupplier() {\n\t\tExecutable assertion = () -> assertEquals(0.5f, 0.45f, 0.03f, () -> \"message\");\n\n\t\tAssertionFailedError e = assertThrows(AssertionFailedError.class, assertion);\n\n\t\tassertMessageStartsWith(e, \"message\");\n\t\tassertMessageEndsWith(e, \"expected: <0.5> but was: <0.45>\");\n\t\tassertExpectedAndActualValues(e, 0.5f, 0.45f);\n\t}\n\n\t@Test\n\tvoid assertEqualsDouble() {\n\t\tassertEquals(1.0d, 1.0d);\n\t\tassertEquals(1.0d, 1.0d, \"message\");\n\t\tassertEquals(1.0d, 1.0d, () -> \"message\");\n\t\tassertEquals(Double.NaN, Double.NaN);\n\t\tassertEquals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);\n\t\tassertEquals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);\n\t\tassertEquals(Double.MIN_VALUE, Double.MIN_VALUE);\n\t\tassertEquals(Double.MAX_VALUE, Double.MAX_VALUE);\n\t\tassertEquals(Double.MIN_NORMAL, Double.MIN_NORMAL);\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithUnequalValues() {\n\t\ttry {\n\t\t\tassertEquals(1.0d, 1.1d);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0d, 1.1d);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithUnequalValuesAndMessage() {\n\t\ttry {\n\t\t\tassertEquals(1.0d, 1.1d, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0d, 1.1d);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithUnequalValuesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(1.0d, 1.1d, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <1.0> but was: <1.1>\");\n\t\t\tassertExpectedAndActualValues(ex, 1.0d, 1.1d);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithDelta() {\n\t\tassertEquals(0.0d, 0.0d, 0.1d);\n\t\tassertEquals(0.0d, 0.0d, 0.1d, \"message\");\n\t\tassertEquals(0.0d, 0.0d, 0.1d, () -> \"message\");\n\t\tassertEquals(0.42d, 0.24d, 0.19d);\n\t\tassertEquals(0.02d, 0.011d, 0.01d);\n\t\tassertEquals(Double.NaN, Double.NaN, 0.2d);\n\t\tassertEquals(0.001d, 0.001d, 0.0d);\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithIllegalDelta() {\n\t\tAssertionFailedError e1 = assertThrows(AssertionFailedError.class, () -> assertEquals(1.1d, 1.11d, -0.5d));\n\t\tassertMessageEndsWith(e1, \"positive delta expected but was: <-0.5>\");\n\n\t\tAssertionFailedError e2 = assertThrows(AssertionFailedError.class, () -> assertEquals(.55d, .56d, -10.5d));\n\t\tassertMessageEndsWith(e2, \"positive delta expected but was: <-10.5>\");\n\n\t\tAssertionFailedError e3 = assertThrows(AssertionFailedError.class, () -> assertEquals(1.1d, 1.1d, Double.NaN));\n\t\tassertMessageEndsWith(e3, \"positive delta expected but was: <NaN>\");\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithDeltaWithUnequalValues() {\n\t\tAssertionFailedError e1 = assertThrows(AssertionFailedError.class, () -> assertEquals(9.9d, 9.7d, 0.1d));\n\t\tassertMessageEndsWith(e1, \"expected: <9.9> but was: <9.7>\");\n\t\tassertExpectedAndActualValues(e1, 9.9d, 9.7d);\n\n\t\tAssertionFailedError e2 = assertThrows(AssertionFailedError.class, () -> assertEquals(0.1d, 0.05d, 0.001d));\n\t\tassertMessageEndsWith(e2, \"expected: <0.1> but was: <0.05>\");\n\t\tassertExpectedAndActualValues(e2, 0.1d, 0.05d);\n\n\t\tAssertionFailedError e3 = assertThrows(AssertionFailedError.class, () -> assertEquals(17.11d, 15.11d, 1.1d));\n\t\tassertMessageEndsWith(e3, \"expected: <17.11> but was: <15.11>\");\n\t\tassertExpectedAndActualValues(e3, 17.11d, 15.11d);\n\n\t\tAssertionFailedError e4 = assertThrows(AssertionFailedError.class, () -> assertEquals(-7.2d, -5.9d, 1.1d));\n\t\tassertMessageEndsWith(e4, \"expected: <-7.2> but was: <-5.9>\");\n\t\tassertExpectedAndActualValues(e4, -7.2d, -5.9d);\n\n\t\tAssertionFailedError e5 = assertThrows(AssertionFailedError.class, () -> assertEquals(+0.0d, -0.001d, .00001d));\n\t\tassertMessageEndsWith(e5, \"expected: <0.0> but was: <-0.001>\");\n\t\tassertExpectedAndActualValues(e5, +0.0d, -0.001d);\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithDeltaWithUnequalValuesAndMessage() {\n\t\tExecutable assertion = () -> assertEquals(42.42d, 42.4d, 0.001d, \"message\");\n\n\t\tAssertionFailedError e = assertThrows(AssertionFailedError.class, assertion);\n\n\t\tassertMessageStartsWith(e, \"message\");\n\t\tassertMessageEndsWith(e, \"expected: <42.42> but was: <42.4>\");\n\t\tassertExpectedAndActualValues(e, 42.42d, 42.4d);\n\t}\n\n\t@Test\n\tvoid assertEqualsDoubleWithDeltaWithUnequalValuesAndMessageSupplier() {\n\t\tExecutable assertion = () -> assertEquals(0.9d, 10.12d, 5.001d, () -> \"message\");\n\n\t\tAssertionFailedError e = assertThrows(AssertionFailedError.class, assertion);\n\n\t\tassertMessageStartsWith(e, \"message\");\n\t\tassertMessageEndsWith(e, \"expected: <0.9> but was: <10.12>\");\n\t\tassertExpectedAndActualValues(e, 0.9d, 10.12d);\n\t}\n\n\t@Test\n\tvoid assertEqualsWithNullReferences() {\n\t\tObject null1 = null;\n\t\tObject null2 = null;\n\n\t\tassertEquals(null1, null);\n\t\tassertEquals(null, null2);\n\t\tassertEquals(null1, null2);\n\t}\n\n\t@Test\n\tvoid assertEqualsWithSameObject() {\n\t\tObject foo = new Object();\n\t\tassertEquals(foo, foo);\n\t\tassertEquals(foo, foo, \"message\");\n\t\tassertEquals(foo, foo, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertEqualsWithEquivalentStrings() {\n\t\tassertEquals(new String(\"foo\"), new String(\"foo\"));\n\t}\n\n\t@Test\n\tvoid assertEqualsWithNullVsObject() {\n\t\ttry {\n\t\t\tassertEquals(null, \"foo\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <null> but was: <foo>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"foo\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsWithObjectVsNull() {\n\t\ttry {\n\t\t\tassertEquals(\"foo\", null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <foo> but was: <null>\");\n\t\t\tassertExpectedAndActualValues(ex, \"foo\", null);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsWithObjectWithNullStringReturnedFromToStringVsNull() {\n\t\ttry {\n\t\t\tassertEquals(\"null\", null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"expected: java.lang.String@\");\n\t\t\tassertMessageEndsWith(ex, \"<null> but was: <null>\");\n\t\t\tassertExpectedAndActualValues(ex, \"null\", null);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsWithNullVsObjectWithNullStringReturnedFromToString() {\n\t\ttry {\n\t\t\tassertEquals(null, \"null\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"expected: <null> but was: java.lang.String@\");\n\t\t\tassertMessageEndsWith(ex, \"<null>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"null\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsWithNullVsObjectAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(null, \"foo\", () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <null> but was: <foo>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"foo\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsWithObjectVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertEquals(\"foo\", null, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageEndsWith(ex, \"expected: <foo> but was: <null>\");\n\t\t\tassertExpectedAndActualValues(ex, \"foo\", null);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertEqualsInvokesEqualsMethodForIdenticalObjects() {\n\t\tObject obj = new EqualsThrowsException();\n\t\tassertThrows(NumberFormatException.class, () -> assertEquals(obj, obj));\n\t}\n\n\t@Test\n\tvoid assertEqualsWithUnequalObjectWhoseToStringImplementationThrowsAnException() {\n\t\ttry {\n\t\t\tassertEquals(new ToStringThrowsException(), \"foo\");\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"expected: <\" + ToStringThrowsException.class.getName() + \"@\");\n\t\t\tassertMessageEndsWith(ex, \"but was: <foo>\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Nested\n\tclass MixedBoxedAndUnboxedPrimitivesTests {\n\n\t\t@Test\n\t\tvoid bytes() {\n\t\t\tbyte primitive = (byte) 42;\n\t\t\tByte wrapper = Byte.valueOf(\"42\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shorts() {\n\t\t\tshort primitive = (short) 42;\n\t\t\tShort wrapper = Short.valueOf(\"42\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid integers() {\n\t\t\tint primitive = 42;\n\t\t\tInteger wrapper = Integer.valueOf(\"42\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid longs() {\n\t\t\tlong primitive = 42L;\n\t\t\tLong wrapper = Long.valueOf(\"42\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid floats() {\n\t\t\tfloat primitive = 42.0f;\n\t\t\tFloat wrapper = Float.valueOf(\"42.0\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, 0.0f);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, 0.0f, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\t\t\tassertEquals(primitive, wrapper, 0.0f, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, 0.0f);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, 0.0f, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t\tassertEquals(wrapper, primitive, 0.0f, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid doubles() {\n\t\t\tdouble primitive = 42.0d;\n\t\t\tDouble wrapper = Double.valueOf(\"42.0\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, 0.0d);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, 0.0d, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\t\t\tassertEquals(primitive, wrapper, 0.0d, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, 0.0d);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, 0.0d, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t\tassertEquals(wrapper, primitive, 0.0d, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid booleans() {\n\t\t\tboolean primitive = true;\n\t\t\tBoolean wrapper = Boolean.valueOf(\"true\");\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid chars() {\n\t\t\tchar primitive = 'a';\n\t\t\tCharacter wrapper = Character.valueOf('a');\n\n\t\t\tassertEquals(primitive, wrapper);\n\t\t\tassertEquals(primitive, wrapper, \"message\");\n\t\t\tassertEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertEquals(wrapper, primitive);\n\t\t\tassertEquals(wrapper, primitive, \"message\");\n\t\t\tassertEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"overrides\")\n\tprivate static class EqualsThrowsException {\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tthrow new NumberFormatException();\n\t\t}\n\t}\n\n\tprivate static class ToStringThrowsException {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tthrow new NumberFormatException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertFalseAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedAndActualValues;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertFalseAssertionsTests {\n\n\t@Test\n\tvoid assertFalseWithBooleanFalse() {\n\t\tassertFalse(false);\n\t\tassertFalse(false, \"test\");\n\t\tassertFalse(false, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanSupplierFalse() {\n\t\tassertFalse(() -> false);\n\t\tassertFalse(() -> false, \"test\");\n\t\tassertFalse(() -> false, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanFalseAndMessageSupplier() {\n\t\tassertFalse(false, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanSupplierFalseAndMessageSupplier() {\n\t\tassertFalse(() -> false, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanTrueAndDefaultMessageWithExpectedAndActualValues() {\n\t\ttry {\n\t\t\tassertFalse(true);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <false> but was: <true>\");\n\t\t\tassertExpectedAndActualValues(ex, false, true);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanTrueAndString() {\n\t\ttry {\n\t\t\tassertFalse(true, \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <false> but was: <true>\");\n\t\t\tassertExpectedAndActualValues(ex, false, true);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanSupplierTrueAndString() {\n\t\ttry {\n\t\t\tassertFalse(() -> true, \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <false> but was: <true>\");\n\t\t\tassertExpectedAndActualValues(ex, false, true);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanTrueAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertFalse(true, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <false> but was: <true>\");\n\t\t\tassertExpectedAndActualValues(ex, false, true);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanSupplierTrueAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertFalse(() -> true, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <false> but was: <true>\");\n\t\t\tassertExpectedAndActualValues(ex, false, true);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertInstanceOfAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.io.IOException;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions#assertInstanceOf(Class, Object)}.\n *\n * @since 5.8\n */\nclass AssertInstanceOfAssertionsTests {\n\n\t@Test\n\tvoid assertInstanceOfFailsNullValue() {\n\t\tassertInstanceOfFails(String.class, null, \"null value\");\n\t}\n\n\t@Test\n\tvoid assertInstanceOfFailsWrongTypeValue() {\n\t\tassertInstanceOfFails(String.class, 1, \"type\");\n\t}\n\n\t@Test\n\tvoid assertInstanceOfFailsWrongExceptionValue() {\n\t\tassertInstanceOfFails(RuntimeException.class, new IOException(), \"type\");\n\t}\n\n\t@Test\n\tvoid assertInstanceOfFailsSuperTypeExceptionValue() {\n\t\tassertInstanceOfFails(IllegalArgumentException.class, new RuntimeException(), \"type\");\n\t}\n\n\tprivate static class BaseClass {\n\t}\n\n\tprivate static class SubClass extends BaseClass {\n\n\t}\n\n\t@Test\n\tvoid assertInstanceOfFailsSuperTypeValue() {\n\t\tassertInstanceOfFails(SubClass.class, new BaseClass(), \"type\");\n\t}\n\n\t@Test\n\tvoid assertInstanceOfSucceedsSameTypeValue() {\n\t\tassertInstanceOfSucceeds(String.class, \"indeed a String\");\n\t\tassertInstanceOfSucceeds(BaseClass.class, new BaseClass());\n\t\tassertInstanceOfSucceeds(SubClass.class, new SubClass());\n\t}\n\n\t@Test\n\tvoid assertInstanceOfSucceedsExpectSuperClassOfValue() {\n\t\tassertInstanceOfSucceeds(CharSequence.class, \"indeed a CharSequence\");\n\t\tassertInstanceOfSucceeds(BaseClass.class, new SubClass());\n\t}\n\n\t@Test\n\tvoid assertInstanceOfSucceedsSameTypeExceptionValue() {\n\t\tassertInstanceOfSucceeds(UnsupportedOperationException.class, new UnsupportedOperationException());\n\t}\n\n\t@Test\n\tvoid assertInstanceOfSucceedsExpectSuperClassOfExceptionValue() {\n\t\tassertInstanceOfSucceeds(RuntimeException.class, new IllegalArgumentException(\"is a RuntimeException\"));\n\t}\n\n\tprivate <T> void assertInstanceOfSucceeds(Class<T> expectedType, Object actualValue) {\n\t\tT res = assertInstanceOf(expectedType, actualValue);\n\t\tassertSame(res, actualValue);\n\t\tres = assertInstanceOf(expectedType, actualValue, \"extra\");\n\t\tassertSame(res, actualValue);\n\t\tres = assertInstanceOf(expectedType, actualValue, () -> \"extra\");\n\t\tassertSame(res, actualValue);\n\t}\n\n\tprivate void assertInstanceOfFails(Class<?> expectedType, @Nullable Object actualValue, String unexpectedSort) {\n\t\tString valueType = actualValue == null ? \"null\" : actualValue.getClass().getCanonicalName();\n\t\tString expectedMessage = \"Unexpected %s, expected: <%s> but was: <%s>\".formatted(unexpectedSort,\n\t\t\texpectedType.getCanonicalName(), valueType);\n\t\tThrowable expectedCause = actualValue instanceof Throwable throwable ? throwable : null;\n\n\t\tassertThrowsWithMessage(expectedMessage, expectedCause, () -> assertInstanceOf(expectedType, actualValue));\n\t\tassertThrowsWithMessage(\"extra ==> \" + expectedMessage, expectedCause,\n\t\t\t() -> assertInstanceOf(expectedType, actualValue, \"extra\"));\n\t\tassertThrowsWithMessage(\"extra ==> \" + expectedMessage, expectedCause,\n\t\t\t() -> assertInstanceOf(expectedType, actualValue, () -> \"extra\"));\n\t}\n\n\tprivate void assertThrowsWithMessage(String expectedMessage, @Nullable Throwable expectedCause,\n\t\t\tExecutable executable) {\n\t\tThrowable throwable = assertThrows(AssertionFailedError.class, executable);\n\t\tassertEquals(expectedMessage, throwable.getMessage());\n\t\tassertSame(expectedCause, throwable.getCause());\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertIterableEqualsAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEndsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertIterableEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.IterableFactory.listOf;\nimport static org.junit.jupiter.api.IterableFactory.setOf;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertIterableEqualsAssertionsTests {\n\n\t@Test\n\tvoid assertIterableEqualsEqualToSelf() {\n\t\tList<Object> list = listOf(\"a\", 'b', 1, 2);\n\t\tassertIterableEquals(list, list);\n\t\tassertIterableEquals(list, list, \"message\");\n\t\tassertIterableEquals(list, list, () -> \"message\");\n\n\t\tSet<Object> set = setOf(\"a\", 'b', 1, 2);\n\t\tassertIterableEquals(set, set);\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsEqualObjectsOfSameType() {\n\t\tassertIterableEquals(listOf(), listOf());\n\t\tassertIterableEquals(listOf(\"abc\"), listOf(\"abc\"));\n\t\tassertIterableEquals(listOf(\"abc\", 1, 2L, 3D), listOf(\"abc\", 1, 2L, 3D));\n\t\tassertIterableEquals(setOf(), setOf());\n\t\tassertIterableEquals(setOf(\"abc\"), setOf(\"abc\"));\n\t\tassertIterableEquals(setOf(\"abc\", 1, 2L, 3D), setOf(\"abc\", 1, 2L, 3D));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterables() {\n\t\tassertIterableEquals(listOf(listOf(listOf())), listOf(listOf(listOf())));\n\t\tassertIterableEquals(setOf(setOf(setOf())), setOf(setOf(setOf())));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithNull() {\n\t\tassertIterableEquals(listOf(null, listOf(null, listOf(null, null)), null, listOf((List<Object>) null)),\n\t\t\tlistOf(null, listOf(null, listOf(null, null)), null, listOf((List<Object>) null)));\n\t\tassertIterableEquals(setOf(null, setOf(null, setOf(null, null)), null, setOf((Set<Object>) null)),\n\t\t\tsetOf(null, setOf(null, setOf(null, null)), null, setOf((Set<Object>) null)));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithStrings() {\n\t\tassertIterableEquals(listOf(\"a\", listOf(listOf(\"b\", listOf(\"c\", \"d\"))), \"e\"),\n\t\t\tlistOf(\"a\", listOf(listOf(\"b\", listOf(\"c\", \"d\"))), \"e\"));\n\t\tassertIterableEquals(setOf(\"a\", setOf(setOf(\"b\", setOf(\"c\", \"d\"))), \"e\"),\n\t\t\tsetOf(\"a\", setOf(setOf(\"b\", setOf(\"c\", \"d\"))), \"e\"));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithIntegers() {\n\t\tassertIterableEquals(listOf(listOf(1), listOf(2), listOf(listOf(3, listOf(4)))),\n\t\t\tlistOf(listOf(1), listOf(2), listOf(listOf(3, listOf(4)))));\n\t\tassertIterableEquals(setOf(setOf(1), setOf(2), setOf(setOf(3, setOf(4)))),\n\t\t\tsetOf(setOf(1), setOf(2), setOf(setOf(3, setOf(4)))));\n\t\tassertIterableEquals(listOf(listOf(1), listOf(listOf(1))), setOf(setOf(1), setOf(setOf(1))));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithDeeplyNestedObject() {\n\t\tassertIterableEquals(listOf(listOf(listOf(listOf(listOf(listOf(listOf(\"abc\"))))))),\n\t\t\tlistOf(listOf(listOf(listOf(listOf(listOf(listOf(\"abc\"))))))));\n\t\tassertIterableEquals(setOf(setOf(setOf(setOf(setOf(setOf(setOf(\"abc\"))))))),\n\t\t\tsetOf(setOf(setOf(setOf(setOf(setOf(setOf(\"abc\"))))))));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithNaN() {\n\t\tassertIterableEquals(listOf(null, listOf(null, Double.NaN, listOf(Float.NaN, null, listOf()))),\n\t\t\tlistOf(null, listOf(null, Double.NaN, listOf(Float.NaN, null, listOf()))));\n\t\tassertIterableEquals(setOf(null, setOf(null, Double.NaN, setOf(Float.NaN, null, setOf()))),\n\t\t\tsetOf(null, setOf(null, Double.NaN, setOf(Float.NaN, null, setOf()))));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesWithObjectsOfDifferentTypes() {\n\t\tassertIterableEquals(listOf(new String(\"a\"), Integer.valueOf(1), listOf(Double.parseDouble(\"1.1\"), \"b\")),\n\t\t\tlistOf(new String(\"a\"), Integer.valueOf(1), listOf(Double.parseDouble(\"1.1\"), \"b\")));\n\t\tassertIterableEquals(setOf(new String(\"a\"), Integer.valueOf(1), setOf(Double.parseDouble(\"1.1\"), \"b\")),\n\t\t\tsetOf(new String(\"a\"), Integer.valueOf(1), setOf(Double.parseDouble(\"1.1\"), \"b\")));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesOfMixedSubtypes() {\n\t\tassertIterableEquals(\n\t\t\tlistOf(1, 2, listOf(3, setOf(4, 5), setOf(6L), listOf(listOf(setOf(7)))), setOf(8), listOf(setOf(9L))),\n\t\t\tlistOf(1, 2, listOf(3, setOf(4, 5), setOf(6L), listOf(listOf(setOf(7)))), setOf(8), listOf(setOf(9L))));\n\n\t\tassertIterableEquals(\n\t\t\tlistOf(\"a\", setOf('b', 'c'), setOf((int) 'd'), listOf(listOf(listOf(\"ef\"), listOf(listOf(\"ghi\"))))),\n\t\t\tsetOf(\"a\", listOf('b', 'c'), listOf((int) 'd'), setOf(setOf(setOf(\"ef\"), setOf(setOf(\"ghi\"))))));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterableVsNull() {\n\t\ttry {\n\t\t\tassertIterableEquals(null, listOf(\"a\", \"b\", 1, listOf()));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected iterable was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf('a', 1, new Object(), 10L), null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual iterable was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterableVsNull() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(listOf(), 1, \"2\", setOf('3', listOf((List<Object>) null))),\n\t\t\t\tlistOf(listOf(), 1, \"2\", setOf('3', listOf(listOf(\"4\")))));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected iterable was <null> at index [3][1][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(setOf(1, 2, listOf(3, listOf(\"4\", setOf(5, setOf(6)))), \"7\"),\n\t\t\t\tsetOf(1, 2, listOf(3, listOf(\"4\", setOf(5, null))), \"7\"));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"actual iterable was <null> at index [2][1][1][1]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterableVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(null, listOf('a', \"b\", 10, 20D), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected iterable was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"hello\", 42), null, \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual iterable was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterableVsNullAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1, listOf(2, 3, listOf(4, 5, listOf((List<Object>) null)))),\n\t\t\t\tlistOf(1, listOf(2, 3, listOf(4, 5, listOf(listOf(6))))), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected iterable was <null> at index [1][2][2][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1, listOf(2, listOf(3, listOf(listOf(4))))),\n\t\t\t\tlistOf(1, listOf(2, listOf(3, listOf((List<Object>) null)))), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual iterable was <null> at index [1][1][1][0]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterableVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(null, setOf(42, \"42\", listOf(42F), 42D), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected iterable was <null>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(listOf(\"a\"), listOf()), null, () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual iterable was <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterableVsNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"1\", \"2\", \"3\", listOf(\"4\", listOf((List<Object>) null))),\n\t\t\t\tlistOf(\"1\", \"2\", \"3\", listOf(\"4\", listOf(listOf(5)))), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"expected iterable was <null> at index [3][1][0]\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(setOf(1, 2, setOf(\"3\", setOf('4', setOf(5, 6, setOf())))),\n\t\t\t\tsetOf(1, 2, setOf(\"3\", setOf('4', setOf(5, 6, null)))), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"actual iterable was <null> at index [2][1][1][2]\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterablesOfDifferentLength() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf('a', \"b\", 'c'), listOf('a', \"b\", 'c', 1));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable lengths differ, expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesOfDifferentLength() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"a\", setOf(\"b\", listOf(\"c\", \"d\", setOf(\"e\", 1, 2, 3)))),\n\t\t\t\tlistOf(\"a\", setOf(\"b\", listOf(\"c\", \"d\", setOf(\"e\", 1, 2, 3, 4, 5)))));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable lengths differ at index [1][1][2], expected: <4> but was: <6>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(listOf(listOf(listOf(listOf(listOf(listOf('a'))))))),\n\t\t\t\tlistOf(listOf(listOf(listOf(listOf(listOf(listOf('a', 'b'))))))));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable lengths differ at index [0][0][0][0][0][0], expected: <1> but was: <2>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterablesOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(setOf('a', 1), setOf('a', 1, new Object()), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable lengths differ, expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesOfDifferentLengthAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf('a', 1, listOf(2, 3)), listOf('a', 1, listOf(2, 3, 4, 5)), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable lengths differ at index [2], expected: <2> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsIterablesOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(setOf(\"a\", \"b\", \"c\"), setOf(\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable lengths differ, expected: <3> but was: <6>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsNestedIterablesOfDifferentLengthAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"a\", setOf(1, 2, 3, listOf(4.0, 5.1, 6.1), 7)),\n\t\t\t\tlistOf(\"a\", setOf(1, 2, 3, listOf(4.0, 5.1, 6.1, 7.0), 8)), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable lengths differ at index [1][3], expected: <3> but was: <4>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentIterables() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1L, \"2\", '3', 4, 5D), listOf(1L, \"2\", '9', 4, 5D));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable contents differ at index [2], expected: <3> but was: <9>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"a\", 10, 11, 12, Double.NaN), listOf(\"a\", 10, 11, 12, 13.55D));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable contents differ at index [4], expected: <NaN> but was: <13.55>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentNestedIterables() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1, 2, listOf(3, listOf(4, listOf(false, true)))),\n\t\t\t\tlistOf(1, 2, listOf(3, listOf(4, listOf(true, false)))));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"iterable contents differ at index [2][1][1][0], expected: <false> but was: <true>\");\n\t\t}\n\n\t\tList<Object> differentElement = listOf();\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1, 2, 3, listOf(listOf(4, listOf(5)))),\n\t\t\t\tlistOf(1, 2, 3, listOf(listOf(4, listOf(differentElement)))));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"iterable contents differ at index [3][0][1][0], expected: <5> but was: <\" + differentElement + \">\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentIterablesAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(1.1D, 2L, \"3\"), listOf(1D, 2L, \"3\"), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [0], expected: <1.1> but was: <1.0>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentNestedIterablesAndMessage() {\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(9, 8, '6', listOf(5, 4, \"3\", listOf(\"2\", '1'))),\n\t\t\t\tlistOf(9, 8, '6', listOf(5, 4, \"3\", listOf(\"99\", '1'))), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [3][3][0], expected: <2> but was: <99>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(9, 8, '6', listOf(5, 4, \"3\", listOf(\"2\", \"1\"))),\n\t\t\t\tlistOf(9, 8, '6', listOf(5, 4, \"3\", listOf(\"99\", \"1\"))), \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [3][3][0], expected: <2> but was: <99>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentIterablesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(setOf(\"one\", 1L, Double.MIN_VALUE, \"abc\"), setOf(\"one\", 1L, 42.42, \"abc\"),\n\t\t\t\t() -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [2], expected: <4.9E-324> but was: <42.42>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsDifferentNestedIterablesAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertIterableEquals(setOf(\"one\", 1L, setOf(\"a\", 'b', setOf(1, setOf(2, 3))), \"abc\"),\n\t\t\t\tsetOf(\"one\", 1L, setOf(\"a\", 'b', setOf(1, setOf(2, 4))), \"abc\"), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [2][2][1][1], expected: <3> but was: <4>\");\n\t\t}\n\n\t\ttry {\n\t\t\tassertIterableEquals(listOf(\"j\", listOf(\"a\"), setOf(42), \"ab\", setOf(1, listOf(3))),\n\t\t\t\tlistOf(\"j\", listOf(\"a\"), setOf(42), \"ab\", setOf(1, listOf(5))), () -> \"message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"message\");\n\t\t\tassertMessageEndsWith(ex, \"iterable contents differ at index [4][1][0], expected: <3> but was: <5>\");\n\t\t}\n\t}\n\n\t@Test\n\t// https://github.com/junit-team/junit-framework/issues/2157\n\tvoid assertIterableEqualsWithListOfPath() {\n\t\tvar expected = listOf(Path.of(\"1\"));\n\t\tvar actual = listOf(Path.of(\"1\"));\n\t\tassertDoesNotThrow(() -> assertIterableEquals(expected, actual));\n\t}\n\n\t@Test\n\tvoid assertIterableEqualsThrowsStackOverflowErrorForInterlockedRecursiveStructures() {\n\t\tvar expected = new ArrayList<>();\n\t\tvar actual = new ArrayList<>();\n\t\tactual.add(expected);\n\t\texpected.add(actual);\n\t\tassertThrows(StackOverflowError.class, () -> assertIterableEquals(expected, actual));\n\t}\n\n\t@Test\n\t// https://github.com/junit-team/junit-framework/issues/2915\n\tvoid assertIterableEqualsWithDifferentListOfPath() {\n\t\ttry {\n\t\t\tvar expected = listOf(Path.of(\"1\").resolve(\"2\"));\n\t\t\tvar actual = listOf(Path.of(\"1\").resolve(\"3\"));\n\t\t\tassertIterableEquals(expected, actual);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"iterable contents differ at index [0][1], expected: <2> but was: <3>\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertLinesMatchAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertLinesMatch.isFastForwardLine;\nimport static org.junit.jupiter.api.AssertLinesMatch.parseFastForwardLimit;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.function.Supplier;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertLinesMatchAssertionsTests {\n\n\t@Test\n\tvoid assertLinesMatchEmptyLists() {\n\t\tassertLinesMatch(Collections.emptyList(), new ArrayList<>());\n\t}\n\n\t@Test\n\tvoid assertLinesMatchSameListInstance() {\n\t\tList<String> list = List.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\tassertLinesMatch(list, list);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchPlainEqualLists() {\n\t\tList<String> expected = List.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\tList<String> actual = List.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingRegexPatterns() {\n\t\tList<String> expected = List.of(\"^first.+line\", \"second\\\\s*line\", \"th.rd l.ne\", \"last line$\");\n\t\tList<String> actual = List.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerAtEndOfExpectedLines() {\n\t\tList<String> expected = List.of(\"first line\", \">> ignore all following lines >>\");\n\t\tList<String> actual = List.of(\"first line\", \"I\", \"II\", \"III\", \"IV\", \"V\", \"VI\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarker() {\n\t\tList<String> expected = List.of(\"first line\", \">> skip lines until next matches >>\", \"V\", \"last line\");\n\t\tList<String> actual = List.of(\"first line\", \"I\", \"II\", \"III\", \"IV\", \"V\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithLimit1() {\n\t\tList<String> expected = List.of(\"first line\", \">> 1 >>\", \"last line\");\n\t\tList<String> actual = List.of(\"first line\", \"skipped\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithLimit3() {\n\t\tList<String> expected = List.of(\">> 3 >>\");\n\t\tList<String> actual = List.of(\"first line\", \"skipped\", \"last line\");\n\t\tassertLinesMatch(expected, actual);\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"unchecked\", \"rawtypes\", \"DataFlowIssue\" })\n\tvoid assertLinesMatchWithNullFails() {\n\t\tassertPreconditionViolationFor(() -> assertLinesMatch(null, (List) null));\n\t\tassertPreconditionViolationFor(() -> assertLinesMatch(null, Collections.emptyList()));\n\t\tassertPreconditionViolationFor(() -> assertLinesMatch(Collections.emptyList(), null));\n\t}\n\n\t@Test\n\tvoid assertLinesMatchWithNullElementsFails() {\n\t\tvar list = List.of(\"1\", \"2\", \"3\");\n\t\tvar withNullElement = Arrays.asList(\"1\", null, \"3\"); // List.of() doesn't permit null values.\n\t\tassertDoesNotThrow(() -> assertLinesMatch(withNullElement, withNullElement));\n\t\tassertPreconditionViolationNotNullFor(\"expected line\", () -> assertLinesMatch(withNullElement, list));\n\t\tassertPreconditionViolationNotNullFor(\"actual line\", () -> assertLinesMatch(list, withNullElement));\n\t}\n\n\tprivate void assertError(AssertionFailedError error, String expectedMessage, List<String> expectedLines,\n\t\t\tList<String> actualLines) {\n\t\tassertEquals(expectedMessage, error.getMessage());\n\t\tassertEquals(String.join(System.lineSeparator(), expectedLines), error.getExpected().getStringRepresentation());\n\t\tassertEquals(String.join(System.lineSeparator(), actualLines), error.getActual().getStringRepresentation());\n\t}\n\n\t@Test\n\tvoid assertLinesMatchMoreExpectedThanActualAvailableFails() {\n\t\tvar expected = List.of(\"first line\", \"second line\", \"third line\");\n\t\tvar actual = List.of(\"first line\", \"third line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"expected 3 lines, but only got 2\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchFailsWithDescriptiveErrorMessage() {\n\t\tvar expected = List.of(\"first line\", \"second line\", \"third line\");\n\t\tvar actual = List.of(\"first line\", \"sec0nd line\", \"third line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tvar expectedMessage = String.join(System.lineSeparator(), List.of( //\n\t\t\t\"expected line #2 doesn't match actual line #2\", //\n\t\t\t\"\\texpected: `second line`\", //\n\t\t\t\"\\t  actual: `sec0nd line`\"));\n\t\tassertError(error, expectedMessage, expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchMoreActualLinesThenExpectedFails() {\n\t\tvar expected = List.of(\"first line\", \"second line\", \"third line\");\n\t\tvar actual = List.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"more actual lines than expected: 1\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithTooLowLimitFails() {\n\t\tvar expected = List.of(\"first line\", \">> 1 >>\");\n\t\tvar actual = List.of(\"first line\", \"skipped\", \"last line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"terminal fast-forward(1) error: fast-forward(2) expected\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithTooHighLimitFails() {\n\t\tvar expected = List.of(\"first line\", \">> 100 >>\");\n\t\tvar actual = List.of(\"first line\", \"skipped\", \"last line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"terminal fast-forward(100) error: fast-forward(2) expected\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithTooHighLimitAndFollowingLineFails() {\n\t\t/*\n\t\t * It is important here that the line counts are expected <= actual, that the\n\t\t * fast-forward exceeds the available actual lines and that it is not a\n\t\t * terminal fast-forward.\n\t\t */\n\t\tvar expected = List.of(\"first line\", \">> 3 >>\", \"not present\");\n\t\tvar actual = List.of(\"first line\", \"first skipped\", \"second skipped\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"fast-forward(3) error: not enough actual lines remaining (2)\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithoutMatchingNextLineFails() {\n\t\tvar expected = List.of(\"first line\", \">> fails, because next line is >>\", \"not present\");\n\t\tvar actual = List.of(\"first line\", \"skipped\", \"last line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"fast-forward(∞) didn't find: `not present`\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchUsingFastForwardMarkerWithExtraExpectLineFails() {\n\t\tvar expected = List.of(\"first line\", \">> fails, because final line is missing >>\", \"last line\", \"not present\");\n\t\tvar actual = List.of(\"first line\", \"first skipped\", \"second skipped\", \"last line\");\n\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual));\n\t\tassertError(error, \"expected line #4:`not present` not found - actual lines depleted\", expected, actual);\n\t}\n\n\t@Test\n\tvoid assertLinesMatchIsFastForwardLine() {\n\t\tassertAll(\"valid fast-forward lines\", //\n\t\t\t() -> assertTrue(isFastForwardLine(\">>>>\")), () -> assertTrue(isFastForwardLine(\">> >>\")),\n\t\t\t() -> assertTrue(isFastForwardLine(\">> stacktrace >>\")),\n\t\t\t() -> assertTrue(isFastForwardLine(\">> single line, non Integer.parse()-able comment >>\")),\n\t\t\t() -> assertTrue(isFastForwardLine(\">>9>>\")), () -> assertTrue(isFastForwardLine(\">> 9 >>\")),\n\t\t\t() -> assertTrue(isFastForwardLine(\">> -9 >>\")), () -> assertTrue(isFastForwardLine(\" >> 9 >> \")),\n\t\t\t() -> assertTrue(isFastForwardLine(\"  >> 9 >>  \")));\n\t}\n\n\t@Test\n\tvoid assertLinesMatchParseFastForwardLimit() {\n\t\tassertAll(\"valid fast-forward limits\", //\n\t\t\t() -> assertEquals(Integer.MAX_VALUE, parseFastForwardLimit(\">>>>\")),\n\t\t\t() -> assertEquals(Integer.MAX_VALUE, parseFastForwardLimit(\">> >>\")),\n\t\t\t() -> assertEquals(Integer.MAX_VALUE, parseFastForwardLimit(\">> stacktrace >>\")),\n\t\t\t() -> assertEquals(Integer.MAX_VALUE, parseFastForwardLimit(\">> non Integer.parse()-able comment >>\")),\n\t\t\t() -> assertEquals(9, parseFastForwardLimit(\">>9>>\")),\n\t\t\t() -> assertEquals(9, parseFastForwardLimit(\">> 9 >>\")),\n\t\t\t() -> assertEquals(9, parseFastForwardLimit(\" >> 9 >> \")),\n\t\t\t() -> assertEquals(9, parseFastForwardLimit(\"  >> 9 >>  \")));\n\t\tassertPreconditionViolationFor(() -> parseFastForwardLimit(\">>0>>\"))//\n\t\t\t\t.withMessage(\"fast-forward(0) limit must be greater than zero\");\n\t\tassertPreconditionViolationFor(() -> parseFastForwardLimit(\">>-1>>\"))//\n\t\t\t\t.withMessage(\"fast-forward(-1) limit must be greater than zero\");\n\t\tassertPreconditionViolationFor(() -> parseFastForwardLimit(\">>-2147483648>>\"))//\n\t\t\t\t.withMessage(\"fast-forward(-2147483648) limit must be greater than zero\");\n\t}\n\n\t@Test\n\tvoid assertLinesMatchMatches() {\n\t\tRandom random = new Random();\n\t\tassertAll(\"do match\", //\n\t\t\t() -> assertTrue(\n\t\t\t\tAssertLinesMatch.matches(\"duration: [\\\\d]+ ms\", \"duration: \" + random.nextInt(1000) + \" ms\")),\n\t\t\t() -> assertTrue(AssertLinesMatch.matches(\"123\", \"123\")),\n\t\t\t() -> assertTrue(AssertLinesMatch.matches(\".*\", \"123\")),\n\t\t\t() -> assertTrue(AssertLinesMatch.matches(\"\\\\d+\", \"123\")));\n\t\tassertAll(\"don't match\", //\n\t\t\t() -> assertFalse(\n\t\t\t\tAssertLinesMatch.matches(\"duration: [\\\\d]+ ms\", \"duration: \" + random.nextGaussian() + \" ms\")),\n\t\t\t() -> assertFalse(AssertLinesMatch.matches(\"12\", \"123\")),\n\t\t\t() -> assertFalse(AssertLinesMatch.matches(\"..+\", \"1\")),\n\t\t\t() -> assertFalse(AssertLinesMatch.matches(\"\\\\d\\\\d+\", \"1\")));\n\t}\n\n\t@Test\n\tvoid largeListsThatDoNotMatchAreTruncated() {\n\t\tvar expected = IntStream.range(1, 999).boxed().map(Object::toString).toList();\n\t\tvar actual = IntStream.range(0, 1000).boxed().map(Object::toString).toList();\n\t\tvar error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertLinesMatch(expected, actual, \"custom message\"));\n\t\tvar expectedMessage = String.join(System.lineSeparator(), List.of( //\n\t\t\t\"custom message ==> expected line #1 doesn't match actual line #1\", //\n\t\t\t\"\\texpected: `1`\", //\n\t\t\t\"\\t  actual: `0`\"));\n\t\tassertError(error, expectedMessage, expected, actual);\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Nested\n\tclass WithCustomFailureMessage {\n\t\t@Test\n\t\tvoid simpleStringMessage() {\n\t\t\tString message = \"XXX\";\n\t\t\tvar expected = List.of(\"a\", \"b\", \"c\");\n\t\t\tvar actual = List.of(\"a\", \"d\", \"c\");\n\t\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual, message));\n\t\t\tvar expectedMessage = String.join(System.lineSeparator(), List.of( //\n\t\t\t\tmessage + \" ==> expected line #2 doesn't match actual line #2\", //\n\t\t\t\t\"\\texpected: `b`\", //\n\t\t\t\t\"\\t  actual: `d`\"));\n\t\t\tassertError(error, expectedMessage, expected, actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid stringSupplierWithMultiLineMessage() {\n\t\t\tvar message = \"XXX\\nYYY\";\n\t\t\tSupplier<@Nullable String> supplier = () -> message;\n\t\t\tvar expected = List.of(\"a\", \"b\", \"c\");\n\t\t\tvar actual = List.of(\"a\", \"d\", \"c\");\n\t\t\tvar error = assertThrows(AssertionFailedError.class, () -> assertLinesMatch(expected, actual, supplier));\n\t\t\tvar expectedMessage = String.join(System.lineSeparator(), List.of( //\n\t\t\t\tmessage + \" ==> expected line #2 doesn't match actual line #2\", //\n\t\t\t\t\"\\texpected: `b`\", //\n\t\t\t\t\"\\t  actual: `d`\"));\n\t\t\tassertError(error, expectedMessage, expected, actual);\n\t\t}\n\t}\n\n\t@Nested\n\tclass WithStreamsOfStrings {\n\t\t@Test\n\t\tvoid assertLinesMatchEmptyStreams() {\n\t\t\tassertLinesMatch(Stream.empty(), Stream.empty());\n\t\t}\n\n\t\t@Test\n\t\tvoid assertLinesMatchSameListInstance() {\n\t\t\tStream<String> stream = Stream.of(\"first line\", \"second line\", \"third line\", \"last line\");\n\t\t\tassertLinesMatch(stream, stream);\n\t\t}\n\n\t\t@Test\n\t\tvoid assertLinesMatchPlainEqualLists() {\n\t\t\tvar expected = \"\"\"\n\t\t\t\t\tfirst line\n\t\t\t\t\tsecond line\n\t\t\t\t\tthird line\n\t\t\t\t\tlast line\n\t\t\t\t\t\"\"\";\n\t\t\tvar actual = \"\"\"\n\t\t\t\t\tfirst line\n\t\t\t\t\tsecond line\n\t\t\t\t\tthird line\n\t\t\t\t\tlast line\n\t\t\t\t\t\"\"\";\n\t\t\tassertLinesMatch(expected.lines(), actual.lines());\n\t\t}\n\n\t\t@Test\n\t\tvoid assertLinesMatchUsingRegexPatterns() {\n\t\t\tvar expected = \"\"\"\n\t\t\t\t\t^first.+line\n\t\t\t\t\tsecond\\\\s*line\n\t\t\t\t\tth.rd l.ne\n\t\t\t\t\tlast line$\n\t\t\t\t\t\"\"\";\n\t\t\tvar actual = \"\"\"\n\t\t\t\t\tfirst line\n\t\t\t\t\tsecond line\n\t\t\t\t\tthird line\n\t\t\t\t\tlast line\n\t\t\t\t\t\"\"\";\n\t\t\tassertLinesMatch(expected.lines(), actual.lines());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotEqualsAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEndsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertNotEqualsAssertionsTests {\n\n\t@Nested\n\tclass AssertNotEqualsByte {\n\n\t\t@Test\n\t\tvoid assertNotEqualsByte() {\n\t\t\tbyte unexpected = 1;\n\t\t\tbyte actual = 2;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tbyte unexpected = 1;\n\t\t\tbyte actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tbyte unexpected = 1;\n\t\t\tbyte actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tbyte unexpected = 1;\n\t\t\tbyte actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsShort {\n\n\t\t@Test\n\t\tvoid assertNotEqualsShort() {\n\t\t\tshort unexpected = 1;\n\t\t\tshort actual = 2;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tshort unexpected = 1;\n\t\t\tshort actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tshort unexpected = 1;\n\t\t\tshort actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tshort unexpected = 1;\n\t\t\tshort actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsChar {\n\n\t\t@Test\n\t\tvoid assertNotEqualsChar() {\n\t\t\tchar unexpected = 'a';\n\t\t\tchar actual = 'b';\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tchar unexpected = 'a';\n\t\t\tchar actual = 'a';\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <a>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tchar unexpected = 'a';\n\t\t\tchar actual = 'a';\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <a>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tchar unexpected = 'a';\n\t\t\tchar actual = 'a';\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <a>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsInt {\n\n\t\t@Test\n\t\tvoid assertNotEqualsInt() {\n\t\t\tint unexpected = 1;\n\t\t\tint actual = 2;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tint unexpected = 1;\n\t\t\tint actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tint unexpected = 1;\n\t\t\tint actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tint unexpected = 1;\n\t\t\tint actual = 1;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsLong {\n\n\t\t@Test\n\t\tvoid assertNotEqualsLong() {\n\t\t\tlong unexpected = 1L;\n\t\t\tlong actual = 2L;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tlong unexpected = 1L;\n\t\t\tlong actual = 1L;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tlong unexpected = 1L;\n\t\t\tlong actual = 1L;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tlong unexpected = 1L;\n\t\t\tlong actual = 1L;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsFloatWithoutDelta {\n\n\t\t@Test\n\t\tvoid assertNotEqualsFloat() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 2.0f;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsForTwoNaNFloat() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(Float.NaN, Float.NaN);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <NaN>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsForPositiveInfinityFloat() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <Infinity>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsForNegativeInfinityFloat() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <-Infinity>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.0f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.0f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.0f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsFloatWithDelta {\n\n\t\t@Test\n\t\tvoid assertNotEqualsFloat() {\n\t\t\tassertNotEquals(1.0f, 1.5f, 0.4f);\n\t\t\tassertNotEquals(1.0f, 1.5f, 0.4f, \"message\");\n\t\t\tassertNotEquals(1.0f, 1.5f, 0.4f, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.5f;\n\t\t\tfloat delta = 0.5f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.5f;\n\t\t\tfloat delta = 0.5f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tfloat unexpected = 1.0f;\n\t\t\tfloat actual = 1.5f;\n\t\t\tfloat delta = 0.5f;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsDoubleWithoutDelta {\n\n\t\t@Test\n\t\tvoid assertNotEqualsDouble() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 2.0d;\n\t\t\tassertNotEquals(unexpected, actual);\n\t\t\tassertNotEquals(unexpected, actual, \"message\");\n\t\t\tassertNotEquals(unexpected, actual, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsForTwoNaNDouble() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(Double.NaN, Double.NaN);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <NaN>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.0d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.0d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.0d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.0>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsDoubleWithDelta {\n\n\t\t@Test\n\t\tvoid assertNotEqualsDouble() {\n\t\t\tassertNotEquals(1.0d, 1.5d, 0.4d);\n\t\t\tassertNotEquals(1.0d, 1.5d, 0.4d, \"message\");\n\t\t\tassertNotEquals(1.0d, 1.5d, 0.4d, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValues() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.5d;\n\t\t\tdouble delta = 0.5d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta);\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageEquals(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessage() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.5d;\n\t\t\tdouble delta = 0.5d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta, \"custom message\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid withEqualValuesWithMessageSupplier() {\n\t\t\tdouble unexpected = 1.0d;\n\t\t\tdouble actual = 1.5d;\n\t\t\tdouble delta = 0.5d;\n\t\t\ttry {\n\t\t\t\tassertNotEquals(unexpected, actual, delta, () -> \"custom message from supplier\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"custom message from supplier\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <1.5>\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass AssertNotEqualsObject {\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithNullVsObject() {\n\t\t\tassertNotEquals(null, \"foo\");\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithObjectVsNull() {\n\t\t\tassertNotEquals(\"foo\", null);\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithDifferentObjects() {\n\t\t\tassertNotEquals(new Object(), new Object());\n\t\t\tassertNotEquals(new Object(), new Object(), \"message\");\n\t\t\tassertNotEquals(new Object(), new Object(), () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithNullVsObjectAndMessageSupplier() {\n\t\t\tassertNotEquals(null, \"foo\", () -> \"test\");\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithEquivalentStringsAndMessage() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(new String(\"foo\"), new String(\"foo\"), \"test\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <foo>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsWithEquivalentStringsAndMessageSupplier() {\n\t\t\ttry {\n\t\t\t\tassertNotEquals(new String(\"foo\"), new String(\"foo\"), () -> \"test\");\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\t\tassertMessageEndsWith(ex, \"expected: not equal but was: <foo>\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid assertNotEqualsInvokesEqualsMethodForIdenticalObjects() {\n\t\t\tObject obj = new EqualsThrowsExceptionClass();\n\t\t\tassertThrows(NumberFormatException.class, () -> assertNotEquals(obj, obj));\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Nested\n\tclass MixedBoxedAndUnboxedPrimitivesTests {\n\n\t\t@Test\n\t\tvoid bytes() {\n\t\t\tbyte primitive = (byte) 42;\n\t\t\tByte wrapper = Byte.valueOf(\"99\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shorts() {\n\t\t\tshort primitive = (short) 42;\n\t\t\tShort wrapper = Short.valueOf(\"99\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid integers() {\n\t\t\tint primitive = 42;\n\t\t\tInteger wrapper = Integer.valueOf(\"99\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid longs() {\n\t\t\tlong primitive = 42L;\n\t\t\tLong wrapper = Long.valueOf(\"99\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid floats() {\n\t\t\tfloat primitive = 42.0f;\n\t\t\tFloat wrapper = Float.valueOf(\"99.0\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, 0.0f);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, 0.0f, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, 0.0f, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, 0.0f);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, 0.0f, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, 0.0f, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid doubles() {\n\t\t\tdouble primitive = 42.0d;\n\t\t\tDouble wrapper = Double.valueOf(\"99.0\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, 0.0d);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, 0.0d, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, 0.0d, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, 0.0d);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, 0.0d, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, 0.0d, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid booleans() {\n\t\t\tboolean primitive = true;\n\t\t\tBoolean wrapper = Boolean.valueOf(\"false\");\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid chars() {\n\t\t\tchar primitive = 'a';\n\t\t\tCharacter wrapper = Character.valueOf('z');\n\n\t\t\tassertNotEquals(primitive, wrapper);\n\t\t\tassertNotEquals(primitive, wrapper, \"message\");\n\t\t\tassertNotEquals(primitive, wrapper, () -> \"message\");\n\n\t\t\tassertNotEquals(wrapper, primitive);\n\t\t\tassertNotEquals(wrapper, primitive, \"message\");\n\t\t\tassertNotEquals(wrapper, primitive, () -> \"message\");\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"overrides\")\n\tprivate static class EqualsThrowsExceptionClass {\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tthrow new NumberFormatException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotNullAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEndsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertNotNullAssertionsTests {\n\n\t@Test\n\tvoid assertNotNullWithNonNullObject() {\n\t\tassertNotNull(\"foo\");\n\t\tassertNotNull(\"foo\", \"message\");\n\t\tassertNotNull(\"foo\", () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertNotNullWithNonNullObjectAndMessageSupplier() {\n\t\tassertNotNull(\"foo\", () -> \"should not fail\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid assertNotNullWithNull() {\n\t\ttry {\n\t\t\tassertNotNull(null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: not <null>\");\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid assertNotNullWithNullAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertNotNull(null, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageEndsWith(ex, \"expected: not <null>\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNotSameAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertNotSame;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertNotSameAssertionsTests {\n\n\t@Test\n\tvoid assertNotSameWithDifferentObjects() {\n\t\tassertNotSame(new Object(), new Object());\n\t\tassertNotSame(new Object(), new Object(), \"message\");\n\t\tassertNotSame(new Object(), new Object(), () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertNotSameWithDifferentObjectsAndMessageSupplier() {\n\t\tassertNotSame(new Object(), new Object(), () -> \"should not fail\");\n\t}\n\n\t@Test\n\tvoid assertNotSameWithObjectVsNull() {\n\t\tassertNotSame(new Object(), null);\n\t}\n\n\t@Test\n\tvoid assertNotSameWithNullVsObject() {\n\t\tassertNotSame(null, new Object());\n\t}\n\n\t@Test\n\tvoid assertNotSameWithTwoNulls() {\n\t\ttry {\n\t\t\tassertNotSame(null, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: not same but was: <null>\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertNotSameWithSameObjectAndMessage() {\n\t\ttry {\n\t\t\tObject foo = new Object();\n\t\t\tassertNotSame(foo, foo, \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageContains(ex, \"expected: not same but was: <java.lang.Object@\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertNotSameWithSameObjectAndMessageSupplier() {\n\t\ttry {\n\t\t\tObject foo = new Object();\n\t\t\tassertNotSame(foo, foo, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageContains(ex, \"expected: not same but was: <java.lang.Object@\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertNullAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedAndActualValues;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageMatches;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertNullAssertionsTests {\n\n\t@Test\n\tvoid assertNullWithNull() {\n\t\tassertNull(null);\n\t\tassertNull(null, \"message\");\n\t\tassertNull(null, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertNullWithNullAndMessageSupplier() {\n\t\tassertNull(null, () -> \"test\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid assertNullWithNonNullObject() {\n\t\ttry {\n\t\t\tassertNull(\"foo\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <null> but was: <foo>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"foo\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertNullWithNonNullObjectWithNullStringReturnedFromToString() {\n\t\tassertNullWithNonNullObjectWithNullStringReturnedFromToString(null);\n\t}\n\n\t@Test\n\tvoid assertNullWithNonNullObjectWithNullStringReturnedFromToStringAndMessageSupplier() {\n\t\tassertNullWithNonNullObjectWithNullStringReturnedFromToString(() -> \"boom\");\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void assertNullWithNonNullObjectWithNullStringReturnedFromToString(\n\t\t\t@Nullable Supplier<@Nullable String> messageSupplier) {\n\t\tString actual = \"null\";\n\t\ttry {\n\t\t\tif (messageSupplier == null) {\n\t\t\t\tassertNull(actual);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertNull(actual, messageSupplier);\n\t\t\t}\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like:\n\t\t\t// expected: <null> but was: java.lang.String@264b3504<null>\n\t\t\tString prefix = (messageSupplier != null ? messageSupplier.get() + \" ==> \" : \"\");\n\t\t\tassertMessageMatches(ex, prefix + \"expected: <null> but was: java\\\\.lang\\\\.String@.+<null>\");\n\t\t\tassertExpectedAndActualValues(ex, null, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertNullWithNonNullObjectWithNullReferenceReturnedFromToString() {\n\t\tassertNullWithNonNullObjectWithNullReferenceReturnedFromToString(null);\n\t}\n\n\t@Test\n\tvoid assertNullWithNonNullObjectWithNullReferenceReturnedFromToStringAndMessageSupplier() {\n\t\tassertNullWithNonNullObjectWithNullReferenceReturnedFromToString(() -> \"boom\");\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate void assertNullWithNonNullObjectWithNullReferenceReturnedFromToString(\n\t\t\t@Nullable Supplier<@Nullable String> messageSupplier) {\n\t\tObject actual = new NullToString();\n\t\ttry {\n\t\t\tif (messageSupplier == null) {\n\t\t\t\tassertNull(actual);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertNull(actual, messageSupplier);\n\t\t\t}\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like:\n\t\t\t// expected: <null> but was: org.junit.jupiter.api.AssertNullAssertionsTests$NullToString@4e7912d8<null>\n\t\t\tString prefix = (messageSupplier != null ? messageSupplier.get() + \" ==> \" : \"\");\n\t\t\tassertMessageMatches(ex, prefix\n\t\t\t\t\t+ \"expected: <null> but was: org\\\\.junit\\\\.jupiter\\\\.api\\\\.AssertNullAssertionsTests\\\\$NullToString@.+<null>\");\n\t\t\tassertExpectedAndActualValues(ex, null, actual);\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid assertNullWithNonNullObjectAndMessage() {\n\t\ttry {\n\t\t\tassertNull(\"foo\", \"a message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"a message ==> expected: <null> but was: <foo>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"foo\");\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unused\")\n\tvoid assertNullWithNonNullObjectAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertNull(\"foo\", () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <null> but was: <foo>\");\n\t\t\tassertExpectedAndActualValues(ex, null, \"foo\");\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tprivate static class NullToString {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertSameAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedAndActualValues;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageMatches;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertSame;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertSameAssertionsTests {\n\n\t@Test\n\tvoid assertSameWithTwoNulls() {\n\t\tassertSame(null, null);\n\t\tassertSame(null, null, () -> \"should not fail\");\n\t}\n\n\t@Test\n\tvoid assertSameWithSameObject() {\n\t\tObject foo = new Object();\n\t\tassertSame(foo, foo);\n\t\tassertSame(foo, foo, \"message\");\n\t\tassertSame(foo, foo, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertSameWithObjectVsNull() {\n\t\tObject expected = new Object();\n\t\ttry {\n\t\t\tassertSame(expected, null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.Object@\");\n\t\t\tassertMessageContains(ex, \"but was: <null>\");\n\t\t\tassertExpectedAndActualValues(ex, expected, null);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertSameWithNullVsObject() {\n\t\tObject actual = new Object();\n\t\ttry {\n\t\t\tassertSame(null, actual);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageContains(ex, \"expected: <null>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.Object@\");\n\t\t\tassertExpectedAndActualValues(ex, null, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertSameWithDifferentObjects() {\n\t\tObject expected = new Object();\n\t\tObject actual = new Object();\n\t\ttry {\n\t\t\tassertSame(expected, actual);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.Object@\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.Object@\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertSameWithEqualPrimitivesAutoboxedToDifferentWrappers() {\n\t\ttry {\n\t\t\tint i = 999;\n\t\t\tassertSame(i, i);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageMatches(ex,\n\t\t\t\t\"expected: java\\\\.lang\\\\.Integer@.+?<999> but was: java\\\\.lang\\\\.Integer@.+?<999>\");\n\t\t\tassertExpectedAndActualValues(ex, 999, 999);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertSameWithEquivalentStringsAndMessageSupplier() {\n\t\tString expected = new String(\"foo\");\n\t\tString actual = new String(\"foo\");\n\t\ttry {\n\t\t\tassertSame(expected, actual, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"test\");\n\t\t\tassertMessageContains(ex, \"expected: java.lang.String@\");\n\t\t\tassertMessageContains(ex, \"but was: java.lang.String@\");\n\t\t\tassertExpectedAndActualValues(ex, expected, actual);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\n@SuppressWarnings(\"ExcessiveLambdaUsage\")\nclass AssertThrowsAssertionsTests {\n\n\tprivate static final Executable nix = () -> {\n\t};\n\n\t@Test\n\tvoid assertThrowsWithMethodReferenceForNonVoidReturnType() {\n\t\tFutureTask<String> future = new FutureTask<>(() -> {\n\t\t\tthrow new RuntimeException(\"boom\");\n\t\t});\n\t\tfuture.run();\n\n\t\tExecutionException exception = assertThrows(ExecutionException.class, future::get);\n\t\tassertNotNull(exception.getCause());\n\t\tassertEquals(\"boom\", exception.getCause().getMessage());\n\t}\n\n\t@Test\n\tvoid assertThrowsWithMethodReferenceForVoidReturnType() {\n\t\tvar object = new Object();\n\t\tIllegalMonitorStateException exception;\n\n\t\texception = assertThrows(IllegalMonitorStateException.class, object::notify);\n\t\tassertNotNull(exception);\n\n\t\t// Note that Object.wait(...) is an overloaded method with a void return type\n\t\texception = assertThrows(IllegalMonitorStateException.class, object::wait);\n\t\tassertNotNull(exception);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowable() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrows(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t});\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowableWithMessage() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrows(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}, \"message\");\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowableWithMessageSupplier() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrows(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}, () -> \"message\");\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsCheckedException() {\n\t\tIOException exception = assertThrows(IOException.class, () -> {\n\t\t\tthrow new IOException();\n\t\t});\n\t\tassertNotNull(exception);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsRuntimeException() {\n\t\tIllegalStateException illegalStateException = assertThrows(IllegalStateException.class, () -> {\n\t\t\tthrow new IllegalStateException();\n\t\t});\n\t\tassertNotNull(illegalStateException);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsError() {\n\t\tStackOverflowError stackOverflowError = assertThrows(StackOverflowError.class,\n\t\t\tAssertionTestUtils::recurseIndefinitely);\n\t\tassertNotNull(stackOverflowError);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnException() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, nix);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Expected java.lang.IllegalStateException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertThrows(IOException.class, nix, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertThrows(IOException.class, nix, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t}, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like this:\n\t\t\t// Custom message ==> Unexpected exception type thrown, expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException>\n\t\t\tassertMessageStartsWith(ex, \"Custom message ==> \");\n\t\t\tassertMessageContains(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t}, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like this:\n\t\t\t// Custom message ==> Unexpected exception type thrown, expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException>\n\t\t\tassertMessageStartsWith(ex, \"Custom message ==> \");\n\t\t\tassertMessageContains(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"serial\")\n\tvoid assertThrowsWithExecutableThatThrowsInstanceOfAnonymousInnerClassAsUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException() {\n\t\t\t\t};\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\t// As of the time of this writing, the class name of the above anonymous inner\n\t\t\t// class is org.junit.jupiter.api.AssertionsAssertThrowsTests$2; however, hard\n\t\t\t// coding \"$2\" is fragile. So we just check for the presence of the \"$\"\n\t\t\t// appended to this class's name.\n\t\t\tassertMessageContains(ex, \"but was: <\" + getClass().getName() + \"$\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsInstanceOfStaticNestedClassAsUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrows(IllegalStateException.class, () -> {\n\t\t\t\tthrow new LocalException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\t// The following verifies that the canonical name is used (i.e., \".\" instead of \"$\").\n\t\t\tassertMessageContains(ex, \"but was: <\" + LocalException.class.getName().replace(\"$\", \".\") + \">\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(LocalException.class);\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tvoid assertThrowsWithExecutableThatThrowsSameExceptionTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(EnigmaThrowable.class)) {\n\t\t\t// Load expected exception type from different class loader\n\t\t\tClass<? extends Throwable> enigmaThrowableClass = (Class<? extends Throwable>) testClassLoader.loadClass(\n\t\t\t\tEnigmaThrowable.class.getName());\n\n\t\t\ttry {\n\t\t\t\tassertThrows(enigmaThrowableClass, () -> {\n\t\t\t\t\tthrow new EnigmaThrowable();\n\t\t\t\t});\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\t// Example Output:\n\t\t\t\t//\n\t\t\t\t// Unexpected exception type thrown,\n\t\t\t\t// expected: <org.junit.jupiter.api.EnigmaThrowable@5d3411d>\n\t\t\t\t// but was: <org.junit.jupiter.api.EnigmaThrowable@2471cca7>\n\n\t\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\t\t// The presence of the \"@\" sign is sufficient to indicate that the hash was\n\t\t\t\t// generated to disambiguate between the two identical class names.\n\t\t\t\tassertMessageContains(ex, \"expected: <org.junit.jupiter.api.EnigmaThrowable@\");\n\t\t\t\tassertMessageContains(ex, \"but was: <org.junit.jupiter.api.EnigmaThrowable@\");\n\t\t\t\tassertThat(ex).hasCauseInstanceOf(EnigmaThrowable.class);\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static class LocalException extends RuntimeException {\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertThrowsExactlyAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrowsExactly;\n\nimport java.io.IOException;\nimport java.io.Serial;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.FutureTask;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.8\n */\n@SuppressWarnings(\"ExcessiveLambdaUsage\")\nclass AssertThrowsExactlyAssertionsTests {\n\n\tprivate static final Executable nix = () -> {\n\t};\n\n\t@Test\n\tvoid assertThrowsExactlyTheSpecifiedExceptionClass() {\n\t\tvar actual = assertThrowsExactly(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t});\n\t\tassertNotNull(actual);\n\t}\n\n\t@Test\n\tvoid assertThrowsExactlyWithTheExpectedChildException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(RuntimeException.class, () -> {\n\t\t\t\tthrow new Exception();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.RuntimeException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.Exception>\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(Exception.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsExactlyWithTheExpectedParentException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(RuntimeException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.RuntimeException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithMethodReferenceForNonVoidReturnType() {\n\t\tFutureTask<String> future = new FutureTask<>(() -> {\n\t\t\tthrow new RuntimeException(\"boom\");\n\t\t});\n\t\tfuture.run();\n\n\t\tExecutionException exception = assertThrowsExactly(ExecutionException.class, future::get);\n\t\tassertNotNull(exception.getCause());\n\t\tassertEquals(\"boom\", exception.getCause().getMessage());\n\t}\n\n\t@Test\n\tvoid assertThrowsWithMethodReferenceForVoidReturnType() {\n\t\tvar object = new Object();\n\t\tIllegalMonitorStateException exception;\n\n\t\texception = assertThrowsExactly(IllegalMonitorStateException.class, object::notify);\n\t\tassertNotNull(exception);\n\n\t\t// Note that Object.wait(...) is an overloaded method with a void return type\n\t\texception = assertThrowsExactly(IllegalMonitorStateException.class, object::wait);\n\t\tassertNotNull(exception);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowable() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t});\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowableWithMessage() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}, \"message\");\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsThrowableWithMessageSupplier() {\n\t\tEnigmaThrowable enigmaThrowable = assertThrowsExactly(EnigmaThrowable.class, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}, () -> \"message\");\n\t\tassertNotNull(enigmaThrowable);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsCheckedException() {\n\t\tIOException exception = assertThrowsExactly(IOException.class, () -> {\n\t\t\tthrow new IOException();\n\t\t});\n\t\tassertNotNull(exception);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsRuntimeException() {\n\t\tIllegalStateException illegalStateException = assertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\tthrow new IllegalStateException();\n\t\t});\n\t\tassertNotNull(illegalStateException);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsError() {\n\t\tStackOverflowError stackOverflowError = assertThrowsExactly(StackOverflowError.class,\n\t\t\tAssertionTestUtils::recurseIndefinitely);\n\t\tassertNotNull(stackOverflowError);\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, nix);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"Expected java.lang.IllegalStateException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IOException.class, nix, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatDoesNotThrowAnExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IOException.class, nix, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionError ex) {\n\t\t\tassertMessageEquals(ex,\n\t\t\t\t\"Custom message ==> Expected java.io.IOException to be thrown, but nothing was thrown.\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageString() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t}, \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like this:\n\t\t\t// Custom message ==> Unexpected exception type thrown, expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException>\n\t\t\tassertMessageStartsWith(ex, \"Custom message ==> \");\n\t\t\tassertMessageContains(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsAnUnexpectedExceptionWithMessageSupplier() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t}, () -> \"Custom message\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\t// Should look something like this:\n\t\t\t// Custom message ==> Unexpected exception type thrown, expected: <java.lang.IllegalStateException> but was: <java.lang.NumberFormatException>\n\t\t\tassertMessageStartsWith(ex, \"Custom message ==> \");\n\t\t\tassertMessageContains(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\tassertMessageContains(ex, \"but was: <java.lang.NumberFormatException>\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"serial\")\n\tvoid assertThrowsWithExecutableThatThrowsInstanceOfAnonymousInnerClassAsUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\t\tthrow new NumberFormatException() {\n\t\t\t\t};\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\t// As of the time of this writing, the class name of the above anonymous inner\n\t\t\t// class is org.junit.jupiter.api.AssertThrowsExactlyAssertionsTests$2; however, hard\n\t\t\t// coding \"$2\" is fragile. So we just check for the presence of the \"$\"\n\t\t\t// appended to this class's name.\n\t\t\tassertMessageContains(ex, \"but was: <\" + getClass().getName() + \"$\");\n\t\t\tassertThat(ex).hasCauseInstanceOf(NumberFormatException.class);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertThrowsWithExecutableThatThrowsInstanceOfStaticNestedClassAsUnexpectedException() {\n\t\ttry {\n\t\t\tassertThrowsExactly(IllegalStateException.class, () -> {\n\t\t\t\tthrow new LocalException();\n\t\t\t});\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\tassertMessageContains(ex, \"expected: <java.lang.IllegalStateException>\");\n\t\t\t// The following verifies that the canonical name is used (i.e., \".\" instead of \"$\").\n\t\t\tassertMessageContains(ex, \"but was: <\" + LocalException.class.getName().replace(\"$\", \".\") + \">\");\n\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(LocalException.class);\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tvoid assertThrowsWithExecutableThatThrowsSameExceptionTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(EnigmaThrowable.class)) {\n\t\t\t// Load expected exception type from different class loader\n\t\t\tClass<? extends Throwable> enigmaThrowableClass = (Class<? extends Throwable>) testClassLoader.loadClass(\n\t\t\t\tEnigmaThrowable.class.getName());\n\n\t\t\ttry {\n\t\t\t\tassertThrowsExactly(enigmaThrowableClass, () -> {\n\t\t\t\t\tthrow new EnigmaThrowable();\n\t\t\t\t});\n\t\t\t\texpectAssertionFailedError();\n\t\t\t}\n\t\t\tcatch (AssertionFailedError ex) {\n\t\t\t\t// Example Output:\n\t\t\t\t//\n\t\t\t\t// Unexpected exception type thrown,\n\t\t\t\t// expected: <org.junit.jupiter.api.EnigmaThrowable@5d3411d>\n\t\t\t\t// but was: <org.junit.jupiter.api.EnigmaThrowable@2471cca7>\n\n\t\t\t\tassertMessageStartsWith(ex, \"Unexpected exception type thrown, \");\n\t\t\t\t// The presence of the \"@\" sign is sufficient to indicate that the hash was\n\t\t\t\t// generated to disambiguate between the two identical class names.\n\t\t\t\tassertMessageContains(ex, \"expected: <org.junit.jupiter.api.EnigmaThrowable@\");\n\t\t\t\tassertMessageContains(ex, \"but was: <org.junit.jupiter.api.EnigmaThrowable@\");\n\t\t\t\tassertThat(ex).hasCauseExactlyInstanceOf(EnigmaThrowable.class);\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static class LocalException extends RuntimeException {\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.time.Duration.ofMillis;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTimeout;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for {@link AssertTimeout}.\n *\n * @since 5.0\n */\nclass AssertTimeoutAssertionsTests {\n\n\tprivate static final ThreadLocal<AtomicBoolean> changed = ThreadLocal.withInitial(() -> new AtomicBoolean(false));\n\n\tprivate final Executable nix = () -> {\n\t};\n\n\t// --- executable ----------------------------------------------------------\n\n\t@Test\n\tvoid assertTimeoutForExecutableThatCompletesBeforeTheTimeout() {\n\t\tchanged.get().set(false);\n\t\tassertTimeout(ofMillis(500), () -> changed.get().set(true));\n\t\tassertTrue(changed.get().get(), \"should have executed in the same thread\");\n\t\tassertTimeout(ofMillis(500), nix, \"message\");\n\t\tassertTimeout(ofMillis(500), nix, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutForExecutableThatThrowsAnException() {\n\t\tRuntimeException exception = assertThrows(RuntimeException.class, () -> assertTimeout(ofMillis(500), () -> {\n\t\t\tthrow new RuntimeException(\"not this time\");\n\t\t}));\n\t\tassertMessageEquals(exception, \"not this time\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutForExecutableThatThrowsAnAssertionFailedError() {\n\t\tAssertionFailedError exception = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeout(ofMillis(500), () -> fail(\"enigma\")));\n\t\tassertMessageEquals(exception, \"enigma\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeout(ofMillis(10), this::nap));\n\t\tassertMessageStartsWith(error, \"execution exceeded timeout of 10 ms by\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutWithMessageForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeout(ofMillis(10), this::nap, \"Tempus Fugit\"));\n\t\tassertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeout(ofMillis(10), this::nap, () -> \"Tempus\" + \" \" + \"Fugit\"));\n\t\tassertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\");\n\t}\n\n\t// --- supplier ------------------------------------------------------------\n\n\t@Test\n\tvoid assertTimeoutForSupplierThatCompletesBeforeTheTimeout() {\n\t\tchanged.get().set(false);\n\t\tString result = assertTimeout(ofMillis(500), () -> {\n\t\t\tchanged.get().set(true);\n\t\t\treturn \"Tempus Fugit\";\n\t\t});\n\t\tassertTrue(changed.get().get(), \"should have executed in the same thread\");\n\t\tassertEquals(\"Tempus Fugit\", result);\n\t\tassertEquals(\"Tempus Fugit\", assertTimeout(ofMillis(500), () -> \"Tempus Fugit\", \"message\"));\n\t\tassertEquals(\"Tempus Fugit\", assertTimeout(ofMillis(500), () -> \"Tempus Fugit\", () -> \"message\"));\n\t}\n\n\t@Test\n\tvoid assertTimeoutForSupplierThatThrowsAnException() {\n\t\tRuntimeException exception = assertThrows(RuntimeException.class, () -> {\n\t\t\tassertTimeout(ofMillis(500),\n\t\t\t\t() -> ExceptionUtils.throwAsUncheckedException(new RuntimeException(\"not this time\")));\n\t\t});\n\t\tassertMessageEquals(exception, \"not this time\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutForSupplierThatThrowsAnAssertionFailedError() {\n\t\tAssertionFailedError exception = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeout(ofMillis(500), () -> fail(\"enigma\"));\n\t\t});\n\t\tassertMessageEquals(exception, \"enigma\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeout(ofMillis(10), () -> {\n\t\t\t\tnap();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t});\n\t\t});\n\t\tassertMessageStartsWith(error, \"execution exceeded timeout of 10 ms by\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutWithMessageForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeout(ofMillis(10), () -> {\n\t\t\t\tnap();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t}, \"Tempus Fugit\");\n\t\t});\n\t\tassertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeout(ofMillis(10), () -> {\n\t\t\t\tnap();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t}, () -> \"Tempus\" + \" \" + \"Fugit\");\n\t\t});\n\t\tassertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\");\n\t}\n\n\t/**\n\t * Take a nap for 100 milliseconds.\n\t */\n\tprivate void nap() throws InterruptedException {\n\t\tlong start = System.nanoTime();\n\t\t// workaround for imprecise clocks (yes, Windows, I'm talking about you)\n\t\tdo {\n\t\t\tThread.sleep(100);\n\t\t} while (System.nanoTime() - start < 100_000_000L);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTimeoutPreemptivelyAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.time.Duration.ofMillis;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\n\nimport java.time.Duration;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for {@link AssertTimeoutPreemptively}.\n *\n * @since 5.0\n */\nclass AssertTimeoutPreemptivelyAssertionsTests {\n\n\tprivate static final Duration PREEMPTIVE_TIMEOUT = ofMillis(WINDOWS.isCurrentOs() ? 1000 : 100);\n\n\tprivate static final ThreadLocal<AtomicBoolean> changed = ThreadLocal.withInitial(() -> new AtomicBoolean(false));\n\n\tprivate final Executable nix = () -> {\n\t};\n\n\t// --- executable ----------------------------------------------------------\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForExecutableThatCompletesBeforeTheTimeout() {\n\t\tchanged.get().set(false);\n\t\tassertTimeoutPreemptively(ofMillis(500), () -> changed.get().set(true));\n\t\tassertFalse(changed.get().get(), \"should have executed in a different thread\");\n\t\tassertTimeoutPreemptively(ofMillis(500), nix, \"message\");\n\t\tassertTimeoutPreemptively(ofMillis(500), nix, () -> \"message\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForExecutableThatThrowsAnException() {\n\t\tRuntimeException exception = assertThrows(RuntimeException.class,\n\t\t\t() -> assertTimeoutPreemptively(ofMillis(500), () -> {\n\t\t\t\tthrow new RuntimeException(\"not this time\");\n\t\t\t}));\n\t\tassertMessageEquals(exception, \"not this time\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForExecutableThatThrowsAnAssertionFailedError() {\n\t\tAssertionFailedError exception = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeoutPreemptively(ofMillis(500), () -> fail(\"enigma\")));\n\t\tassertMessageEquals(exception, \"enigma\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, this::waitForInterrupt));\n\t\tassertMessageEquals(error, \"execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyWithMessageForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, this::waitForInterrupt, \"Tempus Fugit\"));\n\t\tassertMessageEquals(error,\n\t\t\t\"Tempus Fugit ==> execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class,\n\t\t\t() -> assertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, this::waitForInterrupt,\n\t\t\t\t() -> \"Tempus\" + \" \" + \"Fugit\"));\n\t\tassertMessageEquals(error,\n\t\t\t\"Tempus Fugit ==> execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyWithMessageSupplierForExecutableThatCompletesBeforeTheTimeout() {\n\t\tassertTimeoutPreemptively(ofMillis(500), nix, () -> \"Tempus\" + \" \" + \"Fugit\");\n\t}\n\n\t// --- supplier ------------------------------------------------------------\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForSupplierThatCompletesBeforeTheTimeout() {\n\t\tchanged.get().set(false);\n\t\tString result = assertTimeoutPreemptively(ofMillis(500), () -> {\n\t\t\tchanged.get().set(true);\n\t\t\treturn \"Tempus Fugit\";\n\t\t});\n\t\tassertFalse(changed.get().get(), \"should have executed in a different thread\");\n\t\tassertEquals(\"Tempus Fugit\", result);\n\t\tassertEquals(\"Tempus Fugit\", assertTimeoutPreemptively(ofMillis(500), () -> \"Tempus Fugit\", \"message\"));\n\t\tassertEquals(\"Tempus Fugit\", assertTimeoutPreemptively(ofMillis(500), () -> \"Tempus Fugit\", () -> \"message\"));\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForSupplierThatThrowsAnException() {\n\t\tRuntimeException exception = assertThrows(RuntimeException.class, () -> {\n\t\t\tassertTimeoutPreemptively(ofMillis(500),\n\t\t\t\t() -> ExceptionUtils.throwAsUncheckedException(new RuntimeException(\"not this time\")));\n\t\t});\n\t\tassertMessageEquals(exception, \"not this time\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForSupplierThatThrowsAnAssertionFailedError() {\n\t\tAssertionFailedError exception = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeoutPreemptively(ofMillis(500), () -> {\n\t\t\t\tfail(\"enigma\");\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t});\n\t\t});\n\t\tassertMessageEquals(exception, \"enigma\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, () -> {\n\t\t\t\twaitForInterrupt();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t});\n\t\t});\n\n\t\tassertMessageEquals(error, \"execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyWithMessageForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, () -> {\n\t\t\t\twaitForInterrupt();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t}, \"Tempus Fugit\");\n\t\t});\n\n\t\tassertMessageEquals(error,\n\t\t\t\"Tempus Fugit ==> execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> {\n\t\t\tassertTimeoutPreemptively(PREEMPTIVE_TIMEOUT, () -> {\n\t\t\t\twaitForInterrupt();\n\t\t\t\treturn \"Tempus Fugit\";\n\t\t\t}, () -> \"Tempus\" + \" \" + \"Fugit\");\n\t\t});\n\n\t\tassertMessageEquals(error,\n\t\t\t\"Tempus Fugit ==> execution timed out after \" + PREEMPTIVE_TIMEOUT.toMillis() + \" ms\");\n\t\tassertMessageStartsWith(error.getCause(), \"Execution timed out in \");\n\t\tassertStackTraceContains(error.getCause(), \"CountDownLatch\", \"await\");\n\t}\n\n\t@Test\n\tvoid assertTimeoutPreemptivelyUsesThreadsWithSpecificNamePrefix() {\n\t\tAtomicReference<String> threadName = new AtomicReference<>(\"\");\n\t\tassertTimeoutPreemptively(ofMillis(1000), () -> threadName.set(Thread.currentThread().getName()));\n\t\tassertThat(threadName.get()) //\n\t\t\t\t.withFailMessage(\"Thread name does not match the expected prefix\") //\n\t\t\t\t.startsWith(\"junit-timeout-thread-\");\n\t}\n\n\tprivate void waitForInterrupt() {\n\t\ttry {\n\t\t\tassertFalse(Thread.interrupted(), \"Already interrupted\");\n\t\t\tnew CountDownLatch(1).await();\n\t\t}\n\t\tcatch (InterruptedException ignore) {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\t/**\n\t * Assert the given stack trace elements contain an element with the given class name and method name.\n\t */\n\tprivate static void assertStackTraceContains(@Nullable Throwable throwable, String className, String methodName) {\n\t\tassertThat(throwable).isNotNull();\n\t\tassertThat(throwable.getStackTrace()).anySatisfy(element -> {\n\t\t\tassertThat(element.getClassName()).endsWith(className);\n\t\t\tassertThat(element.getMethodName()).isEqualTo(methodName);\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertTrueAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertExpectedAndActualValues;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass AssertTrueAssertionsTests {\n\n\t@Test\n\tvoid assertTrueWithBooleanTrue() {\n\t\tassertTrue(true);\n\t\tassertTrue(true, \"test\");\n\t\tassertTrue(true, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanSupplierTrue() {\n\t\tassertTrue(() -> true);\n\t\tassertTrue(() -> true, \"test\");\n\t\tassertTrue(() -> true, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanTrueAndMessageSupplier() {\n\t\tassertTrue(true, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanSupplierTrueAndMessageSupplier() {\n\t\tassertTrue(() -> true, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanFalseAndDefaultMessageWithExpectedAndActualValues() {\n\t\ttry {\n\t\t\tassertTrue(false);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"expected: <true> but was: <false>\");\n\t\t\tassertExpectedAndActualValues(ex, true, false);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanFalseAndString() {\n\t\ttry {\n\t\t\tassertTrue(false, \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <true> but was: <false>\");\n\t\t\tassertExpectedAndActualValues(ex, true, false);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanFalseAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertTrue(false, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <true> but was: <false>\");\n\t\t\tassertExpectedAndActualValues(ex, true, false);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanSupplierFalseAndString() {\n\t\ttry {\n\t\t\tassertTrue(() -> false, \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <true> but was: <false>\");\n\t\t\tassertExpectedAndActualValues(ex, true, false);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assertTrueWithBooleanSupplierFalseAndMessageSupplier() {\n\t\ttry {\n\t\t\tassertTrue(() -> false, () -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test ==> expected: <true> but was: <false>\");\n\t\t\tassertExpectedAndActualValues(ex, true, false);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionFailureBuilderTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.concurrent.Executors.newSingleThreadExecutor;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\n\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.opentest4j.AssertionFailedError;\n\nclass AssertionFailureBuilderTest {\n\n\t@Test\n\tvoid doesNotTrimByDefault() {\n\t\tvar error = AssertionsFacade.fail();\n\t\tassertStackTraceMatch(error,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.fail(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.doesNotTrimByDefault(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid trimsUpToAssertionsFacade() {\n\t\tvar error = AssertionsFacade.failWithTrimmedStacktrace(AssertionsFacade.class, 0);\n\t\tassertStackTraceMatch(error,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.trimsUpToAssertionsFacade(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid trimsUpToAssertionsFacadeKeepingOne() {\n\t\tvar error = AssertionsFacade.failWithTrimmedStacktrace(AssertionsFacade.class, 1);\n\t\tassertStackTraceMatch(error,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.trimsUpToAssertionsFacadeKeepingOne(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid trimsUpToAssertionFailureBuilder() {\n\t\tvar error = AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilder.class, 0);\n\t\tassertStackTraceMatch(error,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.trimsUpToAssertionFailureBuilder(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid ignoresClassNotInStackTrace() {\n\t\tvar error = AssertionsFacade.failWithTrimmedStacktrace(String.class, 0);\n\t\tassertStackTraceMatch(error,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.ignoresClassNotInStackTrace(AssertionFailureBuilderTest.java:\\\\E.+\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid canTrimToEmptyStacktrace() throws ExecutionException, InterruptedException {\n\t\ttry (ExecutorService service = newSingleThreadExecutor()) {\n\t\t\t// Ensure that the stacktrace starts at Thread.\n\t\t\tvar error = service.submit(() -> AssertionsFacade.failWithTrimmedStacktrace(Thread.class, 0)).get();\n\t\t\tassertThat(error.getStackTrace()).isEmpty();\n\t\t}\n\t}\n\n\t@Test\n\tvoid mustRetainNonNegativeNumberOfFrames() {\n\t\tvar exception = assertThrows(PreconditionViolationException.class, //\n\t\t\t() -> assertionFailure().retainStackTraceElements(-1));\n\t\tassertThat(exception).hasMessage(\"retainStackTraceElements must have a non-negative value\");\n\t}\n\n\tprivate static void assertStackTraceMatch(AssertionFailedError assertionFailedError, String expectedLines) {\n\t\tList<String> stackStraceAsLines = Arrays.stream(assertionFailedError.getStackTrace()) //\n\t\t\t\t.map(StackTraceElement::toString) //\n\t\t\t\t.toList();\n\t\tassertLinesMatch(expectedLines.lines().toList(), stackStraceAsLines);\n\t}\n\n\tstatic class AssertionsFacade {\n\t\tstatic AssertionFailedError fail() {\n\t\t\treturn assertionFailure().build();\n\t\t}\n\n\t\tstatic AssertionFailedError failWithTrimmedStacktrace(Class<?> to, int retain) {\n\t\t\treturn AssertionFailureBuilder.assertionFailure() //\n\t\t\t\t\t.trimStacktrace(to) //\n\t\t\t\t\t.retainStackTraceElements(retain) //\n\t\t\t\t\t.build();\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/AssumptionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.jupiter.api.Assumptions.assumeFalse;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.jupiter.api.Assumptions.assumingThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Objects;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assumptions}.\n *\n * @since 5.0\n */\nclass AssumptionsTests {\n\n\t// --- assumeTrue ----------------------------------------------------\n\n\t@Test\n\tvoid assumeTrueWithBooleanTrue() {\n\t\tString foo = null;\n\t\ttry {\n\t\t\tassumeTrue(true);\n\t\t\tassumeTrue(true, \"message\");\n\t\t\tassumeTrue(true, () -> \"message\");\n\t\t\tfoo = \"foo\";\n\t\t}\n\t\tfinally {\n\t\t\tassertNotNull(foo);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanSupplierTrue() {\n\t\tString foo = null;\n\t\ttry {\n\t\t\tassumeTrue(() -> true);\n\t\t\tassumeTrue(() -> true, \"message\");\n\t\t\tassumeTrue(() -> true, () -> \"message\");\n\t\t\tfoo = \"foo\";\n\t\t}\n\t\tfinally {\n\t\t\tassertNotNull(foo);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanFalse() {\n\t\tassertAssumptionFailure(\"assumption is not true\", () -> assumeTrue(false));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanSupplierFalse() {\n\t\tassertAssumptionFailure(\"assumption is not true\", () -> assumeTrue(() -> false));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanFalseAndStringMessage() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeTrue(false, \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanFalseAndNullStringMessage() {\n\t\tassertAssumptionFailure(null, () -> assumeTrue(false, (String) null));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanSupplierFalseAndStringMessage() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeTrue(() -> false, \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanSupplierFalseAndMessageSupplier() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeTrue(() -> false, () -> \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeTrueWithBooleanFalseAndMessageSupplier() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeTrue(false, () -> \"test\"));\n\t}\n\n\t// --- assumeFalse ----------------------------------------------------\n\n\t@Test\n\tvoid assumeFalseWithBooleanFalse() {\n\t\tString foo = null;\n\t\ttry {\n\t\t\tassumeFalse(false);\n\t\t\tassumeFalse(false, \"message\");\n\t\t\tassumeFalse(false, () -> \"message\");\n\t\t\tfoo = \"foo\";\n\t\t}\n\t\tfinally {\n\t\t\tassertNotNull(foo);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanSupplierFalse() {\n\t\tString foo = null;\n\t\ttry {\n\t\t\tassumeFalse(() -> false);\n\t\t\tassumeFalse(() -> false, \"message\");\n\t\t\tassumeFalse(() -> false, () -> \"message\");\n\t\t\tfoo = \"foo\";\n\t\t}\n\t\tfinally {\n\t\t\tassertNotNull(foo);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanTrue() {\n\t\tassertAssumptionFailure(\"assumption is not false\", () -> assumeFalse(true));\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanSupplierTrue() {\n\t\tassertAssumptionFailure(\"assumption is not false\", () -> assumeFalse(() -> true));\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanTrueAndStringMessage() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeFalse(true, \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanSupplierTrueAndMessage() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeFalse(() -> true, \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanSupplierTrueAndMessageSupplier() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeFalse(() -> true, () -> \"test\"));\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanTrueAndMessageSupplier() {\n\t\tassertAssumptionFailure(\"test\", () -> assumeFalse(true, () -> \"test\"));\n\t}\n\n\t// --- assumingThat --------------------------------------------------\n\n\t@Test\n\tvoid assumingThatWithBooleanTrue() {\n\t\tList<String> list = new ArrayList<>();\n\t\tassumingThat(true, () -> list.add(\"test\"));\n\t\tassertEquals(1, list.size());\n\t\tassertEquals(\"test\", list.getFirst());\n\t}\n\n\t@Test\n\tvoid assumingThatWithBooleanSupplierTrue() {\n\t\tList<String> list = new ArrayList<>();\n\t\tassumingThat(() -> true, () -> list.add(\"test\"));\n\t\tassertEquals(1, list.size());\n\t\tassertEquals(\"test\", list.getFirst());\n\t}\n\n\t@Test\n\tvoid assumingThatWithBooleanFalse() {\n\t\tList<String> list = new ArrayList<>();\n\t\tassumingThat(false, () -> list.add(\"test\"));\n\t\tassertEquals(0, list.size());\n\t}\n\n\t@Test\n\tvoid assumingThatWithBooleanSupplierFalse() {\n\t\tList<String> list = new ArrayList<>();\n\t\tassumingThat(() -> false, () -> list.add(\"test\"));\n\t\tassertEquals(0, list.size());\n\t}\n\n\t@Test\n\tvoid assumingThatWithFailingExecutable() {\n\t\tassertThrows(EnigmaThrowable.class, () -> assumingThat(true, () -> {\n\t\t\tthrow new EnigmaThrowable();\n\t\t}));\n\t}\n\n\t// --- abort ---------------------------------------------------------\n\n\t@Test\n\tvoid abortWithNoArguments() {\n\t\tassertTestAbortedException(null, Assumptions::abort);\n\t}\n\n\t@Test\n\tvoid abortWithStringMessage() {\n\t\tassertTestAbortedException(\"test\", () -> abort(\"test\"));\n\t}\n\n\t@Test\n\tvoid abortWithStringSupplier() {\n\t\tassertTestAbortedException(\"test\", () -> abort(() -> \"test\"));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static void assertAssumptionFailure(@Nullable String msg, Executable executable) {\n\t\tassertTestAbortedException(msg == null ? \"Assumption failed\" : \"Assumption failed: \" + msg, executable);\n\t}\n\n\tprivate static void assertTestAbortedException(@Nullable String expectedMessage, Executable executable) {\n\t\ttry {\n\t\t\texecutable.execute();\n\t\t\texpectTestAbortedException();\n\t\t}\n\t\tcatch (Throwable ex) {\n\t\t\tassertInstanceOf(TestAbortedException.class, ex);\n\t\t\tassertMessageEquals(ex, expectedMessage);\n\t\t}\n\t}\n\n\tprivate static void expectTestAbortedException() {\n\t\tthrow new AssertionError(\"Should have thrown a \" + TestAbortedException.class.getName());\n\t}\n\n\tprivate static void assertMessageEquals(Throwable t, @Nullable String expectedMessage) throws AssertionError {\n\t\tif (!Objects.equals(expectedMessage, t.getMessage())) {\n\t\t\tthrow new AssertionError(\"Message in TestAbortedException should be [\" + expectedMessage + \"], but was [\"\n\t\t\t\t\t+ t.getMessage() + \"].\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/ConstantTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.platform.commons.util.ClassNamePatternFilterUtils;\nimport org.junit.platform.engine.support.hierarchical.DefaultParallelExecutionConfigurationStrategy;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory;\n\npublic class ConstantTests {\n\n\t@Test\n\tvoid constantsAreConsistent() {\n\t\tassertThat(Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ ParallelHierarchicalTestExecutorServiceFactory.EXECUTOR_SERVICE_PROPERTY_NAME);\n\n\t\tassertThat(Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_STRATEGY_PROPERTY_NAME);\n\t\tassertThat(Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_FIXED_PARALLELISM_PROPERTY_NAME);\n\t\tassertThat(Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME).isEqualTo(\n\t\t\tConstants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME);\n\t\tassertThat(Constants.PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_FIXED_SATURATE_PROPERTY_NAME);\n\t\tassertThat(Constants.PARALLEL_CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME);\n\t\tassertThat(Constants.PARALLEL_CONFIG_CUSTOM_CLASS_PROPERTY_NAME).isEqualTo(Constants.PARALLEL_CONFIG_PREFIX\n\t\t\t\t+ DefaultParallelExecutionConfigurationStrategy.CONFIG_CUSTOM_CLASS_PROPERTY_NAME);\n\n\t\tassertThat(Constants.DEACTIVATE_ALL_CONDITIONS_PATTERN).isEqualTo(ClassNamePatternFilterUtils.ALL_PATTERN);\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationInheritanceTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\n\n/**\n * @since 5.8\n */\n@DisplayNameGeneration(ReplaceUnderscores.class)\nclass DisplayNameGenerationInheritanceTestCase {\n\n\t@Nested\n\tclass InnerNestedTestCase {\n\n\t\t@Test\n\t\tvoid this_is_a_test() {\n\t\t}\n\t}\n\n\tstatic class StaticNestedTestCase {\n\n\t\t@Test\n\t\tvoid this_is_a_test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/DisplayNameGenerationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\n\nimport java.lang.reflect.Method;\nimport java.util.EmptyStackException;\nimport java.util.List;\nimport java.util.Stack;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.DisplayNameGenerator.IndicativeSentences.SentenceFragment;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Event;\n\n/**\n * Check generated display names.\n *\n * @see DisplayName\n * @see DisplayNameGenerator\n * @see DisplayNameGeneration\n * @since 5.4\n */\nclass DisplayNameGenerationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid standardGenerator() {\n\t\tcheck(DefaultStyleTestCase.class, //\n\t\t\t\"CONTAINER: DisplayNameGenerationTests$DefaultStyleTestCase\", //\n\t\t\t\"TEST: @DisplayName prevails\", //\n\t\t\t\"TEST: test()\", //\n\t\t\t\"TEST: test(TestInfo)\", //\n\t\t\t\"TEST: testUsingCamelCaseStyle()\", //\n\t\t\t\"TEST: testUsingCamelCase_and_also_UnderScores()\", //\n\t\t\t\"TEST: testUsingCamelCase_and_also_UnderScores_keepingParameterTypeNamesIntact(TestInfo)\", //\n\t\t\t\"TEST: test_with_underscores()\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid simpleGenerator() {\n\t\tcheck(SimpleStyleTestCase.class, //\n\t\t\t\"CONTAINER: DisplayNameGenerationTests$SimpleStyleTestCase\", //\n\t\t\t\"TEST: @DisplayName prevails\", //\n\t\t\t\"TEST: test\", //\n\t\t\t\"TEST: test (TestInfo)\", //\n\t\t\t\"TEST: testUsingCamelCaseStyle\", //\n\t\t\t\"TEST: testUsingCamelCase_and_also_UnderScores\", //\n\t\t\t\"TEST: testUsingCamelCase_and_also_UnderScores_keepingParameterTypeNamesIntact (TestInfo)\", //\n\t\t\t\"TEST: test_with_underscores\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid underscoreGenerator() {\n\t\tvar expectedDisplayNames = new String[] { //\n\t\t\t\t\"<replace me>\", //\n\t\t\t\t\"TEST: @DisplayName prevails\", //\n\t\t\t\t\"TEST: test\", //\n\t\t\t\t\"TEST: test (TestInfo)\", //\n\t\t\t\t\"TEST: test with underscores\", //\n\t\t\t\t\"TEST: testUsingCamelCase and also UnderScores\", //\n\t\t\t\t\"TEST: testUsingCamelCase and also UnderScores keepingParameterTypeNamesIntact (TestInfo)\", //\n\t\t\t\t\"TEST: testUsingCamelCaseStyle\" //\n\t\t};\n\n\t\texpectedDisplayNames[0] = \"CONTAINER: DisplayNameGenerationTests$UnderscoreStyleTestCase\";\n\t\tcheck(UnderscoreStyleTestCase.class, expectedDisplayNames);\n\n\t\texpectedDisplayNames[0] = \"CONTAINER: DisplayNameGenerationTests$UnderscoreStyleInheritedFromSuperClassTestCase\";\n\t\tcheck(UnderscoreStyleInheritedFromSuperClassTestCase.class, expectedDisplayNames);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesGeneratorOnStaticNestedClass() {\n\t\tcheck(IndicativeStyleTestCase.class, //\n\t\t\t\"CONTAINER: DisplayNameGenerationTests$IndicativeStyleTestCase\", //\n\t\t\t\"TEST: @DisplayName prevails\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> test\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> test (TestInfo)\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> test with underscores\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> testUsingCamelCase and also UnderScores\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> testUsingCamelCase and also UnderScores keepingParameterTypeNamesIntact (TestInfo)\", //\n\t\t\t\"TEST: DisplayNameGenerationTests$IndicativeStyleTestCase -> testUsingCamelCaseStyle\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesGeneratorOnTopLevelClass() {\n\t\tcheck(IndicativeSentencesTopLevelTestCase.class, //\n\t\t\t\"CONTAINER: IndicativeSentencesTopLevelTestCase\", //\n\t\t\t\"CONTAINER: IndicativeSentencesTopLevelTestCase -> A year is a leap year\", //\n\t\t\t\"TEST: IndicativeSentencesTopLevelTestCase -> A year is a leap year -> if it is divisible by 4 but not by 100\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesGeneratorOnNestedClass() {\n\t\tcheck(IndicativeSentencesNestedTestCase.class, //\n\t\t\t\"CONTAINER: IndicativeSentencesNestedTestCase\", //\n\t\t\t\"CONTAINER: A year is a leap year\", //\n\t\t\t\"TEST: A year is a leap year -> if it is divisible by 4 but not by 100\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid noNameGenerator() {\n\t\tcheck(NoNameStyleTestCase.class, //\n\t\t\t\"CONTAINER: nn\", //\n\t\t\t\"TEST: @DisplayName prevails\", //\n\t\t\t\"TEST: nn\", //\n\t\t\t\"TEST: nn\", //\n\t\t\t\"TEST: nn\", //\n\t\t\t\"TEST: nn\", //\n\t\t\t\"TEST: nn\", //\n\t\t\t\"TEST: nn\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid checkDisplayNameGeneratedForTestingAStackDemo() {\n\t\tcheck(StackTestCase.class, //\n\t\t\t\"CONTAINER: A stack\", //\n\t\t\t\"TEST: is instantiated using its noarg constructor\", //\n\t\t\t\"CONTAINER: A new stack\", //\n\t\t\t\"TEST: throws an EmptyStackException when peeked\", //\n\t\t\t\"TEST: throws an EmptyStackException when popped\", //\n\t\t\t\"TEST: is empty\", //\n\t\t\t\"CONTAINER: After pushing an element to an empty stack\", //\n\t\t\t\"TEST: peek returns that element without removing it from the stack\", //\n\t\t\t\"TEST: pop returns that element and leaves an empty stack\", //\n\t\t\t\"TEST: the stack is no longer empty\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid checkDisplayNameGeneratedForIndicativeGenerator() {\n\t\tcheck(IndicativeGeneratorTestCase.class, //\n\t\t\t\"CONTAINER: A stack\", //\n\t\t\t\"TEST: A stack, is instantiated with its constructor\", //\n\t\t\t\"CONTAINER: A stack, when new\", //\n\t\t\t\"TEST: A stack, when new, throws EmptyStackException when peeked\", //\n\t\t\t\"CONTAINER: A stack, when new, after pushing an element to an empty stack\", //\n\t\t\t\"TEST: A stack, when new, after pushing an element to an empty stack, is no longer empty\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid checkDisplayNameGeneratedForIndicativeGeneratorWithCustomSeparator() {\n\t\tcheck(IndicativeGeneratorWithCustomSeparatorTestCase.class, //\n\t\t\t\"CONTAINER: A stack\", //\n\t\t\t\"TEST: A stack >> is instantiated with its constructor\", //\n\t\t\t\"CONTAINER: A stack >> when new\", //\n\t\t\t\"TEST: A stack >> when new >> throws EmptyStackException when peeked\", //\n\t\t\t\"CONTAINER: A stack >> when new >> after pushing an element to an empty stack\", //\n\t\t\t\"TEST: A stack >> when new >> after pushing an element to an empty stack >> is no longer empty\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid checkDisplayNameGeneratedForIndicativeGeneratorWithCustomSentenceFragments() {\n\t\tcheck(IndicativeGeneratorWithCustomSentenceFragmentsTestCase.class, //\n\t\t\t\"CONTAINER: A stack\", //\n\t\t\t\"TEST: A stack, is instantiated with its constructor\", //\n\t\t\t\"CONTAINER: A stack, when new\", //\n\t\t\t\"TEST: A stack, when new, throws EmptyStackException when peeked\", //\n\t\t\t\"CONTAINER: A stack, when new, after pushing an element to an empty stack\", //\n\t\t\t\"TEST: A stack, when new, after pushing an element to an empty stack, is no longer empty\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid blankSentenceFragmentOnClassYieldsError() {\n\t\tvar results = discoverTests(selectClass(BlankSentenceFragmentOnClassTestCase.class));\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().severity()).isEqualTo(Severity.ERROR);\n\t\tassertThat(discoveryIssues.getFirst().cause().orElseThrow()) //\n\t\t\t\t.hasMessage(\"@SentenceFragment on [%s] must be declared with a non-blank value.\",\n\t\t\t\t\tBlankSentenceFragmentOnClassTestCase.class);\n\t}\n\n\t@Test\n\tvoid blankSentenceFragmentOnMethodYieldsError() throws Exception {\n\t\tvar results = discoverTests(selectMethod(BlankSentenceFragmentOnMethodTestCase.class, \"test\"));\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().severity()).isEqualTo(Severity.ERROR);\n\t\tassertThat(discoveryIssues.getFirst().cause().orElseThrow()) //\n\t\t\t\t.hasMessage(\"@SentenceFragment on [%s] must be declared with a non-blank value.\",\n\t\t\t\t\tBlankSentenceFragmentOnMethodTestCase.class.getDeclaredMethod(\"test\"));\n\t}\n\n\t@Test\n\tvoid displayNameGenerationInheritance() {\n\t\tcheck(DisplayNameGenerationInheritanceTestCase.InnerNestedTestCase.class, //\n\t\t\t\"CONTAINER: DisplayNameGenerationInheritanceTestCase\", //\n\t\t\t\"CONTAINER: InnerNestedTestCase\", //\n\t\t\t\"TEST: this is a test\"//\n\t\t);\n\n\t\tcheck(DisplayNameGenerationInheritanceTestCase.StaticNestedTestCase.class, //\n\t\t\t\"CONTAINER: DisplayNameGenerationInheritanceTestCase$StaticNestedTestCase\", //\n\t\t\t\"TEST: this_is_a_test()\"//\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesGenerationInheritance() {\n\t\tcheck(IndicativeSentencesGenerationInheritanceTestCase.InnerNestedTestCase.class, //\n\t\t\t\"CONTAINER: IndicativeSentencesGenerationInheritanceTestCase\", //\n\t\t\t\"CONTAINER: IndicativeSentencesGenerationInheritanceTestCase -> InnerNestedTestCase\", //\n\t\t\t\"TEST: IndicativeSentencesGenerationInheritanceTestCase -> InnerNestedTestCase -> this is a test\"//\n\t\t);\n\n\t\tcheck(IndicativeSentencesGenerationInheritanceTestCase.StaticNestedTestCase.class, //\n\t\t\t\"CONTAINER: IndicativeSentencesGenerationInheritanceTestCase$StaticNestedTestCase\", //\n\t\t\t\"TEST: this_is_a_test()\"//\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesRuntimeEnclosingType() {\n\t\tcheck(IndicativeSentencesRuntimeEnclosingTypeScenarioOneTestCase.class, //\n\t\t\t\"CONTAINER: Scenario 1\", //\n\t\t\t\"CONTAINER: Scenario 1 -> Level 1\", //\n\t\t\t\"CONTAINER: Scenario 1 -> Level 1 -> Level 2\", //\n\t\t\t\"TEST: Scenario 1 -> Level 1 -> Level 2 -> this is a test\"//\n\t\t);\n\n\t\tcheck(IndicativeSentencesRuntimeEnclosingTypeScenarioTwoTestCase.class, //\n\t\t\t\"CONTAINER: Scenario 2\", //\n\t\t\t\"CONTAINER: Scenario 2 -> Level 1\", //\n\t\t\t\"CONTAINER: Scenario 2 -> Level 1 -> Level 2\", //\n\t\t\t\"TEST: Scenario 2 -> Level 1 -> Level 2 -> this is a test\"//\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesOnSubClass() {\n\t\tcheck(IndicativeSentencesOnSubClassScenarioOneTestCase.class, //\n\t\t\t\"CONTAINER: IndicativeSentencesOnSubClassScenarioOneTestCase\", //\n\t\t\t\"CONTAINER: IndicativeSentencesOnSubClassScenarioOneTestCase -> Level 1\", //\n\t\t\t\"CONTAINER: IndicativeSentencesOnSubClassScenarioOneTestCase -> Level 1 -> Level 2\", //\n\t\t\t\"TEST: IndicativeSentencesOnSubClassScenarioOneTestCase -> Level 1 -> Level 2 -> this is a test\"//\n\t\t);\n\t}\n\n\t@Test\n\tvoid indicativeSentencesOnClassTemplate() {\n\t\tcheck(ClassTemplateTestCase.class, //\n\t\t\t\"CONTAINER: Class template\", //\n\t\t\t\"CONTAINER: [1] Class template\", //\n\t\t\t\"TEST: Class template, some test\", //\n\t\t\t\"CONTAINER: Class template, Regular Nested Test Case\", //\n\t\t\t\"TEST: Class template, Regular Nested Test Case, some nested test\", //\n\t\t\t\"CONTAINER: Class template, Nested Class Template\", //\n\t\t\t\"CONTAINER: [1] Class template, Nested Class Template\", //\n\t\t\t\"TEST: Class template, Nested Class Template, some nested test\" //\n\t\t);\n\n\t\tassertThat(executeTestsForClass(ClassTemplateTestCase.class).allEvents().started().stream()) //\n\t\t\t\t.map(event -> event.getTestDescriptor().getDisplayName()) //\n\t\t\t\t.containsExactly( //\n\t\t\t\t\t\"JUnit Jupiter\", //\n\t\t\t\t\t\"Class template\", //\n\t\t\t\t\t\"[1] Class template\", //\n\t\t\t\t\t\"Class template, some test\", //\n\t\t\t\t\t\"Class template, Regular Nested Test Case\", //\n\t\t\t\t\t\"Class template, Regular Nested Test Case, some nested test\", //\n\t\t\t\t\t\"Class template, Nested Class Template\", //\n\t\t\t\t\t\"[1] Class template, Nested Class Template\", //\n\t\t\t\t\t\"Class template, Nested Class Template, some nested test\" //\n\t\t\t\t);\n\t}\n\n\tprivate void check(Class<?> testClass, String... expectedDisplayNames) {\n\t\tvar results = executeTestsForClass(testClass);\n\t\tcheck(results, expectedDisplayNames);\n\t}\n\n\tprivate void check(EngineExecutionResults results, String[] expectedDisplayNames) {\n\t\tvar descriptors = results.allEvents().started().stream() //\n\t\t\t\t.map(Event::getTestDescriptor) //\n\t\t\t\t.skip(1); // Skip engine descriptor\n\t\tassertThat(descriptors) //\n\t\t\t\t.map(it -> it.getType() + \": \" + it.getDisplayName()) //\n\t\t\t\t.containsExactlyInAnyOrder(expectedDisplayNames);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@NullMarked\n\tstatic class NoNameGenerator implements DisplayNameGenerator {\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\t\treturn \"nn\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\t\treturn \"nn\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\treturn \"nn\";\n\t\t}\n\t}\n\n\t@DisplayNameGeneration(NoNameGenerator.class)\n\tstatic abstract class AbstractTestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo) {\n\t\t\ttestInfo.getDisplayName();\n\t\t}\n\n\t\t@Test\n\t\tvoid testUsingCamelCaseStyle() {\n\t\t}\n\n\t\t@Test\n\t\tvoid testUsingCamelCase_and_also_UnderScores() {\n\t\t}\n\n\t\t@Test\n\t\tvoid testUsingCamelCase_and_also_UnderScores_keepingParameterTypeNamesIntact(TestInfo testInfo) {\n\t\t\ttestInfo.getDisplayName();\n\t\t}\n\n\t\t@Test\n\t\tvoid test_with_underscores() {\n\t\t}\n\n\t\t@DisplayName(\"@DisplayName prevails\")\n\t\t@Test\n\t\tvoid testDisplayNamePrevails() {\n\t\t}\n\t}\n\n\t@DisplayNameGeneration(DisplayNameGenerator.Standard.class)\n\tstatic class DefaultStyleTestCase extends AbstractTestCase {\n\t}\n\n\t@DisplayNameGeneration(DisplayNameGenerator.Simple.class)\n\tstatic class SimpleStyleTestCase extends AbstractTestCase {\n\t}\n\n\t@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class UnderscoreStyleTestCase extends AbstractTestCase {\n\t}\n\n\t@IndicativeSentencesGeneration(separator = \" -> \", generator = DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class IndicativeStyleTestCase extends AbstractTestCase {\n\t}\n\n\t@DisplayNameGeneration(NoNameGenerator.class)\n\tstatic class NoNameStyleTestCase extends AbstractTestCase {\n\t}\n\n\t// No annotation here! @DisplayNameGeneration is inherited from super class\n\tstatic class UnderscoreStyleInheritedFromSuperClassTestCase extends UnderscoreStyleTestCase {\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@DisplayName(\"A stack\")\n\t@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class StackTestCase {\n\n\t\t@Test\n\t\tvoid is_instantiated_using_its_noarg_constructor() {\n\t\t\tnew Stack<>();\n\t\t}\n\n\t\t@Nested\n\t\tclass A_new_stack {\n\n\t\t\tStack<Object> stack;\n\n\t\t\t@BeforeEach\n\t\t\tvoid createNewStack() {\n\t\t\t\tstack = new Stack<>();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid is_empty() {\n\t\t\t\tassertTrue(stack.isEmpty());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid throws_an_EmptyStackException_when_popped() {\n\t\t\t\tassertThrows(EmptyStackException.class, () -> stack.pop());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid throws_an_EmptyStackException_when_peeked() {\n\t\t\t\tassertThrows(EmptyStackException.class, () -> stack.peek());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass After_pushing_an_element_to_an_empty_stack {\n\n\t\t\t\tString anElement = \"an element\";\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid pushAnElement() {\n\t\t\t\t\tstack.push(anElement);\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid the_stack_is_no_longer_empty() {\n\t\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid pop_returns_that_element_and_leaves_an_empty_stack() {\n\t\t\t\t\tassertEquals(anElement, stack.pop());\n\t\t\t\t\tassertTrue(stack.isEmpty());\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid peek_returns_that_element_without_removing_it_from_the_stack() {\n\t\t\t\t\tassertEquals(anElement, stack.peek());\n\t\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@DisplayName(\"A stack\")\n\t@IndicativeSentencesGeneration(generator = DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class IndicativeGeneratorTestCase {\n\n\t\t@Test\n\t\tvoid is_instantiated_with_its_constructor() {\n\t\t\tnew Stack<>();\n\t\t}\n\n\t\t@Nested\n\t\tclass when_new {\n\n\t\t\tStack<Object> stack;\n\n\t\t\t@BeforeEach\n\t\t\tvoid create_with_new_stack() {\n\t\t\t\tstack = new Stack<>();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid throws_EmptyStackException_when_peeked() {\n\t\t\t\tassertThrows(EmptyStackException.class, () -> stack.peek());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass after_pushing_an_element_to_an_empty_stack {\n\n\t\t\t\tString anElement = \"an element\";\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid push_an_element() {\n\t\t\t\t\tstack.push(anElement);\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid is_no_longer_empty() {\n\t\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@DisplayName(\"A stack\")\n\t@IndicativeSentencesGeneration(separator = \" >> \", generator = DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class IndicativeGeneratorWithCustomSeparatorTestCase {\n\n\t\t@Test\n\t\tvoid is_instantiated_with_its_constructor() {\n\t\t\tnew Stack<>();\n\t\t}\n\n\t\t@Nested\n\t\tclass when_new {\n\n\t\t\tStack<Object> stack;\n\n\t\t\t@BeforeEach\n\t\t\tvoid create_with_new_stack() {\n\t\t\t\tstack = new Stack<>();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid throws_EmptyStackException_when_peeked() {\n\t\t\t\tassertThrows(EmptyStackException.class, () -> stack.peek());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass after_pushing_an_element_to_an_empty_stack {\n\n\t\t\t\tString anElement = \"an element\";\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid push_an_element() {\n\t\t\t\t\tstack.push(anElement);\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid is_no_longer_empty() {\n\t\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SentenceFragment(\"A stack\")\n\t@IndicativeSentencesGeneration\n\tstatic class IndicativeGeneratorWithCustomSentenceFragmentsTestCase {\n\n\t\t@SentenceFragment(\"is instantiated with its constructor\")\n\t\t@Test\n\t\tvoid instantiateViaConstructor() {\n\t\t\tnew Stack<>();\n\t\t}\n\n\t\t@SentenceFragment(\"when new\")\n\t\t@Nested\n\t\tclass NewStackTestCase {\n\n\t\t\tStack<Object> stack;\n\n\t\t\t@BeforeEach\n\t\t\tvoid createNewStack() {\n\t\t\t\tstack = new Stack<>();\n\t\t\t}\n\n\t\t\t@SentenceFragment(\"throws EmptyStackException when peeked\")\n\t\t\t@Test\n\t\t\tvoid throwsExceptionWhenPeeked() {\n\t\t\t\tassertThrows(EmptyStackException.class, () -> stack.peek());\n\t\t\t}\n\n\t\t\t@SentenceFragment(\"after pushing an element to an empty stack\")\n\t\t\t@Nested\n\t\t\tclass ElementPushedOntoStackTestCase {\n\n\t\t\t\tString anElement = \"an element\";\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid pushElementOntoStack() {\n\t\t\t\t\tstack.push(anElement);\n\t\t\t\t}\n\n\t\t\t\t@SentenceFragment(\"is no longer empty\")\n\t\t\t\t@Test\n\t\t\t\tvoid nonEmptyStack() {\n\t\t\t\t\tassertFalse(stack.isEmpty());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ClassTemplate\n\t@ExtendWith(ClassTemplateTestCase.Once.class)\n\t@DisplayName(\"Class template\")\n\t@IndicativeSentencesGeneration(generator = DisplayNameGenerator.ReplaceUnderscores.class)\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\tstatic class ClassTemplateTestCase {\n\n\t\t@Test\n\t\tvoid some_test() {\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\tclass Regular_Nested_Test_Case {\n\t\t\t@Test\n\t\t\tvoid some_nested_test() {\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(2)\n\t\t@ClassTemplate\n\t\tclass Nested_Class_Template {\n\t\t\t@Test\n\t\t\tvoid some_nested_test() {\n\t\t\t}\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Once implements ClassTemplateInvocationContextProvider {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.of(new ClassTemplateInvocationContext() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\t\treturn \"%s %s\".formatted(ClassTemplateInvocationContext.super.getDisplayName(invocationIndex),\n\t\t\t\t\t\t\tcontext.getDisplayName());\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t@IndicativeSentencesGeneration\n\t@SentenceFragment(\"\")\n\tstatic class BlankSentenceFragmentOnClassTestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@IndicativeSentencesGeneration\n\tstatic class BlankSentenceFragmentOnMethodTestCase {\n\t\t@SentenceFragment(\"\\t\")\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/DynamicContainerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\n\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.stream.Stream;\n\n/**\n * @since 6.1\n */\nclass DynamicContainerTests {\n\n\t@Test\n\tvoid appliesConfiguration() {\n\t\tvar child = dynamicTest(\"Test\", Assertions::fail);\n\n\t\tvar container = dynamicContainer(config -> config //\n\t\t\t\t.displayName(\"Container\") //\n\t\t\t\t.testSourceUri(URI.create(\"https://junit.org\")) //\n\t\t\t\t.executionMode(CONCURRENT) //\n\t\t\t\t.childExecutionMode(SAME_THREAD) //\n\t\t\t\t.children(child));\n\n\t\tassertThat(container.getDisplayName()).isEqualTo(\"Container\");\n\t\tassertThat(container.getTestSourceUri()).contains(URI.create(\"https://junit.org\"));\n\t\tassertThat(container.getExecutionMode()).contains(CONCURRENT);\n\t\tassertThat(container.getChildExecutionMode()).contains(SAME_THREAD);\n\t\tassertThat(container.getChildren().map(DynamicNode.class::cast)).containsExactly(child);\n\t}\n\n\t@Test\n\tvoid displayNameMustNotBeBlank() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"displayName\", () -> dynamicContainer(__ -> {\n\t\t}));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"displayName\",\n\t\t\t() -> dynamicContainer(config -> config.displayName(\"\")));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid executionModeMustNotBeNull() {\n\t\tassertPreconditionViolationNotNullFor(\"executionMode\",\n\t\t\t() -> dynamicContainer(config -> config.executionMode(null)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid childExecutionModeMustNotBeNull() {\n\t\tassertPreconditionViolationNotNullFor(\"executionMode\",\n\t\t\t() -> dynamicContainer(config -> config.childExecutionMode(null)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid childrenMustBeConfigured() {\n\t\tassertPreconditionViolationNotNullFor(\"children\",\n\t\t\t() -> dynamicContainer(config -> config.children((DynamicNode[]) null)));\n\t\tassertPreconditionViolationNotNullFor(\"children\",\n\t\t\t() -> dynamicContainer(config -> config.children((Stream<? extends DynamicNode>) null)));\n\t\tassertPreconditionViolationNotNullFor(\"children\",\n\t\t\t() -> dynamicContainer(config -> config.children((Collection<? extends DynamicNode>) null)));\n\t\tassertPreconditionViolationNotNullFor(\"children\",\n\t\t\t() -> dynamicContainer(config -> config.displayName(\"container\")));\n\t\tassertPreconditionViolationFor(() -> dynamicContainer(config -> config.children((DynamicNode) null))) //\n\t\t\t\t.withMessage(\"children must not contain null elements\");\n\t}\n\n\t@Test\n\tvoid childrenMustNotBeConfiguredMoreThanOnce() {\n\t\tassertPreconditionViolationFor(() -> dynamicContainer(config -> config.children().children())) //\n\t\t\t\t.withMessage(\"children can only be set once\");\n\t\tassertPreconditionViolationFor(() -> dynamicContainer(config -> config.children().children(Stream.empty()))) //\n\t\t\t\t.withMessage(\"children can only be set once\");\n\t\tassertPreconditionViolationFor(() -> dynamicContainer(config -> config.children().children(List.of()))) //\n\t\t\t\t.withMessage(\"children can only be set once\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/DynamicTestTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.util.Collections.emptyIterator;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * @since 5.0\n */\nclass DynamicTestTests {\n\n\tprivate static final Executable nix = () -> {\n\t};\n\n\tprivate final List<@Nullable String> assertedValues = new ArrayList<>();\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromStreamPreconditions() {\n\t\tThrowingConsumer<Object> testExecutor = input -> {\n\t\t};\n\t\tFunction<Object, String> displayNameGenerator = Object::toString;\n\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream((Stream<?>) null, displayNameGenerator, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(Stream.empty(), null, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(Stream.empty(), displayNameGenerator, null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromIteratorPreconditions() {\n\t\tThrowingConsumer<Object> testExecutor = input -> {\n\t\t};\n\t\tFunction<Object, String> displayNameGenerator = Object::toString;\n\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> DynamicTest.stream((Iterator<?>) null, displayNameGenerator, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(emptyIterator(), null, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(emptyIterator(), displayNameGenerator, null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromStreamWithNamesPreconditions() {\n\t\tThrowingConsumer<Object> testExecutor = input -> {\n\t\t};\n\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream((Stream<? extends Named<Object>>) null, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(Stream.empty(), null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromIteratorWithNamesPreconditions() {\n\t\tThrowingConsumer<Object> testExecutor = input -> {\n\t\t};\n\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> DynamicTest.stream((Iterator<? extends Named<Object>>) null, testExecutor));\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream(emptyIterator(), null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromStreamWithNamedExecutablesPreconditions() {\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream((Stream<DummyNamedExecutableForTests>) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamFromIteratorWithNamedExecutablesPreconditions() {\n\t\tassertPreconditionViolationFor(() -> DynamicTest.stream((Iterator<DummyNamedExecutableForTests>) null));\n\t}\n\n\t@Test\n\tvoid streamFromStream() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(Stream.of(\"foo\", \"bar\", \"baz\"), String::toUpperCase,\n\t\t\tthis::throwingConsumer);\n\t\tassertStream(stream);\n\t}\n\n\t@Test\n\tvoid streamFromIterator() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(List.of(\"foo\", \"bar\", \"baz\").iterator(), String::toUpperCase,\n\t\t\tthis::throwingConsumer);\n\t\tassertStream(stream);\n\t}\n\n\t@Test\n\tvoid streamFromStreamWithNames() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(\n\t\t\tStream.of(Named.of(\"FOO\", \"foo\"), Named.of(\"BAR\", \"bar\"), Named.of(\"BAZ\", \"baz\")), this::throwingConsumer);\n\t\tassertStream(stream);\n\t}\n\n\t@Test\n\tvoid streamFromIteratorWithNames() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(\n\t\t\tList.of(Named.of(\"FOO\", \"foo\"), Named.of(\"BAR\", \"bar\"), Named.of(\"BAZ\", \"baz\")).iterator(),\n\t\t\tthis::throwingConsumer);\n\t\tassertStream(stream);\n\t}\n\n\t@Test\n\tvoid streamFromStreamWithNamedExecutables() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(\n\t\t\tStream.of(new DummyNamedExecutableForTests(\"foo\", this::throwingConsumer),\n\t\t\t\tnew DummyNamedExecutableForTests(\"bar\", this::throwingConsumer),\n\t\t\t\tnew DummyNamedExecutableForTests(\"baz\", this::throwingConsumer)));\n\n\t\tassertStream(stream);\n\t}\n\n\t@Test\n\tvoid streamFromIteratorWithNamedExecutables() throws Throwable {\n\t\tStream<DynamicTest> stream = DynamicTest.stream(\n\t\t\tList.of(new DummyNamedExecutableForTests(\"foo\", this::throwingConsumer),\n\t\t\t\tnew DummyNamedExecutableForTests(\"bar\", this::throwingConsumer),\n\t\t\t\tnew DummyNamedExecutableForTests(\"baz\", this::throwingConsumer)).iterator());\n\n\t\tassertStream(stream);\n\t}\n\n\tprivate void assertStream(Stream<DynamicTest> stream) throws Throwable {\n\t\tList<DynamicTest> dynamicTests = stream.toList();\n\n\t\tassertThat(dynamicTests).extracting(DynamicTest::getDisplayName).containsExactly(\"FOO\", \"BAR\", \"BAZ\");\n\n\t\tassertThat(assertedValues).isEmpty();\n\n\t\tdynamicTests.get(0).getExecutable().execute();\n\t\tassertThat(assertedValues).containsExactly(\"foo\");\n\n\t\tdynamicTests.get(1).getExecutable().execute();\n\t\tassertThat(assertedValues).containsExactly(\"foo\", \"bar\");\n\n\t\tThrowable t = assertThrows(Throwable.class, () -> dynamicTests.get(2).getExecutable().execute());\n\t\tassertThat(t).hasMessage(\"Baz!\");\n\t\tassertThat(assertedValues).containsExactly(\"foo\", \"bar\");\n\t}\n\n\tprivate void throwingConsumer(@Nullable String str) throws Throwable {\n\t\tif (\"baz\".equals(str)) {\n\t\t\tthrow new Throwable(\"Baz!\");\n\t\t}\n\t\tthis.assertedValues.add(str);\n\t}\n\n\t@Test\n\tvoid reflectiveOperationsThrowingAssertionFailedError() {\n\t\tThrowable t48 = assertThrows(AssertionFailedError.class,\n\t\t\t() -> dynamicTest(\"1 == 48\", this::assert1Equals48Directly).getExecutable().execute());\n\t\tassertThat(t48).hasMessage(\"expected: <1> but was: <48>\");\n\n\t\tThrowable t49 = assertThrows(AssertionFailedError.class, () -> dynamicTest(\"1 == 49\",\n\t\t\tthis::assert1Equals49ReflectivelyAndUnwrapInvocationTargetException).getExecutable().execute());\n\t\tassertThat(t49).hasMessage(\"expected: <1> but was: <49>\");\n\t}\n\n\t@Test\n\tvoid reflectiveOperationThrowingInvocationTargetException() {\n\t\tThrowable t50 = assertThrows(InvocationTargetException.class,\n\t\t\t() -> dynamicTest(\"1 == 50\", this::assert1Equals50Reflectively).getExecutable().execute());\n\t\tassertThat(t50.getCause()).hasMessage(\"expected: <1> but was: <50>\");\n\t}\n\n\t@Test\n\tvoid sourceUriIsNotPresentByDefault() {\n\t\tDynamicTest test = dynamicTest(\"foo\", nix);\n\t\tassertThat(test.getTestSourceUri()).isNotPresent();\n\t\tassertThat(test.toString()).isEqualTo(\"DynamicTest [displayName = 'foo', testSourceUri = null]\");\n\t\tDynamicContainer container = dynamicContainer(\"bar\", Stream.of(test));\n\t\tassertThat(container.getTestSourceUri()).isNotPresent();\n\t\tassertThat(container.toString()).isEqualTo(\"DynamicContainer [displayName = 'bar', testSourceUri = null]\");\n\t}\n\n\t@Test\n\tvoid sourceUriIsReturnedWhenSupplied() {\n\t\tURI testSourceUri = URI.create(\"any://test\");\n\t\tDynamicTest test = dynamicTest(\"foo\", testSourceUri, nix);\n\t\tURI containerSourceUri = URI.create(\"other://container\");\n\t\tDynamicContainer container = dynamicContainer(\"bar\", containerSourceUri, Stream.of(test));\n\n\t\tassertThat(test.getTestSourceUri()).containsSame(testSourceUri);\n\t\tassertThat(test.toString()).isEqualTo(\"DynamicTest [displayName = 'foo', testSourceUri = any://test]\");\n\t\tassertThat(container.getTestSourceUri()).containsSame(containerSourceUri);\n\t\tassertThat(container.toString()).isEqualTo(\n\t\t\t\"DynamicContainer [displayName = 'bar', testSourceUri = other://container]\");\n\t}\n\n\t@Test\n\tvoid appliesConfiguration() {\n\t\tExecutable executable = Assertions::fail;\n\n\t\tvar test = dynamicTest(config -> config //\n\t\t\t\t.displayName(\"Container\") //\n\t\t\t\t.testSourceUri(URI.create(\"https://junit.org\")) //\n\t\t\t\t.executionMode(CONCURRENT).executable(executable));\n\n\t\tassertThat(test.getDisplayName()).isEqualTo(\"Container\");\n\t\tassertThat(test.getTestSourceUri()).contains(URI.create(\"https://junit.org\"));\n\t\tassertThat(test.getExecutionMode()).contains(CONCURRENT);\n\t\tassertThat(test.getExecutable()).isSameAs(executable);\n\t}\n\n\t@Test\n\tvoid displayNameMustNotBeBlank() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"displayName\", () -> dynamicTest(__ -> {\n\t\t}));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"displayName\",\n\t\t\t() -> dynamicTest(config -> config.displayName(\"\")));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid executionModeMustNotBeNull() {\n\t\tassertPreconditionViolationNotNullFor(\"executionMode\", () -> dynamicTest(config -> config.executionMode(null)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid executableModeMustNotBeNull() {\n\t\tassertPreconditionViolationNotNullFor(\"executable\", () -> dynamicTest(config -> config.displayName(\"test\")));\n\t\tassertPreconditionViolationNotNullFor(\"executable\", () -> dynamicTest(config -> config.executable(null)));\n\t}\n\n\tprivate void assert1Equals48Directly() {\n\t\tAssertions.assertEquals(1, 48);\n\t}\n\n\tprivate void assert1Equals49ReflectivelyAndUnwrapInvocationTargetException() throws Throwable {\n\t\tMethod method = Assertions.class.getMethod(\"assertEquals\", int.class, int.class);\n\t\tReflectionSupport.invokeMethod(method, null, 1, 49);\n\t}\n\n\tprivate void assert1Equals50Reflectively() throws Throwable {\n\t\tMethod method = Assertions.class.getMethod(\"assertEquals\", int.class, int.class);\n\t\tmethod.invoke(null, 1, 50);\n\t}\n\n\trecord DummyNamedExecutableForTests(String name, ThrowingConsumer<String> consumer) implements NamedExecutable {\n\n\t\t@Override\n\t\tpublic String getName() {\n\t\t\treturn name.toUpperCase(Locale.ROOT);\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute() throws Throwable {\n\t\t\tconsumer.accept(name);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/EnigmaThrowable.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\n/**\n * This is a top-level type in order to avoid issues with\n * {@link Class#getCanonicalName()} when using different class\n * loaders in tests.\n *\n * @since 5.0\n */\n@SuppressWarnings(\"serial\")\nclass EnigmaThrowable extends Throwable {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/FailAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.junit.jupiter.api.AssertionTestUtils.assertEmptyMessage;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageContains;\nimport static org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals;\nimport static org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Unit tests for JUnit Jupiter {@link Assertions}.\n *\n * @since 5.0\n */\nclass FailAssertionsTests {\n\n\t@Test\n\tvoid failWithoutArgument() {\n\t\ttry {\n\t\t\tfail();\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertEmptyMessage(ex);\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithString() {\n\t\ttry {\n\t\t\tfail(\"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithMessageSupplier() {\n\t\ttry {\n\t\t\tfail(() -> \"test\");\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"test\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithNullString() {\n\t\ttry {\n\t\t\tfail((String) null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertEmptyMessage(ex);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid failWithNullMessageSupplier() {\n\t\ttry {\n\t\t\tfail((Supplier<@Nullable String>) null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertEmptyMessage(ex);\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithStringAndThrowable() {\n\t\ttry {\n\t\t\tfail(\"message\", new Throwable(\"cause\"));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"message\");\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tassertMessageContains(cause, \"cause\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithThrowable() {\n\t\ttry {\n\t\t\tfail(new Throwable(\"cause\"));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertEmptyMessage(ex);\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tassertMessageContains(cause, \"cause\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithStringAndNullThrowable() {\n\t\ttry {\n\t\t\tfail(\"message\", null);\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertMessageEquals(ex, \"message\");\n\t\t\tif (ex.getCause() != null) {\n\t\t\t\tthrow new AssertionError(\"Cause should have been null\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Test\n\tvoid failWithNullStringAndThrowable() {\n\t\ttry {\n\t\t\tfail(null, new Throwable(\"cause\"));\n\t\t\texpectAssertionFailedError();\n\t\t}\n\t\tcatch (AssertionFailedError ex) {\n\t\t\tassertEmptyMessage(ex);\n\t\t\tThrowable cause = ex.getCause();\n\t\t\tassertMessageContains(cause, \"cause\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failUsableAsAnExpression() {\n\t\t// @formatter:off\n\t\tlong count = Stream.empty()\n\t\t\t\t.peek(element -> fail(\"peek should never be called\"))\n\t\t\t\t.filter(element -> fail(\"filter should never be called\", new Throwable(\"cause\")))\n\t\t\t\t.map(element -> Assertions.<Throwable> fail(new Throwable(\"map should never be called\")))\n\t\t\t\t.sorted((e1, e2) -> fail(() -> \"sorted should never be called\"))\n\t\t\t\t.count();\n\t\t// @formatter:on\n\t\tassertEquals(0L, count);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesGenerationInheritanceTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\n\n/**\n * @since 5.8\n */\n@IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores.class)\nclass IndicativeSentencesGenerationInheritanceTestCase {\n\n\t@Nested\n\tclass InnerNestedTestCase {\n\n\t\t@Test\n\t\tvoid this_is_a_test() {\n\t\t}\n\t}\n\n\tstatic class StaticNestedTestCase {\n\n\t\t@Test\n\t\tvoid this_is_a_test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesNestedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\n\n/**\n * This test case declares {@link IndicativeSentencesGeneration} on a test class\n * that is nested directly within a top-level test class.\n *\n * @see IndicativeSentencesTopLevelTestCase\n * @since 5.8\n */\nclass IndicativeSentencesNestedTestCase {\n\n\t@Nested\n\t@IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores.class)\n\tclass A_year_is_a_leap_year {\n\n\t\t@Test\n\t\tvoid if_it_is_divisible_by_4_but_not_by_100() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesOnSubClassScenarioOneTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\n/**\n * @since 5.12\n */\n@IndicativeSentencesGeneration(separator = \" -> \", generator = DisplayNameGenerator.ReplaceUnderscores.class)\nclass IndicativeSentencesOnSubClassScenarioOneTestCase extends IndicativeSentencesOnSubClassTestCase {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesOnSubClassTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\n/**\n * @since 5.12\n */\n@DisplayName(\"Base Scenario\")\nabstract class IndicativeSentencesOnSubClassTestCase {\n\n\t@Nested\n\tclass Level_1 {\n\n\t\t@Nested\n\t\tclass Level_2 {\n\n\t\t\t@Test\n\t\t\tvoid this_is_a_test() {\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesRuntimeEnclosingTypeScenarioOneTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\n/**\n * @since 5.12\n */\n@DisplayName(\"Scenario 1\")\nclass IndicativeSentencesRuntimeEnclosingTypeScenarioOneTestCase\n\t\textends IndicativeSentencesRuntimeEnclosingTypeTestCase {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesRuntimeEnclosingTypeScenarioTwoTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\n/**\n * @since 5.12\n */\n@DisplayName(\"Scenario 2\")\nclass IndicativeSentencesRuntimeEnclosingTypeScenarioTwoTestCase\n\t\textends IndicativeSentencesRuntimeEnclosingTypeTestCase {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesRuntimeEnclosingTypeTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\n\n/**\n * @since 5.12\n */\n@DisplayName(\"Base Scenario\")\n@IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores.class)\nabstract class IndicativeSentencesRuntimeEnclosingTypeTestCase {\n\n\t@Nested\n\tclass Level_1 {\n\n\t\t@Nested\n\t\tclass Level_2 {\n\n\t\t\t@Test\n\t\t\tvoid this_is_a_test() {\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IndicativeSentencesTopLevelTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores;\n\n/**\n * This test case declares {@link IndicativeSentencesGeneration} on a top-level\n * test class that contains a nested test class.\n *\n * @see IndicativeSentencesNestedTestCase\n * @since 5.8\n */\n@IndicativeSentencesGeneration(separator = \" -> \", generator = ReplaceUnderscores.class)\nclass IndicativeSentencesTopLevelTestCase {\n\n\t@Nested\n\tclass A_year_is_a_leap_year {\n\n\t\t@Test\n\t\tvoid if_it_is_divisible_by_4_but_not_by_100() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/IterableFactory.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport java.util.Arrays;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\n\nfinal class IterableFactory {\n\n\t@SuppressWarnings(\"NullableProblems\")\n\tstatic List<Object> listOf(@Nullable Object... objects) {\n\t\treturn Arrays.asList(objects);\n\t}\n\n\tstatic Set<Object> setOf(@Nullable Object... objects) {\n\t\treturn new LinkedHashSet<>(listOf(objects));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/MediaTypeTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * Unit tests for {@link MediaType}.\n *\n * @since 1.14\n * @see org.junit.jupiter.api.extension.DeprecatedMediaTypeTests\n */\nclass MediaTypeTests {\n\n\t@Test\n\tvoid equals() {\n\t\tvar mediaType1 = MediaType.TEXT_PLAIN;\n\t\tvar mediaType2 = MediaType.parse(mediaType1.toString());\n\t\tvar mediaType3 = MediaType.APPLICATION_JSON;\n\n\t\tassertEqualsAndHashCode(mediaType1, mediaType2, mediaType3);\n\t}\n\n\t@Nested\n\tclass ParseTests {\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid parseWithNullOrBlankMediaType(@Nullable String mediaType) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"value\", () -> MediaType.parse(mediaType));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type\", \"type/\", \"/subtype\" })\n\t\tvoid parseWithInvalidMediaType(String mediaType) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.parse(mediaType))//\n\t\t\t\t\t.withMessage(\"Invalid media type: '%s'\", mediaType.strip());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"text/plain\", \"   text/plain   \", \"text/plain; charset=UTF-8\",\n\t\t\t\t\"\\t text/plain; charset=UTF-8 \\t\" })\n\t\tvoid parse(String value) {\n\t\t\tassertThat(MediaType.parse(value)).hasToString(value.strip());\n\t\t}\n\t}\n\n\t@Nested\n\tclass CreateTests {\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankType(@Nullable String type) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"type\", () -> MediaType.create(type, \"json\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankTypeAndCharset(@Nullable String type) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"type\", () -> MediaType.create(type, \"json\", UTF_8));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankSubtype(@Nullable String subtype) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"subtype\", () -> MediaType.create(\"application\", subtype));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankSubtypeAndCharset(@Nullable String subtype) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"subtype\",\n\t\t\t\t() -> MediaType.create(\"application\", subtype, UTF_8));\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullCharset() {\n\t\t\tassertPreconditionViolationNotNullFor(\"charset\", () -> MediaType.create(\"application\", \"json\", null));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type/\", \"/subtype\" })\n\t\tvoid createWithInvalidType(String type) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.create(type, \"json\"))//\n\t\t\t\t\t.withMessage(\"Invalid media type: '%s/json'\", type.strip());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type/\", \"/subtype\" })\n\t\tvoid createWithInvalidSubtype(String subtype) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.create(\"application\", subtype))//\n\t\t\t\t\t.withMessage(\"Invalid media type: 'application/%s'\", subtype.strip());\n\t\t}\n\n\t\t@Test\n\t\tvoid create() {\n\t\t\tassertThat(MediaType.create(\"application\", \"json\")).hasToString(\"application/json\");\n\t\t}\n\n\t\t@Test\n\t\tvoid createWithCharset() {\n\t\t\tassertThat(MediaType.create(\"text\", \"plain\", UTF_8)).hasToString(\"text/plain; charset=UTF-8\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/RandomlyOrderedTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\n\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\nimport java.util.stream.IntStream;\n\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * @since 5.8\n */\nclass RandomlyOrderedTests {\n\n\tprivate static final Set<String> callSequence = Collections.synchronizedSet(new LinkedHashSet<>());\n\n\t@Test\n\tvoid randomSeedForClassAndMethodOrderingIsDeterministic() {\n\t\tIntStream.range(0, 20).forEach(i -> {\n\t\t\tcallSequence.clear();\n\t\t\tvar tests = executeTests(1618034);\n\n\t\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\t\t\tassertThat(callSequence).containsExactlyInAnyOrder(\"B_TestCase#b\", \"B_TestCase#c\", \"B_TestCase#a\",\n\t\t\t\t\"C_TestCase#b\", \"C_TestCase#c\", \"C_TestCase#a\", \"A_TestCase#b\", \"A_TestCase#c\", \"A_TestCase#a\");\n\t\t});\n\t}\n\n\tprivate Events executeTests(@SuppressWarnings(\"SameParameterValue\") long randomSeed) {\n\t\t// @formatter:off\n\t\treturn EngineTestKit\n\t\t\t\t.engine(\"junit-jupiter\")\n\t\t\t\t.configurationParameter(DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME, ClassOrderer.Random.class.getName())\n\t\t\t\t.configurationParameter(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME, MethodOrderer.Random.class.getName())\n\t\t\t\t.configurationParameter(MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME, String.valueOf(randomSeed))\n\t\t\t\t.selectors(selectClasses(A_TestCase.class, B_TestCase.class, C_TestCase.class))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents();\n\t\t// @formatter:on\n\t}\n\n\tabstract static class BaseTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tvar testClass = testInfo.getTestClass().orElseThrow();\n\t\t\tvar testMethod = testInfo.getTestMethod().orElseThrow();\n\n\t\t\tcallSequence.add(testClass.getSimpleName() + \"#\" + testMethod.getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid a() {\n\t\t}\n\n\t\t@Test\n\t\tvoid b() {\n\t\t}\n\n\t\t@Test\n\t\tvoid c() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class A_TestCase extends BaseTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class B_TestCase extends BaseTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class C_TestCase extends BaseTestCase {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/AbstractExecutionConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethod;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\nimport static org.junit.platform.commons.support.ReflectionSupport.newInstance;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Abstract base class for unit testing a concrete {@link ExecutionCondition}\n * implementation.\n *\n * <p><strong>WARNING</strong>: this abstract base class currently does not\n * support tests in {@code @Nested} test classes within the\n * {@linkplain #getTestClass() test class}, since {@link #beforeEach(TestInfo)}\n * instantiates the test class using the no-args default constructor.\n *\n * @since 5.1\n */\n@TestInstance(Lifecycle.PER_CLASS)\nabstract class AbstractExecutionConditionTests {\n\n\tprivate final ExtensionContext context = mock();\n\n\tprivate @Nullable ConditionEvaluationResult result;\n\n\t@BeforeAll\n\tvoid ensureAllTestMethodsAreCovered() {\n\t\tPredicate<Method> isTestMethod = method -> method.isAnnotationPresent(Test.class);\n\n\t\tList<String> methodsToTest = findMethods(getTestClass(), isTestMethod, TOP_DOWN).stream()//\n\t\t\t\t.map(Method::getName).sorted().toList();\n\n\t\tList<String> localTestMethods = findMethods(getClass(), isTestMethod, TOP_DOWN).stream()//\n\t\t\t\t.map(Method::getName).sorted().toList();\n\n\t\tassertThat(localTestMethods).containsExactlyElementsOf(methodsToTest);\n\t}\n\n\t@BeforeEach\n\tvoid beforeEach(TestInfo testInfo) {\n\t\twhen(this.context.getElement()).thenReturn(method(testInfo));\n\t\twhen(this.context.getTestInstance()).thenReturn(Optional.of(newInstance(getTestClass())));\n\t\tdoReturn(getTestClass()).when(this.context).getRequiredTestClass();\n\t}\n\n\tprotected abstract ExecutionCondition getExecutionCondition();\n\n\tprotected abstract Class<?> getTestClass();\n\n\tprotected void evaluateCondition() {\n\t\tthis.result = getExecutionCondition().evaluateExecutionCondition(this.context);\n\t}\n\n\tprotected void assertEnabled() {\n\t\tassertNotNull(this.result);\n\t\tassertFalse(this.result.isDisabled(), \"Should be enabled\");\n\t}\n\n\tprotected void assertDisabled() {\n\t\tassertNotNull(this.result);\n\t\tassertTrue(this.result.isDisabled(), \"Should be disabled\");\n\t}\n\n\tprotected void assertReasonContains(String text) {\n\t\tassertNotNull(this.result);\n\t\tassertThat(this.result.getReason()).hasValueSatisfying(reason -> assertThat(reason).contains(text));\n\t}\n\n\tprotected void assertCustomDisabledReasonIs(String text) {\n\t\tassertNotNull(this.result);\n\t\tif (this.result.isDisabled()) {\n\t\t\tassertThat(this.result.getReason()).hasValueSatisfying(\n\t\t\t\treason -> assertThat(reason).contains(\" ==> \" + text));\n\t\t}\n\t}\n\n\tprivate Optional<AnnotatedElement> method(TestInfo testInfo) {\n\t\treturn method(getTestClass(), testInfo.getTestMethod().orElseThrow().getName());\n\t}\n\n\tprivate Optional<AnnotatedElement> method(Class<?> testClass, String methodName) {\n\t\treturn Optional.of(findMethod(testClass, methodName).orElseThrow());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/ConditionEvaluationResultTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * Unit tests for {@link ConditionEvaluationResult}.\n *\n * @since 5.13.3\n */\nclass ConditionEvaluationResultTests {\n\n\t@Test\n\tvoid enabledWithReason() {\n\t\tvar result = ConditionEvaluationResult.enabled(\"reason\");\n\n\t\tassertThat(result.isDisabled()).isFalse();\n\t\tassertThat(result.getReason()).contains(\"reason\");\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = true, reason = 'reason']\");\n\t}\n\n\t@BlankReasonsTest\n\tvoid enabledWithBlankReason(@Nullable String reason) {\n\t\tvar result = ConditionEvaluationResult.enabled(reason);\n\n\t\tassertThat(result.isDisabled()).isFalse();\n\t\tassertThat(result.getReason()).isEmpty();\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = true, reason = '<unknown>']\");\n\t}\n\n\t@Test\n\tvoid disabledWithDefaultReason() {\n\t\tvar result = ConditionEvaluationResult.disabled(\"default\");\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).contains(\"default\");\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = 'default']\");\n\t}\n\n\t@BlankReasonsTest\n\tvoid disabledWithBlankDefaultReason(@Nullable String reason) {\n\t\tvar result = ConditionEvaluationResult.disabled(reason);\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).isEmpty();\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = '<unknown>']\");\n\t}\n\n\t@BlankReasonsTest\n\tvoid disabledWithDefaultReasonAndBlankCustomReason(@Nullable String customReason) {\n\t\tvar result = ConditionEvaluationResult.disabled(\"default\", customReason);\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).contains(\"default\");\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = 'default']\");\n\t}\n\n\t@BlankReasonsTest\n\tvoid disabledWithBlankDefaultReasonAndCustomReason(@Nullable String reason) {\n\t\tvar result = ConditionEvaluationResult.disabled(reason, \"custom\");\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).contains(\"custom\");\n\t\tassertThat(result).asString().isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = 'custom']\");\n\t}\n\n\t@BlankReasonsTest\n\tvoid disabledWithBlankDefaultReasonAndBlankCustomReason(@Nullable String reason) {\n\t\t// We intentionally use the reason as both the default and custom reason.\n\t\tvar result = ConditionEvaluationResult.disabled(reason, reason);\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).isEmpty();\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = '<unknown>']\");\n\t}\n\n\t@Test\n\tvoid disabledWithDefaultReasonAndCustomReason() {\n\t\tdisabledWithDefaultReasonAndCustomReason(\"default\", \"custom\");\n\t}\n\n\t@Test\n\tvoid disabledWithDefaultReasonAndCustomReasonWithLeadingAndTrailingWhitespace() {\n\t\tdisabledWithDefaultReasonAndCustomReason(\"   default   \", \"   custom   \");\n\t}\n\n\tprivate static void disabledWithDefaultReasonAndCustomReason(String defaultReason, String customReason) {\n\t\tvar result = ConditionEvaluationResult.disabled(defaultReason, customReason);\n\n\t\tassertThat(result.isDisabled()).isTrue();\n\t\tassertThat(result.getReason()).contains(\"default ==> custom\");\n\t\tassertThat(result).asString()//\n\t\t\t\t.isEqualTo(\"ConditionEvaluationResult [enabled = false, reason = 'default ==> custom']\");\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ParameterizedTest\n\t@NullSource\n\t@ValueSource(strings = { \"\", \" \", \"   \", \"\\t\", \"\\f\", \"\\r\", \"\\n\", \"\\r\\n\" })\n\t@interface BlankReasonsTest {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onOtherVersion;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link DisabledForJreRange}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledForJreRangeIntegrationTests}.\n *\n * @since 5.6\n */\nclass DisabledForJreRangeConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String JAVA_VERSION = System.getProperty(\"java.version\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new DisabledForJreRangeCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledForJreRangeIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@DisabledForJreRange is not present\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#defaultValues()\n\t */\n\t@Test\n\tvoid defaultValues() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"You must declare a non-default value for the minimum or maximum value in @DisabledForJreRange\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#effectiveJreDefaultValues()\n\t */\n\t@Test\n\tvoid effectiveJreDefaultValues() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#effectiveVersionDefaultValues()\n\t */\n\t@Test\n\tvoid effectiveVersionDefaultValues() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#min17()\n\t */\n\t@Test\n\tvoid min17() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersion17()\n\t */\n\t@Test\n\tvoid minVersion17() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#maxOther()\n\t */\n\t@Test\n\tvoid maxOther() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#maxVersionMaxInteger()\n\t */\n\t@Test\n\tvoid maxVersionMaxInteger() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersion7()\n\t */\n\t@Test\n\tvoid minVersion7() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"@DisabledForJreRange's minVersion [7] must be greater than or equal to 8\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#maxVersion16()\n\t */\n\t@Test\n\tvoid maxVersion16() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@DisabledForJreRange's minimum value [17] must be less than or equal to its maximum value [16]\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minAndMinVersion()\n\t */\n\t@Test\n\tvoid minAndMinVersion() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@DisabledForJreRange's minimum value must be configured with either a JRE enum constant or numeric version, but not both\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#maxAndMaxVersion()\n\t */\n\t@Test\n\tvoid maxAndMaxVersion() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@DisabledForJreRange's maximum value must be configured with either a JRE enum constant or numeric version, but not both\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minGreaterThanMax()\n\t */\n\t@Test\n\tvoid minGreaterThanMax() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@DisabledForJreRange's minimum value [21] must be less than or equal to its maximum value [17]\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minGreaterThanMaxVersion()\n\t */\n\t@Test\n\tvoid minGreaterThanMaxVersion() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersionGreaterThanMaxVersion()\n\t */\n\t@Test\n\tvoid minVersionGreaterThanMaxVersion() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersionGreaterThanMax()\n\t */\n\t@Test\n\tvoid minVersionGreaterThanMax() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#min18()\n\t */\n\t@Test\n\tvoid min18() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(!onJava17());\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersion18()\n\t */\n\t@Test\n\tvoid minVersion18() {\n\t\tmin18();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#max18()\n\t */\n\t@Test\n\tvoid max18() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(onJava17() || onJava18());\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#maxVersion18()\n\t */\n\t@Test\n\tvoid maxVersion18() {\n\t\tmax18();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#min17Max17()\n\t */\n\t@Test\n\tvoid min17Max17() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(onJava17());\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersion17MaxVersion17()\n\t */\n\t@Test\n\tvoid minVersion17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#min18Max19()\n\t */\n\t@Test\n\tvoid min18Max19() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(onJava18() || onJava19());\n\t\tassertCustomDisabledReasonIs(\"Disabled on Java 18 & 19\");\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minVersion18MaxVersion19()\n\t */\n\t@Test\n\tvoid minVersion18MaxVersion19() {\n\t\tmin18Max19();\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minOtherMaxOther()\n\t */\n\t@Test\n\tvoid minOtherMaxOther() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentJreIf(!(onKnownVersion() || onOtherVersion()));\n\t}\n\n\t/**\n\t * @see DisabledForJreRangeIntegrationTests#minMaxIntegerMaxMaxInteger()\n\t */\n\t@Test\n\tvoid minMaxIntegerMaxMaxInteger() {\n\t\tminOtherMaxOther();\n\t}\n\n\tprivate void assertDisabledOnCurrentJreIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t\telse {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledForJreRangeIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_17;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_18;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_19;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_21;\nimport static org.junit.jupiter.api.condition.JRE.OTHER;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onOtherVersion;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link DisabledForJreRange @DisabledForJreRange}.\n *\n * @since 5.6\n */\nclass DisabledForJreRangeIntegrationTests {\n\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange\n\tvoid defaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(min = JAVA_17, max = OTHER)\n\tvoid effectiveJreDefaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(minVersion = 17, maxVersion = Integer.MAX_VALUE)\n\tvoid effectiveVersionDefaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(min = JAVA_17)\n\tvoid min17() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(minVersion = 17)\n\tvoid minVersion17() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@SuppressWarnings(\"deprecation\")\n\t@DisabledForJreRange(max = OTHER)\n\tvoid maxOther() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(maxVersion = Integer.MAX_VALUE)\n\tvoid maxVersionMaxInteger() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(minVersion = 7)\n\tvoid minVersion7() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(maxVersion = 16)\n\tvoid maxVersion16() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(min = JAVA_18, minVersion = 21)\n\tvoid minAndMinVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(max = JAVA_18, maxVersion = 21)\n\tvoid maxAndMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(min = JAVA_21, max = JAVA_17)\n\tvoid minGreaterThanMax() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(min = JAVA_21, maxVersion = 17)\n\tvoid minGreaterThanMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(minVersion = 21, maxVersion = 17)\n\tvoid minVersionGreaterThanMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledForJreRange(minVersion = 21, max = JAVA_17)\n\tvoid minVersionGreaterThanMax() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@DisabledForJreRange(min = JAVA_18)\n\tvoid min18() {\n\t\tassertFalse(onJava18() || onJava19());\n\t\tassertTrue(onJava17());\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = 18)\n\tvoid minVersion18() {\n\t\tmin18();\n\t}\n\n\t@Test\n\t@DisabledForJreRange(max = JAVA_18)\n\tvoid max18() {\n\t\tassertFalse(onJava17() || onJava18());\n\t}\n\n\t@Test\n\t@DisabledForJreRange(maxVersion = 18)\n\tvoid maxVersion18() {\n\t\tmax18();\n\t}\n\n\t@Test\n\t@DisabledForJreRange(min = JAVA_17, max = JAVA_17)\n\tvoid min17Max17() {\n\t\tassertFalse(onJava17());\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = 17, maxVersion = 17)\n\tvoid minVersion17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t@Test\n\t@DisabledForJreRange(min = JAVA_18, max = JAVA_19, disabledReason = \"Disabled on Java 18 & 19\")\n\tvoid min18Max19() {\n\t\tassertFalse(onJava18() || onJava19());\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = 18, maxVersion = 19, disabledReason = \"Disabled on Java 18 & 19\")\n\tvoid minVersion18MaxVersion19() {\n\t\tmin18Max19();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\t@DisabledForJreRange(min = OTHER, max = OTHER)\n\tvoid minOtherMaxOther() {\n\t\tassertTrue(onKnownVersion() || onOtherVersion());\n\t}\n\n\t@Test\n\t@DisabledForJreRange(minVersion = Integer.MAX_VALUE, maxVersion = Integer.MAX_VALUE)\n\tvoid minMaxIntegerMaxMaxInteger() {\n\t\tminOtherMaxOther();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionClassLoaderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.test.TestClassLoader;\n\n/**\n * Tests for {@link DisabledIfCondition} using custom {@link ClassLoader} arrangements.\n *\n * @since 5.10\n */\npublic class DisabledIfConditionClassLoaderTests {\n\n\t@Test\n\t// No need to introduce a \"disabled\" version of this test, since it would simply be the\n\t// logical inverse of this method and would therefore not provide any further benefit.\n\tvoid enabledWithStaticMethodInTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(getClass(), StaticConditionMethods.class)) {\n\t\t\tvar testClass = testClassLoader.loadClass(getClass().getName());\n\t\t\tassertThat(testClass.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tExtensionContext context = mock();\n\t\t\tMethod annotatedMethod = ReflectionSupport.findMethod(getClass(), \"enabledMethod\").get();\n\t\t\twhen(context.getElement()).thenReturn(Optional.of(annotatedMethod));\n\t\t\tdoReturn(testClass).when(context).getRequiredTestClass();\n\n\t\t\tDisabledIfCondition condition = new DisabledIfCondition();\n\t\t\tConditionEvaluationResult result = condition.evaluateExecutionCondition(context);\n\t\t\tassertThat(result).isNotNull();\n\t\t\tassertThat(result.isDisabled()).isFalse();\n\n\t\t\tMethod conditionMethod = condition.getConditionMethod(\n\t\t\t\t\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\", context);\n\t\t\tassertThat(conditionMethod).isNotNull();\n\t\t\tClass<?> declaringClass = conditionMethod.getDeclaringClass();\n\t\t\tassertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader);\n\t\t\tassertThat(declaringClass.getName()).isEqualTo(StaticConditionMethods.class.getName());\n\t\t\tassertThat(declaringClass).isNotEqualTo(StaticConditionMethods.class);\n\t\t}\n\t}\n\n\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\")\n\tprivate void enabledMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link DisabledIf}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledIfIntegrationTests}.\n *\n * @since 5.7\n */\npublic class DisabledIfConditionTests extends AbstractExecutionConditionTests {\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new DisabledIfCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledIfIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@DisabledIf is not present\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests#disabledBecauseStaticConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid disabledBecauseStaticConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"Disabled for some reason\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests#enabledBecauseStaticConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid enabledBecauseStaticConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@DisabledIf(\"staticMethodThatReturnsFalse\") evaluated to false\"\"\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests#disabledBecauseConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid disabledBecauseConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@DisabledIf(\"methodThatReturnsTrue\") evaluated to true\"\"\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests#enabledBecauseConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid enabledBecauseConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@DisabledIf(\"methodThatReturnsFalse\") evaluated to false\"\"\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests.ExternalConditionMethod#disabledBecauseStaticExternalConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid disabledBecauseStaticExternalConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\") evaluated to true\"\"\");\n\t}\n\n\t/**\n\t * @see DisabledIfIntegrationTests.ExternalConditionMethod#enabledBecauseStaticExternalConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid enabledBecauseStaticExternalConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"\"\"\n\t\t\t\t\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\") evaluated to false\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.DisabledIfEnvironmentVariableIntegrationTests.ENIGMA;\nimport static org.junit.jupiter.api.condition.DisabledIfEnvironmentVariableIntegrationTests.KEY1;\nimport static org.junit.jupiter.api.condition.DisabledIfEnvironmentVariableIntegrationTests.KEY2;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotBlankFor;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link DisabledIfEnvironmentVariableCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledIfEnvironmentVariableIntegrationTests}.\n *\n * @since 5.1\n */\nclass DisabledIfEnvironmentVariableConditionTests extends AbstractExecutionConditionTests {\n\n\t/**\n\t * Stubbed subclass of {@link DisabledIfEnvironmentVariableCondition}.\n\t */\n\tprivate ExecutionCondition condition = new DisabledIfEnvironmentVariableCondition() {\n\n\t\t@Override\n\t\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\t\treturn KEY1.equals(name) ? ENIGMA : null;\n\t\t}\n\t};\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn this.condition;\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledIfEnvironmentVariableIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @DisabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#blankNamedAttribute()\n\t */\n\t@Test\n\tvoid blankNamedAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'named' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#blankMatchesAttribute()\n\t */\n\t@Test\n\tvoid blankMatchesAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'matches' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableMatchesExactly()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableMatchesExactly() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t\tassertCustomDisabledReasonIs(\"That's an enigma\");\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableForComposedAnnotationMatchesExactly()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableForComposedAnnotationMatchesExactly() {\n\t\tthis.condition = new DisabledIfEnvironmentVariableCondition() {\n\n\t\t\t@Override\n\t\t\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\t\t\treturn KEY1.equals(name) || KEY2.equals(name) ? ENIGMA : null;\n\t\t\t}\n\t\t};\n\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableMatchesPattern()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableMatchesPattern() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#enabledBecauseEnvironmentVariableDoesNotMatch()\n\t */\n\t@Test\n\tvoid enabledBecauseEnvironmentVariableDoesNotMatch() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @DisabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see DisabledIfEnvironmentVariableIntegrationTests#enabledBecauseEnvironmentVariableDoesNotExist()\n\t */\n\t@Test\n\tvoid enabledBecauseEnvironmentVariableDoesNotExist() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @DisabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfEnvironmentVariableIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link DisabledIfEnvironmentVariable}.\n *\n * @since 5.1\n */\n@Disabled(\"Disabled since the required environment variables are not set\")\n// Tests (except those with intentional configuration errors) will pass if you set\n// the following environment variables:\n// DisabledIfEnvironmentVariableTests.key1 = enigma\n// DisabledIfEnvironmentVariableTests.key2 = enigma\nclass DisabledIfEnvironmentVariableIntegrationTests {\n\n\tstatic final String KEY1 = \"DisabledIfEnvironmentVariableTests.key1\";\n\tstatic final String KEY2 = \"DisabledIfEnvironmentVariableTests.key2\";\n\tstatic final String ENIGMA = \"enigma\";\n\tstatic final String BOGUS = \"bogus\";\n\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = \"  \", matches = ENIGMA)\n\tvoid blankNamedAttribute() {\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = KEY1, matches = \"  \")\n\tvoid blankMatchesAttribute() {\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = KEY1, matches = ENIGMA, disabledReason = \"That's an enigma\")\n\tvoid disabledBecauseEnvironmentVariableMatchesExactly() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = KEY1, matches = BOGUS)\n\t@CustomDisabled\n\tvoid disabledBecauseEnvironmentVariableForComposedAnnotationMatchesExactly() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = KEY1, matches = \".*e.+gma$\")\n\tvoid disabledBecauseEnvironmentVariableMatchesPattern() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = KEY1, matches = BOGUS)\n\tvoid enabledBecauseEnvironmentVariableDoesNotMatch() {\n\t\tassertNotEquals(BOGUS, System.getenv(KEY1));\n\t}\n\n\t@Test\n\t@DisabledIfEnvironmentVariable(named = BOGUS, matches = \"doesn't matter\")\n\tvoid enabledBecauseEnvironmentVariableDoesNotExist() {\n\t\tassertNull(System.getenv(BOGUS));\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@DisabledIfEnvironmentVariable(named = KEY2, matches = ENIGMA)\n\t@interface CustomDisabled {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link DisabledIf}.\n *\n * @since 5.7\n */\npublic class DisabledIfIntegrationTests {\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@DisabledIf(value = \"staticMethodThatReturnsTrue\", disabledReason = \"Disabled for some reason\")\n\tvoid disabledBecauseStaticConditionMethodReturnsTrue() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIf(\"staticMethodThatReturnsFalse\")\n\tvoid enabledBecauseStaticConditionMethodReturnsFalse() {\n\t}\n\n\t@Test\n\t@DisabledIf(\"methodThatReturnsTrue\")\n\tvoid disabledBecauseConditionMethodReturnsTrue() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIf(\"methodThatReturnsFalse\")\n\tvoid enabledBecauseConditionMethodReturnsFalse() {\n\t}\n\n\t@Test\n\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\")\n\tvoid disabledBecauseStaticExternalConditionMethodReturnsTrue() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\")\n\tvoid enabledBecauseStaticExternalConditionMethodReturnsFalse() {\n\t}\n\n\t@Nested\n\t@DisabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\")\n\tclass ConditionallyDisabledClass {\n\n\t\t@Test\n\t\tvoid disabledBecauseConditionMethodReturnsTrue() {\n\t\t\tfail(\"Should be disabled\");\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static boolean staticMethodThatReturnsTrue() {\n\t\treturn true;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static boolean staticMethodThatReturnsFalse() {\n\t\treturn false;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate boolean methodThatReturnsTrue() {\n\t\treturn true;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate boolean methodThatReturnsFalse() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotBlankFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.util.SetSystemProperty;\n\n/**\n * Unit tests for {@link DisabledIfSystemPropertyCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledIfSystemPropertyIntegrationTests}.\n *\n * @since 5.1\n */\n@SetSystemProperty(key = DisabledIfSystemPropertyIntegrationTests.KEY1, value = DisabledIfSystemPropertyIntegrationTests.ENIGMA)\n@SetSystemProperty(key = DisabledIfSystemPropertyIntegrationTests.KEY2, value = DisabledIfSystemPropertyIntegrationTests.ENIGMA)\nclass DisabledIfSystemPropertyConditionTests extends AbstractExecutionConditionTests {\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new DisabledIfSystemPropertyCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledIfSystemPropertyIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @DisabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#blankNamedAttribute()\n\t */\n\t@Test\n\tvoid blankNamedAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'named' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#blankMatchesAttribute()\n\t */\n\t@Test\n\tvoid blankMatchesAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'matches' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#disabledBecauseSystemPropertyMatchesExactly()\n\t */\n\t@Test\n\tvoid disabledBecauseSystemPropertyMatchesExactly() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t\tassertCustomDisabledReasonIs(\"That's an enigma\");\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#disabledBecauseSystemPropertyForComposedAnnotationMatchesExactly()\n\t */\n\t@Test\n\tvoid disabledBecauseSystemPropertyForComposedAnnotationMatchesExactly() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#disabledBecauseSystemPropertyMatchesPattern()\n\t */\n\t@Test\n\tvoid disabledBecauseSystemPropertyMatchesPattern() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"matches regular expression\");\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#enabledBecauseSystemPropertyDoesNotMatch()\n\t */\n\t@Test\n\tvoid enabledBecauseSystemPropertyDoesNotMatch() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @DisabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see DisabledIfSystemPropertyIntegrationTests#enabledBecauseSystemPropertyDoesNotExist()\n\t */\n\t@Test\n\tvoid enabledBecauseSystemPropertyDoesNotExist() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @DisabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledIfSystemPropertyIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.util.SetSystemProperty;\n\n/**\n * Integration tests for {@link DisabledIfSystemProperty}.\n *\n * @since 5.1\n */\n@SetSystemProperty(key = DisabledIfSystemPropertyIntegrationTests.KEY1, value = DisabledIfSystemPropertyIntegrationTests.ENIGMA)\n@SetSystemProperty(key = DisabledIfSystemPropertyIntegrationTests.KEY2, value = DisabledIfSystemPropertyIntegrationTests.ENIGMA)\nclass DisabledIfSystemPropertyIntegrationTests {\n\n\tstatic final String KEY1 = \"DisabledIfSystemPropertyTests.key1\";\n\tstatic final String KEY2 = \"DisabledIfSystemPropertyTests.key2\";\n\tstatic final String ENIGMA = \"enigma\";\n\tprivate static final String BOGUS = \"bogus\";\n\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\t// no-op\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledIfSystemProperty(named = \"  \", matches = ENIGMA)\n\tvoid blankNamedAttribute() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledIfSystemProperty(named = KEY1, matches = \"  \")\n\tvoid blankMatchesAttribute() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = KEY1, matches = ENIGMA, disabledReason = \"That's an enigma\")\n\tvoid disabledBecauseSystemPropertyMatchesExactly() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = KEY1, matches = BOGUS)\n\t@CustomDisabled\n\tvoid disabledBecauseSystemPropertyForComposedAnnotationMatchesExactly() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = KEY1, matches = \".*e.+gma$\")\n\tvoid disabledBecauseSystemPropertyMatchesPattern() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = KEY1, matches = BOGUS)\n\tvoid enabledBecauseSystemPropertyDoesNotMatch() {\n\t\tassertNotEquals(BOGUS, System.getProperty(KEY1));\n\t}\n\n\t@Test\n\t@DisabledIfSystemProperty(named = BOGUS, matches = \"doesn't matter\")\n\tvoid enabledBecauseSystemPropertyDoesNotExist() {\n\t\tassertNull(System.getProperty(BOGUS));\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@DisabledIfSystemProperty(named = KEY2, matches = ENIGMA)\n\t@interface CustomDisabled {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onAix;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onArchitecture;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onFreebsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onLinux;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onMac;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onOpenbsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onSolaris;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onWindows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link DisabledOnOsCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link DisabledOnOsIntegrationTests}.\n *\n * @since 5.1\n */\nclass DisabledOnOsConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String OS_NAME = System.getProperty(\"os.name\");\n\tprivate static final String ARCH = System.getProperty(\"os.arch\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new DisabledOnOsCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn DisabledOnOsIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@DisabledOnOs is not present\");\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#missingOsAndArchitectureDeclaration()\n\t */\n\t@Test\n\tvoid missingOsAndArchitectureDeclaration() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessageContaining(\"You must declare at least one OS or architecture\");\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#disabledOnEveryOs()\n\t */\n\t@Test\n\tvoid disabledOnEveryOs() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"Disabled on operating system: %s ==> Disabled on every OS\".formatted(OS_NAME));\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#aix()\n\t */\n\t@Test\n\tvoid aix() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onAix());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#freebsd()\n\t */\n\t@Test\n\tvoid freebsd() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onFreebsd());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#linux()\n\t */\n\t@Test\n\tvoid linux() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onLinux());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#macOs()\n\t */\n\t@Test\n\tvoid macOs() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onMac());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#macOsWithComposedAnnotation()\n\t */\n\t@Test\n\tvoid macOsWithComposedAnnotation() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onMac());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#openbsd()\n\t */\n\t@Test\n\tvoid openbsd() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onOpenbsd());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#windows()\n\t */\n\t@Test\n\tvoid windows() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onWindows());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#solaris()\n\t */\n\t@Test\n\tvoid solaris() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(onSolaris());\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#other()\n\t */\n\t@Test\n\tvoid other() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsIf(\n\t\t\t!(onAix() || onFreebsd() || onLinux() || onMac() || onOpenbsd() || onSolaris() || onWindows()));\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#architectureX86_64()\n\t */\n\t@Test\n\tvoid architectureX86_64() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentArchitectureIf(onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see DisabledOnOsIntegrationTests#architectureAarch64()\n\t */\n\t@Test\n\tvoid architectureAarch64() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentArchitectureIf(onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithMacOs()\n\t */\n\t@Test\n\tvoid architectureX86_64WithMacOs() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onMac() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithWindows()\n\t */\n\t@Test\n\tvoid architectureX86_64WithWindows() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onWindows() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithLinux()\n\t */\n\t@Test\n\tvoid architectureX86_64WithLinux() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onLinux() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithMacOs()\n\t */\n\t@Test\n\tvoid aarch64WithMacOs() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onMac() && onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithWindows()\n\t */\n\t@Test\n\tvoid aarch64WithWindows() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onWindows() && onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithLinux()\n\t */\n\t@Test\n\tvoid aarch64WithLinux() {\n\t\tevaluateCondition();\n\t\tassertDisabledOnCurrentOsAndArchitectureIf(onLinux() && onArchitecture(\"aarch64\"));\n\t}\n\n\tprivate void assertDisabledOnCurrentOsIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on operating system: %s\".formatted(OS_NAME));\n\t\t}\n\t\telse {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on operating system: %s\".formatted(OS_NAME));\n\t\t}\n\t}\n\n\tprivate void assertDisabledOnCurrentArchitectureIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on architecture: %s\".formatted(ARCH));\n\t\t}\n\t\telse {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on architecture: %s\".formatted(ARCH));\n\t\t}\n\t}\n\n\tprivate void assertDisabledOnCurrentOsAndArchitectureIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on operating system: %s (%s)\".formatted(OS_NAME, ARCH));\n\t\t}\n\t\telse {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on operating system: %s (%s)\".formatted(OS_NAME, ARCH));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/DisabledOnOsIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onAix;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onArchitecture;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onFreebsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onLinux;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onMac;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onOpenbsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onSolaris;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onWindows;\nimport static org.junit.jupiter.api.condition.OS.AIX;\nimport static org.junit.jupiter.api.condition.OS.FREEBSD;\nimport static org.junit.jupiter.api.condition.OS.LINUX;\nimport static org.junit.jupiter.api.condition.OS.MAC;\nimport static org.junit.jupiter.api.condition.OS.OPENBSD;\nimport static org.junit.jupiter.api.condition.OS.OTHER;\nimport static org.junit.jupiter.api.condition.OS.SOLARIS;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link DisabledOnOs}.\n *\n * @since 5.1\n */\nclass DisabledOnOsIntegrationTests {\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@DisabledOnOs({})\n\tvoid missingOsAndArchitectureDeclaration() {\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = { AIX, FREEBSD, LINUX, MAC, OPENBSD, WINDOWS, SOLARIS,\n\t\t\tOTHER }, disabledReason = \"Disabled on every OS\")\n\tvoid disabledOnEveryOs() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@DisabledOnOs(AIX)\n\tvoid aix() {\n\t\tassertFalse(onAix());\n\t}\n\n\t@Test\n\t@DisabledOnOs(FREEBSD)\n\tvoid freebsd() {\n\t\tassertFalse(onFreebsd());\n\t}\n\n\t@Test\n\t@DisabledOnOs(LINUX)\n\tvoid linux() {\n\t\tassertFalse(onLinux());\n\t}\n\n\t@Test\n\t@DisabledOnOs(MAC)\n\tvoid macOs() {\n\t\tassertFalse(onMac());\n\t}\n\n\t@Test\n\t@DisabledOnMac\n\tvoid macOsWithComposedAnnotation() {\n\t\tassertFalse(onMac());\n\t}\n\n\t@Test\n\t@DisabledOnOs(OPENBSD)\n\tvoid openbsd() {\n\t\tassertFalse(onOpenbsd());\n\t}\n\n\t@Test\n\t@DisabledOnOs(WINDOWS)\n\tvoid windows() {\n\t\tassertFalse(onWindows());\n\t}\n\n\t@Test\n\t@DisabledOnOs(SOLARIS)\n\tvoid solaris() {\n\t\tassertFalse(onSolaris());\n\t}\n\n\t@Test\n\t@DisabledOnOs(OTHER)\n\tvoid other() {\n\t\tassertTrue(onAix() || onFreebsd() || onLinux() || onMac() || onOpenbsd() || onSolaris() || onWindows());\n\t}\n\n\t@Test\n\t@DisabledOnOs(architectures = \"x86_64\")\n\tvoid architectureX86_64() {\n\t\tassertFalse(onArchitecture(\"x_86_64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(architectures = \"aarch64\")\n\tvoid architectureAarch64() {\n\t\tassertFalse(onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = MAC, architectures = \"x86_64\")\n\tvoid architectureX86_64WithMacOs() {\n\t\tassertFalse(onMac() && onArchitecture(\"x_86_64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = WINDOWS, architectures = \"x86_64\")\n\tvoid architectureX86_64WithWindows() {\n\t\tassertFalse(onWindows() && onArchitecture(\"x86_64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = LINUX, architectures = \"x86_64\")\n\tvoid architectureX86_64WithLinux() {\n\t\tassertFalse(onLinux() && onArchitecture(\"x86_64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = MAC, architectures = \"aarch64\")\n\tvoid aarch64WithMacOs() {\n\t\tassertFalse(onMac() && onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = WINDOWS, architectures = \"aarch64\")\n\tvoid aarch64WithWindows() {\n\t\tassertFalse(onWindows() && onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@DisabledOnOs(value = LINUX, architectures = \"aarch64\")\n\tvoid aarch64WithLinux() {\n\t\tassertFalse(onLinux() && onArchitecture(\"aarch64\"));\n\t}\n\t// -------------------------------------------------------------------------\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@DisabledOnOs(MAC)\n\t@interface DisabledOnMac {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava20;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava21;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava22;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava23;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava24;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava25;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava26;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava27;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onOtherVersion;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link EnabledForJreRange @EnabledForJreRange}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledForJreRangeIntegrationTests}.\n *\n * @since 5.6\n */\nclass EnabledForJreRangeConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String JAVA_VERSION = System.getProperty(\"java.version\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new EnabledForJreRangeCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledForJreRangeIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@EnabledForJreRange is not present\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#defaultValues()\n\t */\n\t@Test\n\tvoid defaultValues() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"You must declare a non-default value for the minimum or maximum value in @EnabledForJreRange\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#effectiveJreDefaultValues()\n\t */\n\t@Test\n\tvoid effectiveJreDefaultValues() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#effectiveVersionDefaultValues()\n\t */\n\t@Test\n\tvoid effectiveVersionDefaultValues() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min17()\n\t */\n\t@Test\n\tvoid min17() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion17()\n\t */\n\t@Test\n\tvoid minVersion17() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#maxOther()\n\t */\n\t@Test\n\tvoid maxOther() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#maxVersionMaxInteger()\n\t */\n\t@Test\n\tvoid maxVersionMaxInteger() {\n\t\tdefaultValues();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion7()\n\t */\n\t@Test\n\tvoid minVersion7() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\"@EnabledForJreRange's minVersion [7] must be greater than or equal to 8\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#maxVersion16()\n\t */\n\t@Test\n\tvoid maxVersion16() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@EnabledForJreRange's minimum value [17] must be less than or equal to its maximum value [16]\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minAndMinVersion()\n\t */\n\t@Test\n\tvoid minAndMinVersion() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@EnabledForJreRange's minimum value must be configured with either a JRE enum constant or numeric version, but not both\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#maxAndMaxVersion()\n\t */\n\t@Test\n\tvoid maxAndMaxVersion() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@EnabledForJreRange's maximum value must be configured with either a JRE enum constant or numeric version, but not both\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minGreaterThanMax()\n\t */\n\t@Test\n\tvoid minGreaterThanMax() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"@EnabledForJreRange's minimum value [21] must be less than or equal to its maximum value [17]\");\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minGreaterThanMaxVersion()\n\t */\n\t@Test\n\tvoid minGreaterThanMaxVersion() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersionGreaterThanMaxVersion()\n\t */\n\t@Test\n\tvoid minVersionGreaterThanMaxVersion() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersionGreaterThanMax()\n\t */\n\t@Test\n\tvoid minVersionGreaterThanMax() {\n\t\tminGreaterThanMax();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min20()\n\t */\n\t@Test\n\tvoid min20() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava20() || onJava21() || onJava22() || onJava23() || onJava24() || onJava25()\n\t\t\t\t|| onJava26() || onJava27() || onOtherVersion());\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion20()\n\t */\n\t@Test\n\tvoid minVersion20() {\n\t\tmin20();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#max21()\n\t */\n\t@Test\n\tvoid max21() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava17() || onJava18() || onJava19() || onJava20() || onJava21());\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#maxVersion21()\n\t */\n\t@Test\n\tvoid maxVersion21() {\n\t\tmax21();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min17Max21()\n\t */\n\t@Test\n\tvoid min17Max21() {\n\t\tmax21();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min17Max17()\n\t */\n\t@Test\n\tvoid min17Max17() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava17());\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min17MaxVersion17()\n\t */\n\t@Test\n\tvoid min17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion17Max17()\n\t */\n\t@Test\n\tvoid minVersion17Max17() {\n\t\tmin17Max17();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion17MaxVersion17()\n\t */\n\t@Test\n\tvoid minVersion17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min20Max21()\n\t */\n\t@Test\n\tvoid min20Max21() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava20() || onJava21());\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#min20MaxVersion21()\n\t */\n\t@Test\n\tvoid min20MaxVersion21() {\n\t\tmin20Max21();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion20Max21()\n\t */\n\t@Test\n\tvoid minVersion20Max21() {\n\t\tmin20Max21();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion20MaxVersion21()\n\t */\n\t@Test\n\tvoid minVersion20MaxVersion21() {\n\t\tmin20Max21();\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minVersion21MaxVersionMaxInteger()\n\t */\n\t@Test\n\tvoid minVersion21MaxVersionMaxInteger() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(onJava21() || onJava22() || onJava23() || onJava24() || onJava25() || onJava26()\n\t\t\t\t|| onJava27() || onOtherVersion());\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minOtherMaxOther()\n\t */\n\t@Test\n\tvoid minOtherMaxOther() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentJreIf(!(onKnownVersion() || onOtherVersion()));\n\t}\n\n\t/**\n\t * @see EnabledForJreRangeIntegrationTests#minMaxIntegerMaxMaxInteger()\n\t */\n\t@Test\n\tvoid minMaxIntegerMaxMaxInteger() {\n\t\tminOtherMaxOther();\n\t}\n\n\tprivate void assertEnabledOnCurrentJreIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t\telse {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on JRE version: \" + JAVA_VERSION);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledForJreRangeIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_17;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_18;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_20;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_21;\nimport static org.junit.jupiter.api.condition.JRE.OTHER;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava17;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava18;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava19;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava20;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava21;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava22;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onJava23;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onKnownVersion;\nimport static org.junit.jupiter.api.condition.JavaVersionPredicates.onOtherVersion;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link EnabledForJreRange @EnabledForJreRange}.\n *\n * @since 5.6\n */\nclass EnabledForJreRangeIntegrationTests {\n\n\tprivate static final JRE CURRENT_JRE = JRE.currentJre();\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange\n\tvoid defaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(min = JAVA_17, max = OTHER)\n\tvoid effectiveJreDefaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(minVersion = 17, maxVersion = Integer.MAX_VALUE)\n\tvoid effectiveVersionDefaultValues() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(min = JAVA_17)\n\tvoid min17() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(minVersion = 17)\n\tvoid minVersion17() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@SuppressWarnings(\"deprecation\")\n\t@EnabledForJreRange(max = OTHER)\n\tvoid maxOther() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(maxVersion = Integer.MAX_VALUE)\n\tvoid maxVersionMaxInteger() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(minVersion = 7)\n\tvoid minVersion7() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(maxVersion = 16)\n\tvoid maxVersion16() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(min = JAVA_18, minVersion = 21)\n\tvoid minAndMinVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(max = JAVA_18, maxVersion = 21)\n\tvoid maxAndMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(min = JAVA_21, max = JAVA_17)\n\tvoid minGreaterThanMax() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(min = JAVA_21, maxVersion = 17)\n\tvoid minGreaterThanMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(minVersion = 21, maxVersion = 17)\n\tvoid minVersionGreaterThanMaxVersion() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledForJreRange(minVersion = 21, max = JAVA_17)\n\tvoid minVersionGreaterThanMax() {\n\t\tfail(\"should result in a configuration exception\");\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_20)\n\tvoid min20() {\n\t\tassertTrue(onKnownVersion() || onOtherVersion());\n\t\tassertTrue(JRE.currentVersionNumber() >= 20);\n\t\tassertTrue(CURRENT_JRE.compareTo(JAVA_20) >= 0);\n\t\tassertTrue(CURRENT_JRE.version() >= 20);\n\t\tassertFalse(onJava19());\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 20)\n\tvoid minVersion20() {\n\t\tmin20();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(max = JAVA_21)\n\tvoid max21() {\n\t\tassertTrue(onKnownVersion());\n\t\tassertTrue(JRE.currentVersionNumber() <= 21);\n\t\tassertTrue(CURRENT_JRE.compareTo(JAVA_21) <= 0);\n\t\tassertTrue(CURRENT_JRE.version() <= 21);\n\n\t\tassertTrue(onJava17() || onJava18() || onJava19() || onJava20() || onJava21());\n\t\tassertFalse(onJava22());\n\t}\n\n\t@Test\n\t@EnabledForJreRange(maxVersion = 21)\n\tvoid maxVersion21() {\n\t\tmax21();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_17, max = JAVA_21)\n\tvoid min17Max21() {\n\t\tmax21();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_17, max = JAVA_17)\n\tvoid min17Max17() {\n\t\tassertTrue(onJava17());\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_17, maxVersion = 17)\n\tvoid min17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 17, max = JAVA_17)\n\tvoid minVersion17Max17() {\n\t\tmin17Max17();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 17, maxVersion = 17)\n\tvoid minVersion17MaxVersion17() {\n\t\tmin17Max17();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_20, max = JAVA_21)\n\tvoid min20Max21() {\n\t\tassertTrue(onJava20() || onJava21());\n\t\tassertFalse(onJava17() || onJava23());\n\t}\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_20, maxVersion = 21)\n\tvoid min20MaxVersion21() {\n\t\tmin20Max21();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 20, max = JAVA_21)\n\tvoid minVersion20Max21() {\n\t\tmin20Max21();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 20, maxVersion = 21)\n\tvoid minVersion20MaxVersion21() {\n\t\tmin20Max21();\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = 21, maxVersion = Integer.MAX_VALUE)\n\tvoid minVersion21MaxVersionMaxInteger() {\n\t\tassertTrue(onKnownVersion() || onOtherVersion());\n\t\tassertTrue(JRE.currentVersionNumber() >= 21);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"deprecation\")\n\t@EnabledForJreRange(min = OTHER, max = OTHER)\n\tvoid minOtherMaxOther() {\n\t\tassertFalse(onKnownVersion());\n\t}\n\n\t@Test\n\t@EnabledForJreRange(minVersion = Integer.MAX_VALUE, maxVersion = Integer.MAX_VALUE)\n\tvoid minMaxIntegerMaxMaxInteger() {\n\t\tminOtherMaxOther();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionClassLoaderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.test.TestClassLoader;\n\n/**\n * Tests for {@link EnabledIfCondition} using custom {@link ClassLoader} arrangements.\n *\n * @since 5.10\n */\npublic class EnabledIfConditionClassLoaderTests {\n\n\t@Test\n\t// No need to introduce a \"disabled\" version of this test, since it would simply be the\n\t// logical inverse of this method and would therefore not provide any further benefit.\n\tvoid enabledWithStaticMethodInTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(getClass(), StaticConditionMethods.class)) {\n\t\t\tvar testClass = testClassLoader.loadClass(getClass().getName());\n\t\t\tassertThat(testClass.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tExtensionContext context = mock();\n\t\t\tMethod annotatedMethod = ReflectionSupport.findMethod(getClass(), \"enabledMethod\").get();\n\t\t\twhen(context.getElement()).thenReturn(Optional.of(annotatedMethod));\n\t\t\tdoReturn(testClass).when(context).getRequiredTestClass();\n\n\t\t\tEnabledIfCondition condition = new EnabledIfCondition();\n\t\t\tConditionEvaluationResult result = condition.evaluateExecutionCondition(context);\n\t\t\tassertThat(result).isNotNull();\n\t\t\tassertThat(result.isDisabled()).isFalse();\n\n\t\t\tMethod conditionMethod = condition.getConditionMethod(\n\t\t\t\t\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\", context);\n\t\t\tassertThat(conditionMethod).isNotNull();\n\t\t\tClass<?> declaringClass = conditionMethod.getDeclaringClass();\n\t\t\tassertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader);\n\t\t\tassertThat(declaringClass.getName()).isEqualTo(StaticConditionMethods.class.getName());\n\t\t\tassertThat(declaringClass).isNotEqualTo(StaticConditionMethods.class);\n\t\t}\n\t}\n\n\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\")\n\tprivate void enabledMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link EnabledIf}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledIfIntegrationTests}.\n *\n * @since 5.7\n */\npublic class EnabledIfConditionTests extends AbstractExecutionConditionTests {\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new EnabledIfCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledIfIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@EnabledIf is not present\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests#enabledBecauseStaticConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid enabledBecauseStaticConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@EnabledIf(\"staticMethodThatReturnsTrue\") evaluated to true\"\"\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests#disabledBecauseStaticConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid disabledBecauseStaticConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"Disabled for some reason\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests#enabledBecauseConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid enabledBecauseConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@EnabledIf(\"methodThatReturnsTrue\") evaluated to true\"\"\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests#disabledBecauseConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid disabledBecauseConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@EnabledIf(\"methodThatReturnsFalse\") evaluated to false\"\"\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests.ExternalConditionMethod#enabledBecauseStaticExternalConditionMethodReturnsTrue()\n\t */\n\t@Test\n\tvoid enabledBecauseStaticExternalConditionMethodReturnsTrue() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"\"\"\n\t\t\t\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\") evaluated to true\"\"\");\n\t}\n\n\t/**\n\t * @see EnabledIfIntegrationTests.ExternalConditionMethod#disabledBecauseStaticExternalConditionMethodReturnsFalse()\n\t */\n\t@Test\n\tvoid disabledBecauseStaticExternalConditionMethodReturnsFalse() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\n\t\t\t\"\"\"\n\t\t\t\t\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\") evaluated to false\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.EnabledIfEnvironmentVariableIntegrationTests.BOGUS;\nimport static org.junit.jupiter.api.condition.EnabledIfEnvironmentVariableIntegrationTests.ENIGMA;\nimport static org.junit.jupiter.api.condition.EnabledIfEnvironmentVariableIntegrationTests.KEY1;\nimport static org.junit.jupiter.api.condition.EnabledIfEnvironmentVariableIntegrationTests.KEY2;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotBlankFor;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link EnabledIfEnvironmentVariableCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledIfEnvironmentVariableIntegrationTests}.\n *\n * @since 5.1\n */\nclass EnabledIfEnvironmentVariableConditionTests extends AbstractExecutionConditionTests {\n\n\t/**\n\t * Stubbed subclass of {@link EnabledIfEnvironmentVariableCondition}.\n\t */\n\tprivate ExecutionCondition condition = new EnabledIfEnvironmentVariableCondition() {\n\n\t\t@Override\n\t\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\t\treturn KEY1.equals(name) ? ENIGMA : null;\n\t\t}\n\t};\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn condition;\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledIfEnvironmentVariableIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @EnabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#blankNamedAttribute()\n\t */\n\t@Test\n\tvoid blankNamedAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'named' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#blankMatchesAttribute()\n\t */\n\t@Test\n\tvoid blankMatchesAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'matches' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#enabledBecauseEnvironmentVariableMatchesExactly()\n\t */\n\t@Test\n\tvoid enabledBecauseEnvironmentVariableMatchesExactly() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @EnabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#enabledBecauseBothEnvironmentVariablesMatchExactly()\n\t */\n\t@Test\n\tvoid enabledBecauseBothEnvironmentVariablesMatchExactly() {\n\t\tthis.condition = new EnabledIfEnvironmentVariableCondition() {\n\n\t\t\t@Override\n\t\t\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\t\t\treturn KEY1.equals(name) || KEY2.equals(name) ? ENIGMA : null;\n\t\t\t}\n\t\t};\n\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @EnabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#enabledBecauseEnvironmentVariableMatchesPattern()\n\t */\n\t@Test\n\tvoid enabledBecauseEnvironmentVariableMatchesPattern() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\n\t\t\t\"No @EnabledIfEnvironmentVariable conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableDoesNotMatch()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableDoesNotMatch() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not match regular expression\");\n\t\tassertCustomDisabledReasonIs(\"Not bogus\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableForComposedAnnotationDoesNotMatch()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableForComposedAnnotationDoesNotMatch() {\n\t\tthis.condition = new EnabledIfEnvironmentVariableCondition() {\n\n\t\t\t@Override\n\t\t\tprotected @Nullable String getEnvironmentVariable(String name) {\n\t\t\t\treturn KEY1.equals(name) ? ENIGMA : KEY2.equals(name) ? BOGUS : null;\n\t\t\t}\n\t\t};\n\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not match regular expression\");\n\t}\n\n\t/**\n\t * @see EnabledIfEnvironmentVariableIntegrationTests#disabledBecauseEnvironmentVariableDoesNotExist()\n\t */\n\t@Test\n\tvoid disabledBecauseEnvironmentVariableDoesNotExist() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not exist\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfEnvironmentVariableIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link EnabledIfEnvironmentVariable}.\n *\n * @since 5.1\n */\n@Disabled(\"Disabled since the required environment variables are not set\")\n// Tests (except those with intentional configuration errors) will pass if you set\n// the following environment variables:\n// EnabledIfEnvironmentVariableTests.key1 = enigma\n// EnabledIfEnvironmentVariableTests.key2 = enigma\nclass EnabledIfEnvironmentVariableIntegrationTests {\n\n\tstatic final String KEY1 = \"EnabledIfEnvironmentVariableTests.key1\";\n\tstatic final String KEY2 = \"EnabledIfEnvironmentVariableTests.key2\";\n\tstatic final String ENIGMA = \"enigma\";\n\tstatic final String PUZZLE = \"puzzle\";\n\tstatic final String BOGUS = \"bogus\";\n\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = \"  \", matches = ENIGMA)\n\tvoid blankNamedAttribute() {\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = \"  \")\n\tvoid blankMatchesAttribute() {\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = ENIGMA)\n\tvoid enabledBecauseEnvironmentVariableMatchesExactly() {\n\t\tassertEquals(ENIGMA, System.getenv(KEY1));\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = ENIGMA)\n\t@EnabledIfEnvironmentVariable(named = KEY2, matches = ENIGMA)\n\tvoid enabledBecauseBothEnvironmentVariablesMatchExactly() {\n\t\tassertEquals(ENIGMA, System.getenv(KEY1));\n\t\tassertEquals(ENIGMA, System.getenv(KEY2));\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = \".*e.+ma$\")\n\tvoid enabledBecauseEnvironmentVariableMatchesPattern() {\n\t\tassertEquals(ENIGMA, System.getenv(KEY1));\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = BOGUS, disabledReason = \"Not bogus\")\n\tvoid disabledBecauseEnvironmentVariableDoesNotMatch() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = KEY1, matches = ENIGMA)\n\t@CustomEnabled\n\tvoid disabledBecauseEnvironmentVariableForComposedAnnotationDoesNotMatch() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIfEnvironmentVariable(named = BOGUS, matches = \"doesn't matter\")\n\tvoid disabledBecauseEnvironmentVariableDoesNotExist() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@EnabledIfEnvironmentVariable(named = KEY2, matches = PUZZLE)\n\t@interface CustomEnabled {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link EnabledIf}.\n *\n * @since 5.7\n */\npublic class EnabledIfIntegrationTests {\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@EnabledIf(\"staticMethodThatReturnsTrue\")\n\tvoid enabledBecauseStaticConditionMethodReturnsTrue() {\n\t}\n\n\t@Test\n\t@EnabledIf(value = \"staticMethodThatReturnsFalse\", disabledReason = \"Disabled for some reason\")\n\tvoid disabledBecauseStaticConditionMethodReturnsFalse() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIf(\"methodThatReturnsTrue\")\n\tvoid enabledBecauseConditionMethodReturnsTrue() {\n\t}\n\n\t@Test\n\t@EnabledIf(\"methodThatReturnsFalse\")\n\tvoid disabledBecauseConditionMethodReturnsFalse() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsTrue\")\n\tvoid enabledBecauseStaticExternalConditionMethodReturnsTrue() {\n\t}\n\n\t@Test\n\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\")\n\tvoid disabledBecauseStaticExternalConditionMethodReturnsFalse() {\n\t\tfail(\"Should be disabled\");\n\t}\n\n\t@Nested\n\t@EnabledIf(\"org.junit.jupiter.api.condition.StaticConditionMethods#returnsFalse\")\n\tclass ConditionallyDisabledClass {\n\n\t\t@Test\n\t\tvoid disabledBecauseConditionMethodReturnsFalse() {\n\t\t\tfail(\"Should be disabled\");\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static boolean staticMethodThatReturnsTrue() {\n\t\treturn true;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static boolean staticMethodThatReturnsFalse() {\n\t\treturn false;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate boolean methodThatReturnsTrue() {\n\t\treturn true;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate boolean methodThatReturnsFalse() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotBlankFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.util.SetSystemProperty;\n\n/**\n * Unit tests for {@link EnabledIfSystemPropertyCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledIfSystemPropertyIntegrationTests}.\n *\n * @since 5.1\n */\n@SetSystemProperty(key = EnabledIfSystemPropertyIntegrationTests.KEY1, value = EnabledIfSystemPropertyIntegrationTests.ENIGMA)\n@SetSystemProperty(key = EnabledIfSystemPropertyIntegrationTests.KEY2, value = EnabledIfSystemPropertyIntegrationTests.ENIGMA)\nclass EnabledIfSystemPropertyConditionTests extends AbstractExecutionConditionTests {\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new EnabledIfSystemPropertyCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledIfSystemPropertyIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @EnabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#blankNamedAttribute()\n\t */\n\t@Test\n\tvoid blankNamedAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'named' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#blankMatchesAttribute()\n\t */\n\t@Test\n\tvoid blankMatchesAttribute() {\n\t\tassertPreconditionViolationNotBlankFor(\"The 'matches' attribute\", this::evaluateCondition);\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#enabledBecauseSystemPropertyMatchesExactly()\n\t */\n\t@Test\n\tvoid enabledBecauseSystemPropertyMatchesExactly() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @EnabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#enabledBecauseBothSystemPropertiesMatchExactly()\n\t */\n\t@Test\n\tvoid enabledBecauseBothSystemPropertiesMatchExactly() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @EnabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#enabledBecauseSystemPropertyMatchesPattern()\n\t */\n\t@Test\n\tvoid enabledBecauseSystemPropertyMatchesPattern() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"No @EnabledIfSystemProperty conditions resulting in 'disabled' execution encountered\");\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#disabledBecauseSystemPropertyDoesNotMatch()\n\t */\n\t@Test\n\tvoid disabledBecauseSystemPropertyDoesNotMatch() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not match regular expression\");\n\t\tassertCustomDisabledReasonIs(\"Not bogus\");\n\t}\n\n\t@Test\n\tvoid disabledBecauseSystemPropertyForComposedAnnotationDoesNotMatch() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not match regular expression\");\n\t}\n\n\t/**\n\t * @see EnabledIfSystemPropertyIntegrationTests#disabledBecauseSystemPropertyDoesNotExist()\n\t */\n\t@Test\n\tvoid disabledBecauseSystemPropertyDoesNotExist() {\n\t\tevaluateCondition();\n\t\tassertDisabled();\n\t\tassertReasonContains(\"does not exist\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledIfSystemPropertyIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.util.SetSystemProperty;\n\n/**\n * Integration tests for {@link EnabledIfSystemProperty}.\n *\n * @since 5.1\n */\n@SetSystemProperty(key = EnabledIfSystemPropertyIntegrationTests.KEY1, value = EnabledIfSystemPropertyIntegrationTests.ENIGMA)\n@SetSystemProperty(key = EnabledIfSystemPropertyIntegrationTests.KEY2, value = EnabledIfSystemPropertyIntegrationTests.ENIGMA)\nclass EnabledIfSystemPropertyIntegrationTests {\n\n\tstatic final String KEY1 = \"EnabledIfSystemPropertyTests.key1\";\n\tstatic final String KEY2 = \"EnabledIfSystemPropertyTests.key2\";\n\tstatic final String ENIGMA = \"enigma\";\n\tprivate static final String BOGUS = \"bogus\";\n\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledIfSystemProperty(named = \"  \", matches = ENIGMA)\n\tvoid blankNamedAttribute() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledIfSystemProperty(named = KEY1, matches = \"  \")\n\tvoid blankMatchesAttribute() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = KEY1, matches = ENIGMA)\n\tvoid enabledBecauseSystemPropertyMatchesExactly() {\n\t\tassertEquals(ENIGMA, System.getProperty(KEY1));\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = KEY1, matches = ENIGMA)\n\t@EnabledIfSystemProperty(named = KEY2, matches = ENIGMA)\n\tvoid enabledBecauseBothSystemPropertiesMatchExactly() {\n\t\tassertEquals(ENIGMA, System.getProperty(KEY1));\n\t\tassertEquals(ENIGMA, System.getProperty(KEY2));\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = KEY1, matches = \".*en.+gma$\")\n\tvoid enabledBecauseSystemPropertyMatchesPattern() {\n\t\tassertEquals(ENIGMA, System.getProperty(KEY1));\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = KEY1, matches = BOGUS, disabledReason = \"Not bogus\")\n\tvoid disabledBecauseSystemPropertyDoesNotMatch() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = KEY1, matches = ENIGMA)\n\t@CustomEnabled\n\tvoid disabledBecauseSystemPropertyForComposedAnnotationDoesNotMatch() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Test\n\t@EnabledIfSystemProperty(named = BOGUS, matches = \"doesn't matter\")\n\tvoid disabledBecauseSystemPropertyDoesNotExist() {\n\t\tfail(\"should be disabled\");\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@EnabledIfSystemProperty(named = KEY2, matches = BOGUS)\n\t@interface CustomEnabled {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onAix;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onArchitecture;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onFreebsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onLinux;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onMac;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onOpenbsd;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onSolaris;\nimport static org.junit.jupiter.api.condition.EnabledOnOsIntegrationTests.onWindows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\n\n/**\n * Unit tests for {@link EnabledOnOsCondition}.\n *\n * <p>Note that test method names MUST match the test method names in\n * {@link EnabledOnOsIntegrationTests}.\n *\n * @since 5.1\n */\nclass EnabledOnOsConditionTests extends AbstractExecutionConditionTests {\n\n\tprivate static final String OS_NAME = System.getProperty(\"os.name\");\n\tprivate static final String ARCH = System.getProperty(\"os.arch\");\n\n\t@Override\n\tprotected ExecutionCondition getExecutionCondition() {\n\t\treturn new EnabledOnOsCondition();\n\t}\n\n\t@Override\n\tprotected Class<?> getTestClass() {\n\t\treturn EnabledOnOsIntegrationTests.class;\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#enabledBecauseAnnotationIsNotPresent()\n\t */\n\t@Test\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t\tevaluateCondition();\n\t\tassertEnabled();\n\t\tassertReasonContains(\"@EnabledOnOs is not present\");\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#missingOsAndArchitectureDeclaration()\n\t */\n\t@Test\n\tvoid missingOsAndArchitectureDeclaration() {\n\t\tassertPreconditionViolationFor(this::evaluateCondition)//\n\t\t\t\t.withMessageContaining(\"You must declare at least one OS or architecture\");\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#enabledOnEveryOs()\n\t */\n\t@Test\n\tvoid enabledOnEveryOs() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(true);\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aix()\n\t */\n\t@Test\n\tvoid aix() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onAix());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#freebsd()\n\t */\n\t@Test\n\tvoid freebsd() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onFreebsd());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#linux()\n\t */\n\t@Test\n\tvoid linux() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onLinux());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#macOs()\n\t */\n\t@Test\n\tvoid macOs() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onMac());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#macOsWithComposedAnnotation()\n\t */\n\t@Test\n\tvoid macOsWithComposedAnnotation() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onMac());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#openbsd()\n\t */\n\t@Test\n\tvoid openbsd() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onOpenbsd());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#windows()\n\t */\n\t@Test\n\tvoid windows() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onWindows());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#solaris()\n\t */\n\t@Test\n\tvoid solaris() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(onSolaris());\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#other()\n\t */\n\t@Test\n\tvoid other() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsIf(\n\t\t\t!(onAix() || onFreebsd() || onLinux() || onMac() || onOpenbsd() || onSolaris() || onWindows()));\n\t\tassertCustomDisabledReasonIs(\"Disabled on almost every OS\");\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64()\n\t */\n\t@Test\n\tvoid architectureX86_64() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentArchitectureIf(onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureAarch64()\n\t */\n\t@Test\n\tvoid architectureAarch64() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentArchitectureIf(onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithMacOs()\n\t */\n\t@Test\n\tvoid architectureX86_64WithMacOs() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onMac() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithWindows()\n\t */\n\t@Test\n\tvoid architectureX86_64WithWindows() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onWindows() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#architectureX86_64WithLinux()\n\t */\n\t@Test\n\tvoid architectureX86_64WithLinux() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onLinux() && onArchitecture(\"x86_64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithMacOs()\n\t */\n\t@Test\n\tvoid aarch64WithMacOs() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onMac() && onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithWindows()\n\t */\n\t@Test\n\tvoid aarch64WithWindows() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onWindows() && onArchitecture(\"aarch64\"));\n\t}\n\n\t/**\n\t * @see EnabledOnOsIntegrationTests#aarch64WithLinux()\n\t */\n\t@Test\n\tvoid aarch64WithLinux() {\n\t\tevaluateCondition();\n\t\tassertEnabledOnCurrentOsAndArchitectureIf(onLinux() && onArchitecture(\"aarch64\"));\n\t}\n\n\tprivate void assertEnabledOnCurrentOsIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on operating system: %s\".formatted(OS_NAME));\n\t\t}\n\t\telse {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on operating system: %s\".formatted(OS_NAME));\n\t\t}\n\t}\n\n\tprivate void assertEnabledOnCurrentArchitectureIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on architecture: %s\".formatted(ARCH));\n\t\t}\n\t\telse {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on architecture: %s\".formatted(ARCH));\n\t\t}\n\t}\n\n\tprivate void assertEnabledOnCurrentOsAndArchitectureIf(boolean condition) {\n\t\tif (condition) {\n\t\t\tassertEnabled();\n\t\t\tassertReasonContains(\"Enabled on operating system: %s (%s)\".formatted(OS_NAME, ARCH));\n\t\t}\n\t\telse {\n\t\t\tassertDisabled();\n\t\t\tassertReasonContains(\"Disabled on operating system: %s (%s)\".formatted(OS_NAME, ARCH));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/EnabledOnOsIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.condition.OS.AIX;\nimport static org.junit.jupiter.api.condition.OS.FREEBSD;\nimport static org.junit.jupiter.api.condition.OS.LINUX;\nimport static org.junit.jupiter.api.condition.OS.MAC;\nimport static org.junit.jupiter.api.condition.OS.OPENBSD;\nimport static org.junit.jupiter.api.condition.OS.OTHER;\nimport static org.junit.jupiter.api.condition.OS.SOLARIS;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Locale;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link EnabledOnOs}.\n *\n * @since 5.1\n */\nclass EnabledOnOsIntegrationTests {\n\n\tprivate static final String ARCH = System.getProperty(\"os.arch\").toLowerCase(Locale.ENGLISH);\n\tprivate static final String OS_NAME = System.getProperty(\"os.name\").toLowerCase(Locale.ENGLISH);\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\tvoid enabledBecauseAnnotationIsNotPresent() {\n\t}\n\n\t@Test\n\t@Disabled(\"Only used in a unit test via reflection\")\n\t@EnabledOnOs({})\n\tvoid missingOsAndArchitectureDeclaration() {\n\t}\n\n\t@Test\n\t@EnabledOnOs({ AIX, FREEBSD, LINUX, MAC, OPENBSD, WINDOWS, SOLARIS, OTHER })\n\tvoid enabledOnEveryOs() {\n\t}\n\n\t@Test\n\t@EnabledOnOs(AIX)\n\tvoid aix() {\n\t\tassertTrue(onAix());\n\t}\n\n\t@Test\n\t@EnabledOnOs(FREEBSD)\n\tvoid freebsd() {\n\t\tassertTrue(onFreebsd());\n\t}\n\n\t@Test\n\t@EnabledOnOs(LINUX)\n\tvoid linux() {\n\t\tassertTrue(onLinux());\n\t}\n\n\t@Test\n\t@EnabledOnOs(MAC)\n\tvoid macOs() {\n\t\tassertTrue(onMac());\n\t}\n\n\t@Test\n\t@EnabledOnMac\n\tvoid macOsWithComposedAnnotation() {\n\t\tassertTrue(onMac());\n\t}\n\n\t@Test\n\t@EnabledOnOs(OPENBSD)\n\tvoid openbsd() {\n\t\tassertTrue(onOpenbsd());\n\t}\n\n\t@Test\n\t@EnabledOnOs(WINDOWS)\n\tvoid windows() {\n\t\tassertTrue(onWindows());\n\t}\n\n\t@Test\n\t@EnabledOnOs(SOLARIS)\n\tvoid solaris() {\n\t\tassertTrue(onSolaris());\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = OTHER, disabledReason = \"Disabled on almost every OS\")\n\tvoid other() {\n\t\tassertFalse(onAix() || onFreebsd() || onLinux() || onMac() || onOpenbsd() || onSolaris() || onWindows());\n\t}\n\n\t@Test\n\t@EnabledOnOs(architectures = \"x86_64\")\n\tvoid architectureX86_64() {\n\t\tassertFalse(onArchitecture(\"x_86_64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(architectures = \"aarch64\")\n\tvoid architectureAarch64() {\n\t\tassertTrue(onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = MAC, architectures = \"x86_64\")\n\tvoid architectureX86_64WithMacOs() {\n\t\tassertTrue(onMac());\n\t\tassertTrue(onArchitecture(\"x86_64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = WINDOWS, architectures = \"x86_64\")\n\tvoid architectureX86_64WithWindows() {\n\t\tassertTrue(onWindows());\n\t\tassertTrue(onArchitecture(\"x86_64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = LINUX, architectures = \"x86_64\")\n\tvoid architectureX86_64WithLinux() {\n\t\tassertTrue(onLinux());\n\t\tassertTrue(onArchitecture(\"x86_64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = MAC, architectures = \"aarch64\")\n\tvoid aarch64WithMacOs() {\n\t\tassertTrue(onMac());\n\t\tassertTrue(onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = WINDOWS, architectures = \"aarch64\")\n\tvoid aarch64WithWindows() {\n\t\tassertTrue(onWindows());\n\t\tassertTrue(onArchitecture(\"aarch64\"));\n\t}\n\n\t@Test\n\t@EnabledOnOs(value = LINUX, architectures = \"aarch64\")\n\tvoid aarch64WithLinux() {\n\t\tassertTrue(onLinux());\n\t\tassertTrue(onArchitecture(\"aarch64\"));\n\t}\n\n\tstatic boolean onAix() {\n\t\treturn OS_NAME.contains(\"aix\");\n\t}\n\n\tstatic boolean onArchitecture(String arch) {\n\t\treturn ARCH.contains(arch);\n\t}\n\n\tstatic boolean onFreebsd() {\n\t\treturn OS_NAME.contains(\"freebsd\");\n\t}\n\n\tstatic boolean onLinux() {\n\t\treturn OS_NAME.contains(\"linux\");\n\t}\n\n\tstatic boolean onMac() {\n\t\treturn OS_NAME.contains(\"mac\");\n\t}\n\n\tstatic boolean onOpenbsd() {\n\t\treturn OS_NAME.contains(\"openbsd\");\n\t}\n\n\tstatic boolean onSolaris() {\n\t\treturn OS_NAME.contains(\"solaris\");\n\t}\n\n\tstatic boolean onWindows() {\n\t\treturn OS_NAME.contains(\"windows\");\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@EnabledOnOs(MAC)\n\t@interface EnabledOnMac {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/JRETests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_17;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_18;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_19;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_20;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_21;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_22;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_23;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_24;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_25;\nimport static org.junit.jupiter.api.condition.JRE.OTHER;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link JRE}\n *\n * @since 5.7\n */\npublic class JRETests {\n\n\tprivate static final JRE CURRENT_JRE = JRE.currentJre();\n\n\t@Test\n\t@EnabledOnJre(JAVA_17)\n\tvoid jre17() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_17);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(17);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_18)\n\tvoid jre18() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_18);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(18);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_19)\n\tvoid jre19() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_19);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(19);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_20)\n\tvoid jre20() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_20);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(20);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_21)\n\tvoid jre21() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_21);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(21);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_22)\n\tvoid jre22() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_21);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(22);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_23)\n\tvoid jre23() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_23);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(23);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_24)\n\tvoid jre24() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_24);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(24);\n\t}\n\n\t@Test\n\t@EnabledOnJre(JAVA_25)\n\tvoid jre25() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(JAVA_25);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(25);\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 17)\n\tvoid version17() {\n\t\tjre17();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 18)\n\tvoid version18() {\n\t\tjre18();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 19)\n\tvoid version19() {\n\t\tjre19();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 20)\n\tvoid version20() {\n\t\tjre20();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 21)\n\tvoid version21() {\n\t\tjre21();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 22)\n\tvoid version22() {\n\t\tjre22();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 23)\n\tvoid version23() {\n\t\tjre23();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 24)\n\tvoid version24() {\n\t\tjre24();\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = 25)\n\tvoid version25() {\n\t\tjre25();\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\t@EnabledOnJre(OTHER)\n\tvoid jreOther() {\n\t\tassertThat(CURRENT_JRE).as(\"current version\").isEqualTo(OTHER);\n\t\tassertThat(CURRENT_JRE.version()).as(\"current feature version\").isEqualTo(Integer.MAX_VALUE);\n\t}\n\n\t@Test\n\t@EnabledOnJre(versions = Integer.MAX_VALUE)\n\tvoid versionMaxInteger() {\n\t\tjreOther();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/OSTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nclass OSTests {\n\n\t@ParameterizedTest\n\t@NullAndEmptySource\n\t@ValueSource(strings = { \" \", \"\\t\" })\n\tvoid blankOsNameYieldsNull(String name) {\n\t\tassertNull(OS.parse(name));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"!\", \"?\", \"✨\", \"MINIX\" })\n\tvoid unknownOsNameYieldsOTHER(String name) {\n\t\tassertEquals(OS.OTHER, OS.parse(name));\n\t}\n\n\t@Nested\n\tclass ValidNames {\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"AIX\", \"Aix\", \"LaIxOS\" })\n\t\tvoid aix(String name) {\n\t\t\tassertEquals(OS.AIX, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"FREEBSD\", \"FreeBSD\" })\n\t\tvoid freebsd(String name) {\n\t\t\tassertEquals(OS.FREEBSD, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"LINUX\", \"Linux\" })\n\t\tvoid linux(String name) {\n\t\t\tassertEquals(OS.LINUX, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"MAC\", \"mac\" })\n\t\tvoid mac(String name) {\n\t\t\tassertEquals(OS.MAC, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"OPENBSD\", \"OpenBSD\" })\n\t\tvoid openbsd(String name) {\n\t\t\tassertEquals(OS.OPENBSD, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"SOLARIS\", \"SunOS\" })\n\t\tvoid solaris(String name) {\n\t\t\tassertEquals(OS.SOLARIS, OS.parse(name));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"WINDOW\", \"Microsoft Windows [Version 10.?]\" })\n\t\tvoid windows(String name) {\n\t\t\tassertEquals(OS.WINDOWS, OS.parse(name));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/StaticConditionMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\npublic class StaticConditionMethods {\n\n\tpublic static boolean returnsTrue() {\n\t\treturn true;\n\t}\n\n\tpublic static boolean returnsFalse() {\n\t\treturn false;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/condition/TestDoubleJRETests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.condition;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.condition.JRE.JAVA_23;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link JRE} using {@link TestDoubleJRE}\n *\n * @since 6.1\n */\npublic class TestDoubleJRETests {\n\n\t@Test\n\t@EnabledForJreRange(min = JAVA_23) // \"23\" because \"22\" is the maximum available in TestDoubleJRE\n\t@SuppressWarnings(\"deprecation\")\n\tvoid currentJreIsOtherForUnsupportedJre() {\n\t\tassertEquals(TestDoubleJRE.OTHER, TestDoubleJRE.currentJre());\n\t\tassertTrue(TestDoubleJRE.OTHER.isCurrentVersion());\n\t\tassertTrue(TestDoubleJRE.isCurrentVersion(TestDoubleJRE.OTHER.version()));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/CloseableResourceIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.reportEntry;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\npublic class CloseableResourceIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid closesCloseableResourcesInReverseInsertOrder() {\n\t\texecuteTestsForClass(TestCase.class).allEvents().reportingEntryPublished() //\n\t\t\t\t.assertEventsMatchExactly( //\n\t\t\t\t\treportEntry(Map.of(\"3\", \"closed\")), //\n\t\t\t\t\treportEntry(Map.of(\"2\", \"closed\")), //\n\t\t\t\t\treportEntry(Map.of(\"1\", \"closed\")));\n\t}\n\n\t@ExtendWith(ExtensionContextParameterResolver.class)\n\tstatic class TestCase {\n\t\t@Test\n\t\tvoid closesCloseableResourcesInExtensionContext(ExtensionContext extensionContext) {\n\t\t\tExtensionContext.Store store = extensionContext.getStore(GLOBAL);\n\t\t\tstore.put(\"foo\", reportEntryOnClose(extensionContext, \"1\"));\n\t\t\tstore.put(\"bar\", reportEntryOnClose(extensionContext, \"2\"));\n\t\t\tstore.put(\"baz\", reportEntryOnClose(extensionContext, \"3\"));\n\t\t}\n\n\t\tprivate AutoCloseable reportEntryOnClose(ExtensionContext extensionContext, String key) {\n\t\t\treturn () -> extensionContext.publishReportEntry(Map.of(key, \"closed\"));\n\t\t}\n\t}\n\n\t@Test\n\tvoid exceptionsDuringCloseAreReportedAsSuppressed() {\n\t\texecuteTestsForClass(ExceptionInCloseableResourceTestCase.class).testEvents() //\n\t\t\t\t.assertEventsMatchLoosely(event( //\n\t\t\t\t\ttest(), //\n\t\t\t\t\tfinishedWithFailure( //\n\t\t\t\t\t\tmessage(\"Exception in test\"), //\n\t\t\t\t\t\tsuppressed(0, //\n\t\t\t\t\t\t\tmessage(\"Failed to close extension context\"), //\n\t\t\t\t\t\t\tcause(message(\"Exception in onClose\")) //\n\t\t\t\t\t\t))));\n\t}\n\n\t@ExtendWith(ThrowingOnCloseExtension.class)\n\tstatic class ExceptionInCloseableResourceTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tthrow new RuntimeException(\"Exception in test\");\n\t\t}\n\n\t}\n\n\tstatic class ThrowingOnCloseExtension implements BeforeEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcontext.getStore(GLOBAL).put(\"throwingResource\", new ThrowingResource());\n\t\t}\n\t}\n\n\t@SuppressWarnings({ \"deprecation\", \"try\" })\n\tstatic class ThrowingResource implements ExtensionContext.Store.CloseableResource, AutoCloseable {\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\tthrow new RuntimeException(\"Exception in onClose\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/DeprecatedMediaTypeTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * Unit tests for the deprecated {@link MediaType}.\n *\n * @see org.junit.jupiter.api.MediaTypeTests\n * @see MediaTypeInteroperabilityTests\n */\n@SuppressWarnings(\"removal\")\nclass DeprecatedMediaTypeTests {\n\n\t@Test\n\tvoid equals() {\n\t\tvar mediaType1 = MediaType.TEXT_PLAIN;\n\t\tvar mediaType2 = MediaType.parse(mediaType1.toString());\n\t\tvar mediaType3 = MediaType.APPLICATION_JSON;\n\n\t\tassertEqualsAndHashCode(mediaType1, mediaType2, mediaType3);\n\t}\n\n\t@Nested\n\tclass ParseTests {\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid parseWithNullOrBlankMediaType(@Nullable String mediaType) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"value\", () -> MediaType.parse(mediaType));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type\", \"type/\", \"/subtype\" })\n\t\tvoid parseWithInvalidMediaType(String mediaType) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.parse(mediaType))//\n\t\t\t\t\t.withMessage(\"Invalid media type: '%s'\", mediaType.strip());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"text/plain\", \"   text/plain   \", \"text/plain; charset=UTF-8\",\n\t\t\t\t\"\\t text/plain; charset=UTF-8 \\t\" })\n\t\tvoid parse(String value) {\n\t\t\tassertThat(MediaType.parse(value)).hasToString(value.strip());\n\t\t}\n\t}\n\n\t@Nested\n\tclass CreateTests {\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankType(@Nullable String type) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"type\", () -> MediaType.create(type, \"json\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankTypeAndCharset(@Nullable String type) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"type\", () -> MediaType.create(type, \"json\", UTF_8));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankSubtype(@Nullable String subtype) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"subtype\", () -> MediaType.create(\"application\", subtype));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\t@ValueSource(strings = { \"   \", \" \\t \" })\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullOrBlankSubtypeAndCharset(@Nullable String subtype) {\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"subtype\",\n\t\t\t\t() -> MediaType.create(\"application\", subtype, UTF_8));\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"DataFlowIssue\") // MediaType.create() parameters are not @Nullable\n\t\tvoid createWithNullCharset() {\n\t\t\tassertPreconditionViolationNotNullFor(\"charset\", () -> MediaType.create(\"application\", \"json\", null));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type/\", \"/subtype\" })\n\t\tvoid createWithInvalidType(String type) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.create(type, \"json\"))//\n\t\t\t\t\t.withMessage(\"Invalid media type: '%s/json'\", type.strip());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"/\", \" / \", \"type/\", \"/subtype\" })\n\t\tvoid createWithInvalidSubtype(String subtype) {\n\t\t\tassertPreconditionViolationFor(() -> MediaType.create(\"application\", subtype))//\n\t\t\t\t\t.withMessage(\"Invalid media type: 'application/%s'\", subtype.strip());\n\t\t}\n\n\t\t@Test\n\t\tvoid create() {\n\t\t\tassertThat(MediaType.create(\"application\", \"json\")).hasToString(\"application/json\");\n\t\t}\n\n\t\t@Test\n\t\tvoid createWithCharset() {\n\t\t\tassertThat(MediaType.create(\"text\", \"plain\", UTF_8)).hasToString(\"text/plain; charset=UTF-8\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExecutableInvokerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport java.lang.reflect.Constructor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * @since 5.9\n */\npublic class ExecutableInvokerIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid invokeConstructorViaExtensionContext() {\n\t\tEngineExecutionResults results = executeTestsForClass(ExecuteConstructorTwiceTestCase.class);\n\n\t\tassertEquals(1, results.testEvents().succeeded().count());\n\t\tassertEquals(2, ExecuteConstructorTwiceTestCase.constructorInvocations);\n\t}\n\n\t@Test\n\tvoid invokeMethodViaExtensionContext() {\n\t\tEngineExecutionResults results = executeTestsForClass(ExecuteTestsTwiceTestCase.class);\n\n\t\tassertEquals(1, results.testEvents().succeeded().count());\n\t\tassertEquals(2, ExecuteTestsTwiceTestCase.testInvocations);\n\t}\n\n\t@ExtendWith(ExecuteTestsTwiceExtension.class)\n\tstatic class ExecuteTestsTwiceTestCase {\n\n\t\tstatic int testInvocations = 0;\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@Test\n\t\tvoid testWithResolvedParameter(TestInfo testInfo,\n\t\t\t\t@ExtendWith(ExtensionContextParameterResolver.class) ExtensionContext extensionContext) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tassertEquals(testInfo.getTestMethod().orElseThrow(), extensionContext.getRequiredTestMethod());\n\t\t\ttestInvocations++;\n\t\t}\n\n\t}\n\n\t@ExtendWith(ExecuteConstructorTwiceExtension.class)\n\tstatic class ExecuteConstructorTwiceTestCase {\n\n\t\tstatic int constructorInvocations = 0;\n\n\t\tExecuteConstructorTwiceTestCase(TestInfo testInfo,\n\t\t\t\t@ExtendWith(ExtensionContextParameterResolver.class) ExtensionContext extensionContext) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tassertEquals(testInfo.getTestClass().orElseThrow(), extensionContext.getRequiredTestClass());\n\t\t\tconstructorInvocations++;\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\tstatic class ExecuteTestsTwiceExtension implements AfterTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tcontext.getExecutableInvoker() //\n\t\t\t\t\t.invoke(context.getRequiredTestMethod(), context.getRequiredTestInstance());\n\t\t}\n\n\t}\n\n\tstatic class ExecuteConstructorTwiceExtension implements BeforeAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) throws Exception {\n\t\t\tConstructor<?> constructor = context.getRequiredTestClass() //\n\t\t\t\t\t.getDeclaredConstructor(TestInfo.class, ExtensionContext.class);\n\t\t\tcontext.getExecutableInvoker().invoke(constructor);\n\t\t}\n\n\t}\n\n\tstatic class ExtensionContextParameterResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn ExtensionContext.class.equals(parameterContext.getParameter().getType());\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn extensionContext;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/ExtensionComposabilityTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static java.util.function.Predicate.not;\nimport static java.util.stream.Collectors.toCollection;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicContainer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ClassUtils;\n\n/**\n * Unit tests for extension composability in JUnit Jupiter.\n *\n * <p>The purpose of these tests is to ensure that a concrete extension\n * (a.k.a., the kitchen sink extension) is able to implement all extension\n * APIs supported by JUnit Jupiter without any naming conflicts or\n * ambiguities with regard to method names or method signatures.\n *\n * @since 5.0\n * @see KitchenSinkExtension\n */\nclass ExtensionComposabilityTests {\n\n\t@Test\n\tvoid ensureJupiterExtensionApisAreComposable() {\n\n\t\t// 1) Find all existing top-level Extension APIs\n\t\tList<Class<?>> extensionApis = findExtensionApis();\n\n\t\t// 2) Determine which methods we expect the kitchen sink to implement...\n\n\t\t// @formatter:off\n\t\tList<Method> expectedMethods = extensionApis.stream()\n\t\t\t\t.map(Class::getDeclaredMethods)\n\t\t\t\t.flatMap(Arrays::stream)\n\t\t\t\t.filter(not(Method::isSynthetic))\n\t\t\t\t.filter(not(where(Method::getModifiers, Modifier::isStatic)))\n\t\t\t\t.toList();\n\n\t\tList<String> expectedMethodSignatures = expectedMethods.stream()\n\t\t\t\t.map(this::methodSignature)\n\t\t\t\t.sorted()\n\t\t\t\t.toList();\n\n\t\tList<String> expectedMethodNames = expectedMethods.stream()\n\t\t\t\t.map(Method::getName)\n\t\t\t\t.distinct()\n\t\t\t\t.sorted()\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\n\t\t// 3) Dynamically implement all Extension APIs\n\t\tObject dynamicKitchenSinkExtension = Proxy.newProxyInstance(getClass().getClassLoader(),\n\t\t\textensionApis.toArray(Class[]::new), (proxy, method, args) -> null);\n\n\t\t// 4) Determine what ended up in the kitchen sink...\n\n\t\t// @formatter:off\n\t\tList<Method> actualMethods = Arrays.stream(dynamicKitchenSinkExtension.getClass().getDeclaredMethods())\n\t\t\t\t.filter(ModifierSupport::isNotStatic)\n\t\t\t\t.toList();\n\n\t\tList<String> actualMethodSignatures = actualMethods.stream()\n\t\t\t\t.map(this::methodSignature)\n\t\t\t\t.distinct()\n\t\t\t\t.sorted()\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\n\t\tList<String> actualMethodNames = actualMethods.stream()\n\t\t\t\t.map(Method::getName)\n\t\t\t\t.distinct()\n\t\t\t\t.sorted()\n\t\t\t\t.collect(toCollection(ArrayList::new));\n\t\t// @formatter:on\n\n\t\t// 5) Remove methods from java.lang.Object\n\t\tactualMethodSignatures.remove(\"equals(Object)\");\n\t\tactualMethodSignatures.remove(\"hashCode()\");\n\t\tactualMethodSignatures.remove(\"toString()\");\n\t\tactualMethodNames.remove(\"equals\");\n\t\tactualMethodNames.remove(\"hashCode\");\n\t\tactualMethodNames.remove(\"toString\");\n\n\t\t// 6) Verify our expectations\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertThat(actualMethodSignatures).isEqualTo(expectedMethodSignatures),\n\t\t\t\t() -> assertThat(actualMethodNames).isEqualTo(expectedMethodNames)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@TestFactory\n\tStream<DynamicContainer> kitchenSinkExtensionImplementsAllExtensionApis() {\n\t\tvar declaredMethods = List.of(KitchenSinkExtension.class.getDeclaredMethods());\n\t\treturn findExtensionApis().stream() //\n\t\t\t\t.map(c -> dynamicContainer( //\n\t\t\t\t\tc.getSimpleName(), //\n\t\t\t\t\tStream.concat( //\n\t\t\t\t\t\tStream.of(\n\t\t\t\t\t\t\tdynamicTest(\"implements interface\", () -> c.isAssignableFrom(KitchenSinkExtension.class))), //\n\t\t\t\t\t\tArrays.stream(c.getMethods()) //\n\t\t\t\t\t\t\t\t.filter(ModifierSupport::isNotStatic).map(m -> dynamicTest( //\n\t\t\t\t\t\t\t\t\t\"overrides \" + m.getName(), //\n\t\t\t\t\t\t\t\t\t() -> assertTrue( //\n\t\t\t\t\t\t\t\t\t\tdeclaredMethods.stream().anyMatch(it -> //\n\t\t\t\t\t\t\t\t\t\tit.getName().equals(m.getName()) //\n\t\t\t\t\t\t\t\t\t\t\t\t&& it.getReturnType().equals(m.getReturnType()) //\n\t\t\t\t\t\t\t\t\t\t\t\t&& Arrays.equals(it.getParameterTypes(), m.getParameterTypes()) //\n\t\t\t\t\t\t\t\t\t\t))) //\n\t\t\t\t\t\t\t\t) //\n\t\t\t\t\t) //\n\t\t\t\t));\n\t}\n\n\tprivate List<Class<?>> findExtensionApis() {\n\t\treturn ReflectionSupport.findAllClassesInPackage(Extension.class.getPackage().getName(), this::isExtensionApi,\n\t\t\tname -> true);\n\t}\n\n\tprivate boolean isExtensionApi(Class<?> candidate) {\n\t\treturn candidate.isInterface() && (candidate != Extension.class) && Extension.class.isAssignableFrom(candidate);\n\t}\n\n\tprivate String methodSignature(Method method) {\n\t\treturn \"%s(%s)\".formatted(method.getName(),\n\t\t\tClassUtils.nullSafeToString(Class::getSimpleName, method.getParameterTypes()));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/Heavyweight.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nclass Heavyweight implements ParameterResolver, BeforeEachCallback {\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t\tcontext.getStore(ExtensionContext.Namespace.GLOBAL).put(\"once\", new CloseableOnlyOnceResource());\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext context) {\n\t\treturn Resource.class.equals(parameterContext.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext context) {\n\t\tvar engineContext = context.getRoot();\n\t\tvar store = engineContext.getStore(ExtensionContext.Namespace.GLOBAL);\n\t\tvar resource = store.computeIfAbsent(ResourceValue.class);\n\t\tresource.usages.incrementAndGet();\n\t\treturn resource;\n\t}\n\n\tinterface Resource {\n\t\tString ID = \"org.junit.jupiter.extensions.Heavyweight.Resource\";\n\n\t\tint usages();\n\t}\n\n\t/**\n\t * Demo resource class.\n\t *\n\t * <p>The class implements interface {@link AutoCloseable}\n\t * and interface {@link AutoCloseable} to show and ensure that a single\n\t * {@link ResourceValue#close()} method implementation is needed to comply\n\t * with both interfaces.\n\t */\n\tstatic class ResourceValue implements Resource, AutoCloseable {\n\n\t\tstatic final AtomicInteger creations = new AtomicInteger();\n\t\tprivate final AtomicInteger usages = new AtomicInteger();\n\n\t\t@SuppressWarnings(\"unused\") // used via reflection\n\t\tResourceValue() {\n\t\t\t// Open long-living resources here.\n\t\t\tassertEquals(1, creations.incrementAndGet(), \"Singleton pattern failure!\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\t// Close resources here.\n\t\t\tassertEquals(9, usages.get(), \"Usage counter mismatch!\");\n\t\t}\n\n\t\t@Override\n\t\tpublic int usages() {\n\t\t\treturn usages.get();\n\t\t}\n\t}\n\n\tprivate static class CloseableOnlyOnceResource implements AutoCloseable {\n\n\t\tprivate final AtomicBoolean closed = new AtomicBoolean();\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tassertTrue(closed.compareAndSet(false, true), \"already closed!\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/HeavyweightTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.parallel.ResourceLock;\n\n/**\n * Unit tests for {@link org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource}\n * stored values.\n *\n * @since 1.1\n */\nclass HeavyweightTests {\n\n\t@AfterAll\n\tstatic void afterAll() {\n\t\tHeavyweight.ResourceValue.creations.set(0);\n\t}\n\n\t@Nested\n\t@TestInstance(PER_CLASS)\n\t@ExtendWith(Heavyweight.class)\n\t@ResourceLock(Heavyweight.Resource.ID)\n\tclass Alpha {\n\n\t\tprivate int mark;\n\n\t\t@BeforeAll\n\t\tvoid setMark(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 0);\n\t\t\tmark = resource.usages();\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> alpha1(Heavyweight.Resource resource) {\n\t\t\treturn Stream.of(dynamicTest(\"foo\", () -> assertTrue(resource.usages() > 1)));\n\t\t}\n\n\t\t@Test\n\t\tvoid alpha2(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 1);\n\t\t}\n\n\t\t@Test\n\t\tvoid alpha3(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 1);\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid checkMark(Heavyweight.Resource resource) {\n\t\t\tassertEquals(mark, resource.usages() - 4);\n\t\t}\n\n\t}\n\n\t@Nested\n\t@TestInstance(PER_CLASS)\n\t@ExtendWith(Heavyweight.class)\n\t@ResourceLock(Heavyweight.Resource.ID)\n\tclass Beta {\n\n\t\tprivate int mark;\n\n\t\t@BeforeAll\n\t\tvoid beforeAll(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 0);\n\t\t\tmark = resource.usages();\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 1);\n\t\t}\n\n\t\t@Test\n\t\tvoid beta(Heavyweight.Resource resource) {\n\t\t\tassertTrue(resource.usages() > 2);\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll(Heavyweight.Resource resource) {\n\t\t\tassertEquals(mark, resource.usages() - 3);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/KitchenSinkExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * <em>Kitchen Sink</em> extension that implements every extension API\n * supported by JUnit Jupiter.\n *\n * <p>This extension should never actually be registered for any tests.\n * Rather, its sole purpose is to help ensure (via visual inspection)\n * that a concrete extension is able to implement all extension APIs\n * supported by JUnit Jupiter without any naming conflicts or\n * ambiguities with regard to method names or method signatures.\n * {@link ExtensionComposabilityTests}, on the other hand, serves\n * the same purpose in a dynamic and automated fashion.\n *\n * @since 5.0\n * @see ExtensionComposabilityTests\n */\n// @formatter:off\npublic class KitchenSinkExtension implements\n\n\t// Lifecycle Callbacks\n\tBeforeAllCallback,\n\t\tBeforeClassTemplateInvocationCallback,\n\t\t\tBeforeEachCallback,\n\t\t\t\tBeforeTestExecutionCallback,\n\t\t\t\t\tTestExecutionExceptionHandler,\n\t\t\t\tAfterTestExecutionCallback,\n\t\t\tAfterEachCallback,\n\t\tAfterClassTemplateInvocationCallback,\n\tAfterAllCallback,\n\n\t// Lifecycle methods exception handling\n\tLifecycleMethodExecutionExceptionHandler,\n\n\t// Dependency Injection\n\tTestInstancePreConstructCallback,\n\tTestInstanceFactory,\n\tTestInstancePostProcessor,\n\tTestInstancePreDestroyCallback,\n\tParameterResolver,\n\n\t// Conditional Test Execution\n\tExecutionCondition,\n\n\t// @TestTemplate and @ClassTemplate\n\tTestTemplateInvocationContextProvider,\n\tClassTemplateInvocationContextProvider,\n\n\t// Miscellaneous\n\tTestWatcher,\n\tInvocationInterceptor,\n\tPreInterruptCallback\n\n// @formatter:on\n{\n\n\t// --- Test Class Instantiation --------------------------------------------\n\n\t@Override\n\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\treturn ExtensionContextScope.TEST_METHOD;\n\t}\n\n\t// --- Lifecycle Callbacks -------------------------------------------------\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void beforeClassTemplateInvocation(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void beforeEach(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void beforeTestExecution(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) {\n\t}\n\n\t@Override\n\tpublic void afterTestExecution(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void afterEach(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void afterClassTemplateInvocation(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void afterAll(ExtensionContext context) {\n\t}\n\n\t// --- Lifecycle methods exception handling\n\n\t@Override\n\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t}\n\n\t@Override\n\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t}\n\n\t@Override\n\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t}\n\n\t@Override\n\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t}\n\n\t// --- Dependency Injection ------------------------------------------------\n\n\t@Override\n\tpublic void preConstructTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn null;\n\t}\n\n\t// --- Conditional Test Execution ------------------------------------------\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t// --- @TestTemplate -------------------------------------------------------\n\n\t@Override\n\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n\t// --- @ClassTemplate -------------------------------------------------------\n\n\t@Override\n\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context) {\n\t\tthrow new UnsupportedOperationException();\n\t}\n\n\t@Override\n\tpublic boolean mayReturnZeroClassTemplateInvocationContexts(ExtensionContext context) {\n\t\treturn false;\n\t}\n\n\t// --- TestWatcher ---------------------------------------------------------\n\n\t@Override\n\tpublic void testDisabled(ExtensionContext context, Optional<String> reason) {\n\t}\n\n\t@Override\n\tpublic void testSuccessful(ExtensionContext context) {\n\t}\n\n\t@Override\n\tpublic void testAborted(ExtensionContext context, @Nullable Throwable cause) {\n\t}\n\n\t@Override\n\tpublic void testFailed(ExtensionContext context, @Nullable Throwable cause) {\n\t}\n\n\t// --- InvocationInterceptor -----------------------------------------------\n\n\t@Override\n\tpublic <T> T interceptTestClassConstructor(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Constructor<T>> invocationContext, ExtensionContext extensionContext)\n\t\t\tthrows Throwable {\n\t\treturn InvocationInterceptor.super.interceptTestClassConstructor(invocation, invocationContext,\n\t\t\textensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptBeforeAllMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptBeforeEachMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptTestMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic <T> T interceptTestFactoryMethod(Invocation<T> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\treturn InvocationInterceptor.super.interceptTestFactoryMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptTestTemplateMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptDynamicTest(Invocation<@Nullable Void> invocation,\n\t\t\tDynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptDynamicTest(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptAfterEachMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t@Override\n\tpublic void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\tInvocationInterceptor.super.interceptAfterAllMethod(invocation, invocationContext, extensionContext);\n\t}\n\n\t// --- PreInterruptCallback ------------------------------------------------\n\n\t@Override\n\tpublic void beforeThreadInterrupt(PreInterruptContext preInterruptContext, ExtensionContext extensionContext)\n\t\t\tthrows Exception {\n\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/MediaTypeInteroperabilityTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Interoperability tests for {@link org.junit.jupiter.api.MediaType} and the\n * deprecated {@link org.junit.jupiter.api.extension.MediaType}.\n *\n * @since 1.14\n * @see DeprecatedMediaTypeTests\n */\nclass MediaTypeInteroperabilityTests {\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid newAndDeprecatedMediaTypesAreLogicallyEquivalent() {\n\t\tvar mediaType = org.junit.jupiter.api.MediaType.TEXT_PLAIN_UTF_8;\n\t\tvar deprecatedMediaType = MediaType.TEXT_PLAIN_UTF_8;\n\t\tvar differentMediaType = MediaType.TEXT_PLAIN;\n\n\t\tassertThat(mediaType.getClass()).isNotEqualTo(deprecatedMediaType.getClass());\n\t\tassertEqualsAndHashCode(mediaType, deprecatedMediaType, differentMediaType);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/extension/support/TypeBasedParameterResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.extension.support;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * @since 5.6\n */\nclass TypeBasedParameterResolverTests {\n\n\tprivate final ParameterResolver basicTypeBasedParameterResolver = new BasicTypeBasedParameterResolver();\n\tprivate final ParameterResolver subClassedBasicTypeBasedParameterResolver = new SubClassedBasicTypeBasedParameterResolver();\n\tprivate final ParameterResolver parametrizedTypeBasedParameterResolver = new ParameterizedTypeBasedParameterResolver();\n\n\t@Test\n\tvoid missingTypeTypeBasedParameterResolver() {\n\t\tassertPreconditionViolationFor(MissingTypeTypeBasedParameterResolver::new)//\n\t\t\t\t.withMessage(\"Failed to discover parameter type supported by \"\n\t\t\t\t\t\t+ MissingTypeTypeBasedParameterResolver.class.getName()\n\t\t\t\t\t\t+ \"; potentially caused by lacking parameterized type in class declaration.\");\n\t}\n\n\t@Test\n\tvoid supportsParameterForBasicTypes() {\n\t\tParameter parameter1 = findParameterOfMethod(\"methodWithBasicTypeParameter\", String.class);\n\t\tassertTrue(basicTypeBasedParameterResolver.supportsParameter(parameterContext(parameter1), mock()));\n\t\tassertTrue(subClassedBasicTypeBasedParameterResolver.supportsParameter(parameterContext(parameter1), mock()));\n\n\t\tParameter parameter2 = findParameterOfMethod(\"methodWithObjectParameter\", Object.class);\n\t\tassertFalse(basicTypeBasedParameterResolver.supportsParameter(parameterContext(parameter2), mock()));\n\t}\n\n\t@Test\n\tvoid supportsParameterForParameterizedTypes() {\n\t\tParameter parameter1 = findParameterOfMethod(\"methodWithParameterizedTypeParameter\", Map.class);\n\t\tassertTrue(parametrizedTypeBasedParameterResolver.supportsParameter(parameterContext(parameter1), mock()));\n\n\t\tParameter parameter3 = findParameterOfMethod(\"methodWithAnotherParameterizedTypeParameter\", Map.class);\n\t\tassertFalse(parametrizedTypeBasedParameterResolver.supportsParameter(parameterContext(parameter3), mock()));\n\t}\n\n\t@Test\n\tvoid resolve() {\n\t\tExtensionContext extensionContext = extensionContext();\n\t\tParameterContext parameterContext = parameterContext(\n\t\t\tfindParameterOfMethod(\"methodWithBasicTypeParameter\", String.class));\n\t\tassertEquals(\"Displaying TestAnnotation\",\n\t\t\tbasicTypeBasedParameterResolver.resolveParameter(parameterContext, extensionContext));\n\n\t\tParameter parameter2 = findParameterOfMethod(\"methodWithParameterizedTypeParameter\", Map.class);\n\t\tassertEquals(Map.of(\"ids\", List.of(1, 42)),\n\t\t\tparametrizedTypeBasedParameterResolver.resolveParameter(parameterContext(parameter2), extensionContext));\n\t}\n\n\tprivate static ParameterContext parameterContext(Parameter parameter) {\n\t\tParameterContext parameterContext = mock();\n\t\twhen(parameterContext.getParameter()).thenReturn(parameter);\n\t\treturn parameterContext;\n\t}\n\n\tprivate static ExtensionContext extensionContext() {\n\t\tExtensionContext extensionContext = mock();\n\t\twhen(extensionContext.getDisplayName()).thenReturn(\"Displaying\");\n\t\treturn extensionContext;\n\t}\n\n\tprivate Parameter findParameterOfMethod(String methodName, Class<?>... parameterTypes) {\n\t\tMethod method = ReflectionSupport.findMethod(Sample.class, methodName, parameterTypes).orElseThrow();\n\t\treturn method.getParameters()[0];\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"rawtypes\")\n\tstatic class MissingTypeTypeBasedParameterResolver extends TypeBasedParameterResolver {\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn \"enigma\";\n\t\t}\n\t}\n\n\tstatic class BasicTypeBasedParameterResolver extends TypeBasedParameterResolver<String> {\n\n\t\t@Override\n\t\tpublic String resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\tClass<?> parameterAnnotation = parameterContext.getParameter().getAnnotations()[0].annotationType();\n\t\t\treturn \"%s %s\".formatted(extensionContext.getDisplayName(), parameterAnnotation.getSimpleName());\n\t\t}\n\t}\n\n\tstatic class SubClassedBasicTypeBasedParameterResolver extends BasicTypeBasedParameterResolver {\n\t}\n\n\tstatic class ParameterizedTypeBasedParameterResolver\n\t\t\textends TypeBasedParameterResolver<Map<String, List<Integer>>> {\n\n\t\t@Override\n\t\tpublic Map<String, List<Integer>> resolveParameter(ParameterContext parameterContext,\n\t\t\t\tExtensionContext extensionContext) throws ParameterResolutionException {\n\t\t\treturn Map.of(\"ids\", List.of(1, 42));\n\t\t}\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@interface TestAnnotation {\n\t}\n\n\tstatic class Sample {\n\n\t\tvoid methodWithBasicTypeParameter(@TestAnnotation String string) {\n\t\t}\n\n\t\tvoid methodWithObjectParameter(Object nothing) {\n\t\t}\n\n\t\tvoid methodWithParameterizedTypeParameter(Map<String, List<Integer>> map) {\n\t\t}\n\n\t\tvoid methodWithAnotherParameterizedTypeParameter(Map<String, List<Object>> nothing) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/io/TempDirDeletionStrategyTests.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.io;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.DeletionException;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.IgnoreFailures;\nimport org.junit.platform.commons.logging.LogRecordListener;\n\n/**\n * @since 6.1\n */\nclass TempDirDeletionStrategyTests {\n\n\t@Nested\n\tclass IgnoreFailuresTests {\n\n\t\t@Test\n\t\tvoid logsAndIgnoresFailures(@TempDir Path tempDir, @TrackLogRecords LogRecordListener log) throws Exception {\n\t\t\tvar undeletableDir = Files.createDirectory(tempDir.resolve(\"undeletable\"));\n\t\t\tvar strategy = new IgnoreFailures(new FailingTempDirDeletionStrategy());\n\n\t\t\tAnnotatedElementContext annotatedElementContext = mock();\n\t\t\tvar testMethod = IgnoreFailuresTests.class.getDeclaredMethod(\"logsAndIgnoresFailures\", Path.class,\n\t\t\t\tLogRecordListener.class);\n\t\t\twhen(annotatedElementContext.getAnnotatedElement()).thenReturn(testMethod.getParameters()[0]);\n\n\t\t\tvar result = strategy.delete(undeletableDir, annotatedElementContext, mock());\n\n\t\t\tassertThat(result.isSuccessful());\n\n\t\t\tvar loggedWarnings = log.stream(IgnoreFailures.class, Level.WARNING).toList();\n\n\t\t\tassertThat(loggedWarnings) //\n\t\t\t\t\t.extracting(LogRecord::getMessage) //\n\t\t\t\t\t.containsExactly(\n\t\t\t\t\t\t\"Failed to delete all temporary files for parameter 'tempDir' in method logsAndIgnoresFailures(Path, LogRecordListener)\");\n\n\t\t\tvar exception = loggedWarnings.getFirst().getThrown();\n\t\t\tassertThat(exception).isInstanceOf(DeletionException.class);\n\t\t\tassertThat(exception).hasMessage(\n\t\t\t\t\"Failed to delete temp directory %s. The following paths could not be deleted (see suppressed exceptions for details): <root>\".formatted(\n\t\t\t\t\tundeletableDir.toAbsolutePath()));\n\n\t\t\tassertThat(exception.getSuppressed()).hasSize(1);\n\t\t\tassertThat(exception.getSuppressed()[0]) //\n\t\t\t\t\t.isInstanceOf(IOException.class) //\n\t\t\t\t\t.hasMessage(\"Simulated failure\");\n\t\t} //\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/LockTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.ResourceLocksProvider.Lock;\n\n/**\n * Unit tests for {@link Lock}.\n *\n * @since 5.12\n */\nclass LockTests {\n\n\t@Test\n\tvoid readWriteModeSetByDefault() {\n\t\tassertEquals(READ_WRITE, new Lock(\"a\").getAccessMode());\n\t}\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\t// @formatter:off\n\t\tassertEqualsAndHashCode(\n\t\t\t\tnew Lock(\"a\", READ_WRITE),\n\t\t\t\tnew Lock(\"a\", READ_WRITE),\n\t\t\t\tnew Lock(\"b\", READ_WRITE)\n\t\t);\n\t\tassertEqualsAndHashCode(\n\t\t\t\tnew Lock(\"a\", READ_WRITE),\n\t\t\t\tnew Lock(\"a\", READ_WRITE),\n\t\t\t\tnew Lock(\"a\", READ)\n\t\t);\n\t\t// @formatter:on\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLockAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.util.Throwables.getRootCause;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\nimport org.junit.platform.testkit.engine.Event;\n\n/**\n * Integration tests for {@link ResourceLock} and {@link ResourceLocksProvider}.\n *\n * @since 5.12\n */\nclass ResourceLockAnnotationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final UniqueId uniqueId = UniqueId.root(\"enigma\", \"foo\");\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\t\twhen(configuration.getDefaultExecutionMode()).thenReturn(ExecutionMode.SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid noSharedResources() {\n\t\t// @formatter:off\n\t\tvar classResources = getClassResources(\n\t\t\t\tNoSharedResourcesTestCase.class\n\t\t);\n\t\tassertThat(classResources).isEmpty();\n\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tNoSharedResourcesTestCase.class\n\t\t);\n\t\tassertThat(methodResources).isEmpty();\n\n\t\tvar nestedClassResources = getNestedClassResources(\n\t\t\t\tNoSharedResourcesTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassResources).isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationValue() {\n\t\t// @formatter:off\n\t\tvar classResources = getClassResources(\n\t\t\t\tSharedResourcesViaAnnotationValueTestCase.class\n\t\t);\n\t\tassertThat(classResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE)\n\t\t);\n\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tSharedResourcesViaAnnotationValueTestCase.class\n\t\t);\n\t\tassertThat(methodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ),\n\t\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ_WRITE)\n\t\t);\n\n\t\tvar nestedClassResources = getNestedClassResources(\n\t\t\t\tSharedResourcesViaAnnotationValueTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"c1\", LockMode.READ)\n\t\t);\n\n\t\tvar nestedClassMethodResources = getMethodResources(\n\t\t\t\tSharedResourcesViaAnnotationValueTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassMethodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"c2\", LockMode.READ)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationProviders() {\n\t\t// @formatter:off\n\t\tvar classResources = getClassResources(\n\t\t\t\tSharedResourcesViaAnnotationProvidersTestCase.class\n\t\t);\n\t\tassertThat(classResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ),\n\t\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ)\n\t\t);\n\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tSharedResourcesViaAnnotationProvidersTestCase.class\n\t\t);\n\t\tassertThat(methodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ_WRITE)\n\t\t);\n\n\t\tvar nestedClassResources = getNestedClassResources(\n\t\t\t\tSharedResourcesViaAnnotationProvidersTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"c1\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"c2\", LockMode.READ)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationValueAndProviders() {\n\t\t// @formatter:off\n\t\tvar classResources = getClassResources(\n\t\t\t\tSharedResourcesViaAnnotationValueAndProvidersTestCase.class\n\t\t);\n\t\tassertThat(classResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ)\n\t\t);\n\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tSharedResourcesViaAnnotationValueAndProvidersTestCase.class\n\t\t);\n\t\tassertThat(methodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ),\n\t\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ)\n\t\t);\n\n\t\tvar nestedClassResources = getNestedClassResources(\n\t\t\t\tSharedResourcesViaAnnotationValueAndProvidersTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"c1\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"c2\", LockMode.READ_WRITE),\n\t\t\t\tnew ExclusiveResource(\"c3\", LockMode.READ_WRITE)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationValueAndProvidersForClassTemplate() {\n\t\tvar selector = selectClass(SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase.class);\n\t\tvar engineDescriptor = discoverTests(selector).getEngineDescriptor();\n\t\tengineDescriptor.accept(TestDescriptor::prune);\n\n\t\tvar classTemplateTestDescriptor = (JupiterTestDescriptor) getOnlyElement(engineDescriptor.getChildren());\n\n\t\tvar expectedResources = List.of( //\n\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"c1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"c2\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"c3\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"d1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"d2\", LockMode.READ) //\n\t\t);\n\n\t\tassertThat(classTemplateTestDescriptor.getExclusiveResources()) //\n\t\t\t\t.containsExactlyInAnyOrderElementsOf(expectedResources);\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationValueAndProvidersForClassTemplateInvocation() {\n\t\tvar selector = selectIteration(\n\t\t\tselectClass(SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase.class), 0);\n\t\tvar engineDescriptor = discoverTests(selector).getEngineDescriptor();\n\t\tengineDescriptor.accept(TestDescriptor::prune);\n\n\t\tvar classTemplateTestDescriptor = (JupiterTestDescriptor) getOnlyElement(engineDescriptor.getChildren());\n\n\t\tvar expectedResources = List.of( //\n\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"c1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"c2\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"c3\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"d1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"d2\", LockMode.READ) //\n\t\t);\n\n\t\tassertThat(classTemplateTestDescriptor.getExclusiveResources()) //\n\t\t\t\t.containsExactlyInAnyOrderElementsOf(expectedResources);\n\t}\n\n\t@Test\n\tvoid addSharedResourcesViaAnnotationValueAndProvidersForMethodInClassTemplate() {\n\t\tvar selector = selectMethod(SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase.class, \"test\");\n\t\tvar engineDescriptor = discoverTests(selector).getEngineDescriptor();\n\t\tengineDescriptor.accept(TestDescriptor::prune);\n\n\t\tvar classTemplateTestDescriptor = (JupiterTestDescriptor) getOnlyElement(engineDescriptor.getChildren());\n\n\t\tvar expectedResources = List.of( //\n\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a2\", LockMode.READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"a3\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b1\", LockMode.READ), //\n\t\t\tnew ExclusiveResource(\"b2\", LockMode.READ) //\n\t\t);\n\n\t\tassertThat(classTemplateTestDescriptor.getExclusiveResources()) //\n\t\t\t\t.containsExactlyInAnyOrderElementsOf(expectedResources);\n\t}\n\n\t@Test\n\tvoid sharedResourcesHavingTheSameValueAndModeAreDeduplicated() {\n\t\t// @formatter:off\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tSharedResourcesHavingTheSameValueAndModeAreDeduplicatedTestCase.class\n\t\t);\n\t\tassertThat(methodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid sharedResourcesHavingTheSameValueButDifferentModeAreNotDeduplicated() {\n\t\t// @formatter:off\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tSharedResourcesHavingTheSameValueButDifferentModeAreNotDeduplicatedTestCase.class\n\t\t);\n\t\tassertThat(methodResources).containsExactlyInAnyOrder(\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ),\n\t\t\t\tnew ExclusiveResource(\"a1\", LockMode.READ_WRITE)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tstatic Stream<Class<?>> testMethodsCanNotDeclareSharedResourcesForChildrenArguments() {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\t\tTestCanNotDeclareSharedResourcesForChildrenTestCase.class,\n\t\t\t\tParameterizedTestCanNotDeclareSharedResourcesForChildrenTestCase.class,\n\t\t\t\tRepeatedTestCanNotDeclareSharedResourcesForChildrenTestCase.class,\n\t\t\t\tTestFactoryCanNotDeclareSharedResourcesForChildrenTestCase.class\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"testMethodsCanNotDeclareSharedResourcesForChildrenArguments\")\n\tvoid testMethodsCanNotDeclareSharedResourcesForChildren(Class<?> testClass) {\n\t\tvar messageTemplate = \"'ResourceLockTarget.CHILDREN' is not supported for methods. Invalid method: %s\";\n\t\tassertThrowsJunitExceptionWithMessage( //\n\t\t\ttestClass, //\n\t\t\tmessageTemplate.formatted(getDeclaredTestMethod(testClass)) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid emptyAnnotation() {\n\t\t// @formatter:off\n\t\tvar classResources = getClassResources(\n\t\t\t\tEmptyAnnotationTestCase.class\n\t\t);\n\t\tassertThat(classResources).isEmpty();\n\n\t\tvar methodResources = getMethodResources(\n\t\t\t\tEmptyAnnotationTestCase.class\n\t\t);\n\t\tassertThat(methodResources).isEmpty();\n\n\t\tvar nestedClassResources = getNestedClassResources(\n\t\t\t\tEmptyAnnotationTestCase.NestedClass.class\n\t\t);\n\t\tassertThat(nestedClassResources).isEmpty();\n\t\t// @formatter:on\n\t}\n\n\tprivate Set<ExclusiveResource> getClassResources(Class<?> testClass) {\n\t\treturn getClassTestDescriptor(testClass).getExclusiveResources();\n\t}\n\n\tprivate ClassTestDescriptor getClassTestDescriptor(Class<?> testClass) {\n\t\treturn new ClassTestDescriptor(uniqueId, testClass, configuration);\n\t}\n\n\tprivate Set<ExclusiveResource> getMethodResources(Class<?> testClass) {\n\t\tvar descriptor = new TestMethodTestDescriptor( //\n\t\t\tuniqueId, testClass, getDeclaredTestMethod(testClass), List::of, configuration //\n\t\t);\n\t\tdescriptor.setParent(getClassTestDescriptor(testClass));\n\t\treturn descriptor.getExclusiveResources();\n\t}\n\n\tprivate static Method getDeclaredTestMethod(Class<?> testClass) {\n\t\ttry {\n\t\t\treturn testClass.getDeclaredMethod(\"test\");\n\t\t}\n\t\tcatch (NoSuchMethodException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate Set<ExclusiveResource> getNestedClassResources(Class<?> testClass) {\n\t\tvar descriptor = new NestedClassTestDescriptor(uniqueId, testClass, List::of, configuration);\n\t\tdescriptor.setParent(getClassTestDescriptor(testClass.getEnclosingClass()));\n\t\treturn descriptor.getExclusiveResources();\n\t}\n\n\tprivate void assertThrowsJunitExceptionWithMessage(Class<?> testClass, String message) {\n\t\t// @formatter:off\n\t\tvar events = executeTestsForClass(testClass).allEvents();\n\t\tassertThat(events.filter(finishedWithFailure(instanceOf(JUnitException.class))::matches))\n\t\t\t\t.hasSize(1)\n\t\t\t\t.map(Event::getPayload)\n\t\t\t\t.map(payload -> (TestExecutionResult) payload.orElseThrow())\n\t\t\t\t.map(payload -> getRootCause(payload.getThrowable().orElseThrow()))\n\t\t\t\t.first()\n\t\t\t\t.is(instanceOf(JUnitException.class))\n\t\t\t\t.has(message(message));\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class NoSharedResourcesTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\t\t}\n\t}\n\n\t@ResourceLock(\"a1\")\n\t@ResourceLock(value = \"a2\", mode = ResourceAccessMode.READ_WRITE)\n\t@ResourceLock(value = \"a3\", mode = ResourceAccessMode.READ_WRITE, target = ResourceLockTarget.CHILDREN)\n\tstatic class SharedResourcesViaAnnotationValueTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"b1\", mode = ResourceAccessMode.READ)\n\t\t@ResourceLock(value = \"b2\", target = ResourceLockTarget.SELF)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(value = \"c1\", mode = ResourceAccessMode.READ)\n\t\t@ResourceLock(value = \"c2\", mode = ResourceAccessMode.READ, target = ResourceLockTarget.CHILDREN)\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock(providers = { //\n\t\t\tSharedResourcesViaAnnotationProvidersTestCase.FirstClassLevelProvider.class, //\n\t\t\tSharedResourcesViaAnnotationProvidersTestCase.SecondClassLevelProvider.class //\n\t})\n\tstatic class SharedResourcesViaAnnotationProvidersTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(providers = MethodLevelProvider.class)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(providers = NestedClassLevelProvider.class)\n\t\tclass NestedClass {\n\t\t}\n\n\t\tstatic class FirstClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"a1\", ResourceAccessMode.READ));\n\t\t\t}\n\t\t}\n\n\t\tstatic class SecondClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"a2\", ResourceAccessMode.READ));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\treturn Set.of(new Lock(\"b1\"));\n\t\t\t}\n\t\t}\n\n\t\tstatic class MethodLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\treturn Set.of(new Lock(\"b2\"));\n\t\t\t}\n\t\t}\n\n\t\tstatic class NestedClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"c1\"), new Lock(\"c2\", ResourceAccessMode.READ));\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock( //\n\t\t\tvalue = \"a1\", //\n\t\t\tproviders = SharedResourcesViaAnnotationValueAndProvidersTestCase.FirstClassLevelProvider.class //\n\t)\n\t@ResourceLock( //\n\t\t\tvalue = \"a2\", //\n\t\t\ttarget = ResourceLockTarget.CHILDREN, //\n\t\t\tproviders = SharedResourcesViaAnnotationValueAndProvidersTestCase.SecondClassLevelProvider.class //\n\t)\n\tstatic class SharedResourcesViaAnnotationValueAndProvidersTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"b1\", mode = ResourceAccessMode.READ)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(\"c1\")\n\t\t@ResourceLock(providers = NestedClassLevelProvider.class)\n\t\tclass NestedClass {\n\t\t}\n\n\t\tstatic class FirstClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"a3\", ResourceAccessMode.READ));\n\t\t\t}\n\t\t}\n\n\t\tstatic class SecondClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\treturn Set.of(new Lock(\"b2\", ResourceAccessMode.READ));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"c2\"));\n\t\t\t}\n\t\t}\n\n\t\tstatic class NestedClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"c3\"));\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock( //\n\t\t\tvalue = \"a1\", //\n\t\t\ttarget = ResourceLockTarget.CHILDREN, //\n\t\t\tproviders = SharedResourcesHavingTheSameValueAndModeAreDeduplicatedTestCase.Provider.class //\n\t)\n\tstatic class SharedResourcesHavingTheSameValueAndModeAreDeduplicatedTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"a1\")\n\t\tvoid test() {\n\t\t}\n\n\t\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\treturn Set.of(new Lock(\"a1\"));\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock(value = \"a1\", mode = ResourceAccessMode.READ_WRITE, target = ResourceLockTarget.CHILDREN)\n\tstatic class SharedResourcesHavingTheSameValueButDifferentModeAreNotDeduplicatedTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"a1\", mode = ResourceAccessMode.READ)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class TestCanNotDeclareSharedResourcesForChildrenTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"a1\", target = ResourceLockTarget.CHILDREN)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class ParameterizedTestCanNotDeclareSharedResourcesForChildrenTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(ints = { 1, 2, 3 })\n\t\t@ResourceLock(value = \"a1\", target = ResourceLockTarget.CHILDREN)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class RepeatedTestCanNotDeclareSharedResourcesForChildrenTestCase {\n\n\t\t@RepeatedTest(5)\n\t\t@ResourceLock(value = \"a1\", target = ResourceLockTarget.CHILDREN)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class TestFactoryCanNotDeclareSharedResourcesForChildrenTestCase {\n\n\t\t@TestFactory\n\t\t@ResourceLock(value = \"a1\", target = ResourceLockTarget.CHILDREN)\n\t\tStream<DynamicTest> test() {\n\t\t\treturn Stream.of(DynamicTest.dynamicTest(\"Dynamic test\", () -> {\n\t\t\t}));\n\t\t}\n\t}\n\n\t@ResourceLock\n\tstatic class EmptyAnnotationTestCase {\n\n\t\t@Test\n\t\t@ResourceLock\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock\n\t\tclass NestedClass {\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ResourceLock( //\n\t\t\tvalue = \"a1\", //\n\t\t\tproviders = SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase.FirstClassLevelProvider.class //\n\t)\n\t@ResourceLock( //\n\t\t\tvalue = \"a2\", //\n\t\t\ttarget = ResourceLockTarget.CHILDREN, //\n\t\t\tproviders = SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase.SecondClassLevelProvider.class //\n\t)\n\tstatic class SharedResourcesViaAnnotationValueAndProvidersClassTemplateTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = \"b1\", mode = ResourceAccessMode.READ)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(providers = NestedClassLevelProvider.class)\n\t\tclass NestedClass {\n\t\t\t@Test\n\t\t\t@ResourceLock(\"c1\")\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\t@ResourceLock(value = \"d1\", target = ResourceLockTarget.CHILDREN)\n\t\tclass NestedClassTemplate {\n\t\t\t@Test\n\t\t\t@ResourceLock(value = \"d2\", mode = ResourceAccessMode.READ)\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\tstatic class FirstClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"a3\", ResourceAccessMode.READ));\n\t\t\t}\n\t\t}\n\n\t\tstatic class SecondClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\treturn Set.of(new Lock(\"b2\", ResourceAccessMode.READ));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"c2\"));\n\t\t\t}\n\t\t}\n\n\t\tstatic class NestedClassLevelProvider implements ResourceLocksProvider {\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\treturn Set.of(new Lock(\"c3\"));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/parallel/ResourceLocksProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.parallel;\n\nimport static java.util.Collections.emptySet;\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.Event;\n\n/**\n * Integration tests for {@link ResourceLocksProvider}.\n *\n * @since 5.12\n */\nclass ResourceLocksProviderTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid classLevelProvider() {\n\t\tvar events = execute(ClassLevelProviderTestCase.class);\n\t\tassertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);\n\t}\n\n\t@Test\n\tvoid nestedClassLevelProvider() {\n\t\tvar events = execute(NestedClassLevelProviderTestCase.class);\n\t\tassertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);\n\t}\n\n\t@Test\n\tvoid methodLevelProvider() {\n\t\tvar events = execute(MethodLevelProviderTestCase.class);\n\t\tassertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);\n\t}\n\n\t@Test\n\tvoid methodLevelProviderInNestedClass() {\n\t\tvar events = execute(MethodLevelProviderInNestedClassTestCase.class);\n\t\tassertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);\n\t}\n\n\t@Test\n\tvoid providesAccessToRuntimeEnclosingInstances() {\n\t\tvar events = execute(SubClassLevelProviderTestCase.class);\n\t\tassertThat(events.filter(event(test(), finishedSuccessfully())::matches)).hasSize(2);\n\t}\n\n\tprivate Stream<Event> execute(Class<?> testCase) {\n\t\treturn executeTestsForClass(testCase).allEvents().stream();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ResourceLock(providers = ClassLevelProviderTestCase.Provider.class)\n\tstatic class ClassLevelProviderTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(Provider.isProvideForClassCalled, \"'provideForClass' was not called\");\n\t\t\tassertTrue(Provider.isProvideForTestMethodCalled, \"'provideForMethod(test)' was not called\");\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\tvoid nestedTest() {\n\t\t\t\tassertTrue(Provider.isProvideForNestedClassCalled, \"'provideForNestedClass' was not called\");\n\t\t\t\t// @formatter:off\n\t\t\t\tassertTrue(\n\t\t\t\t\t\tProvider.isProvideForNestedTestMethodCalled,\n\t\t\t\t\t\t\"'provideForMethod(nestedTest)' was not called\"\n\t\t\t\t);\n\t\t\t\t// @formatter:on\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tProvider.isProvideForClassCalled = false;\n\t\t\tProvider.isProvideForTestMethodCalled = false;\n\n\t\t\tProvider.isProvideForNestedClassCalled = false;\n\t\t\tProvider.isProvideForNestedTestMethodCalled = false;\n\t\t}\n\n\t\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t\tprivate static boolean isProvideForClassCalled = false;\n\t\t\tprivate static boolean isProvideForTestMethodCalled = false;\n\n\t\t\tprivate static boolean isProvideForNestedClassCalled = false;\n\t\t\tprivate static boolean isProvideForNestedTestMethodCalled = false;\n\n\t\t\t@Nullable\n\t\t\tprivate Class<?> testClass;\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\tthis.testClass = testClass;\n\t\t\t\tisProvideForClassCalled = true;\n\t\t\t\tassertThat(testClass).isAssignableTo(ClassLevelProviderTestCase.class);\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\tisProvideForNestedClassCalled = true;\n\t\t\t\tassertEquals(List.of(requireNonNull(this.testClass)), enclosingInstanceTypes);\n\t\t\t\tassertEquals(ClassLevelProviderTestCase.NestedClass.class, testClass);\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\tif (ClassLevelProviderTestCase.class.isAssignableFrom(testClass)) {\n\t\t\t\t\tassertEquals(List.of(), enclosingInstanceTypes);\n\t\t\t\t\tassertEquals(\"test\", testMethod.getName());\n\t\t\t\t\tisProvideForTestMethodCalled = true;\n\t\t\t\t\treturn emptySet();\n\t\t\t\t}\n\t\t\t\tif (testClass == ClassLevelProviderTestCase.NestedClass.class) {\n\t\t\t\t\tassertEquals(List.of(this.testClass), enclosingInstanceTypes);\n\t\t\t\t\tassertEquals(\"nestedTest\", testMethod.getName());\n\t\t\t\t\tisProvideForNestedTestMethodCalled = true;\n\t\t\t\t\treturn emptySet();\n\t\t\t\t}\n\t\t\t\tfail(\"Unexpected test class: \" + testClass);\n\t\t\t\treturn emptySet();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class SubClassLevelProviderTestCase extends ClassLevelProviderTestCase {\n\t}\n\n\tstatic class NestedClassLevelProviderTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(providers = NestedClassLevelProviderTestCase.Provider.class)\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\tvoid nestedTest() {\n\t\t\t\tassertTrue(Provider.isProvideForNestedClassCalled, \"'provideForNestedClass' was not called\");\n\t\t\t\tassertTrue(Provider.isProvideForMethodCalled, \"'provideForMethod' was not called\");\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tProvider.isProvideForNestedClassCalled = false;\n\t\t\tProvider.isProvideForMethodCalled = false;\n\t\t}\n\n\t\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t\tprivate static boolean isProvideForNestedClassCalled = false;\n\n\t\t\tprivate static boolean isProvideForMethodCalled = false;\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\tfail(\"'provideForClass' should not be called\");\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\tisProvideForNestedClassCalled = true;\n\t\t\t\tassertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes);\n\t\t\t\tassertEquals(NestedClass.class, testClass);\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\tisProvideForMethodCalled = true;\n\t\t\t\tassertEquals(List.of(NestedClassLevelProviderTestCase.class), enclosingInstanceTypes);\n\t\t\t\tassertEquals(NestedClassLevelProviderTestCase.NestedClass.class, testClass);\n\t\t\t\tassertEquals(\"nestedTest\", testMethod.getName());\n\t\t\t\treturn emptySet();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class MethodLevelProviderTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(providers = MethodLevelProviderTestCase.Provider.class)\n\t\tvoid test() {\n\t\t\tassertTrue(Provider.isProvideForMethodCalled, \"'provideForMethod' was not called\");\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\tvoid nestedTest() {\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tProvider.isProvideForMethodCalled = false;\n\t\t}\n\n\t\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t\tprivate static boolean isProvideForMethodCalled = false;\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\tfail(\"'provideForClass' should not be called\");\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\tfail(\"'provideForNestedClass' should not be called\");\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\tisProvideForMethodCalled = true;\n\t\t\t\tassertEquals(List.of(), enclosingInstanceTypes);\n\t\t\t\tassertEquals(MethodLevelProviderTestCase.class, testClass);\n\t\t\t\tassertEquals(\"test\", testMethod.getName());\n\t\t\t\treturn emptySet();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class MethodLevelProviderInNestedClassTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\t@ResourceLock(providers = MethodLevelProviderInNestedClassTestCase.Provider.class)\n\t\t\tvoid nestedTest() {\n\t\t\t\tassertTrue(Provider.isProvideForMethodCalled, \"'provideForMethod' was not called\");\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tProvider.isProvideForMethodCalled = false;\n\t\t}\n\n\t\tstatic class Provider implements ResourceLocksProvider {\n\n\t\t\tprivate static boolean isProvideForMethodCalled = false;\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForClass(Class<?> testClass) {\n\t\t\t\tfail(\"'provideForClass' should not be called\");\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> testClass) {\n\t\t\t\tfail(\"'provideForNestedClass' should not be called\");\n\t\t\t\treturn emptySet();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<Lock> provideForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\t\tMethod testMethod) {\n\t\t\t\tisProvideForMethodCalled = true;\n\t\t\t\tassertEquals(List.of(MethodLevelProviderInNestedClassTestCase.class), enclosingInstanceTypes);\n\t\t\t\tassertEquals(MethodLevelProviderInNestedClassTestCase.NestedClass.class, testClass);\n\t\t\t\tassertEquals(\"nestedTest\", testMethod.getName());\n\t\t\t\treturn emptySet();\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssertionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.subpackage;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Tests which verify that {@link Assertions} can be subclassed.\n *\n * @since 5.3\n */\nclass SubclassedAssertionsTests extends Assertions {\n\n\t@Test\n\tvoid assertTrueWithBooleanTrue() {\n\t\tassertTrue(true);\n\t\tassertTrue(true, \"test\");\n\t\tassertTrue(true, () -> \"test\");\n\t}\n\n\t@Test\n\tvoid assertFalseWithBooleanTrue() {\n\t\tAssertionFailedError error = assertThrows(AssertionFailedError.class, () -> assertFalse(true));\n\t\tassertEquals(\"expected: <false> but was: <true>\", error.getMessage());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/subpackage/SubclassedAssumptionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.subpackage;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.Test;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Tests which verify that {@link Assumptions} can be subclassed.\n *\n * @since 5.3\n */\nclass SubclassedAssumptionsTests extends Assumptions {\n\n\t@Test\n\tvoid assumeTrueWithBooleanTrue() {\n\t\tString foo = null;\n\t\ttry {\n\t\t\tassumeTrue(true);\n\t\t\tfoo = \"foo\";\n\t\t}\n\t\tfinally {\n\t\t\tassertEquals(\"foo\", foo);\n\t\t}\n\t}\n\n\t@Test\n\tvoid assumeFalseWithBooleanTrue() {\n\t\tTestAbortedException exception = assertThrows(TestAbortedException.class, () -> assumeFalse(true));\n\t\tassertEquals(\"Assumption failed: assumption is not false\", exception.getMessage());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/timeout/PreemptiveTimeoutUtilsTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.timeout;\n\nimport static java.time.Duration.ofMillis;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\nimport static org.junit.jupiter.api.timeout.PreemptiveTimeoutUtils.executeWithPreemptiveTimeout;\n\nimport java.time.Duration;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeoutException;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.opentest4j.AssertionFailedError;\n\nclass PreemptiveTimeoutUtilsTest {\n\n\tprivate static final Duration PREEMPTIVE_TIMEOUT = ofMillis(WINDOWS.isCurrentOs() ? 1000 : 100);\n\tprivate static final PreemptiveTimeoutUtils.TimeoutFailureFactory<TimeoutException> TIMEOUT_EXCEPTION_FACTORY = (__,\n\t\t\t___, ____, _____) -> new TimeoutException();\n\n\t@Test\n\tvoid executeWithPreemptiveTimeoutThrowingTimeoutExceptionWithMessageForSupplierThatCompletesAfterTheTimeout() {\n\t\tassertThrows(TimeoutException.class, () -> executeWithPreemptiveTimeout(PREEMPTIVE_TIMEOUT, () -> {\n\t\t\twaitForInterrupt();\n\t\t\treturn \"Tempus Fugit\";\n\t\t}, () -> \"Tempus Fugit\", TIMEOUT_EXCEPTION_FACTORY));\n\t}\n\n\t@Test\n\tvoid executeWithPreemptiveTimeoutThrowingTimeoutExceptionWithMessageForSupplierThatThrowsAnAssertionFailedError() {\n\t\tAssertionFailedError exception = assertThrows(AssertionFailedError.class,\n\t\t\t() -> executeWithPreemptiveTimeout(ofMillis(500), () -> fail(\"enigma\"), () -> \"Tempus Fugit\",\n\t\t\t\tTIMEOUT_EXCEPTION_FACTORY));\n\t\tassertThat(exception).hasMessage(\"enigma\");\n\t}\n\n\t@Test\n\tvoid executeWithPreemptiveTimeoutThrowingTimeoutExceptionWithMessageForSupplierThatThrowsAnException() {\n\t\tRuntimeException exception = assertThrows(RuntimeException.class,\n\t\t\t() -> executeWithPreemptiveTimeout(ofMillis(500),\n\t\t\t\t() -> ExceptionUtils.throwAsUncheckedException(new RuntimeException(\":(\")), () -> \"Tempus Fugit\",\n\t\t\t\tTIMEOUT_EXCEPTION_FACTORY));\n\t\tassertThat(exception).hasMessage(\":(\");\n\t}\n\n\t@Test\n\tvoid executeWithPreemptiveTimeoutThrowingTimeoutExceptionWithMessageForSupplierThatCompletesBeforeTimeout()\n\t\t\tthrows Exception {\n\t\tvar result = executeWithPreemptiveTimeout(PREEMPTIVE_TIMEOUT, () -> \"Tempus Fugit\", () -> \"Tempus Fugit\",\n\t\t\tTIMEOUT_EXCEPTION_FACTORY);\n\n\t\tassertThat(result).isEqualTo(\"Tempus Fugit\");\n\t}\n\n\tprivate void waitForInterrupt() {\n\t\ttry {\n\t\t\tassertFalse(Thread.interrupted(), \"Already interrupted\");\n\t\t\tnew CountDownLatch(1).await();\n\t\t}\n\t\tcatch (InterruptedException ignore) {\n\t\t\t// ignore\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/util/DefaultLocaleTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.util.Locale;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n@DisplayName(\"DefaultLocale extension\")\nclass DefaultLocaleTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static Locale TEST_DEFAULT_LOCALE;\n\tprivate static Locale DEFAULT_LOCALE_BEFORE_TEST;\n\n\t@BeforeAll\n\tstatic void globalSetUp() {\n\t\tDEFAULT_LOCALE_BEFORE_TEST = Locale.getDefault();\n\t\tTEST_DEFAULT_LOCALE = JupiterLocaleUtils.createLocale(\"custom\");\n\t\tLocale.setDefault(TEST_DEFAULT_LOCALE);\n\t}\n\n\t@AfterAll\n\tstatic void globalTearDown() {\n\t\tLocale.setDefault(DEFAULT_LOCALE_BEFORE_TEST);\n\t}\n\n\t@Nested\n\t@DisplayName(\"applied on the method level\")\n\tclass MethodLevelTests {\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"does nothing when annotation is not present\")\n\t\tvoid testDefaultLocaleNoAnnotation() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(TEST_DEFAULT_LOCALE);\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(\"zh-Hant-TW\")\n\t\t@DisplayName(\"sets the default locale using a language tag\")\n\t\tvoid setsLocaleViaLanguageTag() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(Locale.forLanguageTag(\"zh-Hant-TW\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"en\")\n\t\t@DisplayName(\"sets the default locale using a language\")\n\t\tvoid setsLanguage() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"en\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"en\", country = \"EN\")\n\t\t@DisplayName(\"sets the default locale using a language and a country\")\n\t\tvoid setsLanguageAndCountry() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"en\", \"EN\"));\n\t\t}\n\n\t\t/**\n\t\t * A valid variant checked by {@link sun.util.locale.LanguageTag#isVariant} against BCP 47 (or more detailed RFC 5646) matches either {@code [0-9a-Z]{5-8}} or {@code [0-9][0-9a-Z]{3}}.\n\t\t * It does NOT check if such a variant exists in real.\n\t\t * <br>\n\t\t * The Locale-Builder accepts valid variants, concatenated by minus or underscore (minus will be transformed by the builder).\n\t\t * This means \"en-EN\" is a valid languageTag, but not a valid IETF BCP 47 variant subtag.\n\t\t * <br>\n\t\t * This is very confusing as the <a href=\"https://www.oracle.com/java/technologies/javase/jdk11-suported-locales.html\">official page for supported locales</a> shows that japanese locales return {@code *} or {@code JP} as a variant.\n\t\t * Even more confusing the enum values {@code Locale.JAPAN} and {@code Locale.JAPANESE} don't return a variant.\n\t\t *\n\t\t * @see <a href=\"https://www.rfc-editor.org/rfc/rfc5646.html\">RFC 5646</a>\n\t\t */\n\t\t@Test\n\t\t@DefaultLocale(language = \"ja\", country = \"JP\", variant = \"japanese\")\n\t\t@DisplayName(\"sets the default locale using a language, a country and a variant\")\n\t\tvoid setsLanguageAndCountryAndVariant() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"ja\", \"JP\", \"japanese\"));\n\t\t}\n\n\t}\n\n\t@Test\n\t@WritesDefaultLocale\n\t@DisplayName(\"applied on the class level, should execute tests with configured Locale\")\n\tvoid shouldExecuteTestsWithConfiguredLocale() {\n\t\tEngineExecutionResults results = executeTestsForClass(ClassLevelTestCases.class);\n\n\t\tresults.testEvents().assertThatEvents().haveAtMost(2, finishedSuccessfully());\n\t}\n\n\t@DefaultLocale(language = \"fr\", country = \"FR\")\n\tstatic class ClassLevelTestCases {\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\tvoid shouldExecuteWithClassLevelLocale() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"fr\", \"FR\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"de\", country = \"DE\")\n\t\tvoid shouldBeOverriddenWithMethodLevelLocale() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"de\", \"DE\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultLocale(language = \"en\")\n\t@DisplayName(\"with nested classes\")\n\tclass NestedDefaultLocaleTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"without DefaultLocale annotation\")\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"DefaultLocale should be set from enclosed class when it is not provided in nested\")\n\t\t\tvoid shouldSetLocaleFromEnclosedClass() {\n\t\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"en\");\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DefaultLocale(language = \"de\")\n\t\t@DisplayName(\"with DefaultLocale annotation\")\n\t\tclass AnnotatedNestedClass {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"DefaultLocale should be set from nested class when it is provided\")\n\t\t\tvoid shouldSetLocaleFromNestedClass() {\n\t\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"de\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DefaultLocale(language = \"ch\")\n\t\t\t@DisplayName(\"DefaultLocale should be set from method when it is provided\")\n\t\t\tvoid shouldSetLocaleFromMethodOfNestedClass() {\n\t\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"ch\");\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultLocale(language = \"fi\")\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t@DisplayName(\"correctly sets/resets before/after each/all extension points\")\n\tclass ResettingDefaultLocaleTests {\n\n\t\t@Nested\n\t\t@DefaultLocale(language = \"de\")\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass ResettingDefaultLocaleNestedTests {\n\n\t\t\t@Test\n\t\t\t@DefaultLocale(language = \"en\")\n\t\t\tvoid setForTestMethod() {\n\t\t\t\t// only here to set the locale, so another test can verify whether it was reset;\n\t\t\t\t// still, better to assert the value was actually set\n\t\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"en\");\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\t@ReadsDefaultLocale\n\t\t\tvoid resetAfterTestMethodExecution() {\n\t\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"custom\");\n\t\t\t}\n\n\t\t}\n\n\t\t@AfterAll\n\t\t@ReadsDefaultLocale\n\t\tvoid resetAfterTestMethodExecution() {\n\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"custom\");\n\t\t}\n\n\t}\n\n\t@DefaultLocale(language = \"en\")\n\tstatic class ClassLevelResetTestCase {\n\n\t\t@Test\n\t\tvoid setForTestMethod() {\n\t\t\t// only here to set the locale, so another test can verify whether it was reset;\n\t\t\t// still, better to assert the value was actually set\n\t\t\tassertThat(Locale.getDefault().getLanguage()).isEqualTo(\"en\");\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"when configured incorrect\")\n\tclass ConfigurationFailureTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"on the method level\")\n\t\tclass MethodLevel {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when nothing is configured\")\n\t\t\tvoid shouldFailWhenNothingIsConfigured() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailMissingConfiguration\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when variant is set but country is not\")\n\t\t\tvoid shouldFailWhenVariantIsSetButCountryIsNot() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailMissingCountry\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when languageTag and language is set\")\n\t\t\tvoid shouldFailWhenLanguageTagAndLanguageIsSet() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailLanguageTagAndLanguage\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when languageTag and country is set\")\n\t\t\tvoid shouldFailWhenLanguageTagAndCountryIsSet() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailLanguageTagAndCountry\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when languageTag and variant is set\")\n\t\t\tvoid shouldFailWhenLanguageTagAndVariantIsSet() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailLanguageTagAndVariant\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when invalid BCP 47 variant is set\")\n\t\t\tvoid shouldFailIfNoValidBCP47VariantIsSet() {\n\t\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\t\tselectMethod(MethodLevelInitializationFailureTestCases.class, \"shouldFailNoValidBCP47Variant\"));\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"on the class level\")\n\t\tclass ClassLevel {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"should fail when variant is set but country is not\")\n\t\t\tvoid shouldFailWhenVariantIsSetButCountryIsNot() {\n\t\t\t\tEngineExecutionResults results = executeTestsForClass(ClassLevelInitializationFailureTestCases.class);\n\n\t\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class MethodLevelInitializationFailureTestCases {\n\n\t\t@Test\n\t\t@DefaultLocale\n\t\tvoid shouldFailMissingConfiguration() {\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"de\", variant = \"ch\")\n\t\tvoid shouldFailMissingCountry() {\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(value = \"Something\", language = \"de\")\n\t\tvoid shouldFailLanguageTagAndLanguage() {\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(value = \"Something\", country = \"DE\")\n\t\tvoid shouldFailLanguageTagAndCountry() {\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(value = \"Something\", variant = \"ch\")\n\t\tvoid shouldFailLanguageTagAndVariant() {\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(variant = \"en-GB\")\n\t\tvoid shouldFailNoValidBCP47Variant() {\n\t\t}\n\n\t}\n\n\t@DefaultLocale(language = \"de\", variant = \"ch\")\n\tstatic class ClassLevelInitializationFailureTestCases {\n\n\t\t@Test\n\t\tvoid shouldFail() {\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"used with inheritance\")\n\tclass InheritanceTests extends InheritanceBaseTest {\n\n\t\t@Test\n\t\t@DisplayName(\"should inherit default locale annotation\")\n\t\tvoid shouldInheritClearAndSetProperty() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(JupiterLocaleUtils.createLocale(\"fr\", \"FR\"));\n\t\t}\n\n\t}\n\n\t@DefaultLocale(language = \"fr\", country = \"FR\")\n\tstatic class InheritanceBaseTest {\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"when used with a locale provider\")\n\tclass LocaleProviderTests {\n\n\t\t@Test\n\t\t@DisplayName(\"can get a basic locale from provider\")\n\t\t@DefaultLocale(localeProvider = BasicLocaleProvider.class)\n\t\tvoid canUseProvider() {\n\t\t\tassertThat(Locale.getDefault()).isEqualTo(Locale.FRENCH);\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws a NullPointerException with custom message if provider returns null\")\n\t\tvoid providerReturnsNull() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(BadProviderTestCases.class, \"returnsNull\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(NullPointerException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"LocaleProvider instance returned with null\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws an ExtensionConfigurationException if any other option is present\")\n\t\tvoid mutuallyExclusiveWithValue() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadProviderTestCases.class, \"mutuallyExclusiveWithValue\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(it -> it.contains(\n\t\t\t\t\t\"can only be used with a provider if value, language, country and variant are not set.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws an ExtensionConfigurationException if any other option is present\")\n\t\tvoid mutuallyExclusiveWithLanguage() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadProviderTestCases.class, \"mutuallyExclusiveWithLanguage\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"can only be used with language tag if provider is not set.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws an ExtensionConfigurationException if any other option is present\")\n\t\tvoid mutuallyExclusiveWithCountry() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadProviderTestCases.class, \"mutuallyExclusiveWithCountry\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(it -> it.contains(\n\t\t\t\t\t\"can only be used with a provider if value, language, country and variant are not set.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws an ExtensionConfigurationException if any other option is present\")\n\t\tvoid mutuallyExclusiveWithVariant() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadProviderTestCases.class, \"mutuallyExclusiveWithVariant\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(it -> it.contains(\n\t\t\t\t\t\"can only be used with a provider if value, language, country and variant are not set.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultLocale\n\t\t@DisplayName(\"throws an ExtensionConfigurationException if localeProvider can't be constructed\")\n\t\tvoid badConstructor() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(BadProviderTestCases.class, \"badConstructor\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"could not be constructed because of an exception\"))));\n\t\t}\n\n\t}\n\n\tstatic class BadProviderTestCases {\n\n\t\t@Test\n\t\t@DefaultLocale(value = \"en\", localeProvider = BasicLocaleProvider.class)\n\t\tvoid mutuallyExclusiveWithValue() {\n\t\t\t// can't have both a value and a provider\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(language = \"en\", localeProvider = BasicLocaleProvider.class)\n\t\tvoid mutuallyExclusiveWithLanguage() {\n\t\t\t// can't have both a language property and a provider\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(country = \"EN\", localeProvider = BasicLocaleProvider.class)\n\t\tvoid mutuallyExclusiveWithCountry() {\n\t\t\t// can't have both a country property and a provider\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(variant = \"japanese\", localeProvider = BasicLocaleProvider.class)\n\t\tvoid mutuallyExclusiveWithVariant() {\n\t\t\t// can't have both a variant property and a provider\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(localeProvider = ReturnsNullLocaleProvider.class)\n\t\tvoid returnsNull() {\n\t\t\t// provider should not return 'null'\n\t\t}\n\n\t\t@Test\n\t\t@DefaultLocale(localeProvider = BadConstructorLocaleProvider.class)\n\t\tvoid badConstructor() {\n\t\t\t// provider has to have a no-args constructor\n\t\t}\n\n\t}\n\n\tstatic class BasicLocaleProvider implements LocaleProvider {\n\n\t\t@Override\n\t\tpublic Locale get() {\n\t\t\treturn Locale.FRENCH;\n\t\t}\n\n\t}\n\n\tstatic class ReturnsNullLocaleProvider implements LocaleProvider {\n\n\t\t@Override\n\t\t@SuppressWarnings(\"NullAway\")\n\t\tpublic Locale get() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class BadConstructorLocaleProvider implements LocaleProvider {\n\n\t\tprivate final String language;\n\n\t\tBadConstructorLocaleProvider(String language) {\n\t\t\tthis.language = language;\n\t\t}\n\n\t\t@Override\n\t\tpublic Locale get() {\n\t\t\treturn Locale.forLanguageTag(language);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/util/DefaultTimeZoneTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.util.TimeZone;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n@DisplayName(\"DefaultTimeZone extension\")\nclass DefaultTimeZoneTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static TimeZone TEST_DEFAULT_TIMEZONE;\n\tprivate static TimeZone DEFAULT_TIMEZONE_BEFORE_TEST;\n\n\t@BeforeAll\n\tstatic void globalSetUp() {\n\t\t// we set UTC as test time zone unless it is already\n\t\t// the system's time zone; in that case we use UTC+12\n\t\tDEFAULT_TIMEZONE_BEFORE_TEST = TimeZone.getDefault();\n\t\tTimeZone utc = TimeZone.getTimeZone(\"UTC\");\n\t\tTimeZone utcPlusTwelve = TimeZone.getTimeZone(\"GMT+12:00\");\n\t\tif (DEFAULT_TIMEZONE_BEFORE_TEST.equals(utc))\n\t\t\tTimeZone.setDefault(utcPlusTwelve);\n\t\telse\n\t\t\tTimeZone.setDefault(utc);\n\t\tTEST_DEFAULT_TIMEZONE = TimeZone.getDefault();\n\t}\n\n\t@AfterAll\n\tstatic void globalTearDown() {\n\t\tTimeZone.setDefault(DEFAULT_TIMEZONE_BEFORE_TEST);\n\t}\n\n\t@Nested\n\t@DisplayName(\"when applied on the method level\")\n\tclass MethodLevelTests {\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"does nothing when annotation is not present\")\n\t\tvoid doesNothingWhenAnnotationNotPresent() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TEST_DEFAULT_TIMEZONE);\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"GMT\")\n\t\t@DisplayName(\"does not throw when explicitly set to GMT\")\n\t\tvoid doesNotThrowForExplicitGmt() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"CET\")\n\t\t@DisplayName(\"sets the default time zone using an abbreviation\")\n\t\tvoid setsTimeZoneFromAbbreviation() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"CET\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"America/Los_Angeles\")\n\t\t@DisplayName(\"sets the default time zone using a full name\")\n\t\tvoid setsTimeZoneFromFullName() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"America/Los_Angeles\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultTimeZone(\"GMT-8:00\")\n\t@DisplayName(\"when applied on the class level\")\n\tclass ClassLevelTestCases {\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"sets the default time zone\")\n\t\tvoid shouldExecuteWithClassLevelTimeZone() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-8:00\"));\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"GMT-12:00\")\n\t\t@DisplayName(\"gets overridden by annotation on the method level\")\n\t\tvoid shouldBeOverriddenWithMethodLevelTimeZone() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-12:00\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultTimeZone(\"GMT\")\n\t@DisplayName(\"when explicitly set to GMT on the class level\")\n\tclass ExplicitGmtClassLevelTestCases {\n\n\t\t@Test\n\t\t@DisplayName(\"does not throw and sets to GMT \")\n\t\tvoid explicitGmt() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultTimeZone(\"GMT-8:00\")\n\t@DisplayName(\"with nested classes\")\n\tclass NestedTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"without DefaultTimeZone annotation\")\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\t@ReadsDefaultTimeZone\n\t\t\t@DisplayName(\"DefaultTimeZone should be set from enclosed class when it is not provided in nested\")\n\t\t\tpublic void shouldSetTimeZoneFromEnclosedClass() {\n\t\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-8:00\"));\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DefaultTimeZone(\"GMT-12:00\")\n\t\t@DisplayName(\"with DefaultTimeZone annotation\")\n\t\tclass AnnotatedNestedClass {\n\n\t\t\t@Test\n\t\t\t@ReadsDefaultTimeZone\n\t\t\t@DisplayName(\"DefaultTimeZone should be set from nested class when it is provided\")\n\t\t\tpublic void shouldSetTimeZoneFromNestedClass() {\n\t\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-12:00\"));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DefaultTimeZone(\"GMT-6:00\")\n\t\t\t@DisplayName(\"DefaultTimeZone should be set from method when it is provided\")\n\t\t\tpublic void shouldSetTimeZoneFromMethodOfNestedClass() {\n\t\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-6:00\"));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DefaultTimeZone(\"GMT-12:00\")\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\tclass ResettingDefaultTimeZoneTests {\n\n\t\t@Nested\n\t\t@DefaultTimeZone(\"GMT-3:00\")\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass ResettingDefaultTimeZoneNestedTests {\n\n\t\t\t@Test\n\t\t\t@DefaultTimeZone(\"GMT+6:00\")\n\t\t\tvoid setForTestMethod() {\n\t\t\t\t// only here to set the time zone, so another test can verify whether it was reset;\n\t\t\t\t// still, better to assert the value was actually set\n\t\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT+6:00\"));\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\t@ReadsDefaultTimeZone\n\t\t\tvoid resetAfterTestMethodExecution() {\n\t\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TEST_DEFAULT_TIMEZONE);\n\t\t\t}\n\n\t\t}\n\n\t\t@AfterAll\n\t\t@ReadsDefaultTimeZone\n\t\tvoid resetAfterTestMethodExecution() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TEST_DEFAULT_TIMEZONE);\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"when misconfigured\")\n\tclass ConfigurationTests {\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"on method level, throws exception\")\n\t\tvoid throwsWhenConfigurationIsBad() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadMethodLevelConfigurationTestCases.class, \"badConfiguration\"));\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"@DefaultTimeZone not configured correctly.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"on class level, throws exception\")\n\t\tvoid shouldThrowWithBadConfiguration() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(BadClassLevelConfigurationTestCases.class);\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"@DefaultTimeZone not configured correctly.\"))));\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid verifyMisconfigurationSisNotChangeTimeZone() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TEST_DEFAULT_TIMEZONE);\n\t\t}\n\n\t}\n\n\tstatic class BadMethodLevelConfigurationTestCases {\n\n\t\t@Test\n\t\t@DefaultTimeZone(\"Gibberish\")\n\t\tvoid badConfiguration() {\n\t\t}\n\n\t}\n\n\t@DefaultTimeZone(\"Gibberish\")\n\tstatic class BadClassLevelConfigurationTestCases {\n\n\t\t@Test\n\t\tvoid badConfiguration() {\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"used with inheritance\")\n\tclass InheritanceTests extends InheritanceBaseTest {\n\n\t\t@Test\n\t\t@DisplayName(\"should inherit default time zone annotation\")\n\t\tvoid shouldInheritClearAndSetProperty() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT-8:00\"));\n\t\t}\n\n\t}\n\n\t@DefaultTimeZone(\"GMT-8:00\")\n\tstatic class InheritanceBaseTest {\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"used with TimeZoneProvider\")\n\tclass ProviderTests {\n\n\t\t@Test\n\t\t@DisplayName(\"can get a basic time zone\")\n\t\t@DefaultTimeZone(timeZoneProvider = BasicTimeZoneProvider.class)\n\t\tvoid canGetBasicTimeZone() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"Europe/Prague\"));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"defaults to GMT if the provider returns null\")\n\t\t@DefaultTimeZone(timeZoneProvider = NullProvider.class)\n\t\tvoid defaultToGmt() {\n\t\t\tassertThat(TimeZone.getDefault()).isEqualTo(TimeZone.getTimeZone(\"GMT\"));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"throws ExtensionConfigurationException if the provider is not the only option\")\n\t\tvoid throwsForMutuallyExclusiveOptions() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadTimeZoneProviderTestCases.class, \"notExclusive\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"Either a valid time zone id or a TimeZoneProvider must be provided\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"throws ExtensionConfigurationException if properties are empty\")\n\t\tvoid throwsForEmptyOptions() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(BadTimeZoneProviderTestCases.class, \"empty\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"Either a valid time zone id or a TimeZoneProvider must be provided\"))));\n\t\t}\n\n\t\t@Test\n\t\t@ReadsDefaultTimeZone\n\t\t@DisplayName(\"throws ExtensionConfigurationException if the provider does not have a suitable constructor\")\n\t\tvoid throwsForBadConstructor() {\n\t\t\tEngineExecutionResults results = executeTests(\n\t\t\t\tselectMethod(BadTimeZoneProviderTestCases.class, \"noConstructor\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"Could not instantiate TimeZoneProvider because of exception\"))));\n\t\t}\n\n\t}\n\n\tstatic class BadTimeZoneProviderTestCases {\n\n\t\t@Test\n\t\t@DefaultTimeZone(value = \"GMT\", timeZoneProvider = BasicTimeZoneProvider.class)\n\t\tvoid notExclusive() {\n\t\t\t// can't have both a time zone value and a provider\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone\n\t\tvoid empty() {\n\t\t\t// must have a provider or a time zone\n\t\t}\n\n\t\t@Test\n\t\t@DefaultTimeZone(timeZoneProvider = ComplicatedProvider.class)\n\t\tvoid noConstructor() {\n\t\t\t// provider has to have a no-args constructor\n\t\t}\n\n\t}\n\n\tstatic class BasicTimeZoneProvider implements TimeZoneProvider {\n\n\t\t@Override\n\t\tpublic TimeZone get() {\n\t\t\treturn TimeZone.getTimeZone(\"Europe/Prague\");\n\t\t}\n\n\t}\n\n\tstatic class NullProvider implements TimeZoneProvider {\n\n\t\t@Override\n\t\t@SuppressWarnings(\"NullAway\")\n\t\tpublic TimeZone get() {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n\tstatic class ComplicatedProvider implements TimeZoneProvider {\n\n\t\tprivate final String timeZoneString;\n\n\t\tComplicatedProvider(String timeZoneString) {\n\t\t\tthis.timeZoneString = timeZoneString;\n\t\t}\n\n\t\t@Override\n\t\tpublic TimeZone get() {\n\t\t\treturn TimeZone.getTimeZone(timeZoneString);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/util/JupiterPropertyUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.util.JupiterPropertyUtils.cloneWithoutDefaults;\nimport static org.mockito.Mockito.when;\n\nimport java.io.Serial;\nimport java.util.Optional;\nimport java.util.Properties;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n/**\n * Tests for {@link JupiterPropertyUtils}.\n */\n@MockitoSettings\nclass JupiterPropertyUtilsTests {\n\n\t@Mock\n\tExtensionContext context;\n\n\t@Test\n\tvoid cloneProperties() {\n\t\tvar properties = new Properties();\n\t\tproperties.setProperty(\"a\", \"a\");\n\t\tvar value = new Object();\n\t\tproperties.put(\"a-obj\", value);\n\n\t\tvar clone = cloneWithoutDefaults(context, properties);\n\n\t\tassertThat(clone.stringPropertyNames()).containsExactly(\"a\");\n\t\tassertThat(clone.get(\"a\")).isSameAs(properties.get(\"a\"));\n\n\t\tassertThat(clone.keySet()).containsExactly(\"a\", \"a-obj\");\n\t\tassertThat(clone.getProperty(\"a\")).isEqualTo(\"a\");\n\t\tassertThat(clone.get(\"a-obj\")).isSameAs(value);\n\t}\n\n\t@Test\n\tvoid withDefaults() {\n\t\tvar defaults = new Properties();\n\t\tdefaults.setProperty(\"a\", \"a\");\n\n\t\tvar properties = new Properties(defaults);\n\t\tproperties.setProperty(\"X\", \"X\");\n\n\t\twhen(context.getElement()).thenReturn(Optional.of(JupiterPropertyUtilsTests.class));\n\n\t\tassertThatExceptionOfType(ExtensionConfigurationException.class) //\n\t\t\t\t.isThrownBy(() -> cloneWithoutDefaults(context, properties))//\n\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\tSystemPropertiesExtension was configured to restore the system properties by [%s]. \\\n\t\t\t\t\t\tHowever, it was not possible to create an accurate snapshot of the system properties \\\n\t\t\t\t\t\tusing Properties::clone, because default properties were present: [a]\"\"\",\n\t\t\t\t\tJupiterPropertyUtilsTests.class);\n\t}\n\n\t@Test\n\tvoid withDefaultsAndSubclass() {\n\t\tvar defaults = new CustomProperties();\n\t\tdefaults.setProperty(\"a\", \"a\");\n\n\t\tvar properties = new CustomProperties(defaults);\n\t\tproperties.setProperty(\"b\", \"b\");\n\n\t\tvar clone = cloneWithoutDefaults(context, properties);\n\n\t\tassertThat(clone.stringPropertyNames()).containsExactly(\"a\", \"b\");\n\t\tassertThat(clone.getProperty(\"a\")).isEqualTo(\"a\");\n\t\tassertThat(clone.getProperty(\"b\")).isEqualTo(\"b\");\n\t}\n\n\tstatic final class CustomProperties extends Properties {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tCustomProperties() {\n\t\t\tthis(null);\n\t\t}\n\n\t\tCustomProperties(@Nullable CustomProperties defaults) {\n\t\t\tsuper(defaults);\n\t\t}\n\n\t\t@Override\n\t\tpublic synchronized Object clone() {\n\t\t\tCustomProperties clone = (CustomProperties) super.clone();\n\t\t\tclone.defaults = this.defaults == null ? null : (Properties) this.defaults.clone();\n\t\t\treturn clone;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/api/util/SystemPropertiesExtensionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Properties;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestClassOrder;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n@DisplayName(\"System Properties Extension\")\nclass SystemPropertiesExtensionTests extends AbstractJupiterTestEngineTests {\n\n\t@BeforeAll\n\tstatic void globalSetUp() {\n\t\tSystem.setProperty(\"A\", \"old A\");\n\t\tSystem.setProperty(\"B\", \"old B\");\n\t\tSystem.setProperty(\"C\", \"old C\");\n\n\t\tSystem.clearProperty(\"clear prop D\");\n\t\tSystem.clearProperty(\"clear prop E\");\n\t\tSystem.clearProperty(\"clear prop F\");\n\t}\n\n\t@AfterAll\n\tstatic void globalTearDown() {\n\t\tSystem.clearProperty(\"A\");\n\t\tSystem.clearProperty(\"B\");\n\t\tSystem.clearProperty(\"C\");\n\n\t\tassertThat(System.getProperty(\"clear prop D\")).isNull();\n\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t}\n\n\t@Nested\n\t@DisplayName(\"with @ClearSystemProperty\")\n\t@ClearSystemProperty(key = \"A\")\n\tclass ClearSystemPropertyTests {\n\n\t\t@Test\n\t\t@DisplayName(\"should clear system property\")\n\t\t@ClearSystemProperty(key = \"B\")\n\t\tvoid shouldClearSystemProperty() {\n\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"should be repeatable\")\n\t\t@ClearSystemProperty(key = \"B\")\n\t\t@ClearSystemProperty(key = \"C\")\n\t\tvoid shouldBeRepeatable() {\n\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\tassertThat(System.getProperty(\"C\")).isNull();\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"with @SetSystemProperty\")\n\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\tclass SetSystemPropertyTests {\n\n\t\t@Test\n\t\t@DisplayName(\"should set system property to value\")\n\t\t@SetSystemProperty(key = \"B\", value = \"new B\")\n\t\tvoid shouldSetSystemPropertyToValue() {\n\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"new A\");\n\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"new B\");\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"should be repeatable\")\n\t\t@SetSystemProperty(key = \"B\", value = \"new B\")\n\t\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\t\tvoid shouldBeRepeatable() {\n\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"new A\");\n\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"new B\");\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"with both @ClearSystemProperty and @SetSystemProperty\")\n\t@ClearSystemProperty(key = \"A\")\n\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\tclass CombinedClearAndSetTests {\n\n\t\t@Test\n\t\t@DisplayName(\"should be combinable\")\n\t\t@ClearSystemProperty(key = \"B\")\n\t\t@SetSystemProperty(key = \"clear prop E\", value = \"new E\")\n\t\tvoid clearAndSetSystemPropertyShouldBeCombinable() {\n\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isEqualTo(\"new E\");\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"method level should overwrite class level\")\n\t\t@ClearSystemProperty(key = \"clear prop D\")\n\t\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\t\tvoid methodLevelShouldOverwriteClassLevel() {\n\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"new A\");\n\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"old B\");\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"method level should not clash (in terms of duplicate entries) with class level\")\n\t\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\t\tvoid methodLevelShouldNotClashWithClassLevel() {\n\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"new A\");\n\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"old B\");\n\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"with Set, Clear, and Restore\")\n\t@WritesSystemProperty // Many of these tests write, many also access\n\t@Execution(SAME_THREAD) // Uses instance state\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Uses instance state\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\tclass CombinedClearSetRestoreTests {\n\n\t\tProperties initialState; // Stateful\n\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t\tinitialState = System.getProperties();\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\t@DisplayName(\"Set, Clear & Restore on class\")\n\t\t@ClearSystemProperty(key = \"A\")\n\t\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\t\t@RestoreSystemProperties\n\t\t@TestMethodOrder(OrderAnnotation.class)\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Uses instance state\n\t\tclass SetClearRestoreOnClass {\n\n\t\t\t@AfterAll\n\t\t\tvoid afterAll() {\n\t\t\t\tSystem.setProperties(new Properties()); // Really blow it up after this class\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\t@DisplayName(\"Set, Clear on method w/ direct set Sys Prop\")\n\t\t\t@ClearSystemProperty(key = \"B\")\n\t\t\t@SetSystemProperty(key = \"clear prop E\", value = \"new E\")\n\t\t\tvoid clearSetRestoreShouldBeCombinable() {\n\t\t\t\tassertThat(System.getProperties()).withFailMessage(\n\t\t\t\t\t\"Restore should swap out the Sys Properties instance\").isNotSameAs(initialState);\n\n\t\t\t\t// Direct modification - shouldn't be visible in next test\n\t\t\t\tSystem.setProperty(\"Restore\", \"Restore Me\");\n\t\t\t\tSystem.getProperties().put(\"XYZ\", this);\n\n\t\t\t\tassertThat(System.getProperty(\"Restore\")).isEqualTo(\"Restore Me\");\n\t\t\t\tassertThat(System.getProperties().get(\"XYZ\")).isSameAs(this);\n\n\t\t\t\t// All the others\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\t\t\t\tassertThat(System.getProperty(\"clear prop E\")).isEqualTo(\"new E\");\n\t\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore from class should restore direct mods\")\n\t\t\t@Order(2)\n\t\t\tvoid restoreShouldHaveRevertedDirectModification() {\n\t\t\t\tassertThat(System.getProperty(\"Restore\")).isNull();\n\t\t\t\tassertThat(System.getProperties().get(\"XYZ\")).isNull();\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@Order(2)\n\t\t@DisplayName(\"Prior nested class changes should be restored}\")\n\t\tclass priorNestedChangesRestored {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore from class should restore direct mods\")\n\t\t\tvoid restoreShouldHaveRevertedDirectModification() {\n\t\t\t\tassertThat(System.getProperties()).isSameAs(initialState);\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@Order(3)\n\t\t@DisplayName(\"Set & Clear on class, Restore on method\")\n\t\t@ClearSystemProperty(key = \"A\")\n\t\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\t\t@TestMethodOrder(OrderAnnotation.class)\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Uses instance state\n\t\tclass SetAndClearOnClass {\n\n\t\t\tProperties initialState; // Stateful\n\n\t\t\t@BeforeAll\n\t\t\tvoid beforeAll() {\n\t\t\t\tinitialState = System.getProperties();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\t@DisplayName(\"Set, Clear & Restore on method w/ direct set Sys Prop\")\n\t\t\t@ClearSystemProperty(key = \"B\")\n\t\t\t@SetSystemProperty(key = \"clear prop E\", value = \"new E\")\n\t\t\t@RestoreSystemProperties\n\t\t\tvoid clearSetRestoreShouldBeCombinable() {\n\t\t\t\tassertThat(System.getProperties()).withFailMessage(\n\t\t\t\t\t\"Restore should swap out the Sys Properties instance\").isNotSameAs(initialState);\n\n\t\t\t\t// Direct modification - shouldn't be visible in the next test\n\t\t\t\tSystem.setProperty(\"Restore\", \"Restore Me\");\n\t\t\t\tSystem.getProperties().put(\"XYZ\", this);\n\n\t\t\t\t// All the others\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"C\")).isEqualTo(\"old C\");\n\n\t\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\t\t\t\tassertThat(System.getProperty(\"clear prop E\")).isEqualTo(\"new E\");\n\t\t\t\tassertThat(System.getProperty(\"clear prop F\")).isNull();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore from prior method should restore direct mods\")\n\t\t\t@Order(2)\n\t\t\tvoid restoreShouldHaveRevertedDirectModification() {\n\t\t\t\tassertThat(System.getProperty(\"Restore\")).isNull();\n\t\t\t\tassertThat(System.getProperties().get(\"XYZ\")).isNull();\n\t\t\t\tassertThat(System.getProperties()).isSameAs(initialState);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"@RestoreSystemProperties individual methods tests\")\n\t@WritesSystemProperty // Many of these tests write, many also access\n\tclass RestoreSystemPropertiesUnitTests {\n\n\t\tSystemPropertiesExtension spe;\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tspe = new SystemPropertiesExtension();\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"Attributes of RestoreSystemProperties Annotation\")\n\t\tclass BasicAttributesOfRestoreSystemProperties {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore annotation has correct markers\")\n\t\t\tvoid restoreHasCorrectMarkers() {\n\t\t\t\tassertThat(RestoreSystemProperties.class).hasAnnotations(Inherited.class, WritesSystemProperty.class);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore annotation has correct retention\")\n\t\t\tvoid restoreHasCorrectRetention() {\n\t\t\t\tassertThat(RestoreSystemProperties.class.getAnnotation(Retention.class).value()).isEqualTo(\n\t\t\t\t\tRetentionPolicy.RUNTIME);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Restore annotation has correct targets\")\n\t\t\tvoid restoreHasCorrectTargets() {\n\t\t\t\tassertThat(RestoreSystemProperties.class.getAnnotation(Target.class).value()).containsExactlyInAnyOrder(\n\t\t\t\t\tElementType.METHOD, ElementType.TYPE);\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"RestorableContext Workflow Tests\")\n\t\t@MockitoSettings\n\t\tclass RestorableContextWorkflowTests {\n\n\t\t\t@Mock\n\t\t\tExtensionContext context;\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Workflow of RestorableContext\")\n\t\t\tvoid workflowOfRestorableContexts() {\n\t\t\t\tProperties initialState = System.getProperties(); //This is a live reference\n\n\t\t\t\ttry {\n\t\t\t\t\tProperties returnedFromPrepareToEnter = spe.prepareToEnterRestorableContext(context);\n\t\t\t\t\tProperties postPrepareToEnterSysProps = System.getProperties();\n\t\t\t\t\tspe.prepareToExitRestorableContext(initialState);\n\t\t\t\t\tProperties postPrepareToExitSysProps = System.getProperties();\n\n\t\t\t\t\tassertThat(returnedFromPrepareToEnter) //\n\t\t\t\t\t\t\t.withFailMessage(\n\t\t\t\t\t\t\t\t\"prepareToEnterRestorableContext should return actual original or deep copy\") //\n\t\t\t\t\t\t\t.isSameAs(initialState);\n\n\t\t\t\t\tassertThat(returnedFromPrepareToEnter) //\n\t\t\t\t\t\t\t.withFailMessage(\"prepareToEnterRestorableContext should replace the actual Sys Props\") //\n\t\t\t\t\t\t\t.isNotSameAs(postPrepareToEnterSysProps);\n\n\t\t\t\t\tassertThat(postPrepareToEnterSysProps).isEqualTo(initialState);\n\n\t\t\t\t\tassertThat(postPrepareToExitSysProps).isSameAs(initialState);\n\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tSystem.setProperties(initialState); // Ensure complete recovery\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"with nested classes\")\n\t@ClearSystemProperty(key = \"A\")\n\t@SetSystemProperty(key = \"B\", value = \"new B\")\n\tclass NestedSystemPropertyTests {\n\n\t\t@Nested\n\t\t@TestMethodOrder(OrderAnnotation.class)\n\t\t@DisplayName(\"without SystemProperty annotations\")\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\t@ReadsSystemProperty\n\t\t\t@DisplayName(\"system properties should be set from enclosed class when they are not provided in nested\")\n\t\t\tvoid shouldSetSystemPropertyFromEnclosedClass() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"new B\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\t@ReadsSystemProperty\n\t\t\t@DisplayName(\"system properties should be set from enclosed class after restore\")\n\t\t\tvoid shouldSetSystemPropertyFromEnclosedClassAfterRestore() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"new B\");\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@SetSystemProperty(key = \"B\", value = \"newer B\")\n\t\t@DisplayName(\"with @SetSystemProperty annotation\")\n\t\tclass AnnotatedNestedClass {\n\n\t\t\t@Test\n\t\t\t@ReadsSystemProperty\n\t\t\t@DisplayName(\"system property should be set from nested class when it is provided\")\n\t\t\tvoid shouldSetSystemPropertyFromNestedClass() {\n\t\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"newer B\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@SetSystemProperty(key = \"B\", value = \"newest B\")\n\t\t\t@DisplayName(\"system property should be set from method when it is provided\")\n\t\t\tvoid shouldSetSystemPropertyFromMethodOfNestedClass() {\n\t\t\t\tassertThat(System.getProperty(\"B\")).isEqualTo(\"newest B\");\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@Nested\n\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\tclass ResettingSystemPropertyTests {\n\n\t\t@Nested\n\t\t@SetSystemProperty(key = \"A\", value = \"newer A\")\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass ResettingSystemPropertyAfterEachNestedTests {\n\n\t\t\t@BeforeEach\n\t\t\tvoid changeShouldBeVisible() {\n\t\t\t\t// We already see \"newest A\" because BeforeEachCallBack is invoked before @BeforeEach\n\t\t\t\t// See https://junit.org/junit5/docs/current/user-guide/#extensions-execution-order-overview\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newest A\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@SetSystemProperty(key = \"A\", value = \"newest A\")\n\t\t\tvoid setForTestMethod() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newest A\");\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\t@ReadsSystemProperty\n\t\t\tvoid resetAfterTestMethodExecution() {\n\t\t\t\t// We still see \"newest A\" because AfterEachCallBack is invoked after @AfterEach\n\t\t\t\t// See https://junit.org/junit5/docs/current/user-guide/#extensions-execution-order-overview\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newest A\");\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@SetSystemProperty(key = \"A\", value = \"newer A\")\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass ResettingSystemPropertyAfterAllNestedTests {\n\n\t\t\t@BeforeAll\n\t\t\tvoid changeShouldBeVisible() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newer A\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@SetSystemProperty(key = \"A\", value = \"newest A\")\n\t\t\tvoid setForTestMethod() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newest A\");\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\t@ReadsSystemProperty\n\t\t\tvoid resetAfterTestMethodExecution() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"newer A\");\n\t\t\t}\n\n\t\t}\n\n\t\t@AfterAll\n\t\t@ReadsSystemProperty\n\t\tvoid resetAfterTestContainerExecution() {\n\t\t\tassertThat(System.getProperty(\"A\")).isEqualTo(\"new A\");\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"with incorrect configuration\")\n\tclass ConfigurationFailureTests {\n\n\t\t@Test\n\t\t@DisplayName(\"should fail when clear and set same system property\")\n\t\tvoid shouldFailWhenClearAndSetSameSystemProperty() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(MethodLevelInitializationFailureTestCases.class,\n\t\t\t\t\"shouldFailWhenClearAndSetSameSystemProperty\"));\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(it -> it.contains(\"@DefaultTimeZone not configured correctly.\"))));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"should not fail when clear same system property twice\")\n\t\tvoid shouldNotFailWhenClearSameSystemPropertyTwice() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(MethodLevelInitializationFailureTestCases.class,\n\t\t\t\t\"shouldFailWhenClearSameSystemPropertyTwice\"));\n\n\t\t\tresults.testEvents().assertThatEvents().haveExactly(1, event(test(), finishedSuccessfully()));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"should fail when set same system property twice\")\n\t\tvoid shouldFailWhenSetSameSystemPropertyTwice() {\n\t\t\tEngineExecutionResults results = executeTests(selectMethod(MethodLevelInitializationFailureTestCases.class,\n\t\t\t\t\"shouldFailWhenSetSameSystemPropertyTwice\"));\n\t\t\tresults.testEvents().assertThatEvents().haveAtMost(1,\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class)));\n\t\t}\n\n\t}\n\n\tstatic class MethodLevelInitializationFailureTestCases {\n\n\t\t@Test\n\t\t@DisplayName(\"clearing and setting the same property\")\n\t\t@ClearSystemProperty(key = \"A\")\n\t\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\t\tvoid shouldFailWhenClearAndSetSameSystemProperty() {\n\t\t}\n\n\t\t@Test\n\t\t@ClearSystemProperty(key = \"A\")\n\t\t@ClearSystemProperty(key = \"A\")\n\t\tvoid shouldFailWhenClearSameSystemPropertyTwice() {\n\t\t}\n\n\t\t@Test\n\t\t@SetSystemProperty(key = \"A\", value = \"new A\")\n\t\t@SetSystemProperty(key = \"A\", value = \"new B\")\n\t\tvoid shouldFailWhenSetSameSystemPropertyTwice() {\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"Clear and Set with inheritance\")\n\tclass InheritanceClearAndSetTests extends InheritanceClearAndSetBaseTest {\n\n\t\t@Test\n\t\t@DisplayName(\"should inherit clear and set annotations\")\n\t\tvoid shouldInheritClearAndSetProperty() {\n\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\t\t\tassertThat(System.getProperty(\"B\")).isNull();\n\t\t\tassertThat(System.getProperty(\"clear prop D\")).isEqualTo(\"new D\");\n\t\t\tassertThat(System.getProperty(\"clear prop E\")).isEqualTo(\"new E\");\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"Clear, Set, and Restore with inheritance\")\n\t@TestMethodOrder(OrderAnnotation.class)\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\t@Execution(SAME_THREAD) // Uses instance state\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS) // Uses instance state\n\tclass InheritanceClearSetRestoreTests extends InheritanceClearSetRestoreBaseTest {\n\n\t\tProperties initialState; // Stateful\n\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t\tinitialState = System.getProperties();\n\t\t}\n\n\t\t@Test\n\t\t@Order(1)\n\t\t@DisplayName(\"should inherit clear and set annotations\")\n\t\tvoid shouldInheritClearSetRestore() {\n\t\t\t// Direct modification - shouldn't be visible in the next test\n\t\t\tSystem.setProperty(\"Restore\", \"Restore Me\");\n\t\t\tSystem.getProperties().put(\"XYZ\", this);\n\n\t\t\tassertThat(System.getProperty(\"A\")).isNull(); // The rest are checked elsewhere\n\t\t}\n\n\t\t@Test\n\t\t@Order(2)\n\t\t@DisplayName(\"Restore from class should restore direct mods\")\n\t\tvoid restoreShouldHaveRevertedDirectModification() {\n\t\t\tassertThat(System.getProperty(\"Restore\")).isNull();\n\t\t\tassertThat(System.getProperties().get(\"XYZ\")).isNull();\n\t\t\tassertThat(System.getProperties()) //\n\t\t\t\t\t.withFailMessage(\"Restore should swap out the Sys Properties instance\") //\n\t\t\t\t\t.isNotSameAs(initialState);\n\t\t\tassertThat(System.getProperties()).isEqualTo(initialState);\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\t@DisplayName(\"Set props to ensure inherited restore\")\n\t\t@TestMethodOrder(OrderAnnotation.class)\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass SetSomeValuesToRestore {\n\n\t\t\t@AfterAll\n\t\t\tvoid afterAll() {\n\t\t\t\tSystem.setProperty(\"RestoreAll\", \"Restore Me\"); // This should also be restored\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\t@DisplayName(\"Inherit values and restore behavior\")\n\t\t\tvoid shouldInheritInNestedClass() {\n\t\t\t\tassertThat(System.getProperty(\"A\")).isNull();\n\n\t\t\t\t// Shouldn't be visible in the next test\n\t\t\t\tSystem.setProperty(\"Restore\", \"Restore Me\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\t@DisplayName(\"Verify restore behavior bt methods\")\n\t\t\tvoid verifyRestoreBetweenMethods() {\n\t\t\t\tassertThat(System.getProperty(\"Restore\")).isNull();\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@Order(2)\n\t\t@DisplayName(\"Verify props are restored\")\n\t\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t\tclass VerifyValuesAreRestored {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"Inherit values and restore behavior\")\n\t\t\tvoid shouldInheritInNestedClass() {\n\t\t\t\tassertThat(System.getProperty(\"RestoreAll\")).isNull(); // Should be restored\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@ClearSystemProperty(key = \"A\")\n\t@ClearSystemProperty(key = \"B\")\n\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\t@SetSystemProperty(key = \"clear prop E\", value = \"new E\")\n\tstatic class InheritanceClearAndSetBaseTest {\n\n\t}\n\n\t@ClearSystemProperty(key = \"A\")\n\t@ClearSystemProperty(key = \"B\")\n\t@SetSystemProperty(key = \"clear prop D\", value = \"new D\")\n\t@SetSystemProperty(key = \"clear prop E\", value = \"new E\")\n\t@RestoreSystemProperties\n\tstatic class InheritanceClearSetRestoreBaseTest {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/AbstractJupiterTestEngineTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static kotlin.jvm.JvmClassMappingKt.getJavaClass;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\n\nimport java.util.List;\nimport java.util.function.Consumer;\n\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.testkit.engine.EngineDiscoveryResults;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\nimport kotlin.reflect.KClass;\n\n/**\n * Abstract base class for tests involving the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\npublic abstract class AbstractJupiterTestEngineTests {\n\n\tprivate final JupiterTestEngine engine = new JupiterTestEngine();\n\n\tprotected EngineExecutionResults executeTestsForClass(KClass<?> testClass) {\n\t\treturn executeTestsForClass(getJavaClass(testClass));\n\t}\n\n\tprotected EngineExecutionResults executeTestsForClass(Class<?> testClass) {\n\t\treturn executeTests(selectClass(testClass));\n\t}\n\n\tprotected EngineExecutionResults executeTests(DiscoverySelector... selectors) {\n\t\treturn executeTests(List.of(selectors));\n\t}\n\n\tprotected EngineExecutionResults executeTests(List<? extends DiscoverySelector> selectors) {\n\t\treturn executeTests(request -> request.selectors(selectors));\n\t}\n\n\tprotected EngineExecutionResults executeTests(Consumer<LauncherDiscoveryRequestBuilder> configurer) {\n\t\tvar builder = defaultRequest();\n\t\tconfigurer.accept(builder);\n\t\treturn executeTests(builder);\n\t}\n\n\tprotected EngineExecutionResults executeTests(LauncherDiscoveryRequestBuilder builder) {\n\t\treturn executeTests(builder.build());\n\t}\n\n\tprotected EngineExecutionResults executeTests(LauncherDiscoveryRequest request) {\n\t\treturn EngineTestKit.execute(this.engine, request);\n\t}\n\n\tprotected TestDescriptor discoverTestsWithoutIssues(LauncherDiscoveryRequest request) {\n\t\tvar results = discoverTests(request);\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\t\treturn results.getEngineDescriptor();\n\t}\n\n\tprotected EngineDiscoveryResults discoverTestsForClass(Class<?> testClass) {\n\t\treturn discoverTests(selectClass(testClass));\n\t}\n\n\tprotected EngineDiscoveryResults discoverTests(Consumer<LauncherDiscoveryRequestBuilder> configurer) {\n\t\tvar builder = defaultRequest();\n\t\tconfigurer.accept(builder);\n\t\treturn discoverTests(builder);\n\t}\n\n\tprotected EngineDiscoveryResults discoverTests(DiscoverySelector... selectors) {\n\t\treturn discoverTests(request -> request.selectors(selectors));\n\t}\n\n\tprotected EngineDiscoveryResults discoverTests(LauncherDiscoveryRequestBuilder builder) {\n\t\treturn discoverTests(builder.build());\n\t}\n\n\tprotected EngineDiscoveryResults discoverTests(LauncherDiscoveryRequest request) {\n\t\treturn EngineTestKit.discover(this.engine, request);\n\t}\n\n\tprotected EngineTestKit.Builder jupiterTestEngine() {\n\t\treturn EngineTestKit.engine(this.engine) //\n\t\t\t\t.outputDirectoryCreator(dummyOutputDirectoryCreator()) //\n\t\t\t\t.configurationParameter(STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, String.valueOf(false)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.INFO.name()) //\n\t\t\t\t.enableImplicitConfigurationParameters(false);\n\t}\n\n\tprotected static LauncherDiscoveryRequestBuilder defaultRequest() {\n\t\treturn request() //\n\t\t\t\t.outputDirectoryCreator(dummyOutputDirectoryCreator()) //\n\t\t\t\t.configurationParameter(STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, String.valueOf(false)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.INFO.name()) //\n\t\t\t\t.enableImplicitConfigurationParameters(false);\n\t}\n\n\tprotected UniqueId discoverUniqueId(Class<?> clazz, String methodName) {\n\t\tvar results = discoverTests(selectMethod(clazz, methodName));\n\t\tvar engineDescriptor = results.getEngineDescriptor();\n\t\tvar descendants = engineDescriptor.getDescendants();\n\t\t// @formatter:off\n\t\tvar testDescriptor = descendants.stream()\n\t\t\t\t.skip(descendants.size() - 1)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseGet(() -> fail(\"no descendants\"));\n\t\t// @formatter:on\n\t\treturn testDescriptor.getUniqueId();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/AtypicalJvmMethodNameTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.engine.kotlin.ArbitraryNamingKotlinTestCase.METHOD_NAME;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.kotlin.ArbitraryNamingKotlinTestCase;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for JVM languages that allow special characters\n * in method names (e.g., Kotlin, Groovy, etc.) which are forbidden in\n * Java source code.\n *\n * @since 5.1\n */\nclass AtypicalJvmMethodNameTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid kotlinTestWithMethodNameContainingSpecialCharacters() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ArbitraryNamingKotlinTestCase.class);\n\t\tassertThat(executionResults.testEvents().started().count()).isEqualTo(2);\n\n\t\tTestDescriptor testDescriptor1 = executionResults.testEvents().succeeded().list().get(0).getTestDescriptor();\n\t\tassertAll(//\n\t\t\t() -> assertEquals(METHOD_NAME + \"()\", testDescriptor1.getDisplayName()), //\n\t\t\t() -> assertEquals(METHOD_NAME + \"()\", testDescriptor1.getLegacyReportingName()));\n\n\t\tTestDescriptor testDescriptor2 = executionResults.testEvents().succeeded().list().get(1).getTestDescriptor();\n\t\tassertAll(//\n\t\t\t() -> assertEquals(\"test name ends with parentheses()()\", testDescriptor2.getDisplayName()), //\n\t\t\t() -> assertEquals(\"test name ends with parentheses()()\", testDescriptor2.getLegacyReportingName()));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeAllAndAfterAllComposedAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests that verify support for {@link BeforeAll} and {@link AfterAll}\n * when used as meta-annotations in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n * @see BeforeEachAndAfterEachComposedAnnotationTests\n */\nclass BeforeAllAndAfterAllComposedAnnotationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> methodsInvoked = new ArrayList<>();\n\n\t@Test\n\tvoid beforeAllAndAfterAllAsMetaAnnotations() {\n\t\texecuteTestsForClass(TestCase.class).testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\tassertThat(methodsInvoked).containsExactly(\"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\tstatic class TestCase {\n\n\t\t@CustomBeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tmethodsInvoked.add(\"beforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@CustomAfterAll\n\t\tstatic void afterAll() {\n\t\t\tmethodsInvoked.add(\"afterAll\");\n\t\t}\n\n\t}\n\n\t@BeforeAll\n\t@Retention(RetentionPolicy.RUNTIME)\n\tprivate @interface CustomBeforeAll {\n\t}\n\n\t@AfterAll\n\t@Retention(RetentionPolicy.RUNTIME)\n\tprivate @interface CustomAfterAll {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/BeforeEachAndAfterEachComposedAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests that verify support for {@link BeforeEach} and {@link AfterEach}\n * when used as meta-annotations in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n * @see BeforeAllAndAfterAllComposedAnnotationTests\n */\nclass BeforeEachAndAfterEachComposedAnnotationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> methodsInvoked = new ArrayList<>();\n\n\t@Test\n\tvoid beforeEachAndAfterEachAsMetaAnnotations() {\n\t\texecuteTestsForClass(TestCase.class).testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\tassertThat(methodsInvoked).containsExactly(\"beforeEach\", \"test\", \"afterEach\");\n\t}\n\n\tstatic class TestCase {\n\n\t\t@CustomBeforeEach\n\t\tvoid beforeEach() {\n\t\t\tmethodsInvoked.add(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@CustomAfterEach\n\t\tvoid afterEach() {\n\t\t\tmethodsInvoked.add(\"afterEach\");\n\t\t}\n\n\t}\n\n\t@BeforeEach\n\t@Retention(RetentionPolicy.RUNTIME)\n\tprivate @interface CustomBeforeEach {\n\t}\n\n\t@AfterEach\n\t@Retention(RetentionPolicy.RUNTIME)\n\tprivate @interface CustomAfterEach {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/ClassTemplateInvocationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.TagFilter.excludeTags;\nimport static org.junit.platform.launcher.TagFilter.includeTags;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.legacyReportingName;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.EventConditions.uniqueId;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Function;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.AfterClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * @since 5.13\n */\npublic class ClassTemplateInvocationTests extends AbstractJupiterTestEngineTests {\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { //\n\t\t\t\"class:%s\", //\n\t\t\t\"uid:[engine:junit-jupiter]/[class-template:%s]\" //\n\t})\n\tvoid executesClassTemplateClassTwice(String selectorIdentifierTemplate) {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId1 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar invocation1MethodAId = invocationId1.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\t\tvar invocation1NestedClassId = invocationId1.append(NestedClassTestDescriptor.SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar invocation1NestedMethodBId = invocation1NestedClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar invocation2MethodAId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\t\tvar invocation2NestedClassId = invocationId2.append(NestedClassTestDescriptor.SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar invocation2NestedMethodBId = invocation2NestedClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTests(DiscoverySelectors.parse(\n\t\t\tselectorIdentifierTemplate.formatted(TwoInvocationsTestCase.class.getName())).orElseThrow());\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId1)), displayName(\"[1] A of TwoInvocationsTestCase\"),\n\t\t\t\tlegacyReportingName(\"%s[1]\".formatted(TwoInvocationsTestCase.class.getName()))), //\n\t\t\tevent(container(uniqueId(invocationId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation1MethodAId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation1NestedClassId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation1NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation1MethodAId)), started()), //\n\t\t\tevent(test(uniqueId(invocation1MethodAId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocation1NestedClassId)), started()), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocation1NestedClassId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId1)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of TwoInvocationsTestCase\"),\n\t\t\t\tlegacyReportingName(\"%s[2]\".formatted(TwoInvocationsTestCase.class.getName()))), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation2MethodAId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation2NestedClassId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation2NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation2MethodAId)), started()), //\n\t\t\tevent(test(uniqueId(invocation2MethodAId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocation2NestedClassId)), started()), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocation2NestedClassId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid classTemplateAnnotationIsInherited() {\n\t\tvar results = executeTestsForClass(InheritedTwoInvocationsTestCase.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(12).succeeded(12));\n\t}\n\n\t@Test\n\tvoid executesOnlySelectedMethodsDeclaredInClassTemplate() {\n\t\tvar results = executeTests(selectMethod(TwoInvocationsTestCase.class, \"a\"));\n\n\t\tresults.testEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2)) //\n\t\t\t\t.assertEventsMatchLoosely(event(test(displayName(\"a()\")), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesOnlySelectedMethodsDeclaredInNestedClassOfClassTemplate() {\n\t\tvar results = executeTests(selectNestedMethod(List.of(TwoInvocationsTestCase.class),\n\t\t\tTwoInvocationsTestCase.NestedTestCase.class, \"b\"));\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)) //\n\t\t\t\t.assertEventsMatchLoosely(event(test(displayName(\"b()\")), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesOnlyTestsPassingPostDiscoveryFilter() {\n\t\tvar results = executeTests(request -> request //\n\t\t\t\t.selectors(selectClass(TwoInvocationsTestCase.class)) //\n\t\t\t\t.filters(includeTags(\"nested\")));\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)) //\n\t\t\t\t.assertEventsMatchLoosely(event(test(displayName(\"b()\")), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid prunesEmptyNestedTestClasses() {\n\t\tvar results = executeTests(request -> request //\n\t\t\t\t.selectors(selectClass(TwoInvocationsTestCase.class)) //\n\t\t\t\t.filters(excludeTags(\"nested\")));\n\n\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t.noneMatch(container(TwoInvocationsTestCase.NestedTestCase.class.getSimpleName())::matches);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2)) //\n\t\t\t\t.assertEventsMatchLoosely(event(test(displayName(\"a()\")), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesNestedClassTemplateClassTwiceWithClassSelectorForEnclosingClass() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classId = engineId.append(ClassTestDescriptor.SEGMENT_TYPE,\n\t\t\tNestedClassTemplateWithTwoInvocationsTestCase.class.getName());\n\t\tvar methodAId = classId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\t\tvar nestedClassTemplateId = classId.append(ClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE,\n\t\t\t\"NestedTestCase\");\n\t\tvar invocationId1 = nestedClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar invocation1NestedMethodBId = invocationId1.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\t\tvar invocationId2 = nestedClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar invocation2NestedMethodBId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTestsForClass(NestedClassTemplateWithTwoInvocationsTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classId)), started()), //\n\n\t\t\tevent(test(uniqueId(methodAId)), started()), //\n\t\t\tevent(test(uniqueId(methodAId)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(nestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId1)), displayName(\"[1] A of NestedTestCase\"),\n\t\t\t\tlegacyReportingName(\n\t\t\t\t\t\"%s[1]\".formatted(NestedClassTemplateWithTwoInvocationsTestCase.NestedTestCase.class.getName()))), //\n\t\t\tevent(container(uniqueId(invocationId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation1NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId1)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of NestedTestCase\"),\n\t\t\t\tlegacyReportingName(\n\t\t\t\t\t\"%s[2]\".formatted(NestedClassTemplateWithTwoInvocationsTestCase.NestedTestCase.class.getName()))), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation2NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(nestedClassTemplateId)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesNestedClassTemplateClassTwiceWithNestedClassSelector() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classId = engineId.append(ClassTestDescriptor.SEGMENT_TYPE,\n\t\t\tNestedClassTemplateWithTwoInvocationsTestCase.class.getName());\n\t\tvar nestedClassTemplateId = classId.append(ClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE,\n\t\t\t\"NestedTestCase\");\n\t\tvar invocationId1 = nestedClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar invocation1NestedMethodBId = invocationId1.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\t\tvar invocationId2 = nestedClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar invocation2NestedMethodBId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTestsForClass(NestedClassTemplateWithTwoInvocationsTestCase.NestedTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classId)), started()), //\n\n\t\t\tevent(container(uniqueId(nestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId1)), displayName(\"[1] A of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation1NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation1NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId1)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocation2NestedMethodBId))), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(invocation2NestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(nestedClassTemplateId)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesNestedClassTemplatesTwiceEach() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar outerClassTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoTimesTwoInvocationsTestCase.class.getName());\n\n\t\tvar outerInvocation1Id = outerClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar outerInvocation1NestedClassTemplateId = outerInvocation1Id.append(\n\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar outerInvocation1InnerInvocation1Id = outerInvocation1NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar outerInvocation1InnerInvocation1NestedMethodId = outerInvocation1InnerInvocation1Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar outerInvocation1InnerInvocation2Id = outerInvocation1NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation1InnerInvocation2NestedMethodId = outerInvocation1InnerInvocation2Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tvar outerInvocation2Id = outerClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2NestedClassTemplateId = outerInvocation2Id.append(\n\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar outerInvocation2InnerInvocation1Id = outerInvocation2NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar outerInvocation2InnerInvocation1NestedMethodId = outerInvocation2InnerInvocation1Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar outerInvocation2InnerInvocation2Id = outerInvocation2NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2InnerInvocation2NestedMethodId = outerInvocation2InnerInvocation2Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tvar results = executeTestsForClass(TwoTimesTwoInvocationsTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1Id)),\n\t\t\t\tdisplayName(\"[1] A of TwoTimesTwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation1Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1NestedClassTemplateId))), //\n\t\t\tevent(container(uniqueId(outerInvocation1NestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation1Id)),\n\t\t\t\tdisplayName(\"[1] A of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation1Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation1NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation1NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation1NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation1Id)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation2NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation2NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation2NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerInvocation1NestedClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation1Id)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of TwoTimesTwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2NestedClassTemplateId))), //\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation1Id)),\n\t\t\t\tdisplayName(\"[1] A of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation1Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation1NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation1NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation1NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation1Id)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid invocationContextProviderCanRegisterAdditionalExtensions() {\n\t\tvar results = executeTestsForClass(AdditionalExtensionRegistrationTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t}\n\n\t@Test\n\tvoid eachInvocationHasSeparateExtensionContext() {\n\t\tvar results = executeTestsForClass(SeparateExtensionContextTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t}\n\n\t@Test\n\tvoid supportsTestTemplateMethodsInsideClassTemplateClasses() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tCombinationWithTestTemplateTestCase.class.getName());\n\t\tvar invocationId1 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar testTemplateId1 = invocationId1.append(TestTemplateTestDescriptor.SEGMENT_TYPE, \"test(int)\");\n\t\tvar testTemplate1InvocationId1 = testTemplateId1.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#1\");\n\t\tvar testTemplate1InvocationId2 = testTemplateId1.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#2\");\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar testTemplateId2 = invocationId2.append(TestTemplateTestDescriptor.SEGMENT_TYPE, \"test(int)\");\n\t\tvar testTemplate2InvocationId1 = testTemplateId2.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#1\");\n\t\tvar testTemplate2InvocationId2 = testTemplateId2.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#2\");\n\n\t\tvar results = executeTestsForClass(CombinationWithTestTemplateTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId1)),\n\t\t\t\tdisplayName(\"[1] A of CombinationWithTestTemplateTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplateId1))), //\n\t\t\tevent(container(uniqueId(testTemplateId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplate1InvocationId1))), //\n\t\t\tevent(test(uniqueId(testTemplate1InvocationId1)), started()), //\n\t\t\tevent(test(uniqueId(testTemplate1InvocationId1)), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplate1InvocationId2))), //\n\t\t\tevent(test(uniqueId(testTemplate1InvocationId2)), started()), //\n\t\t\tevent(test(uniqueId(testTemplate1InvocationId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testTemplateId1)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId1)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)),\n\t\t\t\tdisplayName(\"[2] B of CombinationWithTestTemplateTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplateId2))), //\n\t\t\tevent(container(uniqueId(testTemplateId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplate2InvocationId1))), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId1)), started()), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId1)), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplate2InvocationId2))), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId2)), started()), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testTemplateId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid testTemplateInvocationInsideClassTemplateClassCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tCombinationWithTestTemplateTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar testTemplateId2 = invocationId2.append(TestTemplateTestDescriptor.SEGMENT_TYPE, \"test(int)\");\n\t\tvar testTemplate2InvocationId2 = testTemplateId2.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE,\n\t\t\t\"#2\");\n\n\t\tvar results = executeTests(selectUniqueId(testTemplate2InvocationId2));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)),\n\t\t\t\tdisplayName(\"[2] B of CombinationWithTestTemplateTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplateId2))), //\n\t\t\tevent(container(uniqueId(testTemplateId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testTemplate2InvocationId2))), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId2)), started()), //\n\t\t\tevent(test(uniqueId(testTemplate2InvocationId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testTemplateId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid supportsTestFactoryMethodsInsideClassTemplateClasses() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tCombinationWithTestFactoryTestCase.class.getName());\n\t\tvar invocationId1 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar testFactoryId1 = invocationId1.append(TestFactoryTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar testFactory1DynamicTestId1 = testFactoryId1.append(TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE,\n\t\t\t\"#1\");\n\t\tvar testFactory1DynamicTestId2 = testFactoryId1.append(TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE,\n\t\t\t\"#2\");\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar testFactoryId2 = invocationId2.append(TestFactoryTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar testFactory2DynamicTestId1 = testFactoryId2.append(TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE,\n\t\t\t\"#1\");\n\t\tvar testFactory2DynamicTestId2 = testFactoryId2.append(TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE,\n\t\t\t\"#2\");\n\n\t\tvar results = executeTestsForClass(CombinationWithTestFactoryTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId1)),\n\t\t\t\tdisplayName(\"[1] A of CombinationWithTestFactoryTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactoryId1))), //\n\t\t\tevent(container(uniqueId(testFactoryId1)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactory1DynamicTestId1))), //\n\t\t\tevent(test(uniqueId(testFactory1DynamicTestId1)), started()), //\n\t\t\tevent(test(uniqueId(testFactory1DynamicTestId1)), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactory1DynamicTestId2))), //\n\t\t\tevent(test(uniqueId(testFactory1DynamicTestId2)), started()), //\n\t\t\tevent(test(uniqueId(testFactory1DynamicTestId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testFactoryId1)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId1)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)),\n\t\t\t\tdisplayName(\"[2] B of CombinationWithTestFactoryTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactoryId2))), //\n\t\t\tevent(container(uniqueId(testFactoryId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactory2DynamicTestId1))), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId1)), started()), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId1)), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactory2DynamicTestId2))), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId2)), started()), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testFactoryId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid specificDynamicTestInsideClassTemplateClassCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tCombinationWithTestFactoryTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar testFactoryId2 = invocationId2.append(TestFactoryTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar testFactory2DynamicTestId2 = testFactoryId2.append(TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE,\n\t\t\t\"#2\");\n\n\t\tvar results = executeTests(selectUniqueId(testFactory2DynamicTestId2));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)),\n\t\t\t\tdisplayName(\"[2] B of CombinationWithTestFactoryTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactoryId2))), //\n\t\t\tevent(container(uniqueId(testFactoryId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(testFactory2DynamicTestId2))), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId2)), started()), //\n\t\t\tevent(test(uniqueId(testFactory2DynamicTestId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(testFactoryId2)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid failsIfProviderReturnsZeroInvocationContextWithoutOptIn() {\n\t\tvar results = executeTestsForClass(InvalidZeroInvocationTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(InvalidZeroInvocationTestCase.class), started()), //\n\t\t\tevent(container(InvalidZeroInvocationTestCase.class),\n\t\t\t\tfinishedWithFailure(\n\t\t\t\t\tmessage(\"Provider [Ext] did not provide any invocation contexts, but was expected to do so. \"\n\t\t\t\t\t\t\t+ \"You may override mayReturnZeroClassTemplateInvocationContexts() to allow this.\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid succeedsIfProviderReturnsZeroInvocationContextWithOptIn() {\n\t\tvar results = executeTestsForClass(ValidZeroInvocationTestCase.class);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ValidZeroInvocationTestCase.class), started()), //\n\t\t\tevent(container(ValidZeroInvocationTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { NoProviderRegisteredTestCase.class, NoSupportingProviderRegisteredTestCase.class })\n\tvoid failsIfNoSupportingProviderIsRegistered(Class<?> testClass) {\n\t\tvar results = executeTestsForClass(testClass);\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(\n\t\t\t\t\tmessage(\"You must register at least one ClassTemplateInvocationContextProvider that supports \"\n\t\t\t\t\t\t\t+ \"@ClassTemplate class [\" + testClass.getName() + \"]\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid classTemplateInvocationCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar methodAId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\t\tvar nestedClassId = invocationId2.append(NestedClassTestDescriptor.SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar nestedMethodBId = nestedClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTests(selectUniqueId(invocationId2));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of TwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(methodAId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedClassId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedMethodBId))), //\n\t\t\tevent(test(uniqueId(methodAId)), started()), //\n\t\t\tevent(test(uniqueId(methodAId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(nestedClassId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(nestedClassId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid classTemplateInvocationCanBeSelectedByIteration() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar methodAId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\t\tvar nestedClassId = invocationId2.append(NestedClassTestDescriptor.SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar nestedMethodBId = nestedClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTests(selectIteration(selectClass(TwoInvocationsTestCase.class), 1));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of TwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(methodAId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedClassId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedMethodBId))), //\n\t\t\tevent(test(uniqueId(methodAId)), started()), //\n\t\t\tevent(test(uniqueId(methodAId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(nestedClassId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(nestedClassId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { //\n\t\t\t\"class:org.junit.jupiter.engine.ClassTemplateInvocationTests$TwoInvocationsTestCase\", //\n\t\t\t\"uid:[engine:junit-jupiter]/[class-template:org.junit.jupiter.engine.ClassTemplateInvocationTests$TwoInvocationsTestCase]\" //\n\t})\n\tvoid executesAllInvocationsForRedundantSelectors(String classTemplateSelectorIdentifier) {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\n\t\tvar results = executeTests(selectUniqueId(invocationId2),\n\t\t\tDiscoverySelectors.parse(classTemplateSelectorIdentifier).orElseThrow());\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t}\n\n\t@Test\n\tvoid methodInClassTemplateInvocationCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar methodAId = invocationId2.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"a()\");\n\n\t\tvar results = executeTests(selectUniqueId(methodAId));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of TwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(methodAId))), //\n\t\t\tevent(test(uniqueId(methodAId)), started()), //\n\t\t\tevent(test(uniqueId(methodAId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nestedMethodInClassTemplateInvocationCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar classTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoInvocationsTestCase.class.getName());\n\t\tvar invocationId2 = classTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar nestedClassId = invocationId2.append(NestedClassTestDescriptor.SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar nestedMethodBId = nestedClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTests(selectUniqueId(nestedMethodBId));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(classTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(invocationId2)), displayName(\"[2] B of TwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(invocationId2)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedClassId))), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(nestedMethodBId))), //\n\t\t\tevent(container(uniqueId(nestedClassId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), started()), //\n\t\t\tevent(test(uniqueId(nestedMethodBId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(nestedClassId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(invocationId2)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(classTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nestedClassTemplateInvocationCanBeSelectedByUniqueId() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar outerClassTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoTimesTwoInvocationsWithMultipleMethodsTestCase.class.getName());\n\t\tvar outerInvocation2Id = outerClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2NestedClassTemplateId = outerInvocation2Id.append(\n\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar outerInvocation2InnerInvocation2Id = outerInvocation2NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2InnerInvocation2NestedMethodId = outerInvocation2InnerInvocation2Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"b()\");\n\n\t\tvar results = executeTests(selectUniqueId(outerInvocation2InnerInvocation2NestedMethodId));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of TwoTimesTwoInvocationsWithMultipleMethodsTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2NestedClassTemplateId))), //\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nestedClassTemplateInvocationCanBeSelectedByIteration() {\n\t\tvar engineId = UniqueId.forEngine(JupiterEngineDescriptor.ENGINE_ID);\n\t\tvar outerClassTemplateId = engineId.append(ClassTemplateTestDescriptor.STANDALONE_CLASS_SEGMENT_TYPE,\n\t\t\tTwoTimesTwoInvocationsTestCase.class.getName());\n\t\tvar outerInvocation1Id = outerClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\t\tvar outerInvocation1NestedClassTemplateId = outerInvocation1Id.append(\n\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar outerInvocation1InnerInvocation2Id = outerInvocation1NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation1InnerInvocation2NestedMethodId = outerInvocation1InnerInvocation2Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\t\tvar outerInvocation2Id = outerClassTemplateId.append(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2NestedClassTemplateId = outerInvocation2Id.append(\n\t\t\tClassTemplateTestDescriptor.NESTED_CLASS_SEGMENT_TYPE, \"NestedTestCase\");\n\t\tvar outerInvocation2InnerInvocation2Id = outerInvocation2NestedClassTemplateId.append(\n\t\t\tClassTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\t\tvar outerInvocation2InnerInvocation2NestedMethodId = outerInvocation2InnerInvocation2Id.append(\n\t\t\tTestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tvar results = executeTests(selectIteration(selectNestedClass(List.of(TwoTimesTwoInvocationsTestCase.class),\n\t\t\tTwoTimesTwoInvocationsTestCase.NestedTestCase.class), 1));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1Id)),\n\t\t\t\tdisplayName(\"[1] A of TwoTimesTwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation1Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1NestedClassTemplateId))), //\n\t\t\tevent(container(uniqueId(outerInvocation1NestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation1InnerInvocation2NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation2NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation1InnerInvocation2NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation1InnerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerInvocation1NestedClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation1Id)), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of TwoTimesTwoInvocationsTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2NestedClassTemplateId))), //\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), started()), //\n\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2Id)),\n\t\t\t\tdisplayName(\"[2] B of NestedTestCase\")), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), started()), //\n\t\t\tevent(dynamicTestRegistered(uniqueId(outerInvocation2InnerInvocation2NestedMethodId))), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), started()), //\n\t\t\tevent(test(uniqueId(outerInvocation2InnerInvocation2NestedMethodId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2InnerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerInvocation2NestedClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(container(uniqueId(outerInvocation2Id)), finishedSuccessfully()), //\n\n\t\t\tevent(container(uniqueId(outerClassTemplateId)), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid executesLifecycleCallbacksInNestedClassTemplates() {\n\t\tvar results = executeTestsForClass(TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.started(10).succeeded(10));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(8).succeeded(8));\n\n\t\t// @formatter:off\n\t\tassertThat(allReportEntryValues(results)).containsExactly(\n\t\t\t\"beforeAll: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\",\n\t\t\t\t\"beforeClassTemplateInvocation: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\",\n\t\t\t\t\t\"beforeAll: NestedTestCase\",\n\t\t\t\t\t\t\"beforeClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\t\"beforeEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\t\"afterEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\"beforeEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\t\"afterEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\"afterClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\"beforeClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\t\"beforeEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\t\"afterEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\"beforeEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\t\"afterEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\"afterClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\"afterAll: NestedTestCase\",\n\t\t\t\t\"afterClassTemplateInvocation: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\",\n\t\t\t\t\"beforeClassTemplateInvocation: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\",\n\t\t\t\t\t\"beforeAll: NestedTestCase\",\n\t\t\t\t\t\t\"beforeClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\t\"beforeEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\t\"afterEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\"beforeEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\t\"afterEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\"afterClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\"beforeClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\t\t\"beforeEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\t\"afterEach: test1 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test1 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\"beforeEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\t\t\"beforeEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\t\"afterEach: test2 [NestedTestCase]\",\n\t\t\t\t\t\t\t\"afterEach: test2 [TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase]\",\n\t\t\t\t\t\t\"afterClassTemplateInvocation: NestedTestCase\",\n\t\t\t\t\t\"afterAll: NestedTestCase\",\n\t\t\t\t\"afterClassTemplateInvocation: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\",\n\t\t\t\"afterAll: TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid guaranteesWrappingBehaviorForCallbacks() {\n\t\tvar results = executeTestsForClass(CallbackWrappingBehaviorTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(allReportEntryValues(results)).containsExactly(\n\t\t\t\t\"1st -> beforeClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"2nd -> beforeClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"test\",\n\t\t\t\t\"2nd -> afterClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"1st -> afterClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"1st -> beforeClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"2nd -> beforeClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"test\",\n\t\t\t\t\"2nd -> afterClassTemplateInvocation: CallbackWrappingBehaviorTestCase\",\n\t\t\t\t\"1st -> afterClassTemplateInvocation: CallbackWrappingBehaviorTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid propagatesExceptionsFromCallbacks() {\n\n\t\tvar results = executeTestsForClass(CallbackExceptionBehaviorTestCase.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).failed(2).succeeded(2));\n\n\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(2, finishedWithFailure( //\n\t\t\t\t\tmessage(\"2nd -> afterClassTemplateInvocation: CallbackExceptionBehaviorTestCase\"), //\n\t\t\t\t\tsuppressed(0, message(\"1st -> beforeClassTemplateInvocation: CallbackExceptionBehaviorTestCase\")), //\n\t\t\t\t\tsuppressed(1, message(\"1st -> afterClassTemplateInvocation: CallbackExceptionBehaviorTestCase\"))));\n\n\t\tassertThat(allReportEntryValues(results).distinct()) //\n\t\t\t\t.containsExactly(\"1st -> beforeClassTemplateInvocation: CallbackExceptionBehaviorTestCase\", //\n\t\t\t\t\t\"2nd -> afterClassTemplateInvocation: CallbackExceptionBehaviorTestCase\", //\n\t\t\t\t\t\"1st -> afterClassTemplateInvocation: CallbackExceptionBehaviorTestCase\");\n\t}\n\n\t@Test\n\tvoid templateWithPreparations() {\n\t\tvar results = executeTestsForClass(ClassTemplateWithPreparationsTestCase.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\tassertTrue(CustomCloseableResource.closed, \"resource in store was closed\");\n\t}\n\n\t@Test\n\tvoid propagatesTagsFromEnclosingClassesToNestedClassTemplates() {\n\t\tvar request = defaultRequest() //\n\t\t\t\t.selectors(selectClass(NestedClassTemplateWithTagOnEnclosingClassTestCase.class)) //\n\t\t\t\t.build();\n\t\tvar engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tvar classDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tvar nestedClassTemplateDescriptor = getOnlyElement(classDescriptor.getChildren());\n\n\t\tassertThat(classDescriptor.getTags()).extracting(TestTag::getName) //\n\t\t\t\t.containsExactly(\"top-level\");\n\t\tassertThat(nestedClassTemplateDescriptor.getTags()).extracting(TestTag::getName) //\n\t\t\t\t.containsExactlyInAnyOrder(\"top-level\", \"nested\");\n\t}\n\n\t@Test\n\tvoid ignoresComposedAnnotations() {\n\t\tvar request = defaultRequest() //\n\t\t\t\t.selectors(selectClass(ParameterizedClass.class)) //\n\t\t\t\t.build();\n\n\t\tvar engineDescriptor = discoverTestsWithoutIssues(request);\n\n\t\tassertThat(engineDescriptor.getDescendants()).isEmpty();\n\t}\n\n\t@Test\n\tvoid classTemplateWithResourceLockCollectsExclusiveResources() {\n\t\tvar results = discoverTestsForClass(ClassTemplateWithResourceLockTestCase.class);\n\t\tvar classTemplateDescriptor = (ClassTemplateTestDescriptor) getOnlyElement(\n\t\t\tresults.getEngineDescriptor().getChildren());\n\n\t\tassertThat(classTemplateDescriptor.getExclusiveResources()).extracting(\n\t\t\tExclusiveResource::getKey).containsExactly(\"test-resource\");\n\t}\n\n\t@Test\n\tvoid classTemplateWithResourceLockExecutesSuccessfully() {\n\t\tvar results = executeTestsForClass(ClassTemplateWithResourceLockTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\tresults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static Stream<String> allReportEntryValues(EngineExecutionResults results) {\n\t\treturn results.allEvents().reportingEntryPublished() //\n\t\t\t\t.map(event -> event.getRequiredPayload(ReportEntry.class)) //\n\t\t\t\t.map(ReportEntry::getKeyValuePairs) //\n\t\t\t\t.map(Map::values) //\n\t\t\t\t.flatMap(Collection::stream);\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\tstatic class TwoInvocationsTestCase {\n\t\t@Test\n\t\tvoid a() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\t@Tag(\"nested\")\n\t\t\tvoid b() {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class NestedClassTemplateWithTwoInvocationsTestCase {\n\t\t@Test\n\t\tvoid a() {\n\t\t}\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\tvoid b() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ClassTemplate\n\tstatic class TwoTimesTwoInvocationsTestCase {\n\t\t@Nested\n\t\t@ClassTemplate\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\tstatic class TwoInvocationsWithExtensionTestCase {\n\t\t@Test\n\t\tvoid a() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\t@Tag(\"nested\")\n\t\t\tvoid b() {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TwoInvocationsClassTemplateInvocationContextProvider\n\t\t\timplements ClassTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<Ctx> provideClassTemplateInvocationContexts(ExtensionContext context) {\n\t\t\tvar suffix = \" of %s\".formatted(context.getRequiredTestClass().getSimpleName());\n\t\t\treturn Stream.of(new Ctx(\"A\" + suffix), new Ctx(\"B\" + suffix));\n\t\t}\n\n\t\trecord Ctx(String displayName) implements ClassTemplateInvocationContext {\n\t\t\t@Override\n\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\tvar defaultDisplayName = ClassTemplateInvocationContext.super.getDisplayName(invocationIndex);\n\t\t\t\treturn \"%s %s\".formatted(defaultDisplayName, displayName);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ClassTemplate\n\t@ExtendWith(AdditionalExtensionRegistrationTestCase.Ext.class)\n\tstatic class AdditionalExtensionRegistrationTestCase {\n\n\t\t@Test\n\t\tvoid test(Data data) {\n\t\t\tassertNotNull(data);\n\t\t\tassertNotNull(data.value());\n\t\t}\n\n\t\tstatic class Ext implements ClassTemplateInvocationContextProvider {\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<Ctx> provideClassTemplateInvocationContexts(ExtensionContext context) {\n\t\t\t\treturn Stream.of(new Data(\"A\"), new Data(\"B\")).map(Ctx::new);\n\t\t\t}\n\t\t}\n\n\t\trecord Ctx(Data data) implements ClassTemplateInvocationContext {\n\t\t\t@Override\n\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\treturn this.data.value();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\treturn List.of(new ParameterResolver() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext,\n\t\t\t\t\t\t\tExtensionContext extensionContext) throws ParameterResolutionException {\n\t\t\t\t\t\treturn Data.class.equals(parameterContext.getParameter().getType());\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\t\t\treturn Ctx.this.data;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\trecord Data(String value) {\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ExtendWith(SeparateExtensionContextTestCase.SomeResourceExtension.class)\n\tstatic class SeparateExtensionContextTestCase {\n\n\t\t@Test\n\t\tvoid test(SomeResource someResource) {\n\t\t\tassertFalse(someResource.closed);\n\t\t}\n\n\t\tstatic class SomeResourceExtension implements BeforeAllCallback, ParameterResolver {\n\n\t\t\t@Override\n\t\t\tpublic void beforeAll(ExtensionContext context) throws Exception {\n\t\t\t\tcontext.getStore(Namespace.GLOBAL).put(\"someResource\", new SomeResource());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\tvar parentContext = extensionContext.getParent().orElseThrow();\n\t\t\t\tassertAll( //\n\t\t\t\t\t() -> assertEquals(SeparateExtensionContextTestCase.class, parentContext.getRequiredTestClass()), //\n\t\t\t\t\t() -> assertEquals(SeparateExtensionContextTestCase.class,\n\t\t\t\t\t\tparentContext.getElement().orElseThrow()), //\n\t\t\t\t\t() -> assertEquals(TestInstance.Lifecycle.PER_METHOD,\n\t\t\t\t\t\tparentContext.getTestInstanceLifecycle().orElseThrow()) //\n\t\t\t\t);\n\t\t\t\treturn SomeResource.class.equals(parameterContext.getParameter().getType());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext,\n\t\t\t\t\tExtensionContext extensionContext) throws ParameterResolutionException {\n\t\t\t\treturn extensionContext.getStore(Namespace.GLOBAL).get(\"someResource\");\n\t\t\t}\n\t\t}\n\n\t\tstatic class SomeResource implements AutoCloseable {\n\t\t\tprivate boolean closed;\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tthis.closed = true;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\tstatic class CombinationWithTestTemplateTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(ints = { 1, 2 })\n\t\tvoid test(int i) {\n\t\t\tassertNotEquals(0, i);\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\tstatic class CombinationWithTestFactoryTestCase {\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> test() {\n\t\t\treturn IntStream.of(1, 2) //\n\t\t\t\t\t.mapToObj(i -> dynamicTest(\"test\" + i, () -> assertNotEquals(0, i)));\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(InvalidZeroInvocationTestCase.Ext.class)\n\tstatic class InvalidZeroInvocationTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\tstatic class Ext implements ClassTemplateInvocationContextProvider {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.empty();\n\t\t\t}\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(ValidZeroInvocationTestCase.Ext.class)\n\tstatic class ValidZeroInvocationTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\tstatic class Ext implements ClassTemplateInvocationContextProvider {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean mayReturnZeroClassTemplateInvocationContexts(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ClassTemplate\n\tstatic class NoProviderRegisteredTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(NoSupportingProviderRegisteredTestCase.Ext.class)\n\tstatic class NoSupportingProviderRegisteredTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\tstatic class Ext implements ClassTemplateInvocationContextProvider {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\tthrow new RuntimeException(\"should not be called\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ClassTemplate\n\tstatic class TwoTimesTwoInvocationsWithMultipleMethodsTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\tvoid a() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid b() {\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\tclass AnotherNestedTestCase {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ExtendWith(ClassTemplateInvocationCallbacks.class)\n\t@ClassTemplate\n\tstatic class TwoTimesTwoInvocationsWithLifecycleCallbacksTestCase extends LifecycleCallbacks {\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\tclass NestedTestCase extends LifecycleCallbacks {\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"test1\")\n\t\t\tvoid test1(TestReporter testReporter) {\n\t\t\t\ttestReporter.publishEntry(\"test1\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"test2\")\n\t\t\tvoid test2(TestReporter testReporter) {\n\t\t\t\ttestReporter.publishEntry(\"test2\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class LifecycleCallbacks {\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestReporter testReporter, TestInfo testInfo) {\n\t\t\ttestReporter.publishEntry(\"beforeAll: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestReporter testReporter, TestInfo testInfo) {\n\t\t\ttestReporter.publishEntry(\n\t\t\t\t\"beforeEach: \" + testInfo.getDisplayName() + \" [\" + getClass().getSimpleName() + \"]\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestReporter testReporter, TestInfo testInfo) {\n\t\t\ttestReporter.publishEntry(\n\t\t\t\t\"afterEach: \" + testInfo.getDisplayName() + \" [\" + getClass().getSimpleName() + \"]\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestReporter testReporter, TestInfo testInfo) {\n\t\t\ttestReporter.publishEntry(\"afterAll: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith({ PreparingClassTemplateInvocationContextProvider.class, CompanionExtension.class })\n\tstatic class ClassTemplateWithPreparationsTestCase {\n\n\t\t@Test\n\t\tvoid test(CustomCloseableResource resource) {\n\t\t\tassertNotNull(resource);\n\t\t\tassertFalse(CustomCloseableResource.closed, \"should not be closed yet\");\n\t\t}\n\n\t}\n\n\tprivate static class PreparingClassTemplateInvocationContextProvider\n\t\t\timplements ClassTemplateInvocationContextProvider {\n\n\t\tstatic final Namespace NAMESPACE = Namespace.create(PreparingClassTemplateInvocationContextProvider.class);\n\n\t\t@Override\n\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<? extends ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\tExtensionContext context) {\n\t\t\tvar invocationContext = new PreparingClassTemplateInvocationContext();\n\t\t\treturn Stream.of(invocationContext, invocationContext);\n\t\t}\n\n\t}\n\n\tprivate static class PreparingClassTemplateInvocationContext implements ClassTemplateInvocationContext {\n\n\t\t@Override\n\t\tpublic void prepareInvocation(ExtensionContext context) {\n\t\t\tCustomCloseableResource.closed = false;\n\t\t\tcontext.getStore(PreparingClassTemplateInvocationContextProvider.NAMESPACE) //\n\t\t\t\t\t.put(\"resource\", new CustomCloseableResource());\n\t\t}\n\n\t}\n\n\tprivate static class CompanionExtension implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn ExtensionContextScope.TEST_METHOD;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn CustomCloseableResource.class.equals(parameterContext.getParameter().getType());\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn extensionContext.getStore(PreparingClassTemplateInvocationContextProvider.NAMESPACE).get(\"resource\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tprivate static class CustomCloseableResource implements ExtensionContext.Store.CloseableResource {\n\n\t\tstatic boolean closed;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tclosed = true;\n\t\t}\n\n\t}\n\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ClassTemplate\n\tstatic class CallbackWrappingBehaviorTestCase {\n\n\t\t@RegisterExtension\n\t\t@Order(1)\n\t\tstatic Extension first = new ClassTemplateInvocationCallbacks(\"1st -> \");\n\n\t\t@RegisterExtension\n\t\t@Order(2)\n\t\tstatic Extension second = new ClassTemplateInvocationCallbacks(\"2nd -> \");\n\n\t\t@Test\n\t\tvoid test(TestReporter testReporter) {\n\t\t\ttestReporter.publishEntry(\"test\");\n\t\t}\n\t}\n\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ClassTemplate\n\tstatic class CallbackExceptionBehaviorTestCase {\n\n\t\t@RegisterExtension\n\t\t@Order(1)\n\t\tstatic Extension first = new ClassTemplateInvocationCallbacks(\"1st -> \", TestAbortedException::new);\n\n\t\t@RegisterExtension\n\t\t@Order(2)\n\t\tstatic Extension second = new ClassTemplateInvocationCallbacks(\"2nd -> \", AssertionFailedError::new);\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\tstatic class ClassTemplateInvocationCallbacks\n\t\t\timplements BeforeClassTemplateInvocationCallback, AfterClassTemplateInvocationCallback {\n\n\t\tprivate final String prefix;\n\t\tprivate final Function<String, @Nullable Throwable> exceptionFactory;\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tClassTemplateInvocationCallbacks() {\n\t\t\tthis(\"\");\n\t\t}\n\n\t\tClassTemplateInvocationCallbacks(String prefix) {\n\t\t\tthis(prefix, __ -> null);\n\t\t}\n\n\t\tClassTemplateInvocationCallbacks(String prefix, Function<String, @Nullable Throwable> exceptionFactory) {\n\t\t\tthis.prefix = prefix;\n\t\t\tthis.exceptionFactory = exceptionFactory;\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeClassTemplateInvocation(ExtensionContext context) {\n\t\t\thandle(\"beforeClassTemplateInvocation\", context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterClassTemplateInvocation(ExtensionContext context) {\n\t\t\thandle(\"afterClassTemplateInvocation\", context);\n\t\t}\n\n\t\tprivate void handle(String methodName, ExtensionContext context) {\n\t\t\tvar message = format(methodName, context);\n\t\t\tcontext.publishReportEntry(message);\n\t\t\tvar throwable = exceptionFactory.apply(message);\n\t\t\tif (throwable != null) {\n\t\t\t\tthrow throwAsUncheckedException(throwable);\n\t\t\t}\n\t\t}\n\n\t\tprivate String format(String methodName, ExtensionContext context) {\n\t\t\treturn \"%s%s: %s\".formatted(prefix, methodName, context.getRequiredTestClass().getSimpleName());\n\t\t}\n\t}\n\n\tstatic class InheritedTwoInvocationsTestCase extends TwoInvocationsTestCase {\n\t\t@Test\n\t\tvoid c() {\n\t\t}\n\t}\n\n\t@Tag(\"top-level\")\n\tstatic class NestedClassTemplateWithTagOnEnclosingClassTestCase {\n\t\t@Nested\n\t\t@ClassTemplate\n\t\t@Tag(\"nested\")\n\t\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t\tclass NestedTestCase {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(TwoInvocationsClassTemplateInvocationContextProvider.class)\n\t@ResourceLock(\"test-resource\")\n\tstatic class ClassTemplateWithResourceLockTestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t\t// This test verifies that @ResourceLock works with @ClassTemplate (issue #5155)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultExecutionModeTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.Node;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\n\nclass DefaultExecutionModeTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid defaultExecutionModeIsReadFromConfigurationParameter() {\n\t\tassertUsesExpectedExecutionMode(null, SAME_THREAD);\n\t\tassertUsesExpectedExecutionMode(SAME_THREAD, SAME_THREAD);\n\t\tassertUsesExpectedExecutionMode(CONCURRENT, CONCURRENT);\n\t}\n\n\tprivate void assertUsesExpectedExecutionMode(@Nullable ExecutionMode defaultExecutionMode,\n\t\t\tExecutionMode expectedExecutionMode) {\n\t\tvar engineDescriptor = discoverTestsWithDefaultExecutionMode(TestCase.class, defaultExecutionMode);\n\t\tassertExecutionModeRecursively(engineDescriptor, expectedExecutionMode);\n\t}\n\n\t@Test\n\tvoid annotationOverridesDefaultExecutionModeToConcurrentForAllDescendants() {\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(ConcurrentTestCase.class, null, CONCURRENT);\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(ConcurrentTestCase.class, SAME_THREAD, CONCURRENT);\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(ConcurrentTestCase.class, CONCURRENT, CONCURRENT);\n\t}\n\n\t@Test\n\tvoid annotationOverridesDefaultExecutionModeToSameThreadForAllDescendants() {\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(SameThreadTestCase.class, null, SAME_THREAD);\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(SameThreadTestCase.class, SAME_THREAD,\n\t\t\tSAME_THREAD);\n\t\tassertUsesExpectedExecutionModeForTestClassAndItsDescendants(SameThreadTestCase.class, CONCURRENT, SAME_THREAD);\n\t}\n\n\tprivate void assertUsesExpectedExecutionModeForTestClassAndItsDescendants(Class<?> testClass,\n\t\t\t@Nullable ExecutionMode defaultExecutionMode, ExecutionMode expectedExecutionMode) {\n\t\tvar engineDescriptor = discoverTestsWithDefaultExecutionMode(testClass, defaultExecutionMode);\n\t\tengineDescriptor.getChildren().forEach(child -> assertExecutionModeRecursively(child, expectedExecutionMode));\n\t}\n\n\tprivate void assertExecutionModeRecursively(TestDescriptor testDescriptor, ExecutionMode expectedExecutionMode) {\n\t\tassertExecutionMode(testDescriptor, expectedExecutionMode);\n\t\ttestDescriptor.getChildren().forEach(child -> assertExecutionModeRecursively(child, expectedExecutionMode));\n\t}\n\n\t@Test\n\tvoid methodsInTestClassesWithInstancePerClassHaveExecutionModeSameThread() {\n\t\tvar engineDescriptor = discoverTestsWithDefaultExecutionMode(SimpleTestInstancePerClassTestCase.class,\n\t\t\tCONCURRENT);\n\t\tvar classDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tclassDescriptor.getChildren().forEach(child -> assertExecutionModeRecursively(child, SAME_THREAD));\n\t}\n\n\t@Test\n\tvoid methodsInNestedTestClassesWithInstancePerClassInHierarchyHaveExecutionModeSameThread() {\n\t\tvar engineDescriptor = discoverTestsWithDefaultExecutionMode(OuterTestCase.class, CONCURRENT);\n\t\tvar outerTestCaseClassDescriptor = firstChild(engineDescriptor, ClassTestDescriptor.class);\n\t\tvar outerTestMethodDescriptor = firstChild(outerTestCaseClassDescriptor, TestMethodTestDescriptor.class);\n\t\tvar level1NestedClassDescriptor = firstChild(outerTestCaseClassDescriptor, NestedClassTestDescriptor.class);\n\t\tvar level1TestMethodDescriptor = firstChild(level1NestedClassDescriptor, TestMethodTestDescriptor.class);\n\t\tvar level2NestedClassDescriptor = firstChild(level1NestedClassDescriptor, NestedClassTestDescriptor.class);\n\t\tvar level2TestMethodDescriptor = firstChild(level2NestedClassDescriptor, TestMethodTestDescriptor.class);\n\t\tvar level3NestedClassDescriptor = firstChild(level2NestedClassDescriptor, NestedClassTestDescriptor.class);\n\t\tvar level3TestMethodDescriptor = firstChild(level3NestedClassDescriptor, TestMethodTestDescriptor.class);\n\n\t\tassertExecutionMode(outerTestCaseClassDescriptor, CONCURRENT);\n\t\tassertExecutionMode(outerTestMethodDescriptor, CONCURRENT);\n\t\tassertExecutionMode(level1NestedClassDescriptor, CONCURRENT);\n\t\tassertExecutionMode(level1TestMethodDescriptor, CONCURRENT);\n\t\tassertExecutionMode(level2NestedClassDescriptor, CONCURRENT);\n\t\tassertExecutionMode(level2TestMethodDescriptor, SAME_THREAD);\n\t\tassertExecutionMode(level3NestedClassDescriptor, SAME_THREAD);\n\t\tassertExecutionMode(level3TestMethodDescriptor, SAME_THREAD);\n\t}\n\n\tprivate JupiterEngineDescriptor discoverTestsWithDefaultExecutionMode(Class<?> testClass,\n\t\t\t@Nullable ExecutionMode executionMode) {\n\t\tLauncherDiscoveryRequestBuilder request = request().selectors(selectClass(testClass));\n\t\tif (executionMode != null) {\n\t\t\trequest.configurationParameter(Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME, executionMode.name());\n\t\t}\n\t\treturn (JupiterEngineDescriptor) discoverTests(request.build()).getEngineDescriptor();\n\t}\n\n\tprivate static void assertExecutionMode(TestDescriptor testDescriptor, ExecutionMode expectedExecutionMode) {\n\t\tassertThat(((Node<?>) testDescriptor).getExecutionMode()) //\n\t\t\t\t.describedAs(\"ExecutionMode for %s\", testDescriptor) //\n\t\t\t\t.isEqualTo(expectedExecutionMode);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate <T extends TestDescriptor> T firstChild(TestDescriptor engineDescriptor, Class<T> testDescriptorClass) {\n\t\treturn (T) engineDescriptor.getChildren().stream() //\n\t\t\t\t.filter(testDescriptorClass::isInstance) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.orElseGet(() -> fail(\"No child of type \" + testDescriptorClass + \" found\"));\n\t}\n\n\tstatic class TestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class SimpleTestInstancePerClassTestCase extends TestCase {\n\t}\n\n\t@Execution(org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT)\n\tstatic class ConcurrentTestCase extends TestCase {\n\t}\n\n\t@Execution(org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD)\n\tstatic class SameThreadTestCase extends TestCase {\n\t}\n\n\tstatic class OuterTestCase {\n\t\t@Nested\n\t\tclass LevelOne {\n\t\t\t@Nested\n\t\t\t@TestInstance(PER_CLASS)\n\t\t\tclass LevelTwo {\n\t\t\t\t@Nested\n\t\t\t\tclass LevelThree {\n\t\t\t\t\t@Test\n\t\t\t\t\tvoid test() {\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/DefaultMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.engine.execution.injection.sample.DoubleParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.LongParameterResolver;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for selecting and executing default\n * methods from interfaces in conjunction with the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass DefaultMethodTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static boolean beforeAllInvoked;\n\tprivate static boolean afterAllInvoked;\n\tprivate static boolean defaultMethodInvoked;\n\tprivate static boolean overriddenDefaultMethodInvoked;\n\tprivate static boolean localMethodInvoked;\n\n\t@BeforeEach\n\tvoid resetFlags() {\n\t\tbeforeAllInvoked = false;\n\t\tafterAllInvoked = false;\n\t\tdefaultMethodInvoked = false;\n\t\toverriddenDefaultMethodInvoked = false;\n\t\tlocalMethodInvoked = false;\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithDefaultMethodFromInterfaceSelectedByFullyQualifedMethodName() {\n\t\tString fqmn = TestCaseWithDefaultMethod.class.getName() + \"#test\";\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(fqmn)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertTrue(beforeAllInvoked, \"@BeforeAll static method invoked from interface\"),\n\t\t\t\t() -> assertTrue(afterAllInvoked, \"@AfterAll static method invoked from interface\"),\n\t\t\t\t() -> assertTrue(defaultMethodInvoked, \"default @Test method invoked from interface\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithDefaultMethodFromGenericInterfaceSelectedByFullyQualifedMethodName() {\n\t\tString fqmn = GenericTestCaseWithDefaultMethod.class.getName() + \"#test(\" + Long.class.getName() + \")\";\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(fqmn)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertTrue(beforeAllInvoked, \"@BeforeAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(afterAllInvoked, \"@AfterAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(defaultMethodInvoked, \"default @Test method invoked from interface\"),\n\t\t\t\t() -> assertFalse(localMethodInvoked, \"local @Test method should not have been invoked from class\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithOverloadedMethodNextToGenericDefaultMethodSelectedByFullyQualifedMethodName() {\n\n\t\tString fqmn = GenericTestCaseWithDefaultMethod.class.getName() + \"#test(\" + Double.class.getName() + \")\";\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(fqmn)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertTrue(beforeAllInvoked, \"@BeforeAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(afterAllInvoked, \"@AfterAll default method invoked from interface\"),\n\t\t\t\t() -> assertFalse(defaultMethodInvoked, \"default @Test method should not have been invoked from interface\"),\n\t\t\t\t() -> assertTrue(localMethodInvoked, \"local @Test method invoked from class\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t\t() -> assertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithOverloadedMethodNextToGenericDefaultMethodSelectedByClass() {\n\t\tClass<?> clazz = GenericTestCaseWithDefaultMethod.class;\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(clazz)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertTrue(beforeAllInvoked, \"@BeforeAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(afterAllInvoked, \"@AfterAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(defaultMethodInvoked, \"default @Test method invoked from interface\"),\n\t\t\t\t() -> assertTrue(localMethodInvoked, \"local @Test method invoked from class\"),\n\t\t\t\t() -> assertEquals(2, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t\t() -> assertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithOverriddenGenericDefaultMethodSelectedByClass() {\n\t\tClass<?> clazz = GenericTestCaseWithOverriddenDefaultMethod.class;\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(clazz)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertTrue(beforeAllInvoked, \"@BeforeAll default method invoked from interface\"),\n\t\t\t\t() -> assertTrue(afterAllInvoked, \"@AfterAll default method invoked from interface\"),\n\t\t\t\t() -> assertFalse(defaultMethodInvoked, \"default @Test method should not have been invoked from interface\"),\n\t\t\t\t() -> assertTrue(overriddenDefaultMethodInvoked, \"overridden default @Test method invoked from interface\"),\n\t\t\t\t() -> assertTrue(localMethodInvoked, \"local @Test method invoked from class\"),\n\t\t\t\t// If defaultMethodInvoked is false and the following ends up being\n\t\t\t\t// 3 instead of 2, that means that the overriding method gets invoked\n\t\t\t\t// twice: once as itself and a second time \"as\" the default method which\n\t\t\t\t// should not have been \"discovered\" since it is overridden.\n\t\t\t\t() -> assertEquals(2, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t\t() -> assertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tinterface TestInterface {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tbeforeAllInvoked = true;\n\t\t}\n\n\t\t@Test\n\t\tdefault void test() {\n\t\t\tdefaultMethodInvoked = true;\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tafterAllInvoked = true;\n\t\t}\n\n\t}\n\n\tstatic class TestCaseWithDefaultMethod implements TestInterface {\n\t}\n\n\t@ExtendWith({ LongParameterResolver.class, DoubleParameterResolver.class })\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tinterface GenericTestInterface<N extends Number> {\n\n\t\t@BeforeAll\n\t\tdefault void beforeAll() {\n\t\t\tbeforeAllInvoked = true;\n\t\t}\n\n\t\t@Test\n\t\tdefault void test(N number) {\n\t\t\tdefaultMethodInvoked = true;\n\t\t\tassertThat(number.intValue()).isEqualTo(42);\n\t\t}\n\n\t\t@AfterAll\n\t\tdefault void afterAll() {\n\t\t\tafterAllInvoked = true;\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class GenericTestCaseWithDefaultMethod implements GenericTestInterface<Long> {\n\n\t\t@Test\n\t\tvoid test(Double number) {\n\t\t\tlocalMethodInvoked = true;\n\t\t\tassertThat(number).isEqualTo(42.0);\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class GenericTestCaseWithOverriddenDefaultMethod implements GenericTestInterface<Long> {\n\n\t\t@Test\n\t\t@Override\n\t\tpublic void test(Long number) {\n\t\t\toverriddenDefaultMethodInvoked = true;\n\t\t\tassertThat(number.intValue()).isEqualTo(42);\n\t\t}\n\n\t\t@Test\n\t\tvoid test(Double number) {\n\t\t\tlocalMethodInvoked = true;\n\t\t\tassertThat(number).isEqualTo(42.0);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/DisabledTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for {@link Disabled @Disabled} in the\n * {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass DisabledTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid executeTestsWithDisabledTestClass() {\n\t\tEngineExecutionResults results = executeTestsForClass(DisabledTestClassTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.skipped(1));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(0));\n\t}\n\n\t@Test\n\tvoid executeTestsWithDisabledTestMethods() throws Exception {\n\t\tString methodName = \"disabledTest\";\n\t\tMethod method = DisabledTestMethodsTestCase.class.getDeclaredMethod(methodName);\n\n\t\texecuteTestsForClass(DisabledTestMethodsTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.skipped(1).started(1).finished(1).aborted(0).succeeded(1).failed(0))//\n\t\t\t\t.skipped().assertEventsMatchExactly(\n\t\t\t\t\tevent(test(methodName), skippedWithReason(method + \" is @Disabled\")));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t@Disabled\n\tstatic class DisabledTestClassTestCase {\n\n\t\t@Test\n\t\tvoid disabledTest() {\n\t\t\tfail(\"this should be @Disabled\");\n\t\t}\n\t}\n\n\tstatic class DisabledTestMethodsTestCase {\n\n\t\t@Test\n\t\tvoid enabledTest() {\n\t\t}\n\n\t\t@Test\n\t\t@Disabled\n\t\tvoid disabledTest() {\n\t\t\tfail(\"this should be @Disabled\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/DynamicNodeGenerationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.util.Collections.singleton;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Fail.fail;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.DYNAMIC_CONTAINER_SEGMENT_TYPE;\nimport static org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for {@link TestFactory @TestFactory}, {@link DynamicTest},\n * and {@link org.junit.jupiter.api.DynamicContainer}.\n *\n * @since 5.0\n */\nclass DynamicNodeGenerationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid testFactoryMethodsAreCorrectlyDiscoveredForClassSelector() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(MyDynamicTestCase.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTests(request).getEngineDescriptor();\n\t\tassertThat(engineDescriptor.getDescendants()).as(\"# resolved test descriptors\").hasSize(13);\n\t}\n\n\t@Test\n\tvoid testFactoryMethodIsCorrectlyDiscoveredForMethodSelector() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicStream\")).build();\n\t\tTestDescriptor engineDescriptor = discoverTests(request).getEngineDescriptor();\n\t\tassertThat(engineDescriptor.getDescendants()).as(\"# resolved test descriptors\").hasSize(2);\n\t}\n\n\t@Test\n\tvoid dynamicTestsAreExecutedFromStream() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(MyDynamicTestCase.class, \"dynamicStream\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicStream\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamicStream\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid dynamicTestsAreExecutedFromCollection() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicCollection\"));\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertAll( //\n\t\t\t() -> assertEquals(3, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(3, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid dynamicTestsAreExecutedFromIterator() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicIterator\"));\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertAll( //\n\t\t\t() -> assertEquals(3, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(3, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid dynamicTestsAreExecutedFromIterable() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicIterable\"));\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\t// @TestFactory methods are counted as both container and test\n\t\tassertAll( //\n\t\t\t() -> assertEquals(3, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(3, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid singleDynamicTestIsExecutedWhenDiscoveredByUniqueId() {\n\t\tUniqueId uniqueId = discoverUniqueId(MyDynamicTestCase.class, \"dynamicStream\") //\n\t\t\t\t.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#2\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectUniqueId(uniqueId));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicStream\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamicStream\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid singleDynamicTestIsExecutedWhenDiscoveredByIterationIndex() {\n\t\tvar methodSelector = selectMethod(MyDynamicTestCase.class, \"dynamicStream\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectIteration(methodSelector, 1));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicStream\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamicStream\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid multipleDynamicTestsAreExecutedWhenDiscoveredByIterationIndexAndUniqueId() {\n\t\tUniqueId uniqueId = discoverUniqueId(MyDynamicTestCase.class, \"threeTests\") //\n\t\t\t\t.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#3\");\n\n\t\tvar methodSelector = selectMethod(MyDynamicTestCase.class, \"threeTests\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectIteration(methodSelector, 1),\n\t\t\tselectUniqueId(uniqueId));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"threeTests\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"two\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"two\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#3\")), //\n\t\t\tevent(test(\"dynamic-test:#3\", \"three\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#3\", \"three\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"threeTests\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid dynamicContainersAreExecutedFromIterable() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicContainerWithIterable\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicContainerWithIterable\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-container:#1\")), //\n\t\t\tevent(container(\"dynamic-container:#1\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamic-container:#1\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"dynamicContainerWithIterable\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertAll( //\n\t\t\t() -> assertEquals(4, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(1, containers.dynamicallyRegistered().count(), \"# dynamic containers registered\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic tests registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(4, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid singleDynamicTestInNestedDynamicContainerIsExecutedWhenDiscoveredByUniqueId() {\n\t\tUniqueId uniqueId = discoverUniqueId(MyDynamicTestCase.class, \"twoNestedContainersWithTwoTestsEach\") //\n\t\t\t\t.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#1\") //\n\t\t\t\t.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#1\") //\n\t\t\t\t.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#2\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectUniqueId(uniqueId));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"twoNestedContainersWithTwoTestsEach\"), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"a\"))), //\n\t\t\tevent(container(displayName(\"a\")), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"a1\"))), //\n\t\t\tevent(container(displayName(\"a1\")), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(displayName(\"a1\")), finishedSuccessfully()), //\n\t\t\tevent(container(displayName(\"a\")), finishedSuccessfully()), //\n\t\t\tevent(container(\"twoNestedContainersWithTwoTestsEach\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid allDynamicTestInNestedDynamicContainerAreExecutedWhenContainerIsDiscoveredByUniqueId() {\n\t\tUniqueId uniqueId = discoverUniqueId(MyDynamicTestCase.class, \"twoNestedContainersWithTwoTestsEach\") //\n\t\t\t\t.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#2\") //\n\t\t\t\t.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#1\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectUniqueId(uniqueId));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"twoNestedContainersWithTwoTestsEach\"), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"b\"))), //\n\t\t\tevent(container(displayName(\"b\")), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"b1\"))), //\n\t\t\tevent(container(displayName(\"b1\")), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(displayName(\"b1\")), finishedSuccessfully()), //\n\t\t\tevent(container(displayName(\"b\")), finishedSuccessfully()), //\n\t\t\tevent(container(\"twoNestedContainersWithTwoTestsEach\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nestedDynamicContainersAreExecuted() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"nestedDynamicContainers\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"nestedDynamicContainers\"), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"gift wrap\"))), //\n\t\t\tevent(container(displayName(\"gift wrap\")), started()), //\n\t\t\tevent(dynamicTestRegistered(displayName(\"box\"))), //\n\t\t\tevent(container(displayName(\"box\")), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(displayName(\"box\")), finishedSuccessfully()), //\n\t\t\tevent(container(displayName(\"gift wrap\")), finishedSuccessfully()), //\n\t\t\tevent(container(\"nestedDynamicContainers\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertAll( //\n\t\t\t() -> assertEquals(5, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(2, containers.dynamicallyRegistered().count(), \"# dynamic containers registered\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic tests registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(5, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid legacyReportingNames() {\n\t\tEvents dynamicRegistrations = executeTests(selectMethod(MyDynamicTestCase.class, \"nestedDynamicContainers\"))//\n\t\t\t\t.allEvents().dynamicallyRegistered();\n\n\t\t// @formatter:off\n\t\tStream<String> legacyReportingNames = dynamicRegistrations\n\t\t\t\t.map(Event::getTestDescriptor)\n\t\t\t\t.map(TestDescriptor::getLegacyReportingName);\n\t\tassertThat(legacyReportingNames)\n\t\t\t\t.containsExactly(\"nestedDynamicContainers()[1]\", \"nestedDynamicContainers()[1][1]\",\n\t\t\t\t\t\t\"nestedDynamicContainers()[1][1][1]\", \"nestedDynamicContainers()[1][1][2]\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid dynamicContainersAreExecutedFromExceptionThrowingStream() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicContainerWithExceptionThrowingStream\"));\n\n\t\tassertTrue(MyDynamicTestCase.exceptionThrowingStreamClosed.get(), \"stream should be closed\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicContainerWithExceptionThrowingStream\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-container:#1\")), //\n\t\t\tevent(container(\"dynamic-container:#1\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamic-container:#1\"),\n\t\t\t\tfinishedWithFailure(instanceOf(ArrayIndexOutOfBoundsException.class))), //\n\t\t\tevent(container(\"dynamicContainerWithExceptionThrowingStream\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertAll( //\n\t\t\t() -> assertEquals(4, containers.started().count(), \"# container started\"),\n\t\t\t() -> assertEquals(1, containers.dynamicallyRegistered().count(), \"# dynamic containers registered\"),\n\t\t\t() -> assertEquals(2, tests.dynamicallyRegistered().count(), \"# dynamic tests registered\"),\n\t\t\t() -> assertEquals(2, tests.started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, tests.succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(1, tests.failed().count(), \"# tests failed\"),\n\t\t\t() -> assertEquals(4, containers.finished().count(), \"# container finished\"));\n\t}\n\n\t@Test\n\tvoid dynamicContainersChildrenMustNotBeNull() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"dynamicContainerWithNullChildren\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"dynamicContainerWithNullChildren\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-container:#1\")), //\n\t\t\tevent(container(\"dynamic-container:#1\"), started()), //\n\t\t\tevent(container(\"dynamic-container:#1\"), //\n\t\t\t\tfinishedWithFailure(message(\"individual dynamic node must not be null\"))), //\n\t\t\tevent(container(\"dynamicContainerWithNullChildren\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid testFactoryMethodsMayReturnSingleDynamicContainer() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(MyDynamicTestCase.class, \"singleContainer\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"singleContainer\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-container:#1\")), //\n\t\t\tevent(container(\"dynamic-container:#1\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#2\")), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#2\", \"failingTest\"), finishedWithFailure(message(\"failing\"))), //\n\t\t\tevent(container(\"dynamic-container:#1\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"singleContainer\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid testFactoryMethodsMayReturnSingleDynamicTest() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(MyDynamicTestCase.class, \"singleTest\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MyDynamicTestCase.class), started()), //\n\t\t\tevent(container(\"singleTest\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"dynamic-test:#1\")), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), started()), //\n\t\t\tevent(test(\"dynamic-test:#1\", \"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"singleTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(MyDynamicTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\tstatic class MyDynamicTestCase {\n\n\t\tprivate static final List<DynamicTest> list = Arrays.asList(\n\t\t\tdynamicTest(\"succeedingTest\", () -> assertTrue(true, \"succeeding\")),\n\t\t\tdynamicTest(\"failingTest\", () -> fail(\"failing\")));\n\n\t\tprivate static final AtomicBoolean exceptionThrowingStreamClosed = new AtomicBoolean(false);\n\n\t\t@TestFactory\n\t\tCollection<DynamicTest> dynamicCollection() {\n\t\t\treturn list;\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> dynamicStream() {\n\t\t\treturn list.stream();\n\t\t}\n\n\t\t@TestFactory\n\t\tIterator<DynamicTest> dynamicIterator() {\n\t\t\treturn list.iterator();\n\t\t}\n\n\t\t@TestFactory\n\t\tIterable<DynamicTest> dynamicIterable() {\n\t\t\treturn this::dynamicIterator;\n\t\t}\n\n\t\t@TestFactory\n\t\tIterable<DynamicNode> dynamicContainerWithIterable() {\n\t\t\treturn Set.of(dynamicContainer(\"box\", list));\n\t\t}\n\n\t\t@TestFactory\n\t\tIterable<DynamicNode> nestedDynamicContainers() {\n\t\t\treturn Set.of(dynamicContainer(\"gift wrap\", Set.of(dynamicContainer(\"box\", list))));\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicNode> twoNestedContainersWithTwoTestsEach() {\n\t\t\treturn Stream.of( //\n\t\t\t\tdynamicContainer(\"a\", Set.of(dynamicContainer(\"a1\", list))), //\n\t\t\t\tdynamicContainer(\"b\", Set.of(dynamicContainer(\"b1\", list))) //\n\t\t\t);\n\t\t}\n\n\t\t@TestFactory\n\t\tIterable<DynamicNode> dynamicContainerWithExceptionThrowingStream() {\n\t\t\t// @formatter:off\n\t\t\treturn Set.of(dynamicContainer(\"box\",\n\t\t\t\t\tIntStream.rangeClosed(0, 100)\n\t\t\t\t\t\t\t.mapToObj(list::get)\n\t\t\t\t\t\t\t.onClose(() -> exceptionThrowingStreamClosed.set(true))));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@TestFactory\n\t\tIterable<DynamicNode> dynamicContainerWithNullChildren() {\n\t\t\treturn Set.of(dynamicContainer(\"box\", singleton(null)));\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicNode singleContainer() {\n\t\t\treturn dynamicContainer(\"box\", list);\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicNode singleTest() {\n\t\t\treturn dynamicTest(\"succeedingTest\", () -> assertTrue(true));\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicNode> threeTests() {\n\t\t\treturn Stream.of( //\n\t\t\t\tdynamicTest(\"one\", () -> assertTrue(true)), //\n\t\t\t\tdynamicTest(\"two\", () -> assertTrue(true)), //\n\t\t\t\tdynamicTest(\"three\", () -> assertTrue(true)) //\n\t\t\t);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/ExceptionHandlingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.io.IOException;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Integration tests that verify correct exception handling in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass ExceptionHandlingTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid failureInTestMethodIsRegistered() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(FailureTestCase.class, \"failingTest\"));\n\t\tEvents tests = executionResults.testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1));\n\n\t\ttests.failed().assertEventsMatchExactly( //\n\t\t\tevent(test(\"failingTest\"),\n\t\t\t\tfinishedWithFailure(instanceOf(AssertionFailedError.class), message(\"always fails\"))));\n\t}\n\n\t@Test\n\tvoid uncheckedExceptionInTestMethodIsRegistered() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(FailureTestCase.class, \"testWithUncheckedException\"));\n\t\tEvents tests = executionResults.testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1));\n\n\t\ttests.failed().assertEventsMatchExactly( //\n\t\t\tevent(test(\"testWithUncheckedException\"),\n\t\t\t\tfinishedWithFailure(instanceOf(RuntimeException.class), message(\"unchecked\"))));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInTestMethodIsRegistered() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(FailureTestCase.class, \"testWithCheckedException\"));\n\t\tEvents tests = executionResults.testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1));\n\n\t\ttests.failed().assertEventsMatchExactly( //\n\t\t\tevent(test(\"testWithCheckedException\"),\n\t\t\t\tfinishedWithFailure(instanceOf(IOException.class), message(\"checked\"))));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInBeforeEachIsRegistered() {\n\t\tFailureTestCase.exceptionToThrowInBeforeEach = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(FailureTestCase.class, \"succeedingTest\"));\n\t\tEvents tests = executionResults.testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1));\n\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(\"succeedingTest\"), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInAfterEachIsRegistered() {\n\t\tFailureTestCase.exceptionToThrowInAfterEach = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(FailureTestCase.class, \"succeedingTest\"));\n\t\tEvents tests = executionResults.testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1));\n\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(\"succeedingTest\"), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInAfterEachIsSuppressedByExceptionInTest() {\n\t\tClass<?> testClass = FailureTestCase.class;\n\n\t\tFailureTestCase.exceptionToThrowInAfterEach = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(testClass, \"testWithUncheckedException\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"testWithUncheckedException\"), started()), //\n\t\t\tevent(test(\"testWithUncheckedException\"), //\n\t\t\t\tfinishedWithFailure( //\n\t\t\t\t\tinstanceOf(RuntimeException.class), //\n\t\t\t\t\tmessage(\"unchecked\"), //\n\t\t\t\t\tsuppressed(0, instanceOf(IOException.class), message(\"checked\")))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionInAfterEachTakesPrecedenceOverFailedAssumptionInTest() {\n\t\tFailureTestCase.exceptionToThrowInAfterEach = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(FailureTestCase.class, \"abortedTest\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(FailureTestCase.class), started()), //\n\t\t\tevent(test(\"abortedTest\"), started()), //\n\t\t\tevent(test(\"abortedTest\"), //\n\t\t\t\tfinishedWithFailure(instanceOf(IOException.class), message(\"checked\"), //\n\t\t\t\t\tsuppressed(0, instanceOf(TestAbortedException.class)))), //\n\t\t\tevent(container(FailureTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInBeforeAllIsRegistered() {\n\t\tClass<?> testClass = FailureTestCase.class;\n\n\t\tFailureTestCase.exceptionToThrowInBeforeAll = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(testClass, \"succeedingTest\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid checkedExceptionInAfterAllIsRegistered() {\n\t\tClass<?> testClass = FailureTestCase.class;\n\n\t\tFailureTestCase.exceptionToThrowInAfterAll = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(testClass, \"succeedingTest\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"succeedingTest\"), started()), //\n\t\t\tevent(test(\"succeedingTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionInAfterAllCallbackDoesNotHideExceptionInBeforeAllCallback() {\n\t\tClass<?> testClass = TestCaseWithThrowingBeforeAllAndAfterAllCallbacks.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), finishedWithFailure( //\n\t\t\t\tmessage(\"beforeAll callback\"), //\n\t\t\t\tsuppressed(0, message(\"afterAll callback\")))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionsInConstructorAndAfterAllCallbackAreReportedWhenTestInstancePerMethodIsUsed() {\n\t\tClass<?> testClass = TestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerMethodLifecycle.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"test\"), started()), //\n\t\t\tevent(test(\"test\"), finishedWithFailure(message(\"constructor\"))), //\n\t\t\tevent(container(testClass), finishedWithFailure(message(\"afterAll callback\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionInConstructorPreventsExecutionOfAfterAllCallbacksWhenTestInstancePerClassIsUsed() {\n\t\tClass<?> testClass = TestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerClassLifecycle.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), finishedWithFailure(message(\"constructor\"))),\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid failureInAfterAllTakesPrecedenceOverTestAbortedExceptionInBeforeAll() {\n\t\tFailureTestCase.exceptionToThrowInBeforeAll = Optional.of(new TestAbortedException(\"aborted\"));\n\t\tFailureTestCase.exceptionToThrowInAfterAll = Optional.of(new IOException(\"checked\"));\n\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(FailureTestCase.class, \"succeedingTest\"));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(FailureTestCase.class), started()), //\n\t\t\tevent(container(FailureTestCase.class),\n\t\t\t\tfinishedWithFailure(instanceOf(IOException.class), message(\"checked\"),\n\t\t\t\t\tsuppressed(0, instanceOf(TestAbortedException.class), message(\"aborted\")))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@AfterEach\n\tvoid cleanUpExceptions() {\n\t\tFailureTestCase.exceptionToThrowInBeforeAll = Optional.empty();\n\t\tFailureTestCase.exceptionToThrowInAfterAll = Optional.empty();\n\t\tFailureTestCase.exceptionToThrowInBeforeEach = Optional.empty();\n\t\tFailureTestCase.exceptionToThrowInAfterEach = Optional.empty();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"OptionalUsedAsFieldOrParameterType\")\n\tstatic class FailureTestCase {\n\n\t\tstatic Optional<Throwable> exceptionToThrowInBeforeAll = Optional.empty();\n\t\tstatic Optional<Throwable> exceptionToThrowInAfterAll = Optional.empty();\n\t\tstatic Optional<Throwable> exceptionToThrowInBeforeEach = Optional.empty();\n\t\tstatic Optional<Throwable> exceptionToThrowInAfterEach = Optional.empty();\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() throws Throwable {\n\t\t\tif (exceptionToThrowInBeforeAll.isPresent()) {\n\t\t\t\tthrow exceptionToThrowInBeforeAll.get();\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() throws Throwable {\n\t\t\tif (exceptionToThrowInAfterAll.isPresent()) {\n\t\t\t\tthrow exceptionToThrowInAfterAll.get();\n\t\t\t}\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() throws Throwable {\n\t\t\tif (exceptionToThrowInBeforeEach.isPresent()) {\n\t\t\t\tthrow exceptionToThrowInBeforeEach.get();\n\t\t\t}\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() throws Throwable {\n\t\t\tif (exceptionToThrowInAfterEach.isPresent()) {\n\t\t\t\tthrow exceptionToThrowInAfterEach.get();\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest() {\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tAssertions.fail(\"always fails\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testWithUncheckedException() {\n\t\t\tthrow new RuntimeException(\"unchecked\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testWithCheckedException() throws IOException {\n\t\t\tthrow new IOException(\"checked\");\n\t\t}\n\n\t\t@Test\n\t\tvoid abortedTest() {\n\t\t\tabort(\"abortedTest\");\n\t\t}\n\n\t}\n\n\t@TestInstance(PER_METHOD)\n\t@ExtendWith(ThrowingAfterAllCallback.class)\n\tstatic class TestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerMethodLifecycle {\n\t\tTestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerMethodLifecycle() {\n\t\t\tthrow new IllegalStateException(\"constructor\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\t@ExtendWith(ThrowingAfterAllCallback.class)\n\tstatic class TestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerClassLifecycle {\n\t\tTestCaseWithInvalidConstructorAndThrowingAfterAllCallbackAndPerClassLifecycle() {\n\t\t\tthrow new IllegalStateException(\"constructor\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@ExtendWith(ThrowingBeforeAllCallback.class)\n\t@ExtendWith(ThrowingAfterAllCallback.class)\n\tstatic class TestCaseWithThrowingBeforeAllAndAfterAllCallbacks {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class ThrowingBeforeAllCallback implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tthrow new IllegalStateException(\"beforeAll callback\");\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class ThrowingAfterAllCallback implements AfterAllCallback {\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tthrow new IllegalStateException(\"afterAll callback\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/ExecutionCancellationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.reportEntry;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.platform.engine.CancellationToken;\n\nclass ExecutionCancellationTests extends AbstractJupiterTestEngineTests {\n\n\t@BeforeEach\n\tvoid initializeCancellationToken() {\n\t\tTestCase.cancellationToken = CancellationToken.create();\n\t}\n\n\t@AfterEach\n\tvoid resetCancellationToken() {\n\t\tTestCase.cancellationToken = null;\n\t}\n\n\t@Test\n\tvoid canCancelExecutionWhileTestClassIsRunning() {\n\t\tvar testClass = RegularTestCase.class;\n\n\t\tvar results = jupiterTestEngine() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.cancellationToken(TestCase.requiredCancellationToken()) //\n\t\t\t\t.execute();\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(1).finished(1).skipped(1));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"first\"), started()), //\n\t\t\tevent(test(\"first\"), reportEntry(Map.of(\"cancelled\", \"true\"))), //\n\t\t\tevent(test(\"first\"), finishedSuccessfully()), //\n\t\t\tevent(test(\"second\"), skippedWithReason(\"Execution cancelled\")), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid canCancelExecutionWhileDynamicTestsAreRunning() {\n\t\tvar testClass = DynamicTestCase.class;\n\n\t\tvar results = jupiterTestEngine() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.cancellationToken(TestCase.requiredCancellationToken()) //\n\t\t\t\t.execute();\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.skipped(1));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(1).finished(1).skipped(0));\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(\"testFactory\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"#1\"), displayName(\"first\")), //\n\t\t\tevent(test(\"#1\"), started()), //\n\t\t\tevent(test(\"#1\"), finishedSuccessfully()), //\n\t\t\tevent(dynamicTestRegistered(\"#2\"), displayName(\"container\")), //\n\t\t\tevent(container(\"#2\"), skippedWithReason(\"Execution cancelled\")), //\n\t\t\tevent(container(\"testFactory\"), finishedSuccessfully()), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\tstatic class TestCase {\n\n\t\tstatic @Nullable CancellationToken cancellationToken;\n\n\t\tstatic CancellationToken requiredCancellationToken() {\n\t\t\treturn requireNonNull(cancellationToken);\n\t\t}\n\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class RegularTestCase extends TestCase {\n\n\t\t@Test\n\t\t@Order(1)\n\t\tvoid first() {\n\t\t\trequiredCancellationToken().cancel();\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"cancelled\", String.valueOf(requiredCancellationToken().isCancellationRequested()));\n\t\t}\n\n\t\t@Test\n\t\t@Order(2)\n\t\tvoid second() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\tstatic class DynamicTestCase extends TestCase {\n\n\t\t@TestFactory\n\t\tStream<DynamicNode> testFactory() {\n\t\t\treturn Stream.of( //\n\t\t\t\tdynamicTest(\"first\", () -> requiredCancellationToken().cancel()), //\n\t\t\t\tdynamicContainer(\"container\", Stream.of( //\n\t\t\t\t\tdynamicTest(\"second\", () -> fail(\"should not be called\")) //\n\t\t\t\t)) //\n\t\t\t);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/FailedAssumptionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport org.junit.Assume;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for failed assumptions in the\n * {@link JupiterTestEngine}.\n *\n * @since 5.4\n */\nclass FailedAssumptionsTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid testAbortedExceptionInBeforeAll() {\n\t\tEngineExecutionResults results = executeTestsForClass(TestAbortedExceptionInBeforeAllTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.aborted(1));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(0));\n\t}\n\n\t@Test\n\tvoid assumptionViolatedExceptionInBeforeAll() {\n\t\tEngineExecutionResults results = executeTestsForClass(AssumptionViolatedExceptionInBeforeAllTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.aborted(1));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(0));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tstatic class TestAbortedExceptionInBeforeAllTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tAssumptions.abort();\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class AssumptionViolatedExceptionInBeforeAllTestCase {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tAssume.assumeTrue(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/InvalidLifecycleMethodConfigurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.lang.annotation.Annotation;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\n\n/**\n * Integration tests that verify proper handling of invalid configuration for\n * lifecycle methods in conjunction with the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass InvalidLifecycleMethodConfigurationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid executeValidTestCaseAlongsideTestCaseWithInvalidNonStaticBeforeAllDeclaration() {\n\t\tassertReportsError(TestCaseWithInvalidNonStaticBeforeAllMethod.class, BeforeAll.class);\n\t}\n\n\t@Test\n\tvoid executeValidTestCaseAlongsideTestCaseWithInvalidNonStaticAfterAllDeclaration() {\n\t\tassertReportsError(TestCaseWithInvalidNonStaticAfterAllMethod.class, AfterAll.class);\n\t}\n\n\t@Test\n\tvoid executeValidTestCaseAlongsideTestCaseWithInvalidStaticBeforeEachDeclaration() {\n\t\tassertReportsError(TestCaseWithInvalidStaticBeforeEachMethod.class, BeforeEach.class);\n\t}\n\n\t@Test\n\tvoid executeValidTestCaseAlongsideTestCaseWithInvalidStaticAfterEachDeclaration() {\n\t\tassertReportsError(TestCaseWithInvalidStaticAfterEachMethod.class, AfterEach.class);\n\t}\n\n\tprivate void assertReportsError(Class<?> invalidTestClass, Class<? extends Annotation> annotationType) {\n\t\tvar results = discoverTestsForClass(invalidTestClass);\n\n\t\tassertThat(results.getDiscoveryIssues()) //\n\t\t\t\t.filteredOn(where(DiscoveryIssue::severity, isEqual(Severity.ERROR))) //\n\t\t\t\t.extracting(DiscoveryIssue::message) //\n\t\t\t\t.asString().contains(\"@%s method\".formatted(annotationType.getSimpleName()));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCaseWithInvalidNonStaticBeforeAllMethod {\n\n\t\t// must be static\n\t\t@SuppressWarnings(\"unused\")\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCaseWithInvalidNonStaticAfterAllMethod {\n\n\t\t// must be static\n\t\t@SuppressWarnings(\"unused\")\n\t\t@AfterAll\n\t\tvoid afterAll() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCaseWithInvalidStaticBeforeEachMethod {\n\n\t\t// must NOT be static\n\t\t@SuppressWarnings(\"unused\")\n\t\t@BeforeEach\n\t\tstatic void beforeEach() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCaseWithInvalidStaticAfterEachMethod {\n\n\t\t// must NOT be static\n\t\t@SuppressWarnings(\"unused\")\n\t\t@AfterEach\n\t\tstatic void afterEach() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/JupiterTestEngineTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.launcher.core.NamespacedHierarchicalStoreProviders;\n\n/**\n * @since  5.13\n */\npublic class JupiterTestEngineTests {\n\n\tprivate final JupiterEngineDescriptor jupiterEngineDescriptor = mock();\n\n\tprivate final ConfigurationParameters configurationParameters = mock();\n\n\tprivate final EngineExecutionListener engineExecutionListener = mock();\n\n\tprivate final ExecutionRequest executionRequest = mock();\n\n\tprivate final JupiterTestEngine engine = new JupiterTestEngine();\n\n\tprivate final JupiterTestEngine jupiter = new JupiterTestEngine();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(executionRequest.getEngineExecutionListener()).thenReturn(engineExecutionListener);\n\t\twhen(executionRequest.getConfigurationParameters()).thenReturn(configurationParameters);\n\t\twhen(executionRequest.getRootTestDescriptor()).thenReturn(jupiterEngineDescriptor);\n\t}\n\n\t@Test\n\tvoid createExecutionContextWithValidRequest() {\n\t\twhen(executionRequest.getStore()).thenReturn(\n\t\t\tNamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStore());\n\n\t\tJupiterEngineExecutionContext context = engine.createExecutionContext(executionRequest);\n\t\tassertThat(context).isNotNull();\n\t}\n\n\t@Test\n\tvoid createExecutionContextWithNoParentsRequestLevelStore() {\n\t\twhen(executionRequest.getStore()).thenReturn(\n\t\t\tNamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStoreWithNoParent());\n\n\t\tassertThatThrownBy(() -> engine //\n\t\t\t\t.createExecutionContext(executionRequest)) //\n\t\t\t\t\t\t.isInstanceOf(JUnitException.class) //\n\t\t\t\t\t\t.hasMessageContaining(\"Request-level store must have a parent\");\n\t}\n\n\t@Test\n\tvoid id() {\n\t\tassertEquals(\"junit-jupiter\", jupiter.getId());\n\t}\n\n\t@Test\n\tvoid groupId() {\n\t\tassertEquals(\"org.junit.jupiter\", jupiter.getGroupId().orElseThrow());\n\t}\n\n\t@Test\n\tvoid artifactId() {\n\t\tassertEquals(\"junit-jupiter-engine\", jupiter.getArtifactId().orElseThrow());\n\t}\n\n\t@Test\n\tvoid version() {\n\t\tassertThat(jupiter.getVersion().orElseThrow()).isIn( //\n\t\t\tSystem.getProperty(\"developmentVersion\"), // with Test Distribution\n\t\t\t\"DEVELOPMENT\" // without Test Distribution\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/LifecycleMethodOverridingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.subpackage.SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase;\n\n/**\n * Integration tests that explicitly demonstrate the overriding rules for\n * lifecycle methods in the {@link JupiterTestEngine}.\n *\n * @since 5.9\n */\nclass LifecycleMethodOverridingTests {\n\n\t@Nested\n\t@DisplayName(\"A package-private lifecycle method can be overridden by\")\n\tclass PackagePrivateSuperClassTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"a protected lifecycle method in a subclass\")\n\t\tclass ProtectedExtendsPackagePrivateLifecycleMethod\n\t\t\t\textends SuperClassWithPackagePrivateLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tprotected void beforeEach() {\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"a package-private lifecycle method in a subclass\")\n\t\tclass PackagePrivateExtendsPackagePrivateLifecycleMethod\n\t\t\t\textends SuperClassWithPackagePrivateLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"a public lifecycle method in a subclass\")\n\t\tclass PublicExtendsPackagePrivateLifecycleMethod extends SuperClassWithPackagePrivateLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tpublic void beforeEach() {\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Nested\n\t@DisplayName(\"A package-private lifecycle method from a different package cannot be overridden by\")\n\tclass PackagePrivateSuperClassInDifferentPackageTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"a protected lifecycle method in a subclass\")\n\t\tclass ProtectedExtendsPackagePrivateLifecycleMethod\n\t\t\t\textends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase {\n\n\t\t\t// @Override\n\t\t\t@BeforeEach\n\t\t\tprotected void beforeEach() {\n\t\t\t\tassertThat(super.beforeEachInvoked).isTrue();\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"a package-private lifecycle method in a subclass\")\n\t\tclass PackagePrivateExtendsPackagePrivateLifecycleMethod\n\t\t\t\textends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase {\n\n\t\t\t// @Override\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t\tassertThat(super.beforeEachInvoked).isTrue();\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"a public lifecycle method in a subclass\")\n\t\tclass PublicExtendsPackagePrivateLifecycleMethod\n\t\t\t\textends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase {\n\n\t\t\t// @Override\n\t\t\t@BeforeEach\n\t\t\tpublic void beforeEach() {\n\t\t\t\tassertThat(super.beforeEachInvoked).isTrue();\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Nested\n\t@DisplayName(\"A protected lifecycle method can be overridden by\")\n\tclass ProtectedSuperClassTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"a protected lifecycle method in a subclass\")\n\t\tclass ProtectedExtendsPackagePrivate extends SuperClassWithProtectedLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tprotected void beforeEach() {\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"a public lifecycle method in a subclass\")\n\t\tclass PublicExtendsPackagePrivate extends SuperClassWithProtectedLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tpublic void beforeEach() {\n\t\t\t}\n\n\t\t}\n\t}\n\n\t@Nested\n\t@DisplayName(\"A public lifecycle method can be overridden by\")\n\tclass PublicSuperClassTests {\n\n\t\t@Nested\n\t\t@DisplayName(\"a public lifecycle method in a subclass\")\n\t\tclass PublicExtendsPackagePrivate extends SuperClassWithPublicLifecycleMethodTestCase {\n\n\t\t\t@Override\n\t\t\t@BeforeEach\n\t\t\tpublic void beforeEach() {\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n\n// -------------------------------------------------------------------------\n\nclass SuperClassWithPackagePrivateLifecycleMethodTestCase {\n\n\t@BeforeEach\n\tvoid beforeEach() {\n\t\tfail();\n\t}\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n\nclass SuperClassWithProtectedLifecycleMethodTestCase {\n\n\t@BeforeEach\n\tprotected void beforeEach() {\n\t\tfail();\n\t}\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n\nclass SuperClassWithPublicLifecycleMethodTestCase {\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tfail();\n\t}\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/MultipleTestableAnnotationsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.RepetitionInfo;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * Integration tests that verify the correct behavior for methods annotated\n * with multiple testable annotations simultaneously.\n *\n * @since 5.0\n */\nclass MultipleTestableAnnotationsTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid testAndRepeatedTest() throws Exception {\n\t\tvar results = discoverTestsForClass(TestCase.class);\n\n\t\tvar discoveryIssue = getOnlyElement(results.getDiscoveryIssues());\n\n\t\tassertThat(discoveryIssue.severity()) //\n\t\t\t\t.isEqualTo(Severity.WARNING);\n\t\tassertThat(discoveryIssue.message()) //\n\t\t\t\t.matches(\"Possible configuration error: method .+ resulted in multiple TestDescriptors .+\");\n\t\tassertThat(discoveryIssue.source()) //\n\t\t\t\t.contains(\n\t\t\t\t\tMethodSource.from(TestCase.class.getDeclaredMethod(\"testAndRepeatedTest\", RepetitionInfo.class)));\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class TestCase {\n\n\t\t@Test\n\t\t@RepeatedTest(1)\n\t\tvoid testAndRepeatedTest(RepetitionInfo repetitionInfo) {\n\t\t\tassertNotNull(repetitionInfo);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedTestClassesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.regex.Pattern;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass;\nimport org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass.RecursiveNestedClass;\nimport org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass.RecursiveNestedSiblingClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests that verify support for {@linkplain Nested nested contexts}\n * in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass NestedTestClassesTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid nestedTestsAreCorrectlyDiscovered() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(TestCaseWithNesting.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(5, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource\n\tvoid nestedTestsAreExecutedInTheRightOrder(Consumer<LauncherDiscoveryRequestBuilder> configurer) {\n\t\tEngineExecutionResults executionResults = executeTests(configurer);\n\n\t\tEvents tests = executionResults.testEvents();\n\t\tassertEquals(3, tests.started().count(), \"# tests started\");\n\t\tassertEquals(2, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, tests.failed().count(), \"# tests failed\");\n\n\t\tassertThat(tests.started().map(it -> it.getTestDescriptor().getDisplayName())) //\n\t\t\t\t.containsExactlyInAnyOrder(\"someTest()\", \"successful()\", \"failing()\") //\n\t\t\t\t.containsSubsequence(\"someTest()\", \"successful()\") //\n\t\t\t\t.containsSubsequence(\"someTest()\", \"failing()\");\n\n\t\tEvents containers = executionResults.containerEvents();\n\t\tassertEquals(3, containers.started().count(), \"# containers started\");\n\t\tassertEquals(3, containers.finished().count(), \"# containers finished\");\n\t}\n\n\tstatic List<Named<Consumer<LauncherDiscoveryRequestBuilder>>> nestedTestsAreExecutedInTheRightOrder() {\n\t\treturn List.of( //\n\t\t\tNamed.of(\"class selector\", request -> request //\n\t\t\t\t\t.selectors(selectClass(TestCaseWithNesting.class))),\n\t\t\tNamed.of(\"package selector\", request -> request //\n\t\t\t\t\t.selectors(selectPackage(TestCaseWithNesting.class.getPackageName())) //\n\t\t\t\t\t.filters(includeClassNamePatterns(Pattern.quote(TestCaseWithNesting.class.getName()) + \".*\"))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid doublyNestedTestsAreCorrectlyDiscovered() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectClass(TestCaseWithDoubleNesting.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(8, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid doublyNestedTestsAreExecuted() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestCaseWithDoubleNesting.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(5, tests.started().count(), \"# tests started\");\n\t\tassertEquals(3, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(2, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(4, containers.started().count(), \"# containers started\");\n\t\tassertEquals(4, containers.finished().count(), \"# containers finished\");\n\n\t\tassertAll(\"before each counts\", //\n\t\t\t() -> assertEquals(5, TestCaseWithDoubleNesting.beforeTopCount),\n\t\t\t() -> assertEquals(4, TestCaseWithDoubleNesting.beforeNestedCount),\n\t\t\t() -> assertEquals(2, TestCaseWithDoubleNesting.beforeDoublyNestedCount));\n\n\t\tassertAll(\"after each counts\", //\n\t\t\t() -> assertEquals(5, TestCaseWithDoubleNesting.afterTopCount),\n\t\t\t() -> assertEquals(4, TestCaseWithDoubleNesting.afterNestedCount),\n\t\t\t() -> assertEquals(2, TestCaseWithDoubleNesting.afterDoublyNestedCount));\n\n\t}\n\n\t@Test\n\tvoid inheritedNestedTestsAreExecuted() {\n\t\tvar discoveryIssues = discoverTestsForClass(TestCaseWithInheritedNested.class).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().source()) //\n\t\t\t\t.contains(ClassSource.from(InterfaceWithNestedClass.NestedInInterface.class));\n\n\t\tvar executionResults = executeTests(request -> request //\n\t\t\t\t.selectors(selectClass(TestCaseWithInheritedNested.class)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.ERROR.name()));\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(3, tests.started().count(), \"# tests started\");\n\t\tassertEquals(2, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(4, containers.started().count(), \"# containers started\");\n\t\tassertEquals(4, containers.finished().count(), \"# containers finished\");\n\t}\n\n\t@Test\n\tvoid extendedNestedTestsAreExecuted() {\n\t\tvar discoveryIssues = discoverTestsForClass(TestCaseWithExtendedNested.class).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().source()) //\n\t\t\t\t.contains(ClassSource.from(InterfaceWithNestedClass.NestedInInterface.class));\n\n\t\tvar executionResults = executeTests(request -> request //\n\t\t\t\t.selectors(selectClass(TestCaseWithExtendedNested.class)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.ERROR.name()));\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(6, tests.started().count(), \"# tests started\");\n\t\tassertEquals(4, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(2, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(8, containers.started().count(), \"# containers started\");\n\t\tassertEquals(8, containers.finished().count(), \"# containers finished\");\n\t}\n\n\t@Test\n\tvoid deeplyNestedInheritedMethodsAreExecutedWhenSelectedViaUniqueId() {\n\t\tvar selectors = List.of( //\n\t\t\tselectUniqueId(\n\t\t\t\t\"[engine:junit-jupiter]/[class:org.junit.jupiter.engine.NestedTestClassesTests$TestCaseWithExtendedNested]/[nested-class:ConcreteInner1]/[nested-class:NestedInAbstractClass]/[nested-class:SecondLevelInherited]/[method:test()]\"),\n\t\t\tselectUniqueId(\n\t\t\t\t\"[engine:junit-jupiter]/[class:org.junit.jupiter.engine.NestedTestClassesTests$TestCaseWithExtendedNested]/[nested-class:ConcreteInner2]/[nested-class:NestedInAbstractClass]/[nested-class:SecondLevelInherited]/[method:test()]\"));\n\n\t\tvar discoveryIssues = discoverTests(request -> request.selectors(selectors)).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().source()) //\n\t\t\t\t.contains(ClassSource.from(InterfaceWithNestedClass.NestedInInterface.class));\n\n\t\tvar executionResults = executeTests(request -> request //\n\t\t\t\t.selectors(selectors) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.ERROR.name()));\n\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(2, tests.started().count(), \"# tests started\");\n\t\tassertEquals(2, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(8, containers.started().count(), \"# containers started\");\n\t\tassertEquals(8, containers.finished().count(), \"# containers finished\");\n\t}\n\n\t/**\n\t * @since 1.6\n\t */\n\t@Test\n\tvoid recursiveNestedTestClassHierarchiesAreNotExecuted() {\n\t\tassertNestedCycle(OuterClass.class, RecursiveNestedClass.class, OuterClass.class);\n\t\tassertNestedCycle(NestedClass.class, RecursiveNestedClass.class, OuterClass.class);\n\t\tassertNestedCycle(RecursiveNestedClass.class, RecursiveNestedClass.class, OuterClass.class);\n\t}\n\n\t/**\n\t * NOTE: We do not actually support this as a feature, but we currently only\n\t * check for cycles if a class is selected. Thus, the tests in this method\n\t * pass, since the selection of a particular method does not result in a\n\t * lookup for nested test classes.\n\t *\n\t * @since 1.6\n\t */\n\t@Test\n\tvoid individualMethodsWithinRecursiveNestedTestClassHierarchiesAreExecuted() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(OuterClass.class, \"outer\"));\n\t\texecutionResults.containerEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\texecutionResults = executeTests(selectMethod(NestedClass.class, \"nested\"));\n\t\texecutionResults.containerEvents().assertStatistics(stats -> stats.started(3).succeeded(3));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\texecutionResults = executeTests(selectMethod(RecursiveNestedClass.class, \"nested\"));\n\t\texecutionResults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\texecutionResults = executeTests(selectMethod(RecursiveNestedSiblingClass.class, \"nested\"));\n\t\texecutionResults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\tvoid doesNotReportDiscoveryIssueForClassWithAbstractInnerClass() {\n\t\tvar discoveryIssues = discoverTestsForClass(ConcreteWithExtendedInnerClassTestCase.class).getDiscoveryIssues();\n\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid doesNotReportDiscoveryIssueForAbstractInnerClass() {\n\t\tvar discoveryIssues = discoverTestsForClass(\n\t\t\tAbstractBaseWithInnerClassTestCase.AbstractInnerClass.class).getDiscoveryIssues();\n\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid nestedTestsWithCustomAnnotationAreCorrectlyDiscovered() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectClass(CustomAnnotationTestCase.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(3, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { TopLevelComposedNested.class, CustomAnnotationTestCase.MyNested.class })\n\tvoid ignoresComposedAnnotations(Class<?> annotationType) {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(annotationType)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(0, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\tprivate void assertNestedCycle(Class<?> start, Class<?> from, Class<?> to) {\n\t\tvar results = executeTestsForClass(start);\n\t\tvar expectedMessage = \"Cause: org.junit.platform.commons.JUnitException: Detected cycle in inner class hierarchy between %s and %s\".formatted(\n\t\t\tfrom.getName(), to.getName());\n\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, finishedWithFailure(message(it -> it.contains(expectedMessage))));\n\t}\n\n\t@Test\n\tvoid discoversButWarnsAboutTopLevelNestedTestClasses() {\n\t\tvar results = discoverTestsForClass(TopLevelNestedTestCase.class);\n\n\t\tvar engineDescriptor = results.getEngineDescriptor();\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Top-level class '%s' must not be annotated with @Nested. \"\n\t\t\t\t\t\t\t+ \"It will be executed anyway for backward compatibility. \"\n\t\t\t\t\t\t\t+ \"You should remove the @Nested annotation to resolve this warning.\",\n\t\t\t\t\tTopLevelNestedTestCase.class.getName());\n\t}\n\n\t@Test\n\tvoid discoversButWarnsAboutStaticNestedTestClasses() {\n\t\tvar results = discoverTestsForClass(StaticNestedTestCase.TestCase.class);\n\n\t\tvar engineDescriptor = results.getEngineDescriptor();\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"@Nested class '%s' must not be static. \"\n\t\t\t\t\t\t\t+ \"It will only be executed if discovered as a standalone test class. \"\n\t\t\t\t\t\t\t+ \"You should remove the annotation or make it non-static to resolve this warning.\",\n\t\t\t\t\tStaticNestedTestCase.TestCase.class.getName());\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tstatic class TestCaseWithNesting {\n\n\t\t@Test\n\t\tvoid someTest() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\t@Test\n\t\t\tvoid successful() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid failing() {\n\t\t\t\tAssertions.fail(\"Something went horribly wrong\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TestCaseWithDoubleNesting {\n\n\t\tstatic int beforeTopCount = 0;\n\t\tstatic int beforeNestedCount = 0;\n\t\tstatic int beforeDoublyNestedCount = 0;\n\n\t\tstatic int afterTopCount = 0;\n\t\tstatic int afterNestedCount = 0;\n\t\tstatic int afterDoublyNestedCount = 0;\n\n\t\t@BeforeEach\n\t\tvoid beforeTop() {\n\t\t\tbeforeTopCount++;\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterTop() {\n\t\t\tafterTopCount++;\n\t\t}\n\n\t\t@Test\n\t\tvoid someTest() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeNested() {\n\t\t\t\tbeforeNestedCount++;\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterNested() {\n\t\t\t\tafterNestedCount++;\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid successful() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid failing() {\n\t\t\t\tAssertions.fail(\"Something went horribly wrong\");\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass DoublyNestedTestCase {\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid beforeDoublyNested() {\n\t\t\t\t\tbeforeDoublyNestedCount++;\n\t\t\t\t}\n\n\t\t\t\t@BeforeEach\n\t\t\t\tvoid afterDoublyNested() {\n\t\t\t\t\tafterDoublyNestedCount++;\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid successful() {\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid failing() {\n\t\t\t\t\tAssertions.fail(\"Something went horribly wrong\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tinterface InterfaceWithNestedClass {\n\n\t\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"NewClassNamingConvention\" })\n\t\t@Nested\n\t\tclass NestedInInterface {\n\n\t\t\t@Test\n\t\t\tvoid notExecutedByImplementingClass() {\n\t\t\t\tAssertions.fail(\"class in interface is static and should have been filtered out\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic abstract class AbstractSuperClass implements InterfaceWithNestedClass {\n\n\t\t@Nested\n\t\tclass NestedInAbstractClass {\n\n\t\t\t@Test\n\t\t\tvoid successful() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid failing() {\n\t\t\t\tAssertions.fail(\"something went wrong\");\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass SecondLevelInherited {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TestCaseWithInheritedNested extends AbstractSuperClass {\n\t\t// empty on purpose\n\t}\n\n\tstatic class TestCaseWithExtendedNested {\n\t\t@Nested\n\t\tclass ConcreteInner1 extends AbstractSuperClass {\n\t\t}\n\n\t\t@Nested\n\t\tclass ConcreteInner2 extends AbstractSuperClass {\n\t\t}\n\t}\n\n\tstatic class AbstractOuterClass {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class OuterClass extends AbstractOuterClass {\n\n\t\t@Test\n\t\tvoid outer() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\n\t\t\t@Test\n\t\t\tvoid nested() {\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass RecursiveNestedClass extends OuterClass {\n\n\t\t\t\t@Test\n\t\t\t\tvoid nested() {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t// sibling of OuterClass due to common super type\n\t\t\tclass RecursiveNestedSiblingClass extends AbstractOuterClass {\n\n\t\t\t\t@Test\n\t\t\t\tvoid nested() {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class AbstractBaseWithInnerClassTestCase {\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tabstract class AbstractInnerClass {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class ConcreteWithExtendedInnerClassTestCase extends AbstractBaseWithInnerClassTestCase {\n\t\t@Nested\n\t\tclass NestedTests extends AbstractInnerClass {\n\t\t}\n\t}\n\n\tstatic class CustomAnnotationTestCase {\n\n\t\t@Nested\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@Target(ElementType.TYPE)\n\t\t@interface MyNested {\n\t\t}\n\n\t\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"InnerClassMayBeStatic\" })\n\t\t@MyNested\n\t\tclass Inner {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithInheritanceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n@NullUnmarked\nclass NestedWithInheritanceTests extends SuperClass {\n\n\tstatic List<String> lifecycleInvokingClassNames;\n\n\tstatic String OUTER = NestedWithInheritanceTests.class.getSimpleName();\n\tstatic String NESTED = NestedClass.class.getSimpleName();\n\tstatic String NESTEDNESTED = NestedClass.NestedNestedClass.class.getSimpleName();\n\n\t@Nested\n\tclass NestedClass extends SuperClass {\n\n\t\t@Test\n\t\tpublic void test() {\n\t\t\tassertThat(lifecycleInvokingClassNames).containsExactly(OUTER, NESTED);\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedNestedClass extends SuperClass {\n\n\t\t\t@Test\n\t\t\tpublic void test() {\n\t\t\t\tassertThat(lifecycleInvokingClassNames).containsExactly(OUTER, NESTED, NESTEDNESTED);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n\nclass SuperClass {\n\n\t@BeforeAll\n\tstatic void setup() {\n\t\tNestedWithInheritanceTests.lifecycleInvokingClassNames = new ArrayList<>();\n\t}\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tString invokingClass = this.getClass().getSimpleName();\n\t\tNestedWithInheritanceTests.lifecycleInvokingClassNames.add(invokingClass);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/NestedWithSeparateInheritanceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\nclass NestedWithSeparateInheritanceTests extends SuperClass1 {\n\n\tstatic @Nullable List<String> lifecycleInvokingClassNames;\n\n\tstatic String OUTER = NestedWithSeparateInheritanceTests.class.getSimpleName();\n\tstatic String NESTED = NestedClass.class.getSimpleName();\n\tstatic String NESTEDNESTED = NestedClass.NestedNestedClass.class.getSimpleName();\n\n\t@Nested\n\tclass NestedClass extends SuperClass2 {\n\n\t\t@Test\n\t\tpublic void test() {\n\t\t\tassertThat(lifecycleInvokingClassNames).containsExactly(OUTER, NESTED);\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedNestedClass extends SuperClass3 {\n\n\t\t\t@Test\n\t\t\tpublic void test() {\n\t\t\t\tassertThat(lifecycleInvokingClassNames).containsExactly(OUTER, NESTED, NESTEDNESTED);\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n\nclass SuperClass1 {\n\n\t@BeforeAll\n\tstatic void setup() {\n\t\tNestedWithSeparateInheritanceTests.lifecycleInvokingClassNames = new ArrayList<>();\n\t}\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tString invokingClass = this.getClass().getSimpleName();\n\t\trequireNonNull(NestedWithSeparateInheritanceTests.lifecycleInvokingClassNames).add(invokingClass);\n\t}\n\n}\n\nclass SuperClass2 {\n\n\t@BeforeAll\n\tstatic void setup() {\n\t\tNestedWithSeparateInheritanceTests.lifecycleInvokingClassNames = new ArrayList<>();\n\t}\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tString invokingClass = this.getClass().getSimpleName();\n\t\trequireNonNull(NestedWithSeparateInheritanceTests.lifecycleInvokingClassNames).add(invokingClass);\n\t}\n\n}\n\nclass SuperClass3 {\n\n\t@BeforeAll\n\tstatic void setup() {\n\t\tNestedWithSeparateInheritanceTests.lifecycleInvokingClassNames = new ArrayList<>();\n\t}\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tString invokingClass = this.getClass().getSimpleName();\n\t\trequireNonNull(NestedWithSeparateInheritanceTests.lifecycleInvokingClassNames).add(invokingClass);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/OverloadedTestMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for support of overloaded test methods in conjunction with\n * the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass OverloadedTestMethodTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid executeTestCaseWithOverloadedMethodsAndThenRerunOnlyOneOfTheMethodsSelectedByUniqueId() {\n\t\tEvents tests = executeTestsForClass(TestCase.class).testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(2).succeeded(2).failed(0));\n\n\t\tOptional<Event> first = tests.succeeded().filter(\n\t\t\tevent -> event.getTestDescriptor().getUniqueId().toString().contains(TestInfo.class.getName())).findFirst();\n\t\tassertTrue(first.isPresent());\n\t\tTestIdentifier testIdentifier = TestIdentifier.from(first.get().getTestDescriptor());\n\t\tUniqueId uniqueId = testIdentifier.getUniqueIdObject();\n\n\t\ttests = executeTests(selectUniqueId(uniqueId)).testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));\n\n\t\tfirst = tests.succeeded().filter(\n\t\t\tevent -> event.getTestDescriptor().getUniqueId().toString().contains(TestInfo.class.getName())).findFirst();\n\t\tassertTrue(first.isPresent());\n\t}\n\n\t@Test\n\tvoid executeTestCaseWithOverloadedMethodsWithSingleMethodThatAcceptsArgumentsSelectedByFullyQualifiedMethodName() {\n\t\tString fqmn = TestCase.class.getName() + \"#test(\" + TestInfo.class.getName() + \")\";\n\t\tEvents tests = executeTests(selectMethod(fqmn)).testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));\n\n\t\tOptional<Event> first = tests.succeeded().stream().filter(\n\t\t\tevent -> event.getTestDescriptor().getUniqueId().toString().contains(TestInfo.class.getName())).findFirst();\n\t\tassertTrue(first.isPresent());\n\t}\n\n\tstatic class TestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/RecordTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Test;\n\nclass RecordTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid recordsAreTestClasses() {\n\t\texecuteTestsForClass(TestCase.class).testEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.finished(2).succeeded(1).failed(1));\n\t}\n\n\trecord TestCase() {\n\n\t\t@Test\n\t\tvoid succeedingTest() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail(\"always fails\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/ReportingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.hierarchicalOutputDirectoryCreator;\nimport static org.junit.platform.testkit.engine.EventConditions.fileEntry;\nimport static org.junit.platform.testkit.engine.EventConditions.reportEntry;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.platform.engine.reporting.FileEntry;\n\n/**\n * @since 5.0\n */\nclass ReportingTests extends AbstractJupiterTestEngineTests {\n\n\t@ParameterizedTest\n\t@CsvSource(textBlock = \"\"\"\n\t\t\tPER_CLASS,  1, 7, 5\n\t\t\tPER_METHOD, 0, 9, 7\n\t\t\t\"\"\")\n\tvoid reportAndFileEntriesArePublished(Lifecycle lifecycle, int containerEntries, int testReportEntries,\n\t\t\tint testFileEntries, @TempDir Path tempDir) {\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(MyReportingTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, lifecycle.name()) //\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(tempDir));\n\n\t\tvar results = executeTests(request);\n\n\t\tresults //\n\t\t\t\t.containerEvents() //\n\t\t\t\t.assertStatistics(stats -> stats //\n\t\t\t\t\t\t.started(2) //\n\t\t\t\t\t\t.succeeded(2) //\n\t\t\t\t\t\t.reportingEntryPublished(containerEntries) //\n\t\t\t\t\t\t.fileEntryPublished(containerEntries));\n\n\t\tresults //\n\t\t\t\t.testEvents() //\n\t\t\t\t.assertStatistics(stats -> stats //\n\t\t\t\t\t\t.started(2) //\n\t\t\t\t\t\t.succeeded(2) //\n\t\t\t\t\t\t.reportingEntryPublished(testReportEntries) //\n\t\t\t\t\t\t.fileEntryPublished(testFileEntries)) //\n\t\t\t\t.assertThatEvents() //\n\t\t\t\t.haveExactly(2, reportEntry(Map.of(\"value\", \"@BeforeEach\"))) //\n\t\t\t\t.haveExactly(2, fileEntry(nameAndContent(\"beforeEach\", MediaType.TEXT_PLAIN_UTF_8))) //\n\t\t\t\t.haveExactly(1, reportEntry(Map.of())) //\n\t\t\t\t.haveExactly(1, reportEntry(Map.of(\"user name\", \"dk38\"))) //\n\t\t\t\t.haveExactly(1, reportEntry(Map.of(\"value\", \"message\"))) //\n\t\t\t\t.haveExactly(1, fileEntry(nameAndContent(\"succeedingTest\", MediaType.APPLICATION_OCTET_STREAM))) //\n\t\t\t\t.haveExactly(2, reportEntry(Map.of(\"value\", \"@AfterEach\"))) //\n\t\t\t\t.haveExactly(2, fileEntry(nameAndContent(\"afterEach\", MediaType.TEXT_PLAIN_UTF_8)));\n\t}\n\n\tstatic class MyReportingTestCase {\n\n\t\tMyReportingTestCase(TestReporter reporter) {\n\t\t\t// Reported on class-level for PER_CLASS lifecycle and on method-level for PER_METHOD lifecycle\n\t\t\treporter.publishEntry(\"Constructor\");\n\t\t\treporter.publishFile(\"constructor\", MediaType.TEXT_PLAIN_UTF_8,\n\t\t\t\tfile -> Files.writeString(file, \"constructor\"));\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"@BeforeEach\");\n\t\t\treporter.publishFile(\"beforeEach\", MediaType.TEXT_PLAIN_UTF_8,\n\t\t\t\tfile -> Files.writeString(file, \"beforeEach\"));\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"@AfterEach\");\n\t\t\treporter.publishFile(\"afterEach\", MediaType.TEXT_PLAIN_UTF_8, file -> Files.writeString(file, \"afterEach\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest(TestReporter reporter) {\n\t\t\treporter.publishEntry(Map.of());\n\t\t\treporter.publishEntry(\"user name\", \"dk38\");\n\t\t\treporter.publishEntry(\"message\");\n\t\t\treporter.publishFile(\"succeedingTest\", MediaType.APPLICATION_OCTET_STREAM,\n\t\t\t\tfile -> Files.writeString(file, \"succeedingTest\"));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid invalidReportData(TestReporter reporter) {\n\n\t\t\t// Maps\n\t\t\tMap<String, String> map = new HashMap<>();\n\n\t\t\tmap.put(\"key\", null);\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry(map));\n\n\t\t\tmap.clear();\n\t\t\tmap.put(null, \"value\");\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry(map));\n\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry((Map<String, String>) null));\n\n\t\t\t// Key-Value pair\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry(null, \"bar\"));\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry(\"foo\", null));\n\n\t\t\t// Value\n\t\t\tassertPreconditionViolationFor(() -> reporter.publishEntry((String) null));\n\t\t}\n\n\t}\n\n\tprivate static Predicate<FileEntry> nameAndContent(String expectedName, MediaType mediaType) {\n\t\tPredicate<Path> filePredicate = file -> {\n\t\t\ttry {\n\t\t\t\treturn Path.of(expectedName).equals(file.getFileName()) && expectedName.equals(Files.readString(file));\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(e);\n\t\t\t}\n\t\t};\n\t\treturn fileEntry -> filePredicate.test(fileEntry.getPath()) //\n\t\t\t\t&& mediaType.toString().equals(fileEntry.getMediaType().orElse(null));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/SealedClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Test;\n\nclass SealedClassTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid sealedTestClassesAreTestClasses() {\n\t\texecuteTestsForClass(TestCase.class).testEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.finished(2).succeeded(1).failed(1));\n\t}\n\n\tsealed abstract static class AbstractTestCase permits TestCase {\n\n\t\t@Test\n\t\tvoid succeedingTest() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail(\"always fails\");\n\t\t}\n\t}\n\n\tstatic final class TestCase extends AbstractTestCase {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/StandardTestClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Tests for discovery and execution of standard test cases for the\n * {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass StandardTestClassTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid standardTestClassIsCorrectlyDiscovered() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(MyStandardTestCase.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTests(request).getEngineDescriptor();\n\t\tassertEquals(1 /*class*/ + 6 /*methods*/, engineDescriptor.getDescendants().size(),\n\t\t\t\"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid moreThanOneTestClassIsCorrectlyDiscovered() {\n\t\tLauncherDiscoveryRequest request = //\n\t\t\trequest().selectors(selectClasses(FirstOfTwoTestCases.class, SecondOfTwoTestCases.class)).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTests(request).getEngineDescriptor();\n\t\tassertEquals(2 /*class*/ + 6 /*methods*/, engineDescriptor.getDescendants().size(),\n\t\t\t\"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid moreThanOneTestClassIsExecuted() {\n\t\tLauncherDiscoveryRequest request = //\n\t\t\trequest().selectors(selectClasses(FirstOfTwoTestCases.class, SecondOfTwoTestCases.class)).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(6, tests.started().count(), \"# tests started\");\n\t\tassertEquals(5, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(3, containers.started().count(), \"# containers started\");\n\t\tassertEquals(3, containers.finished().count(), \"# containers finished\");\n\t}\n\n\t@Test\n\tvoid allTestsInClassAreRunWithBeforeEachAndAfterEachMethods() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(MyStandardTestCase.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(2, containers.started().count(), \"# containers started\");\n\t\tassertEquals(2, containers.finished().count(), \"# containers finished\");\n\n\t\tassertEquals(6, tests.started().count(), \"# tests started\");\n\t\tassertEquals(2, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(3, tests.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(6, MyStandardTestCase.countBefore1, \"# before1 calls\");\n\t\tassertEquals(6, MyStandardTestCase.countBefore2, \"# before2 calls\");\n\t\tassertEquals(6, MyStandardTestCase.countAfter, \"# after each calls\");\n\t}\n\n\t@Test\n\tvoid testsFailWhenBeforeEachFails() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestCaseWithFailingBefore.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(2, tests.started().count(), \"# tests started\");\n\t\tassertEquals(0, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(2, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(2, containers.started().count(), \"# containers started\");\n\t\tassertEquals(2, containers.finished().count(), \"# containers finished\");\n\n\t\tassertEquals(2, TestCaseWithFailingBefore.countBefore, \"# before each calls\");\n\t}\n\n\t@Test\n\tvoid testsFailWhenAfterEachFails() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestCaseWithFailingAfter.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(1, tests.started().count(), \"# tests started\");\n\t\tassertEquals(0, tests.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, tests.failed().count(), \"# tests failed\");\n\n\t\tassertEquals(2, containers.started().count(), \"# containers started\");\n\t\tassertEquals(2, containers.finished().count(), \"# containers finished\");\n\n\t\tassertTrue(TestCaseWithFailingAfter.testExecuted, \"test executed?\");\n\t}\n\n\tstatic class MyStandardTestCase {\n\n\t\tstatic int countBefore1 = 0;\n\t\tstatic int countBefore2 = 0;\n\t\tstatic int countAfter = 0;\n\n\t\t@BeforeEach\n\t\tvoid before1() {\n\t\t\tcountBefore1++;\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid before2() {\n\t\t\tcountBefore2++;\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid after() {\n\t\t\tcountAfter++;\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest1() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest2() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail(\"always fails\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"Test aborted via the OTA's TestAbortedException\")\n\t\tvoid testAbortedOpenTest4J() {\n\t\t\tthrow new TestAbortedException(\"aborted!\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"Test aborted via JUnit 4's AssumptionViolatedException\")\n\t\tvoid testAbortedJUnit4() {\n\t\t\tthrow new org.junit.AssumptionViolatedException(\"aborted!\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"Test aborted via JUnit 4's legacy, deprecated AssumptionViolatedException\")\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tvoid testAbortedJUnit4Legacy() {\n\t\t\tthrow new org.junit.internal.AssumptionViolatedException(\"aborted!\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class FirstOfTwoTestCases {\n\n\t\t@Test\n\t\tvoid succeedingTest1() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest2() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail(\"always fails\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class SecondOfTwoTestCases {\n\n\t\t@Test\n\t\tvoid succeedingTest1() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest2() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest3() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t}\n\n\tstatic class TestCaseWithFailingBefore {\n\n\t\tstatic int countBefore = 0;\n\n\t\t@BeforeEach\n\t\tvoid before() {\n\t\t\tcountBefore++;\n\t\t\tthrow new RuntimeException(\"Problem during setup\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t}\n\n\tstatic class TestCaseWithFailingAfter {\n\n\t\tstatic boolean testExecuted = false;\n\n\t\t@AfterEach\n\t\tvoid after() {\n\t\t\tthrow new RuntimeException(\"Problem during 'after'\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\ttestExecuted = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/StaticNestedBeforeAllAndAfterAllMethodsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n/**\n * Integration tests that verify support for {@code static} {@link BeforeAll} and\n * {@link AfterAll} methods in {@link Nested} tests.\n *\n * @since 5.9\n * @see BeforeAllAndAfterAllComposedAnnotationTests\n */\nclass StaticNestedBeforeAllAndAfterAllMethodsTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> methodsInvoked = new ArrayList<>();\n\n\t@DisplayName(\"static @BeforeAll and @AfterAll methods in @Nested test class\")\n\t@Test\n\tvoid staticBeforeAllAndAfterAllMethodsInNestedTestClass() {\n\t\texecuteTestsForClass(TestCase.class).testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\tassertThat(methodsInvoked).containsExactly(//\n\t\t\t\"@BeforeAll: top-level\", //\n\t\t\t\"@Test: top-level\", //\n\t\t\t\"@BeforeAll: nested\", //\n\t\t\t\"@Test: nested\", //\n\t\t\t\"@AfterAll: nested\", //\n\t\t\t\"@AfterAll: top-level\"//\n\t\t);\n\t}\n\n\tstatic class TestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tmethodsInvoked.add(\"@BeforeAll: top-level\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"@Test: top-level\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tmethodsInvoked.add(\"@AfterAll: top-level\");\n\t\t}\n\n\t\t@Nested\n\t\t// Lifecycle.PER_METHOD is the default, but we declare it here in order\n\t\t// to be very explicit about what we are testing, namely static lifecycle\n\t\t// methods in an inner class WITHOUT Lifecycle.PER_CLASS semantics.\n\t\t@TestInstance(Lifecycle.PER_METHOD)\n\t\tclass NestedTestCase {\n\n\t\t\t@BeforeAll\n\t\t\tstatic void beforeAllInner() {\n\t\t\t\tmethodsInvoked.add(\"@BeforeAll: nested\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tmethodsInvoked.add(\"@Test: nested\");\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\tstatic void afterAllInner() {\n\t\t\t\tmethodsInvoked.add(\"@AfterAll: nested\");\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/StaticNestedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\nclass StaticNestedTestCase {\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@Nested\n\tstatic class TestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestClassInheritanceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for test class hierarchy support in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass TestClassInheritanceTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid initStatics() {\n\t\tcallSequence.clear();\n\t\tLocalTestCase.countBeforeInvoked = 0;\n\t\tLocalTestCase.countAfterInvoked = 0;\n\t\tAbstractTestCase.countSuperBeforeInvoked = 0;\n\t\tAbstractTestCase.countSuperAfterInvoked = 0;\n\t}\n\n\t@Test\n\tvoid executeAllTestsInClass() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(LocalTestCase.class);\n\n\t\tassertEquals(6, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(3, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(1, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(2, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\tassertEquals(6, LocalTestCase.countBeforeInvoked, \"# before calls\");\n\t\tassertEquals(6, LocalTestCase.countAfterInvoked, \"# after calls\");\n\t\tassertEquals(6, AbstractTestCase.countSuperBeforeInvoked, \"# super before calls\");\n\t\tassertEquals(6, AbstractTestCase.countSuperAfterInvoked, \"# super after calls\");\n\t}\n\n\t@Test\n\tvoid executeSingleTest() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(LocalTestCase.class, \"alwaysPasses\"));\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestDeclaredInSuperClass() {\n\t\tEngineExecutionResults executionResults = executeTests(selectMethod(LocalTestCase.class, \"superTest\"));\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\tassertEquals(1, LocalTestCase.countBeforeInvoked, \"# after calls\");\n\t\tassertEquals(1, LocalTestCase.countAfterInvoked, \"# after calls\");\n\t\tassertEquals(1, AbstractTestCase.countSuperBeforeInvoked, \"# super before calls\");\n\t\tassertEquals(1, AbstractTestCase.countSuperAfterInvoked, \"# super after calls\");\n\n\t}\n\n\t@Test\n\tvoid executeTestWithExceptionThrownInAfterMethod() {\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\tselectMethod(LocalTestCase.class, \"throwExceptionInAfterMethod\"));\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid beforeAndAfterMethodsInTestClassHierarchy() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestCase3.class);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(1, executionResults.testEvents().started().count(), \"# tests started\"),\n\t\t\t() -> assertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\"),\n\t\t\t() -> assertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\"),\n\t\t\t() -> assertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\"),\n\t\t\t() -> assertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\")\n\t\t);\n\t\t// @formatter:on\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"beforeAll1\",\n\t\t\t\t\"beforeAll2\",\n\t\t\t\t\t\"beforeAll3\",\n\t\t\t\t\t\t\"beforeEach1\",\n\t\t\t\t\t\t\t\"beforeEach2\",\n\t\t\t\t\t\t\t\t\"beforeEach3\",\n\t\t\t\t\t\t\t\t\t\"test3\",\n\t\t\t\t\t\t\t\t\"afterEach3\",\n\t\t\t\t\t\t\t\"afterEach2\",\n\t\t\t\t\t\t\"afterEach1\",\n\t\t\t\t\t\"afterAll3\",\n\t\t\t\t\"afterAll2\",\n\t\t\t\"afterAll1\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static abstract class AbstractTestCase {\n\n\t\tstatic int countSuperBeforeInvoked = 0;\n\t\tstatic int countSuperAfterInvoked = 0;\n\n\t\t@BeforeEach\n\t\tvoid superBefore() {\n\t\t\tcountSuperBeforeInvoked++;\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid superAfter() {\n\t\t\tcountSuperAfterInvoked++;\n\t\t}\n\n\t\t@Test\n\t\tvoid superTest() {\n\t\t\t/* no-op */\n\t\t}\n\t}\n\n\tstatic class LocalTestCase extends AbstractTestCase {\n\n\t\tboolean throwExceptionInAfterMethod = false;\n\n\t\tstatic int countBeforeInvoked = 0;\n\t\tstatic int countAfterInvoked = 0;\n\n\t\t@BeforeEach\n\t\tvoid before() {\n\t\t\tcountBeforeInvoked++;\n\t\t\t// Reset state, since the test instance is retained across all test methods;\n\t\t\t// otherwise, after() always throws an exception.\n\t\t\tthis.throwExceptionInAfterMethod = false;\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid after() {\n\t\t\tcountAfterInvoked++;\n\t\t\tif (this.throwExceptionInAfterMethod) {\n\t\t\t\tthrow new RuntimeException(\"Exception thrown from @AfterEach method\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid otherTest() {\n\t\t\t/* no-op */\n\t\t}\n\n\t\t@Test\n\t\tvoid throwExceptionInAfterMethod() {\n\t\t\tthis.throwExceptionInAfterMethod = true;\n\t\t}\n\n\t\t@Test\n\t\tvoid alwaysPasses() {\n\t\t\t/* no-op */\n\t\t}\n\n\t\t@Test\n\t\tvoid aborted() {\n\t\t\tabort();\n\t\t}\n\n\t\t@Test\n\t\tvoid alwaysFails() {\n\t\t\tfail(\"#fail\");\n\t\t}\n\t}\n\n\tstatic class TestCase1 {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll1() {\n\t\t\tcallSequence.add(\"beforeAll1\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach1() {\n\t\t\tcallSequence.add(\"beforeEach1\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach1() {\n\t\t\tcallSequence.add(\"afterEach1\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll1() {\n\t\t\tcallSequence.add(\"afterAll1\");\n\t\t}\n\t}\n\n\tstatic class TestCase2 extends TestCase1 {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll2() {\n\t\t\tcallSequence.add(\"beforeAll2\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach2() {\n\t\t\tcallSequence.add(\"beforeEach2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach2() {\n\t\t\tcallSequence.add(\"afterEach2\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll2() {\n\t\t\tcallSequence.add(\"afterAll2\");\n\t\t}\n\t}\n\n\tstatic class TestCase3 extends TestCase2 {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll3() {\n\t\t\tcallSequence.add(\"beforeAll3\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach3() {\n\t\t\tcallSequence.add(\"beforeEach3\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t\tcallSequence.add(\"test3\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach3() {\n\t\t\tcallSequence.add(\"afterEach3\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll3() {\n\t\t\tcallSequence.add(\"afterAll3\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleConfigurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.util.ClearSystemProperty;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for {@link TestInstance @TestInstance} lifecycle\n * configuration support, not to be confused with {@link TestInstanceLifecycleTests}.\n *\n * <p>Specifically, this class tests custom lifecycle configuration specified\n * via {@code @TestInstance} as well as via {@link ConfigurationParameters}\n * supplied to the {@link Launcher} or via a JVM system property.\n *\n * @since 5.0\n * @see TestInstanceLifecycleTests\n */\n@ClearSystemProperty(key = TestInstanceLifecycleConfigurationTests.KEY)\nclass TestInstanceLifecycleConfigurationTests extends AbstractJupiterTestEngineTests {\n\n\tstatic final String KEY = Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\n\n\tprivate static final List<String> methodsInvoked = new ArrayList<>();\n\n\t@BeforeEach\n\t@AfterEach\n\tvoid reset() {\n\t\tmethodsInvoked.clear();\n\t}\n\n\t@Test\n\tvoid instancePerMethodUsingStandardDefaultConfiguration() {\n\t\tperformAssertions(AssumedInstancePerTestMethodTestCase.class, 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerClassConfiguredViaExplicitAnnotationDeclaration() {\n\t\tperformAssertions(ExplicitInstancePerClassTestCase.class, 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerClassConfiguredViaSystemProperty() {\n\t\tClass<?> testClass = AssumedInstancePerClassTestCase.class;\n\n\t\t// Should fail by default...\n\t\tperformAssertions(testClass, 1, 1, 0);\n\n\t\t// Should pass with the system property set\n\t\tSystem.setProperty(KEY, PER_CLASS.name());\n\t\tperformAssertions(testClass, 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerClassConfiguredViaConfigParam() {\n\t\tClass<?> testClass = AssumedInstancePerClassTestCase.class;\n\n\t\t// Should fail by default...\n\t\tperformAssertions(testClass, 1, 1, 0);\n\n\t\t// Should pass with the config param\n\t\tperformAssertions(testClass, Map.of(KEY, PER_CLASS.name()), 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerClassConfiguredViaConfigParamThatOverridesSystemProperty() {\n\t\tClass<?> testClass = AssumedInstancePerClassTestCase.class;\n\n\t\t// Should fail with system property\n\t\tSystem.setProperty(KEY, PER_METHOD.name());\n\t\tperformAssertions(testClass, 1, 1, 0);\n\n\t\t// Should pass with the config param\n\t\tperformAssertions(testClass, Map.of(KEY, PER_CLASS.name()), 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerMethodConfiguredViaExplicitAnnotationDeclarationThatOverridesSystemProperty() {\n\t\tSystem.setProperty(KEY, PER_CLASS.name());\n\t\tperformAssertions(ExplicitInstancePerTestMethodTestCase.class, 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\t@Test\n\tvoid instancePerMethodConfiguredViaExplicitAnnotationDeclarationThatOverridesConfigParam() {\n\t\tClass<?> testClass = ExplicitInstancePerTestMethodTestCase.class;\n\t\tperformAssertions(testClass, Map.of(KEY, PER_CLASS.name()), 2, 0, 1, \"beforeAll\", \"test\", \"afterAll\");\n\t}\n\n\tprivate void performAssertions(Class<?> testClass, int containers, int containersFailed, int tests,\n\t\t\tString... methods) {\n\n\t\tperformAssertions(testClass, Map.of(), containers, containersFailed, tests, methods);\n\t}\n\n\tprivate void performAssertions(Class<?> testClass, Map<String, String> configParams, int numContainers,\n\t\t\tint numFailedContainers, int numTests, String... methods) {\n\n\t\t// @formatter:off\n\t\tEngineExecutionResults executionResults = executeTests(\n\t\t\trequest()\n\t\t\t\t.selectors(selectClass(testClass))\n\t\t\t\t.configurationParameters(configParams)\n\t\t\t\t.configurationParameter(DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"execution\")\n\t\t\t\t.build()\n\t\t);\n\t\t// @formatter:on\n\n\t\texecutionResults.containerEvents().assertStatistics(//\n\t\t\tstats -> stats.started(numContainers).finished(numContainers).failed(numFailedContainers));\n\t\texecutionResults.testEvents().assertStatistics(//\n\t\t\tstats -> stats.started(numTests).finished(numTests));\n\n\t\tassertEquals(Arrays.asList(methods), methodsInvoked);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@TestInstance(PER_METHOD)\n\tstatic class ExplicitInstancePerTestMethodTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tmethodsInvoked.add(\"beforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAllStatic() {\n\t\t\tmethodsInvoked.add(\"afterAll\");\n\t\t}\n\n\t}\n\n\t/**\n\t * \"per-method\" lifecycle mode is assumed since the {@code @BeforeAll} and\n\t * {@code @AfterAll} methods are static, even though there is no explicit\n\t * {@code @TestInstance} declaration.\n\t */\n\tstatic class AssumedInstancePerTestMethodTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tmethodsInvoked.add(\"beforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAllStatic() {\n\t\t\tmethodsInvoked.add(\"afterAll\");\n\t\t}\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class ExplicitInstancePerClassTestCase {\n\n\t\t@BeforeAll\n\t\tvoid beforeAll(TestInfo testInfo) {\n\t\t\tmethodsInvoked.add(\"beforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll(TestInfo testInfo) {\n\t\t\tmethodsInvoked.add(\"afterAll\");\n\t\t}\n\n\t}\n\n\t/**\n\t * \"per-class\" lifecycle mode is assumed since the {@code @BeforeAll} and\n\t * {@code @AfterAll} methods are non-static, even though there is no\n\t * explicit {@code @TestInstance} declaration.\n\t */\n\tstatic class AssumedInstancePerClassTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@BeforeAll\n\t\tvoid beforeAll(TestInfo testInfo) {\n\t\t\tmethodsInvoked.add(\"beforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tmethodsInvoked.add(\"test\");\n\t\t}\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@AfterAll\n\t\tvoid afterAll(TestInfo testInfo) {\n\t\t\tmethodsInvoked.add(\"afterAll\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleKotlinTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.engine.kotlin.InstancePerClassKotlinTestCase;\nimport org.junit.jupiter.engine.kotlin.InstancePerMethodKotlinTestCase;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Kotlin-specific integration tests for {@link TestInstance @TestInstance}\n * lifecycle support.\n *\n * @since 5.1\n * @see TestInstanceLifecycleConfigurationTests\n * @see TestInstanceLifecycleTests\n */\nclass TestInstanceLifecycleKotlinTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid instancePerClassCanBeUsedForKotlinTestClasses() {\n\t\tClass<?> testClass = InstancePerClassKotlinTestCase.class;\n\t\tInstancePerClassKotlinTestCase.TEST_INSTANCES.clear();\n\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertThat(executionResults.testEvents().finished().count()).isEqualTo(2);\n\t\tassertThat(InstancePerClassKotlinTestCase.TEST_INSTANCES.keySet()).hasSize(1);\n\t\tassertThat(getOnlyElement(InstancePerClassKotlinTestCase.TEST_INSTANCES.values())) //\n\t\t\t\t.containsEntry(\"beforeAll\", 1) //\n\t\t\t\t.containsEntry(\"beforeEach\", 2) //\n\t\t\t\t.containsEntry(\"test\", 2) //\n\t\t\t\t.containsEntry(\"afterEach\", 2) //\n\t\t\t\t.containsEntry(\"afterAll\", 1);\n\t}\n\n\t@Test\n\tvoid instancePerMethodIsDefaultForKotlinTestClasses() {\n\t\tClass<?> testClass = InstancePerMethodKotlinTestCase.class;\n\t\tInstancePerMethodKotlinTestCase.TEST_INSTANCES.clear();\n\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertThat(executionResults.testEvents().finished().count()).isEqualTo(2);\n\t\tList<Object> instances = new ArrayList<>(InstancePerMethodKotlinTestCase.TEST_INSTANCES.keySet());\n\t\tassertThat(instances) //\n\t\t\t\t.hasSize(3) //\n\t\t\t\t.extracting(o -> (Object) o.getClass()) //\n\t\t\t\t.containsExactly(InstancePerMethodKotlinTestCase.Companion.getClass(), //\n\t\t\t\t\tInstancePerMethodKotlinTestCase.class, //\n\t\t\t\t\tInstancePerMethodKotlinTestCase.class);\n\t\tassertThat(InstancePerMethodKotlinTestCase.TEST_INSTANCES.get(instances.get(0))) //\n\t\t\t\t.containsEntry(\"beforeAll\", 1) //\n\t\t\t\t.containsEntry(\"afterAll\", 1);\n\t\tassertThat(InstancePerMethodKotlinTestCase.TEST_INSTANCES.get(instances.get(1))) //\n\t\t\t\t.containsEntry(\"beforeEach\", 1) //\n\t\t\t\t.containsEntry(\"test\", 1) //\n\t\t\t\t.containsEntry(\"afterEach\", 1);\n\t\tassertThat(InstancePerMethodKotlinTestCase.TEST_INSTANCES.get(instances.get(2))) //\n\t\t\t\t.containsEntry(\"beforeEach\", 1) //\n\t\t\t\t.containsEntry(\"test\", 1) //\n\t\t\t\t.containsEntry(\"afterEach\", 1);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestInstanceLifecycleTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static java.lang.String.join;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNotSame;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.jupiter.engine.execution.DefaultTestInstances;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for {@link TestInstance @TestInstance} lifecycle support.\n *\n * @since 5.0\n * @see TestInstanceLifecycleConfigurationTests\n * @see TestInstanceLifecycleKotlinTests\n */\nclass TestInstanceLifecycleTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final Map<Class<?>, List<Lifecycle>> lifecyclesMap = new LinkedHashMap<>();\n\tprivate static final Map<String, TestInstances> instanceMap = new LinkedHashMap<>();\n\tprivate static final List<String> testsInvoked = new ArrayList<>();\n\tprivate static final Map<Class<?>, Integer> instanceCount = new LinkedHashMap<>();\n\n\tprivate static int beforeAllCount;\n\tprivate static int afterAllCount;\n\tprivate static int beforeEachCount;\n\tprivate static int afterEachCount;\n\n\t@BeforeEach\n\tvoid init() {\n\t\tlifecyclesMap.clear();\n\t\tinstanceMap.clear();\n\t\ttestsInvoked.clear();\n\t\tinstanceCount.clear();\n\t\tbeforeAllCount = 0;\n\t\tafterAllCount = 0;\n\t\tbeforeEachCount = 0;\n\t\tafterEachCount = 0;\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@Test\n\tvoid instancePerMethod() {\n\t\tClass<?> testClass = InstancePerMethodTestCase.class;\n\t\tint containers = 3;\n\t\tint tests = 3;\n\t\tMap.Entry<Class<?>, Integer>[] instances = instanceCounts(entry(InstancePerMethodTestCase.class, 3));\n\t\tint allMethods = 1;\n\t\tint eachMethods = 3;\n\n\t\tperformAssertions(testClass, containers, tests, instances, allMethods, eachMethods);\n\n\t\tString containerExecutionConditionKey = executionConditionKey(testClass, null);\n\t\tString postProcessTestInstanceKey = postProcessTestInstanceKey(testClass);\n\t\tString preDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(testClass);\n\t\tString beforeAllCallbackKey = beforeAllCallbackKey(testClass);\n\t\tString afterAllCallbackKey = afterAllCallbackKey(testClass);\n\t\tString testTemplateKey = testTemplateKey(testClass, \"singletonTest\");\n\t\tString testExecutionConditionKey1 = executionConditionKey(testClass, testsInvoked.getFirst());\n\t\tString beforeEachCallbackKey1 = beforeEachCallbackKey(testClass, testsInvoked.get(0));\n\t\tString afterEachCallbackKey1 = afterEachCallbackKey(testClass, testsInvoked.get(0));\n\t\tString testExecutionConditionKey2 = executionConditionKey(testClass, testsInvoked.get(1));\n\t\tString beforeEachCallbackKey2 = beforeEachCallbackKey(testClass, testsInvoked.get(1));\n\t\tString afterEachCallbackKey2 = afterEachCallbackKey(testClass, testsInvoked.get(1));\n\t\tString testExecutionConditionKey3 = executionConditionKey(testClass, testsInvoked.get(2));\n\t\tString beforeEachCallbackKey3 = beforeEachCallbackKey(testClass, testsInvoked.get(2));\n\t\tString afterEachCallbackKey3 = afterEachCallbackKey(testClass, testsInvoked.get(2));\n\n\t\t// @formatter:off\n\t\tassertThat(instanceMap.keySet()).containsExactlyInAnyOrder(\n\t\t\t\tcontainerExecutionConditionKey,\n\t\t\t\tbeforeAllCallbackKey,\n\t\t\t\tpostProcessTestInstanceKey,\n\t\t\t\tpreDestroyCallbackTestInstanceKey,\n\t\t\t\ttestTemplateKey,\n\t\t\t\ttestExecutionConditionKey1,\n\t\t\t\tbeforeEachCallbackKey1,\n\t\t\t\tafterEachCallbackKey1,\n\t\t\t\ttestExecutionConditionKey2,\n\t\t\t\tbeforeEachCallbackKey2,\n\t\t\t\tafterEachCallbackKey2,\n\t\t\t\ttestExecutionConditionKey3,\n\t\t\t\tbeforeEachCallbackKey3,\n\t\t\t\tafterEachCallbackKey3,\n\t\t\t\tafterAllCallbackKey\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertNull(instanceMap.get(containerExecutionConditionKey));\n\t\tassertNull(instanceMap.get(beforeAllCallbackKey));\n\t\tassertNull(instanceMap.get(afterAllCallbackKey));\n\n\t\tTestInstances testInstances = instanceMap.get(beforeEachCallbackKey1);\n\t\tassertNotNull(testInstances.getInnermostInstance());\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey1));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey1));\n\n\t\ttestInstances = instanceMap.get(beforeEachCallbackKey2);\n\t\tassertNotNull(testInstances.getInnermostInstance());\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey2));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey2));\n\n\t\ttestInstances = instanceMap.get(beforeEachCallbackKey3);\n\t\tassertNotNull(testInstances.getInnermostInstance());\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey3));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey3));\n\t\tassertSame(testInstances.getInnermostInstance(),\n\t\t\tinstanceMap.get(postProcessTestInstanceKey).getInnermostInstance());\n\t\tassertSame(testInstances.getInnermostInstance(),\n\t\t\tinstanceMap.get(preDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(testClass);\n\t\tassertThat(lifecyclesMap.get(testClass).stream()).allMatch(Lifecycle.PER_METHOD::equals);\n\t}\n\n\t@Test\n\tvoid instancePerClass() {\n\t\tinstancePerClass(InstancePerClassTestCase.class, instanceCounts(entry(InstancePerClassTestCase.class, 1)));\n\t}\n\n\t@Test\n\tvoid instancePerClassWithInheritedLifecycleMode() {\n\t\tinstancePerClass(SubInstancePerClassTestCase.class,\n\t\t\tinstanceCounts(entry(SubInstancePerClassTestCase.class, 1)));\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\tprivate void instancePerClass(Class<?> testClass, Map.Entry<Class<?>, Integer>[] instances) {\n\t\tint containers = 3;\n\t\tint tests = 3;\n\t\tint allMethods = 2;\n\t\tint eachMethods = 3;\n\n\t\tperformAssertions(testClass, containers, tests, instances, allMethods, eachMethods);\n\n\t\tString containerExecutionConditionKey = executionConditionKey(testClass, null);\n\t\tString testTemplateKey = testTemplateKey(testClass, \"singletonTest\");\n\t\tString postProcessTestInstanceKey = postProcessTestInstanceKey(testClass);\n\t\tString preDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(testClass);\n\t\tString beforeAllCallbackKey = beforeAllCallbackKey(testClass);\n\t\tString afterAllCallbackKey = afterAllCallbackKey(testClass);\n\t\tString testExecutionConditionKey1 = executionConditionKey(testClass, testsInvoked.getFirst());\n\t\tString beforeEachCallbackKey1 = beforeEachCallbackKey(testClass, testsInvoked.get(0));\n\t\tString afterEachCallbackKey1 = afterEachCallbackKey(testClass, testsInvoked.get(0));\n\t\tString testExecutionConditionKey2 = executionConditionKey(testClass, testsInvoked.get(1));\n\t\tString beforeEachCallbackKey2 = beforeEachCallbackKey(testClass, testsInvoked.get(1));\n\t\tString afterEachCallbackKey2 = afterEachCallbackKey(testClass, testsInvoked.get(1));\n\t\tString testExecutionConditionKey3 = executionConditionKey(testClass, testsInvoked.get(2));\n\t\tString beforeEachCallbackKey3 = beforeEachCallbackKey(testClass, testsInvoked.get(2));\n\t\tString afterEachCallbackKey3 = afterEachCallbackKey(testClass, testsInvoked.get(2));\n\n\t\t// @formatter:off\n\t\tassertThat(instanceMap.keySet()).containsExactlyInAnyOrder(\n\t\t\t\tpostProcessTestInstanceKey,\n\t\t\t\tpreDestroyCallbackTestInstanceKey,\n\t\t\t\tcontainerExecutionConditionKey,\n\t\t\t\tbeforeAllCallbackKey,\n\t\t\t\ttestTemplateKey,\n\t\t\t\ttestExecutionConditionKey1,\n\t\t\t\tbeforeEachCallbackKey1,\n\t\t\t\tafterEachCallbackKey1,\n\t\t\t\ttestExecutionConditionKey2,\n\t\t\t\tbeforeEachCallbackKey2,\n\t\t\t\tafterEachCallbackKey2,\n\t\t\t\ttestExecutionConditionKey3,\n\t\t\t\tbeforeEachCallbackKey3,\n\t\t\t\tafterEachCallbackKey3,\n\t\t\t\tafterAllCallbackKey\n\t\t);\n\t\t// @formatter:on\n\n\t\tTestInstances testInstances = instanceMap.get(beforeAllCallbackKey);\n\t\tassertNotNull(testInstances.getInnermostInstance());\n\t\tassertSame(testInstances, instanceMap.get(afterAllCallbackKey));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey1));\n\t\tassertSame(testInstances, instanceMap.get(beforeEachCallbackKey1));\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey1));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey2));\n\t\tassertSame(testInstances, instanceMap.get(beforeEachCallbackKey2));\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey2));\n\t\tassertSame(testInstances, instanceMap.get(testExecutionConditionKey3));\n\t\tassertSame(testInstances, instanceMap.get(beforeEachCallbackKey3));\n\t\tassertSame(testInstances, instanceMap.get(afterEachCallbackKey3));\n\t\tassertSame(testInstances.getInnermostInstance(),\n\t\t\tinstanceMap.get(postProcessTestInstanceKey).getInnermostInstance());\n\t\tassertSame(testInstances.getInnermostInstance(),\n\t\t\tinstanceMap.get(preDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tassertNull(instanceMap.get(containerExecutionConditionKey));\n\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(testClass);\n\t\tassertThat(lifecyclesMap.get(testClass).stream()).allMatch(Lifecycle.PER_CLASS::equals);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@Test\n\tvoid instancePerMethodWithNestedTestClass() {\n\t\tClass<?> testClass = InstancePerMethodOuterTestCase.class;\n\t\tClass<?> nestedTestClass = InstancePerMethodOuterTestCase.NestedInstancePerMethodTestCase.class;\n\t\tint containers = 4;\n\t\tint tests = 4;\n\t\tMap.Entry<Class<?>, Integer>[] instances = instanceCounts(entry(testClass, 4), entry(nestedTestClass, 3));\n\t\tint allMethods = 1;\n\t\tint eachMethods = 3;\n\n\t\tperformAssertions(testClass, containers, tests, instances, allMethods, eachMethods);\n\n\t\tString containerExecutionConditionKey = executionConditionKey(testClass, null);\n\t\tString nestedContainerExecutionConditionKey = executionConditionKey(nestedTestClass, null);\n\t\tString nestedTestTemplateKey = testTemplateKey(nestedTestClass, \"singletonTest\");\n\t\tString postProcessTestInstanceKey = postProcessTestInstanceKey(testClass);\n\t\tString nestedPostProcessTestInstanceKey = postProcessTestInstanceKey(nestedTestClass);\n\t\tString preDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(testClass);\n\t\tString nestedPreDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(nestedTestClass);\n\t\tString beforeAllCallbackKey = beforeAllCallbackKey(testClass);\n\t\tString afterAllCallbackKey = afterAllCallbackKey(testClass);\n\t\tString outerTestExecutionConditionKey = executionConditionKey(testClass, \"outerTest\");\n\t\tString beforeEachCallbackKey = beforeEachCallbackKey(testClass, \"outerTest\");\n\t\tString afterEachCallbackKey = afterEachCallbackKey(testClass, \"outerTest\");\n\t\tString nestedBeforeAllCallbackKey = beforeAllCallbackKey(nestedTestClass);\n\t\tString nestedAfterAllCallbackKey = afterAllCallbackKey(nestedTestClass);\n\t\tString nestedExecutionConditionKey1 = executionConditionKey(nestedTestClass, testsInvoked.getFirst());\n\t\tString nestedBeforeEachCallbackKey1 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedAfterEachCallbackKey1 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedExecutionConditionKey2 = executionConditionKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedBeforeEachCallbackKey2 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedAfterEachCallbackKey2 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedExecutionConditionKey3 = executionConditionKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedBeforeEachCallbackKey3 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedAfterEachCallbackKey3 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\n\t\t// @formatter:off\n\t\tassertThat(instanceMap.keySet()).containsExactlyInAnyOrder(\n\t\t\t\tcontainerExecutionConditionKey,\n\t\t\t\tnestedTestTemplateKey,\n\t\t\t\tnestedContainerExecutionConditionKey,\n\t\t\t\tpostProcessTestInstanceKey,\n\t\t\t\tnestedPostProcessTestInstanceKey,\n\t\t\t\tpreDestroyCallbackTestInstanceKey,\n\t\t\t\tnestedPreDestroyCallbackTestInstanceKey,\n\t\t\t\tbeforeAllCallbackKey,\n\t\t\t\tafterAllCallbackKey,\n\t\t\t\touterTestExecutionConditionKey,\n\t\t\t\tbeforeEachCallbackKey,\n\t\t\t\tafterEachCallbackKey,\n\t\t\t\tnestedBeforeAllCallbackKey,\n\t\t\t\tnestedAfterAllCallbackKey,\n\t\t\t\tnestedExecutionConditionKey1,\n\t\t\t\tnestedBeforeEachCallbackKey1,\n\t\t\t\tnestedAfterEachCallbackKey1,\n\t\t\t\tnestedExecutionConditionKey2,\n\t\t\t\tnestedBeforeEachCallbackKey2,\n\t\t\t\tnestedAfterEachCallbackKey2,\n\t\t\t\tnestedExecutionConditionKey3,\n\t\t\t\tnestedBeforeEachCallbackKey3,\n\t\t\t\tnestedAfterEachCallbackKey3\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertNull(instanceMap.get(containerExecutionConditionKey));\n\t\tassertNull(instanceMap.get(beforeAllCallbackKey));\n\t\tassertNull(instanceMap.get(afterAllCallbackKey));\n\t\tassertNull(instanceMap.get(nestedContainerExecutionConditionKey));\n\t\tassertNull(instanceMap.get(nestedBeforeAllCallbackKey));\n\t\tassertNull(instanceMap.get(nestedAfterAllCallbackKey));\n\n\t\tTestInstances outerInstances = instanceMap.get(beforeEachCallbackKey);\n\t\tassertNotNull(outerInstances.getInnermostInstance());\n\t\tassertSame(outerInstances, instanceMap.get(afterEachCallbackKey));\n\t\tassertSame(outerInstances, instanceMap.get(outerTestExecutionConditionKey));\n\n\t\tTestInstances nestedInstances1 = instanceMap.get(nestedBeforeEachCallbackKey1);\n\t\tassertNotNull(nestedInstances1.getInnermostInstance());\n\t\tassertNotSame(outerInstances.getInnermostInstance(), nestedInstances1.getInnermostInstance());\n\t\tassertSame(nestedInstances1, instanceMap.get(nestedAfterEachCallbackKey1));\n\t\tassertSame(nestedInstances1, instanceMap.get(nestedExecutionConditionKey1));\n\n\t\tTestInstances nestedInstances2 = instanceMap.get(nestedBeforeEachCallbackKey2);\n\t\tassertNotNull(nestedInstances2.getInnermostInstance());\n\t\tassertNotSame(outerInstances.getInnermostInstance(), nestedInstances2.getInnermostInstance());\n\t\tassertNotSame(nestedInstances1.getInnermostInstance(), nestedInstances2.getInnermostInstance());\n\t\tassertSame(nestedInstances2, instanceMap.get(nestedAfterEachCallbackKey2));\n\t\tassertSame(nestedInstances2, instanceMap.get(nestedExecutionConditionKey2));\n\n\t\tTestInstances nestedInstances3 = instanceMap.get(nestedPostProcessTestInstanceKey);\n\t\tassertNotNull(nestedInstances3.getInnermostInstance());\n\t\tassertNotSame(outerInstances.getInnermostInstance(), nestedInstances3.getInnermostInstance());\n\t\tassertNotSame(nestedInstances1.getInnermostInstance(), nestedInstances3.getInnermostInstance());\n\t\tassertSame(nestedInstances3.getInnermostInstance(),\n\t\t\tinstanceMap.get(nestedAfterEachCallbackKey3).getInnermostInstance());\n\t\tassertSame(nestedInstances3.getInnermostInstance(),\n\t\t\tinstanceMap.get(nestedExecutionConditionKey3).getInnermostInstance());\n\t\tassertSame(nestedInstances3.getInnermostInstance(),\n\t\t\tinstanceMap.get(nestedPreDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tObject outerInstance1 = instanceMap.get(nestedExecutionConditionKey1).findInstance(testClass).get();\n\t\tObject outerInstance2 = instanceMap.get(nestedExecutionConditionKey2).findInstance(testClass).get();\n\t\tObject outerInstance3 = instanceMap.get(nestedExecutionConditionKey3).findInstance(testClass).get();\n\t\tassertNotSame(outerInstance1, outerInstance2);\n\t\tassertNotSame(outerInstance1, outerInstance3);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey1).getAllInstances()).containsExactly(outerInstance1,\n\t\t\tnestedInstances1.getInnermostInstance());\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey2).getAllInstances()).containsExactly(outerInstance2,\n\t\t\tnestedInstances2.getInnermostInstance());\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey3).getAllInstances()).containsExactly(outerInstance3,\n\t\t\tnestedInstances3.getInnermostInstance());\n\n\t\t// The last tracked instance stored under postProcessTestInstanceKey\n\t\t// is only created in order to instantiate the nested test class for\n\t\t// test2().\n\t\tassertSame(outerInstance3, instanceMap.get(postProcessTestInstanceKey).getInnermostInstance());\n\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(testClass, nestedTestClass);\n\t\tassertThat(lifecyclesMap.get(testClass).stream()).allMatch(Lifecycle.PER_METHOD::equals);\n\t\tassertThat(lifecyclesMap.get(nestedTestClass).stream()).allMatch(Lifecycle.PER_METHOD::equals);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@Test\n\tvoid instancePerClassWithNestedTestClass() {\n\t\tClass<?> testClass = InstancePerClassOuterTestCase.class;\n\t\tClass<?> nestedTestClass = InstancePerClassOuterTestCase.NestedInstancePerClassTestCase.class;\n\t\tint containers = 4;\n\t\tint tests = 4;\n\t\tMap.Entry<Class<?>, Integer>[] instances = instanceCounts(entry(testClass, 1), entry(nestedTestClass, 1));\n\t\tint allMethods = 2;\n\t\tint eachMethods = 3;\n\n\t\tperformAssertions(testClass, containers, tests, instances, allMethods, eachMethods);\n\n\t\tString containerExecutionConditionKey = executionConditionKey(testClass, null);\n\t\tString nestedContainerExecutionConditionKey = executionConditionKey(nestedTestClass, null);\n\t\tString nestedTestTemplateKey = testTemplateKey(nestedTestClass, \"singletonTest\");\n\t\tString postProcessTestInstanceKey = postProcessTestInstanceKey(testClass);\n\t\tString nestedPostProcessTestInstanceKey = postProcessTestInstanceKey(nestedTestClass);\n\t\tString preDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(testClass);\n\t\tString nestedPreDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(nestedTestClass);\n\t\tString beforeAllCallbackKey = beforeAllCallbackKey(testClass);\n\t\tString afterAllCallbackKey = afterAllCallbackKey(testClass);\n\t\tString outerTestExecutionConditionKey = executionConditionKey(testClass, \"outerTest\");\n\t\tString beforeEachCallbackKey = beforeEachCallbackKey(testClass, \"outerTest\");\n\t\tString afterEachCallbackKey = afterEachCallbackKey(testClass, \"outerTest\");\n\t\tString nestedBeforeAllCallbackKey = beforeAllCallbackKey(nestedTestClass);\n\t\tString nestedAfterAllCallbackKey = afterAllCallbackKey(nestedTestClass);\n\t\tString nestedExecutionConditionKey1 = executionConditionKey(nestedTestClass, testsInvoked.getFirst());\n\t\tString nestedBeforeEachCallbackKey1 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedAfterEachCallbackKey1 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedExecutionConditionKey2 = executionConditionKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedBeforeEachCallbackKey2 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedAfterEachCallbackKey2 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedExecutionConditionKey3 = executionConditionKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedBeforeEachCallbackKey3 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedAfterEachCallbackKey3 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\n\t\t// @formatter:off\n\t\tassertThat(instanceMap.keySet()).containsExactlyInAnyOrder(\n\t\t\t\tcontainerExecutionConditionKey,\n\t\t\t\tnestedTestTemplateKey,\n\t\t\t\tnestedContainerExecutionConditionKey,\n\t\t\t\tpostProcessTestInstanceKey,\n\t\t\t\tnestedPostProcessTestInstanceKey,\n\t\t\t\tpreDestroyCallbackTestInstanceKey,\n\t\t\t\tnestedPreDestroyCallbackTestInstanceKey,\n\t\t\t\tbeforeAllCallbackKey,\n\t\t\t\tafterAllCallbackKey,\n\t\t\t\touterTestExecutionConditionKey,\n\t\t\t\tbeforeEachCallbackKey,\n\t\t\t\tafterEachCallbackKey,\n\t\t\t\tnestedBeforeAllCallbackKey,\n\t\t\t\tnestedAfterAllCallbackKey,\n\t\t\t\tnestedExecutionConditionKey1,\n\t\t\t\tnestedBeforeEachCallbackKey1,\n\t\t\t\tnestedAfterEachCallbackKey1,\n\t\t\t\tnestedExecutionConditionKey2,\n\t\t\t\tnestedBeforeEachCallbackKey2,\n\t\t\t\tnestedAfterEachCallbackKey2,\n\t\t\t\tnestedExecutionConditionKey3,\n\t\t\t\tnestedBeforeEachCallbackKey3,\n\t\t\t\tnestedAfterEachCallbackKey3\n\t\t);\n\t\t// @formatter:on\n\n\t\tObject instance = instanceMap.get(postProcessTestInstanceKey).getInnermostInstance();\n\t\tassertNotNull(instance);\n\t\tassertNull(instanceMap.get(containerExecutionConditionKey));\n\t\tassertSame(instance, instanceMap.get(beforeAllCallbackKey).getInnermostInstance());\n\t\tassertSame(instance, instanceMap.get(afterAllCallbackKey).getInnermostInstance());\n\t\tassertSame(instance, instanceMap.get(outerTestExecutionConditionKey).getInnermostInstance());\n\t\tassertSame(instance, instanceMap.get(beforeEachCallbackKey).getInnermostInstance());\n\t\tassertSame(instance, instanceMap.get(afterEachCallbackKey).getInnermostInstance());\n\t\tassertSame(instance, instanceMap.get(preDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tObject nestedInstance = instanceMap.get(nestedPostProcessTestInstanceKey).getInnermostInstance();\n\t\tassertNotNull(nestedInstance);\n\t\tassertNotSame(instance, nestedInstance);\n\t\tassertNull(instanceMap.get(nestedContainerExecutionConditionKey));\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeAllCallbackKey).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterAllCallbackKey).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedPreDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tObject outerInstance = instanceMap.get(nestedExecutionConditionKey1).findInstance(testClass).get();\n\t\tassertSame(outerInstance, instance);\n\t\tassertSame(outerInstance, instanceMap.get(postProcessTestInstanceKey).getInnermostInstance());\n\t\tassertSame(outerInstance, instanceMap.get(preDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(testClass, nestedTestClass);\n\t\tassertThat(lifecyclesMap.get(testClass).stream()).allMatch(Lifecycle.PER_CLASS::equals);\n\t\tassertThat(lifecyclesMap.get(nestedTestClass).stream()).allMatch(Lifecycle.PER_CLASS::equals);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@Test\n\tvoid instancePerMethodOnOuterTestClassWithInstancePerClassOnNestedTestClass() {\n\t\tClass<?> testClass = MixedLifecyclesOuterTestCase.class;\n\t\tClass<?> nestedTestClass = MixedLifecyclesOuterTestCase.NestedInstancePerClassTestCase.class;\n\t\tint containers = 4;\n\t\tint tests = 4;\n\t\tMap.Entry<Class<?>, Integer>[] instances = instanceCounts(entry(testClass, 2), entry(nestedTestClass, 1));\n\t\tint allMethods = 1;\n\t\tint eachMethods = 7;\n\n\t\tperformAssertions(testClass, containers, tests, instances, allMethods, eachMethods);\n\n\t\tString containerExecutionConditionKey = executionConditionKey(testClass, null);\n\t\tString nestedContainerExecutionConditionKey = executionConditionKey(nestedTestClass, null);\n\t\tString nestedTestTemplateKey = testTemplateKey(nestedTestClass, \"singletonTest\");\n\t\tString postProcessTestInstanceKey = postProcessTestInstanceKey(testClass);\n\t\tString nestedPostProcessTestInstanceKey = postProcessTestInstanceKey(nestedTestClass);\n\t\tString preDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(testClass);\n\t\tString nestedPreDestroyCallbackTestInstanceKey = preDestroyCallbackTestInstanceKey(nestedTestClass);\n\t\tString beforeAllCallbackKey = beforeAllCallbackKey(testClass);\n\t\tString afterAllCallbackKey = afterAllCallbackKey(testClass);\n\t\tString outerTestExecutionConditionKey = executionConditionKey(testClass, \"outerTest\");\n\t\tString beforeEachCallbackKey = beforeEachCallbackKey(testClass, \"outerTest\");\n\t\tString afterEachCallbackKey = afterEachCallbackKey(testClass, \"outerTest\");\n\t\tString nestedBeforeAllCallbackKey = beforeAllCallbackKey(nestedTestClass);\n\t\tString nestedAfterAllCallbackKey = afterAllCallbackKey(nestedTestClass);\n\t\tString nestedExecutionConditionKey1 = executionConditionKey(nestedTestClass, testsInvoked.getFirst());\n\t\tString nestedBeforeEachCallbackKey1 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedAfterEachCallbackKey1 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(0));\n\t\tString nestedExecutionConditionKey2 = executionConditionKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedBeforeEachCallbackKey2 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedAfterEachCallbackKey2 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(1));\n\t\tString nestedExecutionConditionKey3 = executionConditionKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedBeforeEachCallbackKey3 = beforeEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\t\tString nestedAfterEachCallbackKey3 = afterEachCallbackKey(nestedTestClass, testsInvoked.get(2));\n\n\t\t// @formatter:off\n\t\tassertThat(instanceMap.keySet()).containsExactlyInAnyOrder(\n\t\t\t\tcontainerExecutionConditionKey,\n\t\t\t\tnestedTestTemplateKey,\n\t\t\t\tnestedContainerExecutionConditionKey,\n\t\t\t\tpostProcessTestInstanceKey,\n\t\t\t\tnestedPostProcessTestInstanceKey,\n\t\t\t\tpreDestroyCallbackTestInstanceKey,\n\t\t\t\tnestedPreDestroyCallbackTestInstanceKey,\n\t\t\t\tbeforeAllCallbackKey,\n\t\t\t\tafterAllCallbackKey,\n\t\t\t\touterTestExecutionConditionKey,\n\t\t\t\tbeforeEachCallbackKey,\n\t\t\t\tafterEachCallbackKey,\n\t\t\t\tnestedBeforeAllCallbackKey,\n\t\t\t\tnestedAfterAllCallbackKey,\n\t\t\t\tnestedExecutionConditionKey1,\n\t\t\t\tnestedBeforeEachCallbackKey1,\n\t\t\t\tnestedAfterEachCallbackKey1,\n\t\t\t\tnestedExecutionConditionKey2,\n\t\t\t\tnestedBeforeEachCallbackKey2,\n\t\t\t\tnestedAfterEachCallbackKey2,\n\t\t\t\tnestedExecutionConditionKey3,\n\t\t\t\tnestedBeforeEachCallbackKey3,\n\t\t\t\tnestedAfterEachCallbackKey3\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertNull(instanceMap.get(containerExecutionConditionKey));\n\t\tassertNull(instanceMap.get(beforeAllCallbackKey));\n\t\tassertNull(instanceMap.get(afterAllCallbackKey));\n\n\t\tTestInstances outerInstances = instanceMap.get(beforeEachCallbackKey);\n\t\tassertSame(outerInstances, instanceMap.get(afterEachCallbackKey));\n\t\tassertSame(outerInstances, instanceMap.get(outerTestExecutionConditionKey));\n\n\t\tObject nestedInstance = instanceMap.get(nestedPostProcessTestInstanceKey).getInnermostInstance();\n\t\tassertNotNull(nestedInstance);\n\t\tassertNotSame(outerInstances.getInnermostInstance(), nestedInstance);\n\t\tassertNull(instanceMap.get(nestedContainerExecutionConditionKey));\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeAllCallbackKey).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterAllCallbackKey).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey1).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey2).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedExecutionConditionKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedBeforeEachCallbackKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedAfterEachCallbackKey3).getInnermostInstance());\n\t\tassertSame(nestedInstance, instanceMap.get(nestedPreDestroyCallbackTestInstanceKey).getInnermostInstance());\n\n\t\t// The last tracked instance stored under postProcessTestInstanceKey\n\t\t// is only created in order to instantiate the nested test class.\n\t\tObject outerInstance = instanceMap.get(nestedExecutionConditionKey1).findInstance(testClass).get();\n\t\tassertEquals(outerInstances.getInnermostInstance().getClass(), outerInstance.getClass());\n\t\tassertNotSame(outerInstances.getInnermostInstance(), outerInstance);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey1).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey2).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedExecutionConditionKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedBeforeEachCallbackKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\t\tassertThat(instanceMap.get(nestedAfterEachCallbackKey3).getAllInstances()).containsExactly(outerInstance,\n\t\t\tnestedInstance);\n\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(testClass, nestedTestClass);\n\t\tassertThat(lifecyclesMap.get(testClass).stream()).allMatch(Lifecycle.PER_METHOD::equals);\n\t\tassertThat(lifecyclesMap.get(nestedTestClass).stream()).allMatch(Lifecycle.PER_CLASS::equals);\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(Lifecycle.class)\n\tvoid classTemplate(Lifecycle lifecycle) {\n\t\tvar classTemplate = ClassTemplateWithDefaultLifecycleTestCase.class;\n\n\t\tvar results = executeTests(r -> r //\n\t\t\t\t.selectors(selectClass(classTemplate)) //\n\t\t\t\t.configurationParameter(Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, lifecycle.name()));\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.failed(0));\n\t\tresults.testEvents().assertStatistics(stats -> stats.succeeded(4));\n\n\t\tassertThat(instanceCount).containsExactly(entry(classTemplate, lifecycle == Lifecycle.PER_CLASS ? 1 : 4));\n\t\tassertThat(lifecyclesMap.keySet()).containsExactly(classTemplate);\n\t\tassertThat(lifecyclesMap.get(classTemplate)).filteredOn(Objects::nonNull).containsOnly(lifecycle);\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(Lifecycle.class)\n\tvoid classTemplateWithNestedClass(Lifecycle lifecycle) {\n\t\tvar classTemplate = ClassTemplateWithDefaultLifecycleAndNestedClassTestCase.class;\n\t\tvar nestedClass = ClassTemplateWithDefaultLifecycleAndNestedClassTestCase.InnerTestCase.class;\n\n\t\tvar results = executeTests(r -> r //\n\t\t\t\t.selectors(selectClass(classTemplate)) //\n\t\t\t\t.configurationParameter(Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, lifecycle.name()));\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.failed(0));\n\t\tresults.testEvents().assertStatistics(stats -> stats.succeeded(4));\n\n\t\tassertThat(instanceCount).containsExactly( //\n\t\t\tentry(classTemplate, lifecycle == Lifecycle.PER_CLASS ? 1 : 4), //\n\t\t\tentry(nestedClass, lifecycle == Lifecycle.PER_CLASS ? 2 : 4));\n\t\tassertThat(lifecyclesMap.keySet()).containsExactlyInAnyOrder(classTemplate, nestedClass);\n\t\tassertThat(lifecyclesMap.get(classTemplate)).filteredOn(Objects::nonNull).containsOnly(lifecycle);\n\t\tassertThat(lifecyclesMap.get(nestedClass)).filteredOn(Objects::nonNull).containsOnly(lifecycle);\n\t}\n\n\tprivate void performAssertions(Class<?> testClass, int numContainers, int numTests,\n\t\t\tMap.Entry<Class<?>, Integer>[] instanceCountEntries, int allMethods, int eachMethods) {\n\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\texecutionResults.containerEvents().assertStatistics(\n\t\t\tstats -> stats.started(numContainers).finished(numContainers));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(numTests).finished(numTests));\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertThat(instanceCount).describedAs(\"instance count\").contains(instanceCountEntries),\n\t\t\t() -> assertEquals(allMethods, beforeAllCount, \"@BeforeAll count\"),\n\t\t\t() -> assertEquals(allMethods, afterAllCount, \"@AfterAll count\"),\n\t\t\t() -> assertEquals(eachMethods, beforeEachCount, \"@BeforeEach count\"),\n\t\t\t() -> assertEquals(eachMethods, afterEachCount, \"@AfterEach count\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate Map.Entry<Class<?>, Integer>[] instanceCounts(Map.Entry<Class<?>, Integer>... entries) {\n\t\treturn entries;\n\t}\n\n\tprivate static void incrementInstanceCount(Class<?> testClass) {\n\t\tinstanceCount.compute(testClass, (key, value) -> value == null ? 1 : value + 1);\n\t}\n\n\tprivate static String executionConditionKey(Class<?> testClass, @Nullable String testMethod) {\n\t\treturn concat(ExecutionCondition.class, testClass, testMethod);\n\t}\n\n\tprivate static String postProcessTestInstanceKey(Class<?> testClass) {\n\t\treturn concat(TestInstancePostProcessor.class, testClass);\n\t}\n\n\tprivate static String preDestroyCallbackTestInstanceKey(Class<?> testClass) {\n\t\treturn concat(TestInstancePreDestroyCallback.class, testClass);\n\t}\n\n\tprivate static String beforeAllCallbackKey(Class<?> testClass) {\n\t\treturn concat(BeforeAllCallback.class, testClass);\n\t}\n\n\tprivate static String afterAllCallbackKey(Class<?> testClass) {\n\t\treturn concat(AfterAllCallback.class, testClass);\n\t}\n\n\tprivate static String beforeEachCallbackKey(Class<?> testClass, String testMethod) {\n\t\treturn concat(BeforeEachCallback.class, testClass, testMethod);\n\t}\n\n\tprivate static String afterEachCallbackKey(Class<?> testClass, String testMethod) {\n\t\treturn concat(AfterEachCallback.class, testClass, testMethod);\n\t}\n\n\tprivate static String testTemplateKey(Class<?> testClass, String testMethod) {\n\t\treturn concat(TestTemplateInvocationContextProvider.class, testClass, testMethod);\n\t}\n\n\tprivate static String concat(Class<?> c1, Class<?> c2, @Nullable String str) {\n\t\treturn concat(c1.getSimpleName(), c2.getSimpleName(), str);\n\t}\n\n\tprivate static String concat(Class<?> c1, Class<?> c2) {\n\t\treturn concat(c1.getSimpleName(), c2.getSimpleName());\n\t}\n\n\tprivate static String concat(@Nullable String... args) {\n\t\treturn join(\".\", args);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith(InstanceTrackingExtension.class)\n\t// The following is commented out b/c it's the default.\n\t// @TestInstance(Lifecycle.PER_METHOD)\n\tstatic class InstancePerMethodTestCase {\n\n\t\tInstancePerMethodTestCase() {\n\t\t\tincrementInstanceCount(InstancePerMethodTestCase.class);\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAllStatic(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tbeforeAllCount++;\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tbeforeEachCount++;\n\t\t}\n\n\t\t@Test\n\t\tvoid test1(TestInfo testInfo) {\n\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid test2(TestInfo testInfo) {\n\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t}\n\n\t\t@SingletonTest\n\t\tvoid singletonTest(TestInfo testInfo) {\n\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tafterEachCount++;\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAllStatic(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tafterAllCount++;\n\t\t}\n\n\t}\n\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tstatic class InstancePerClassTestCase extends InstancePerMethodTestCase {\n\n\t\tInstancePerClassTestCase() {\n\t\t\tincrementInstanceCount(InstancePerClassTestCase.class);\n\t\t}\n\n\t\t@BeforeAll\n\t\tvoid beforeAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tbeforeAllCount++;\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tafterAllCount++;\n\t\t}\n\n\t}\n\n\tstatic class SubInstancePerClassTestCase extends InstancePerClassTestCase {\n\t\tSubInstancePerClassTestCase() {\n\t\t\tincrementInstanceCount(SubInstancePerClassTestCase.class);\n\t\t}\n\t}\n\n\t@ExtendWith(InstanceTrackingExtension.class)\n\t// The following is commented out b/c it's the default.\n\t// @TestInstance(Lifecycle.PER_METHOD)\n\tstatic class InstancePerMethodOuterTestCase {\n\n\t\tInstancePerMethodOuterTestCase() {\n\t\t\tincrementInstanceCount(InstancePerMethodOuterTestCase.class);\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tbeforeAllCount++;\n\t\t}\n\n\t\t@SuppressWarnings(\"NullAway\")\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tafterAllCount++;\n\t\t}\n\n\t\t@Nested\n\t\t// The following is commented out b/c it's the default.\n\t\t// @TestInstance(Lifecycle.PER_METHOD)\n\t\tclass NestedInstancePerMethodTestCase {\n\n\t\t\tNestedInstancePerMethodTestCase() {\n\t\t\t\tincrementInstanceCount(NestedInstancePerMethodTestCase.class);\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t\tbeforeEachCount++;\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test1(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test2(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@SingletonTest\n\t\t\tvoid singletonTest(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEach() {\n\t\t\t\tafterEachCount++;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(InstanceTrackingExtension.class)\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tstatic class InstancePerClassOuterTestCase {\n\n\t\tInstancePerClassOuterTestCase() {\n\t\t\tincrementInstanceCount(InstancePerClassOuterTestCase.class);\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tbeforeAllCount++;\n\t\t}\n\n\t\t@SuppressWarnings(\"NullAway\")\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t\tafterAllCount++;\n\t\t}\n\n\t\t@Nested\n\t\t@TestInstance(Lifecycle.PER_CLASS)\n\t\tclass NestedInstancePerClassTestCase {\n\n\t\t\tNestedInstancePerClassTestCase() {\n\t\t\t\tincrementInstanceCount(NestedInstancePerClassTestCase.class);\n\t\t\t}\n\n\t\t\t@BeforeAll\n\t\t\tvoid beforeAll(TestInfo testInfo) {\n\t\t\t\tassertNotNull(testInfo);\n\t\t\t\tbeforeAllCount++;\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t\tbeforeEachCount++;\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test1(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test2(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@SingletonTest\n\t\t\tvoid singletonTest(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEach() {\n\t\t\t\tafterEachCount++;\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\tvoid afterAll(TestInfo testInfo) {\n\t\t\t\tassertNotNull(testInfo);\n\t\t\t\tafterAllCount++;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(InstanceTrackingExtension.class)\n\t// The following is commented out b/c it's the default.\n\t// @TestInstance(Lifecycle.PER_METHOD)\n\tstatic class MixedLifecyclesOuterTestCase {\n\n\t\tMixedLifecyclesOuterTestCase() {\n\t\t\tincrementInstanceCount(MixedLifecyclesOuterTestCase.class);\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tbeforeEachCount++;\n\t\t}\n\n\t\t@SuppressWarnings(\"NullAway\")\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tafterEachCount++;\n\t\t}\n\n\t\t@Nested\n\t\t@TestInstance(Lifecycle.PER_CLASS)\n\t\tclass NestedInstancePerClassTestCase {\n\n\t\t\tNestedInstancePerClassTestCase() {\n\t\t\t\tincrementInstanceCount(NestedInstancePerClassTestCase.class);\n\t\t\t}\n\n\t\t\t@BeforeAll\n\t\t\tvoid beforeAll(TestInfo testInfo) {\n\t\t\t\tassertNotNull(testInfo);\n\t\t\t\tbeforeAllCount++;\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach() {\n\t\t\t\tbeforeEachCount++;\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test1(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@Test\n\t\t\tvoid test2(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"NullAway\")\n\t\t\t@SingletonTest\n\t\t\tvoid singletonTest(TestInfo testInfo) {\n\t\t\t\tassertSame(this, instanceMap.get(postProcessTestInstanceKey(getClass())).getInnermostInstance());\n\t\t\t\ttestsInvoked.add(testInfo.getTestMethod().get().getName());\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEach() {\n\t\t\t\tafterEachCount++;\n\t\t\t}\n\n\t\t\t@AfterAll\n\t\t\tvoid afterAll(TestInfo testInfo) {\n\t\t\t\tassertNotNull(testInfo);\n\t\t\t\tafterAllCount++;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Intentionally not implementing BeforeTestExecutionCallback, AfterTestExecutionCallback,\n\t// and TestExecutionExceptionHandler, since they are analogous to BeforeEachCallback and\n\t// AfterEachCallback with regard to instance scope and Lifecycle.\n\tstatic class InstanceTrackingExtension\n\t\t\timplements ExecutionCondition, TestInstancePostProcessor, TestInstancePreDestroyCallback, BeforeAllCallback,\n\t\t\tAfterAllCallback, BeforeEachCallback, AfterEachCallback, TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tString testMethod = context.getTestMethod().map(Method::getName).orElse(null);\n\t\t\tif (testMethod == null) {\n\t\t\t\tassertThat(context.getTestInstance()).isNotPresent();\n\t\t\t\tif (!isAnnotated(context.getRequiredTestClass().getEnclosingClass(), ClassTemplate.class)) {\n\t\t\t\t\tassertThat(instanceCount.getOrDefault(context.getRequiredTestClass(), 0)).isEqualTo(0);\n\t\t\t\t}\n\t\t\t}\n\t\t\tinstanceMap.put(executionConditionKey(context.getRequiredTestClass(), testMethod),\n\t\t\t\tcontext.getTestInstances().orElse(null));\n\n\t\t\treturn ConditionEvaluationResult.enabled(\"enigma\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tassertThat(context.getTestInstance()).isNotPresent();\n\t\t\tassertNotNull(testInstance);\n\t\t\tinstanceMap.put(postProcessTestInstanceKey(testInstance.getClass()), DefaultTestInstances.of(testInstance));\n\t\t}\n\n\t\t@Override\n\t\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tassertThat(context.getTestInstance()).isPresent();\n\t\t\tinstanceMap.put(preDestroyCallbackTestInstanceKey(context.getRequiredTestClass()),\n\t\t\t\tDefaultTestInstances.of(context.getTestInstance().get()));\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tinstanceMap.put(beforeAllCallbackKey(context.getRequiredTestClass()),\n\t\t\t\tcontext.getTestInstances().orElse(null));\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tinstanceMap.put(afterAllCallbackKey(context.getRequiredTestClass()),\n\t\t\t\tcontext.getTestInstances().orElse(null));\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tinstanceMap.put(\n\t\t\t\tbeforeEachCallbackKey(context.getRequiredTestClass(), context.getRequiredTestMethod().getName()),\n\t\t\t\tcontext.getRequiredTestInstances());\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tinstanceMap.put(\n\t\t\t\tafterEachCallbackKey(context.getRequiredTestClass(), context.getRequiredTestMethod().getName()),\n\t\t\t\tcontext.getRequiredTestInstances());\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\treturn isAnnotated(context.getTestMethod(), SingletonTest.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\ttrackLifecycle(context);\n\t\t\tinstanceMap.put(testTemplateKey(context.getRequiredTestClass(), context.getRequiredTestMethod().getName()),\n\t\t\t\tcontext.getTestInstances().orElse(null));\n\n\t\t\treturn Stream.of(new TestTemplateInvocationContext() {\n\t\t\t});\n\t\t}\n\n\t\tprivate static void trackLifecycle(ExtensionContext context) {\n\t\t\tassertThat(context.getRoot().getTestInstanceLifecycle()).isEmpty();\n\t\t\tlifecyclesMap.computeIfAbsent(context.getRequiredTestClass(), clazz -> new ArrayList<>()).add(\n\t\t\t\tcontext.getTestInstanceLifecycle().orElse(null));\n\t\t}\n\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@TestTemplate\n\t@interface SingletonTest {\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(Twice.class)\n\t@ExtendWith(InstanceTrackingExtension.class)\n\tstatic class ClassTemplateWithDefaultLifecycleTestCase {\n\n\t\tClassTemplateWithDefaultLifecycleTestCase() {\n\t\t\tincrementInstanceCount(ClassTemplateWithDefaultLifecycleTestCase.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n\n\t@ClassTemplate\n\t@ExtendWith(Twice.class)\n\t@ExtendWith(InstanceTrackingExtension.class)\n\tstatic class ClassTemplateWithDefaultLifecycleAndNestedClassTestCase {\n\n\t\tClassTemplateWithDefaultLifecycleAndNestedClassTestCase() {\n\t\t\tincrementInstanceCount(ClassTemplateWithDefaultLifecycleAndNestedClassTestCase.class);\n\t\t}\n\n\t\t@Nested\n\t\tclass InnerTestCase {\n\n\t\t\tInnerTestCase() {\n\t\t\t\tincrementInstanceCount(InnerTestCase.class);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test1() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test2() {\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class Twice implements ClassTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(new Ctx(), new Ctx());\n\t\t}\n\n\t\tprivate record Ctx() implements ClassTemplateInvocationContext {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestMethodOverridingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\n\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.engine.subpackage.SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * @since 5.14.1\n */\nclass TestMethodOverridingTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid bothPackagePrivateTestMethodsAreDiscovered() throws Exception {\n\t\tvar results = discoverTestsForClass(DuplicateTestMethodDueToPackagePrivateVisibilityTestCase.class);\n\t\tvar classDescriptor = getOnlyElement(results.getEngineDescriptor().getChildren());\n\n\t\tvar parentUniqueId = classDescriptor.getUniqueId();\n\t\tvar inheritedMethodUniqueId = parentUniqueId.append(\"method\",\n\t\t\t\"org.junit.jupiter.engine.subpackage.SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase#\"\n\t\t\t\t\t+ \"test(org.junit.jupiter.api.TestInfo, org.junit.jupiter.api.TestReporter)\");\n\t\tvar declaredMethodUniqueId = parentUniqueId.append(\"method\",\n\t\t\t\"test(org.junit.jupiter.api.TestInfo, org.junit.jupiter.api.TestReporter)\");\n\n\t\tvar inheritedMethod = SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.class.getDeclaredMethod(\n\t\t\t\"test\", TestInfo.class, TestReporter.class);\n\t\tvar declaredMethod = DuplicateTestMethodDueToPackagePrivateVisibilityTestCase.class.getDeclaredMethod(\"test\",\n\t\t\tTestInfo.class, TestReporter.class);\n\n\t\tassertThat(classDescriptor.getChildren()) //\n\t\t\t\t.hasSize(2) //\n\t\t\t\t.extracting(TestDescriptor::getUniqueId, TestMethodOverridingTests::getSourceMethod) //\n\t\t\t\t.containsExactly(tuple(inheritedMethodUniqueId, inheritedMethod),\n\t\t\t\t\ttuple(declaredMethodUniqueId, declaredMethod));\n\n\t\tresults = discoverTests(selectUniqueId(inheritedMethodUniqueId));\n\t\tclassDescriptor = getOnlyElement(results.getEngineDescriptor().getChildren());\n\t\tassertThat(classDescriptor.getChildren()) //\n\t\t\t\t.extracting(TestDescriptor::getUniqueId, TestMethodOverridingTests::getSourceMethod) //\n\t\t\t\t.containsExactly(tuple(inheritedMethodUniqueId, inheritedMethod));\n\n\t\tresults = discoverTests(selectUniqueId(declaredMethodUniqueId));\n\t\tclassDescriptor = getOnlyElement(results.getEngineDescriptor().getChildren());\n\t\tassertThat(classDescriptor.getChildren()) //\n\t\t\t\t.extracting(TestDescriptor::getUniqueId, TestMethodOverridingTests::getSourceMethod) //\n\t\t\t\t.containsExactly(tuple(declaredMethodUniqueId, declaredMethod));\n\t}\n\n\t@Test\n\tvoid bothPackagePrivateTestMethodsAreExecuted() throws Exception {\n\t\tvar results = executeTestsForClass(DuplicateTestMethodDueToPackagePrivateVisibilityTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\tassertThat(allReportEntries(results)) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\tMap.of(\"invokedSuper\",\n\t\t\t\t\t\tSuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.class.getDeclaredMethod(\n\t\t\t\t\t\t\t\"test\", TestInfo.class, TestReporter.class).toGenericString()),\n\t\t\t\t\tMap.of(\"invokedChild\",\n\t\t\t\t\t\tDuplicateTestMethodDueToPackagePrivateVisibilityTestCase.class.getDeclaredMethod(\"test\",\n\t\t\t\t\t\t\tTestInfo.class, TestReporter.class).toGenericString()));\n\t}\n\n\tprivate static Method getSourceMethod(TestDescriptor it) {\n\t\treturn ((MethodSource) it.getSource().orElseThrow()).getJavaMethod();\n\t}\n\n\tprivate static Stream<Map<String, String>> allReportEntries(EngineExecutionResults results) {\n\t\treturn results.allEvents().reportingEntryPublished() //\n\t\t\t\t.map(event -> event.getRequiredPayload(ReportEntry.class)) //\n\t\t\t\t.map(ReportEntry::getKeyValuePairs);\n\t}\n\n\tstatic class DuplicateTestMethodDueToPackagePrivateVisibilityTestCase\n\t\t\textends SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase {\n\n\t\t// @Override\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, TestReporter reporter) {\n\t\t\treporter.publishEntry(\"invokedChild\", testInfo.getTestMethod().orElseThrow().toGenericString());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TestTemplateInvocationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.Events;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * @since 5.0\n */\nclass TestTemplateInvocationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid templateWithSingleRegisteredExtensionIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithSingleRegisteredExtension\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithSingleRegisteredExtension\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(container(\"templateWithSingleRegisteredExtension\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid parentRelationshipIsEstablished() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithSingleRegisteredExtension\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tTestDescriptor templateMethodDescriptor = findTestDescriptor(executionResults,\n\t\t\tcontainer(\"templateWithSingleRegisteredExtension\"));\n\t\tTestDescriptor invocationDescriptor = findTestDescriptor(executionResults, test(\"test-template-invocation:#1\"));\n\t\tassertThat(invocationDescriptor.getParent()).hasValue(templateMethodDescriptor);\n\t}\n\n\t@Test\n\tvoid beforeAndAfterEachMethodsAreExecutedAroundInvocation() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(TestTemplateTestClassWithBeforeAndAfterEach.class, \"testTemplateWithTwoInvocations\")).build();\n\n\t\texecuteTests(request);\n\n\t\tassertThat(TestTemplateTestClassWithBeforeAndAfterEach.lifecycleEvents).containsExactly(\n\t\t\t\"beforeAll:TestTemplateInvocationTests$TestTemplateTestClassWithBeforeAndAfterEach\", \"beforeEach:[1]\",\n\t\t\t\"afterEach:[1]\", \"beforeEach:[2]\", \"afterEach:[2]\",\n\t\t\t\"afterAll:TestTemplateInvocationTests$TestTemplateTestClassWithBeforeAndAfterEach\");\n\t}\n\n\t@Test\n\tvoid templateWithTwoRegisteredExtensionsIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithTwoRegisteredExtensions\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithTwoRegisteredExtensions\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\"), displayName(\"[1]\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\"), displayName(\"[2]\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(container(\"templateWithTwoRegisteredExtensions\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid legacyReportingNames() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithTwoRegisteredExtensions\")).build();\n\n\t\tEngineExecutionResults results = executeTests(request);\n\t\tEvents events = results.allEvents();\n\n\t\tevents.assertStatistics(stats -> stats.dynamicallyRegistered(2));\n\t\t//  events.dynamicallyRegistered().debug();\n\t\t//  results.testEvents().dynamicallyRegistered().debug();\n\t\t//  results.containerEvents().dynamicallyRegistered().debug();\n\n\t\t// @formatter:off\n\t\tStream<String> legacyReportingNames = events.dynamicallyRegistered()\n\t\t\t\t.map(Event::getTestDescriptor)\n\t\t\t\t.map(TestDescriptor::getLegacyReportingName);\n\t\t// @formatter:off\n\t\tassertThat(legacyReportingNames).containsExactly(\"templateWithTwoRegisteredExtensions()[1]\",\n\t\t\t\t\t\t\"templateWithTwoRegisteredExtensions()[2]\");\n\t}\n\n\t@Test\n\tvoid templateWithTwoInvocationsFromSingleExtensionIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithTwoInvocationsFromSingleExtension\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithTwoInvocationsFromSingleExtension\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\"), displayName(\"[1]\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\"), displayName(\"[2]\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(container(\"templateWithTwoInvocationsFromSingleExtension\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid singleInvocationIsExecutedWhenDiscoveredByUniqueId() {\n\t\tUniqueId uniqueId = discoverUniqueId(MyTestTemplateTestCase.class,\n\t\t\t\"templateWithTwoInvocationsFromSingleExtension\") //\n\t\t\t\t\t.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#2\");\n\n\t\tEngineExecutionResults executionResults = executeTests(selectUniqueId(uniqueId));\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithTwoInvocationsFromSingleExtension\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\"), displayName(\"[2]\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(container(\"templateWithTwoInvocationsFromSingleExtension\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid templateWithDisabledInvocationsIsSkipped() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithDisabledInvocations\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithDisabledInvocations\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), skippedWithReason(\"always disabled\")), //\n\t\t\t\tevent(container(\"templateWithDisabledInvocations\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid disabledTemplateIsSkipped() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"disabledTemplate\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"disabledTemplate\"), skippedWithReason(\"always disabled\"))));\n\t}\n\n\t@Test\n\tvoid templateWithCustomizedDisplayNamesIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithCustomizedDisplayNames\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithCustomizedDisplayNames\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\"),\n\t\t\t\t\tdisplayName(\"1 --> templateWithCustomizedDisplayNames()\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"),\n\t\t\t\t\tfinishedWithFailure(message(\"invocation is expected to fail\"))), //\n\t\t\t\tevent(container(\"templateWithCustomizedDisplayNames\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid templateWithDynamicParameterResolverIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(MyTestTemplateTestCase.class,\n\t\t\t\"templateWithDynamicParameterResolver\", \"java.lang.String\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithDynamicParameterResolver\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\"), displayName(\"[1] foo\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedWithFailure(message(\"foo\"))), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\"), displayName(\"[2] bar\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"bar\"))), //\n\t\t\t\tevent(container(\"templateWithDynamicParameterResolver\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid contextParameterResolverCanResolveConstructorArguments() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCaseWithConstructor.class, \"template\", \"java.lang.String\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"template\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\"), displayName(\"[1] foo\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedSuccessfully()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\"), displayName(\"[2] bar\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedSuccessfully()), //\n\t\t\t\tevent(container(\"template\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid templateWithDynamicTestInstancePostProcessorIsInvoked() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithDynamicTestInstancePostProcessor\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithDynamicTestInstancePostProcessor\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedWithFailure(message(\"foo\"))), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#2\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"bar\"))), //\n\t\t\t\tevent(container(\"templateWithDynamicTestInstancePostProcessor\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid lifecycleCallbacksAreExecutedForInvocation() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectClass(TestTemplateTestClassWithDynamicLifecycleCallbacks.class)).build();\n\n\t\texecuteTests(request);\n\n\t\t// @formatter:off\n\t\tassertThat(TestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents).containsExactly(\n\t\t\t\"beforeEach\",\n\t\t\t\t\"beforeTestExecution\",\n\t\t\t\t\t\"testTemplate:foo\",\n\t\t\t\t\t\"handleTestExecutionException\",\n\t\t\t\t\"afterTestExecution\",\n\t\t\t\"afterEach\",\n\t\t\t\"beforeEach\",\n\t\t\t\t\"beforeTestExecution\",\n\t\t\t\t\t\"testTemplate:bar\",\n\t\t\t\t\"afterTestExecution\",\n\t\t\t\"afterEach\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid extensionIsAskedForSupportBeforeItMustProvide() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithWrongParameterType\", int.class.getName())).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithWrongParameterType\"), started()), //\n\t\t\t\tevent(container(\"templateWithWrongParameterType\"), finishedWithFailure(message(s -> s.startsWith(\n\t\t\t\t\t\"You must register at least one TestTemplateInvocationContextProvider that supports @TestTemplate method [\"))))));\n\t}\n\n\t@Test\n\tvoid templateWithSupportingProviderButNoInvocationsReportsFailure() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithSupportingProviderButNoInvocations\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithSupportingProviderButNoInvocations\"), started()), //\n\t\t\t\tevent(container(\"templateWithSupportingProviderButNoInvocations\"),\n\t\t\t\t\tfinishedWithFailure(message(\n\t\t\t\t\t\t\"Provider [%s] did not provide any invocation contexts, but was expected to do so. \".formatted(\n\t\t\t\t\t\t\tInvocationContextProviderThatSupportsEverythingButProvidesNothing.class.getSimpleName())\n\t\t\t\t\t\t\t\t+ \"You may override mayReturnZeroTestTemplateInvocationContexts() to allow this.\")))));\n\t}\n\n\t@Test\n\tvoid templateWithSupportingProviderAllowingNoInvocationsDoesNotFail() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithSupportingProviderAllowingNoInvocations\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class,\n\t\t\t\tevent(container(\"templateWithSupportingProviderAllowingNoInvocations\"), started()),\n\t\t\t\tevent(container(\"templateWithSupportingProviderAllowingNoInvocations\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid templateWithMixedProvidersNoInvocationReportsFailure() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(MyTestTemplateTestCase.class,\n\t\t\t\"templateWithMultipleProvidersAllowingAndRestrictingToProvideNothing\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithMultipleProvidersAllowingAndRestrictingToProvideNothing\"), started()), //\n\t\t\t\tevent(container(\"templateWithMultipleProvidersAllowingAndRestrictingToProvideNothing\"),\n\t\t\t\t\tfinishedWithFailure(message(\n\t\t\t\t\t\t\"Provider [%s] did not provide any invocation contexts, but was expected to do so. \".formatted(\n\t\t\t\t\t\t\tInvocationContextProviderThatSupportsEverythingButProvidesNothing.class.getSimpleName())\n\t\t\t\t\t\t\t\t+ \"You may override mayReturnZeroTestTemplateInvocationContexts() to allow this.\")))));\n\t}\n\n\t@Test\n\tvoid templateWithCloseableStream() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectMethod(MyTestTemplateTestCase.class, \"templateWithCloseableStream\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertThat(InvocationContextProviderWithCloseableStream.streamClosed.get()).describedAs(\n\t\t\t\"streamClosed\").isTrue();\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\twrappedInContainerEvents(MyTestTemplateTestCase.class, //\n\t\t\t\tevent(container(\"templateWithCloseableStream\"), started()), //\n\t\t\t\tevent(dynamicTestRegistered(\"test-template-invocation:#1\")), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), started()), //\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedSuccessfully()), //\n\t\t\t\tevent(container(\"templateWithCloseableStream\"), finishedSuccessfully())));\n\t}\n\n\t@Test\n\tvoid templateWithPreparations() {\n\t\tvar results = executeTestsForClass(TestTemplateWithPreparationsTestCase.class);\n\n\t\tassertTrue(CustomCloseableResource.closed, \"resource in store was closed\");\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t}\n\n\tprivate TestDescriptor findTestDescriptor(EngineExecutionResults executionResults, Condition<Event> condition) {\n\t\t// @formatter:off\n\t\treturn executionResults.allEvents()\n\t\t\t\t.filter(condition::matches)\n\t\t\t\t.findAny()\n\t\t\t\t.map(Event::getTestDescriptor)\n\t\t\t\t.orElseThrow(() -> new AssertionFailedError(\"Could not find event for condition: \" + condition));\n\t\t// @formatter:on\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings({ \"unchecked\", \"varargs\", \"rawtypes\" })\n\tprivate final Condition<? super Event>[] wrappedInContainerEvents(Class<MyTestTemplateTestCase> clazz,\n\t\t\tCondition<? super Event>... wrappedConditions) {\n\n\t\tList<Condition<? super Event>> conditions = new ArrayList<>();\n\t\tconditions.add(event(engine(), started()));\n\t\tconditions.add(event(container(clazz), started()));\n\t\tCollections.addAll(conditions, wrappedConditions);\n\t\tconditions.add(event(container(clazz), finishedSuccessfully()));\n\t\tconditions.add(event(engine(), finishedSuccessfully()));\n\t\treturn conditions.toArray(new Condition[0]);\n\t}\n\n\tstatic class MyTestTemplateTestCase {\n\n\t\t@TestTemplate\n\t\tvoid templateWithoutRegisteredExtension() {\n\t\t}\n\n\t\t@ExtendWith(SingleInvocationContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid templateWithSingleRegisteredExtension() {\n\t\t\tfail(\"invocation is expected to fail\");\n\t\t}\n\n\t\t@ExtendWith({ SingleInvocationContextProvider.class,\n\t\t\t\tAnotherInvocationContextProviderWithASingleInvocation.class })\n\t\t@TestTemplate\n\t\tvoid templateWithTwoRegisteredExtensions() {\n\t\t\tfail(\"invocation is expected to fail\");\n\t\t}\n\n\t\t@ExtendWith(TwoInvocationsContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid templateWithTwoInvocationsFromSingleExtension() {\n\t\t\tfail(\"invocation is expected to fail\");\n\t\t}\n\n\t\t@ExtendWith({ SingleInvocationContextProviderWithDisabledInvocations.class })\n\t\t@TestTemplate\n\t\tvoid templateWithDisabledInvocations() {\n\t\t\tfail(\"this is never called\");\n\t\t}\n\n\t\t@ExtendWith(AlwaysDisabledExecutionCondition.class)\n\t\t@TestTemplate\n\t\tvoid disabledTemplate() {\n\t\t\tfail(\"this is never called\");\n\t\t}\n\n\t\t@ExtendWith(InvocationContextProviderWithCustomizedDisplayNames.class)\n\t\t@TestTemplate\n\t\tvoid templateWithCustomizedDisplayNames() {\n\t\t\tfail(\"invocation is expected to fail\");\n\t\t}\n\n\t\t@ExtendWith(StringParameterResolvingInvocationContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid templateWithDynamicParameterResolver(String parameter) {\n\t\t\tfail(parameter);\n\t\t}\n\n\t\t@ExtendWith(StringParameterResolvingInvocationContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid templateWithWrongParameterType(int parameter) {\n\t\t\tfail(\"never called: \" + parameter);\n\t\t}\n\n\t\t@Nullable\n\t\tString parameterInstanceVariable;\n\n\t\t@ExtendWith(StringParameterInjectingInvocationContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid templateWithDynamicTestInstancePostProcessor() {\n\t\t\tfail(parameterInstanceVariable);\n\t\t}\n\n\t\t@ExtendWith(InvocationContextProviderThatSupportsEverythingButProvidesNothing.class)\n\t\t@TestTemplate\n\t\tvoid templateWithSupportingProviderButNoInvocations() {\n\t\t\tfail(\"never called\");\n\t\t}\n\n\t\t@ExtendWith(InvocationContextProviderThatSupportsEverythingAllowsProvideNothing.class)\n\t\t@TestTemplate\n\t\tvoid templateWithSupportingProviderAllowingNoInvocations() {\n\t\t\tfail(\"never called\");\n\t\t}\n\n\t\t@ExtendWith(InvocationContextProviderThatSupportsEverythingButProvidesNothing.class)\n\t\t@ExtendWith(InvocationContextProviderThatSupportsEverythingAllowsProvideNothing.class)\n\t\t@TestTemplate\n\t\tvoid templateWithMultipleProvidersAllowingAndRestrictingToProvideNothing() {\n\t\t\tfail(\"never called\");\n\t\t}\n\n\t\t@ExtendWith(InvocationContextProviderWithCloseableStream.class)\n\t\t@TestTemplate\n\t\tvoid templateWithCloseableStream() {\n\t\t}\n\t}\n\n\t@ExtendWith(StringParameterResolvingInvocationContextProvider.class)\n\tstatic class MyTestTemplateTestCaseWithConstructor {\n\n\t\tprivate final String constructorParameter;\n\n\t\tMyTestTemplateTestCaseWithConstructor(String constructorParameter) {\n\t\t\tthis.constructorParameter = constructorParameter;\n\t\t}\n\n\t\t@TestTemplate\n\t\tvoid template(String parameter) {\n\t\t\tassertEquals(constructorParameter, parameter);\n\t\t}\n\t}\n\n\tstatic class TestTemplateTestClassWithBeforeAndAfterEach {\n\n\t\tprivate static final List<String> lifecycleEvents = new ArrayList<>();\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"beforeAll:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"afterAll:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"beforeEach:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"afterEach:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@ExtendWith(TwoInvocationsContextProvider.class)\n\t\t@TestTemplate\n\t\tvoid testTemplateWithTwoInvocations() {\n\t\t\tfail(\"invocation is expected to fail\");\n\t\t}\n\t}\n\n\tstatic class TestTemplateTestClassWithDynamicLifecycleCallbacks {\n\n\t\tprivate static List<String> lifecycleEvents = new ArrayList<>();\n\n\t\t@ExtendWith(InvocationContextProviderWithDynamicLifecycleCallbacks.class)\n\t\t@TestTemplate\n\t\tvoid testTemplate(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"testTemplate:\" + testInfo.getDisplayName());\n\t\t\tassertEquals(\"bar\", testInfo.getDisplayName());\n\t\t}\n\t}\n\n\tprivate static class SingleInvocationContextProvider implements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(emptyTestTemplateInvocationContext());\n\t\t}\n\t}\n\n\tprivate static class SingleInvocationContextProviderWithDisabledInvocations\n\t\t\textends SingleInvocationContextProvider {\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(new TestTemplateInvocationContext() {\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of(new AlwaysDisabledExecutionCondition());\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static class AnotherInvocationContextProviderWithASingleInvocation\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(emptyTestTemplateInvocationContext());\n\t\t}\n\t}\n\n\tprivate static class TwoInvocationsContextProvider implements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(emptyTestTemplateInvocationContext(), emptyTestTemplateInvocationContext());\n\t\t}\n\t}\n\n\tprivate static class AlwaysDisabledExecutionCondition implements ExecutionCondition {\n\n\t\t@Override\n\t\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\t\treturn ConditionEvaluationResult.disabled(\"always disabled\");\n\t\t}\n\t}\n\n\tprivate static class InvocationContextProviderWithCustomizedDisplayNames\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.<TestTemplateInvocationContext> generate(() -> new TestTemplateInvocationContext() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn invocationIndex + \" --> \" + context.getDisplayName();\n\t\t\t\t}\n\t\t\t}).limit(1);\n\t\t}\n\t}\n\n\tprivate static class StringParameterResolvingInvocationContextProvider\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\t// @formatter:off\n\t\t\treturn context.getTestMethod()\n\t\t\t\t.map(Method::getParameterTypes)\n\t\t\t\t.map(Arrays::stream)\n\t\t\t\t.map(parameters -> parameters.anyMatch(Predicate.isEqual(String.class)))\n\t\t\t\t.orElse(false);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(createContext(\"foo\"), createContext(\"bar\"));\n\t\t}\n\n\t\tprivate TestTemplateInvocationContext createContext(String argument) {\n\t\t\treturn new TestTemplateInvocationContext() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn TestTemplateInvocationContext.super.getDisplayName(invocationIndex) + \" \" + argument;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of(new ParameterResolver() {\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext,\n\t\t\t\t\t\t\t\tExtensionContext extensionContext) throws ParameterResolutionException {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic Object resolveParameter(ParameterContext parameterContext,\n\t\t\t\t\t\t\t\tExtensionContext extensionContext) throws ParameterResolutionException {\n\t\t\t\t\t\t\treturn argument;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate static class StringParameterInjectingInvocationContextProvider\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(createContext(\"foo\"), createContext(\"bar\"));\n\t\t}\n\n\t\tprivate TestTemplateInvocationContext createContext(String argument) {\n\t\t\treturn new TestTemplateInvocationContext() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn TestTemplateInvocationContext.super.getDisplayName(invocationIndex) + \" \" + argument;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of((TestInstancePostProcessor) (testInstance, context) -> {\n\t\t\t\t\t\tField field = testInstance.getClass().getDeclaredField(\"parameterInstanceVariable\");\n\t\t\t\t\t\tfield.setAccessible(true);\n\t\t\t\t\t\tfield.set(testInstance, argument);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate static class InvocationContextProviderWithDynamicLifecycleCallbacks\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(createContext(\"foo\"), createContext(\"bar\"));\n\t\t}\n\n\t\tprivate TestTemplateInvocationContext createContext(String argument) {\n\t\t\treturn new TestTemplateInvocationContext() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName(int invocationIndex) {\n\t\t\t\t\treturn argument;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic List<Extension> getAdditionalExtensions() {\n\t\t\t\t\treturn List.of(new LifecycleCallbackExtension());\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tprivate static class LifecycleCallbackExtension implements BeforeEachCallback, BeforeTestExecutionCallback,\n\t\t\t\tTestExecutionExceptionHandler, AfterTestExecutionCallback, AfterEachCallback {\n\n\t\t\t@Override\n\t\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\t\tTestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add(\"beforeEach\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\t\tTestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add(\"beforeTestExecution\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\t\tTestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add(\"handleTestExecutionException\");\n\t\t\t\tthrow new AssertionError(throwable);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\t\tTestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add(\"afterTestExecution\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\t\tTestTemplateTestClassWithDynamicLifecycleCallbacks.lifecycleEvents.add(\"afterEach\");\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class InvocationContextProviderThatSupportsEverythingButProvidesNothing\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tprivate static class InvocationContextProviderThatSupportsEverythingAllowsProvideNothing\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext extensionContext) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tprivate static class InvocationContextProviderWithCloseableStream implements TestTemplateInvocationContextProvider {\n\n\t\tprivate static AtomicBoolean streamClosed = new AtomicBoolean(false);\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(emptyTestTemplateInvocationContext()).onClose(() -> streamClosed.set(true));\n\t\t}\n\t}\n\n\tprivate static TestTemplateInvocationContext emptyTestTemplateInvocationContext() {\n\t\treturn new TestTemplateInvocationContext() {\n\t\t};\n\t}\n\n\tstatic class TestTemplateWithPreparationsTestCase {\n\n\t\t@TestTemplate\n\t\t@ExtendWith({ PreparingTestTemplateInvocationContextProvider.class, CompanionExtension.class })\n\t\tvoid test(CustomCloseableResource resource) {\n\t\t\tassertNotNull(resource);\n\t\t\tassertFalse(CustomCloseableResource.closed, \"should not be closed yet\");\n\t\t}\n\n\t}\n\n\tprivate static class PreparingTestTemplateInvocationContextProvider\n\t\t\timplements TestTemplateInvocationContextProvider {\n\n\t\tstatic final Namespace NAMESPACE = Namespace.create(PreparingTestTemplateInvocationContextProvider.class);\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(new PreparingTestTemplateInvocationContext());\n\t\t}\n\n\t}\n\n\tprivate static class PreparingTestTemplateInvocationContext implements TestTemplateInvocationContext {\n\n\t\t@Override\n\t\tpublic void prepareInvocation(ExtensionContext context) {\n\t\t\tcontext.getStore(PreparingTestTemplateInvocationContextProvider.NAMESPACE) //\n\t\t\t\t\t.put(\"resource\", new CustomCloseableResource());\n\t\t}\n\n\t}\n\n\tprivate static class CompanionExtension implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn ExtensionContextScope.TEST_METHOD;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn CustomCloseableResource.class.equals(parameterContext.getParameter().getType());\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn extensionContext.getStore(PreparingTestTemplateInvocationContextProvider.NAMESPACE).get(\"resource\");\n\t\t}\n\n\t}\n\n\tprivate static class CustomCloseableResource implements AutoCloseable {\n\n\t\tstatic boolean closed;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tclosed = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TopLevelComposedNested.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Nested;\n\n@Nested\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\npublic @interface TopLevelComposedNested {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/TopLevelNestedTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n@Nested\npublic class TopLevelNestedTestCase {\n\n\t@Test\n\tvoid test() {\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNonGenericTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * @since 5.0\n */\n@ExtendWith(NumberResolver.class)\nabstract class AbstractNonGenericTests {\n\n\t@Test\n\tvoid mA() {\n\t\tBridgeMethodTests.sequence.add(\"mA()\");\n\t}\n\n\t@Test\n\tvoid test(Number value) {\n\t\tBridgeMethodTests.sequence.add(\"A.test(Number)\");\n\t\tAssertions.assertEquals(42, value);\n\t}\n\n\tstatic class B extends AbstractNonGenericTests {\n\n\t\t@Test\n\t\tvoid mB() {\n\t\t\tBridgeMethodTests.sequence.add(\"mB()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(Byte value) {\n\t\t\tBridgeMethodTests.sequence.add(\"B.test(Byte)\");\n\t\t\tAssertions.assertEquals(123, value.intValue());\n\t\t}\n\n\t}\n\n\tstatic class C extends B {\n\n\t\t@Test\n\t\tvoid mC() {\n\t\t\tBridgeMethodTests.sequence.add(\"mC()\");\n\t\t}\n\n\t\t@Override\n\t\t@Test\n\t\tvoid test(Byte value) {\n\t\t\tBridgeMethodTests.sequence.add(\"C.test(Byte)\");\n\t\t\tAssertions.assertEquals(123, value.intValue());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/AbstractNumberTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n/**\n * @since 5.0\n */\n@ExtendWith(NumberResolver.class)\nabstract class AbstractNumberTests<N extends Number> {\n\n\t@Test\n\tvoid test(N number) {\n\t\tBridgeMethodTests.sequence.add(\"test(N)\");\n\t\tassertNotNull(number);\n\t\tassertEquals(123, number.intValue());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/BridgeMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.bridge.NumberTestGroup.ByteTestCase;\nimport org.junit.jupiter.engine.bridge.NumberTestGroup.ShortTestCase;\n\n/**\n * @since 5.0\n */\nclass BridgeMethodTests extends AbstractJupiterTestEngineTests {\n\n\tstatic List<String> sequence = new ArrayList<>();\n\n\t@Test\n\tvoid childrenHaveBridgeMethods() throws Exception {\n\t\tassertFalse(ChildWithBridgeMethods.class.getMethod(\"anotherBeforeEach\").isBridge());\n\t\tassertFalse(ChildWithBridgeMethods.class.getMethod(\"anotherAfterEach\").isBridge());\n\t\tassertTrue(ChildWithBridgeMethods.class.getMethod(\"beforeEach\").isBridge());\n\t\tassertTrue(ChildWithBridgeMethods.class.getMethod(\"afterEach\").isBridge());\n\n\t\tassertTrue(ByteTestCase.class.getDeclaredMethod(\"test\", Number.class).isBridge());\n\t\tassertFalse(ByteTestCase.class.getDeclaredMethod(\"test\", Byte.class).isBridge());\n\n\t\tassertTrue(ShortTestCase.class.getDeclaredMethod(\"test\", Number.class).isBridge());\n\t\tassertFalse(ShortTestCase.class.getDeclaredMethod(\"test\", Short.class).isBridge());\n\t}\n\n\t@Test\n\tvoid childHasNoBridgeMethods() throws Exception {\n\t\tassertFalse(ChildWithoutBridgeMethods.class.getMethod(\"anotherBeforeEach\").isBridge());\n\t\tassertFalse(ChildWithoutBridgeMethods.class.getMethod(\"anotherAfterEach\").isBridge());\n\t\tassertFalse(ChildWithoutBridgeMethods.class.getMethod(\"beforeEach\").isBridge());\n\t\tassertFalse(ChildWithoutBridgeMethods.class.getMethod(\"afterEach\").isBridge());\n\t}\n\n\t@Test\n\tvoid compareMethodExecutionSequenceOrder() {\n\t\tString withoutBridgeMethods = execute(1, ChildWithoutBridgeMethods.class);\n\t\tString withBridgeMethods = execute(1, ChildWithBridgeMethods.class);\n\t\tassertEquals(withoutBridgeMethods, withBridgeMethods);\n\t}\n\n\t@TestFactory\n\tList<DynamicTest> ensureSingleTestMethodsExecute() {\n\t\treturn Arrays.asList( //\n\t\t\tdynamicTest(\"Byte\", //\n\t\t\t\t() -> assertEquals(\"[test(Byte) BEGIN, test(N), test(Byte) END, test(Long) BEGIN, test(Long) END]\", //\n\t\t\t\t\texecute(2, ByteTestCase.class))),\n\t\t\tdynamicTest(\"Short\", //\n\t\t\t\t() -> assertEquals(\"[test(Long) BEGIN, test(Long) END, test(Short) BEGIN, test(N), test(Short) END]\", //\n\t\t\t\t\texecute(2, ShortTestCase.class))));\n\t}\n\n\t@Test\n\tvoid inheritedNonGenericMethodsAreExecuted() {\n\t\tString b = execute(4, AbstractNonGenericTests.B.class);\n\t\tassertAll(\"Missing expected test(s) in sequence: \" + b, //\n\t\t\t() -> assertTrue(b.contains(\"A.test(Number)\")), //\n\t\t\t() -> assertTrue(b.contains(\"mA()\")), //\n\t\t\t() -> assertTrue(b.contains(\"mB()\")), //\n\t\t\t() -> assertTrue(b.contains(\"B.test(Byte)\")) //\n\t\t);\n\t\tString c = execute(5, AbstractNonGenericTests.C.class);\n\t\tassertAll(\"Missing expected test(s) in sequence: \" + c, //\n\t\t\t() -> assertTrue(c.contains(\"A.test(Number)\")), //\n\t\t\t() -> assertTrue(c.contains(\"mA()\")), //\n\t\t\t() -> assertTrue(c.contains(\"mB()\")), //\n\t\t\t() -> assertTrue(c.contains(\"mC()\")), //\n\t\t\t() -> assertTrue(c.contains(\"C.test(Byte)\")) //\n\t\t);\n\t}\n\n\tprivate String execute(int expectedTestFinishedCount, Class<?> testClass) {\n\t\tsequence.clear();\n\t\texecuteTestsForClass(testClass).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(expectedTestFinishedCount));\n\t\treturn sequence.toString();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithBridgeMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\n// modifier \"public\" is necessary for creating bridge methods by the compiler\npublic class ChildWithBridgeMethods extends PackagePrivateParent {\n\n\t@BeforeEach\n\tpublic void anotherBeforeEach() {\n\t\tBridgeMethodTests.sequence.add(\"child.anotherBeforeEach()\");\n\t}\n\n\t@Test\n\tvoid test() {\n\t\tBridgeMethodTests.sequence.add(\"child.test()\");\n\t}\n\n\t@AfterEach\n\tpublic void anotherAfterEach() {\n\t\tBridgeMethodTests.sequence.add(\"child.anotherAfterEach()\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/ChildWithoutBridgeMethods.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\n// modifier \"public\" is *not* present for not creating bridge methods by the compiler\nclass ChildWithoutBridgeMethods extends PackagePrivateParent {\n\n\t@BeforeEach\n\tpublic void anotherBeforeEach() {\n\t\tBridgeMethodTests.sequence.add(\"child.anotherBeforeEach()\");\n\t}\n\n\t@Test\n\tvoid test() {\n\t\tBridgeMethodTests.sequence.add(\"child.test()\");\n\t}\n\n\t@AfterEach\n\tpublic void anotherAfterEach() {\n\t\tBridgeMethodTests.sequence.add(\"child.anotherAfterEach()\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * @since 5.0\n */\nclass NumberResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\n\t\treturn Number.class.isAssignableFrom(parameterContext.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\tthrows ParameterResolutionException {\n\n\t\tClass<?> type = parameterContext.getParameter().getType();\n\t\tif (type == Number.class) {\n\t\t\treturn 42;\n\t\t}\n\t\ttry {\n\t\t\treturn type.getMethod(\"valueOf\", String.class).invoke(null, \"123\");\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"Could not resolve number type: \" + type, e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/NumberTestGroup.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\ninterface NumberTestGroup {\n\n\tclass ByteTestCase extends AbstractNumberTests<Byte> {\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test(Byte number) {\n\t\t\tBridgeMethodTests.sequence.add(\"test(Byte) BEGIN\");\n\t\t\tsuper.test(number);\n\t\t\tBridgeMethodTests.sequence.add(\"test(Byte) END\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(Long number) {\n\t\t\tBridgeMethodTests.sequence.add(\"test(Long) BEGIN\");\n\t\t\tassertNotNull(number);\n\t\t\tassertEquals(123, number.intValue());\n\t\t\tBridgeMethodTests.sequence.add(\"test(Long) END\");\n\t\t}\n\t}\n\n\tclass ShortTestCase extends AbstractNumberTests<Short> {\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test(Short number) {\n\t\t\tBridgeMethodTests.sequence.add(\"test(Short) BEGIN\");\n\t\t\tsuper.test(number);\n\t\t\tBridgeMethodTests.sequence.add(\"test(Short) END\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(Long number) {\n\t\t\tBridgeMethodTests.sequence.add(\"test(Long) BEGIN\");\n\t\t\tassertNotNull(number);\n\t\t\tassertEquals(123, number.intValue());\n\t\t\tBridgeMethodTests.sequence.add(\"test(Long) END\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/bridge/PackagePrivateParent.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.bridge;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\n\n/**\n * @since 5.0\n */\nclass PackagePrivateParent {\n\n\t@BeforeAll\n\tstatic void beforeAll() {\n\t\tBridgeMethodTests.sequence.add(\"static parent.beforeAll()\");\n\t}\n\n\t@AfterAll\n\tstatic void afterAll() {\n\t\tBridgeMethodTests.sequence.add(\"static parent.afterAll()\");\n\t}\n\n\t@BeforeEach\n\tpublic void beforeEach() {\n\t\tBridgeMethodTests.sequence.add(\"parent.beforeEach()\");\n\t}\n\n\t@AfterEach\n\tpublic void afterEach() {\n\t\tBridgeMethodTests.sequence.add(\"parent.afterEach()\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/config/CachingJupiterConfigurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.io.CleanupMode.NEVER;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.only;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\nimport java.util.function.Predicate;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.descriptor.CustomDisplayNameGenerator;\n\n/**\n * Unit tests for {@link CachingJupiterConfiguration}.\n */\nclass CachingJupiterConfigurationTests {\n\n\tprivate final JupiterConfiguration delegate = mock();\n\tprivate final JupiterConfiguration cache = new CachingJupiterConfiguration(delegate);\n\n\t@Test\n\tvoid cachesDefaultExecutionMode() {\n\t\twhen(delegate.getDefaultExecutionMode()).thenReturn(ExecutionMode.CONCURRENT);\n\n\t\tassertThat(cache.getDefaultExecutionMode()).isEqualTo(ExecutionMode.CONCURRENT);\n\t\tassertThat(cache.getDefaultExecutionMode()).isEqualTo(ExecutionMode.CONCURRENT);\n\n\t\tverify(delegate, only()).getDefaultExecutionMode();\n\t}\n\n\t@Test\n\tvoid cachesDefaultTestInstanceLifecycle() {\n\t\twhen(delegate.getDefaultTestInstanceLifecycle()).thenReturn(Lifecycle.PER_CLASS);\n\n\t\tassertThat(cache.getDefaultTestInstanceLifecycle()).isEqualTo(Lifecycle.PER_CLASS);\n\t\tassertThat(cache.getDefaultTestInstanceLifecycle()).isEqualTo(Lifecycle.PER_CLASS);\n\n\t\tverify(delegate, only()).getDefaultTestInstanceLifecycle();\n\t}\n\n\t@Test\n\tvoid cachesExecutionConditionFilter() {\n\t\tPredicate<ExecutionCondition> predicate = executionCondition -> true;\n\t\twhen(delegate.getExecutionConditionFilter()).thenReturn(predicate);\n\n\t\tassertThat(cache.getExecutionConditionFilter()).isSameAs(predicate);\n\t\tassertThat(cache.getExecutionConditionFilter()).isSameAs(predicate);\n\n\t\tverify(delegate, only()).getExecutionConditionFilter();\n\t}\n\n\t@Test\n\tvoid cachesExtensionAutoDetectionEnabled() {\n\t\twhen(delegate.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\n\t\tassertThat(cache.isExtensionAutoDetectionEnabled()).isTrue();\n\t\tassertThat(cache.isExtensionAutoDetectionEnabled()).isTrue();\n\n\t\tverify(delegate, only()).isExtensionAutoDetectionEnabled();\n\t}\n\n\t@Test\n\tvoid cachesParallelExecutionEnabled() {\n\t\twhen(delegate.isParallelExecutionEnabled()).thenReturn(true);\n\n\t\tassertThat(cache.isParallelExecutionEnabled()).isTrue();\n\t\tassertThat(cache.isParallelExecutionEnabled()).isTrue();\n\n\t\tverify(delegate, only()).isParallelExecutionEnabled();\n\t}\n\n\t@Test\n\tvoid cachesDefaultDisplayNameGenerator() {\n\t\tCustomDisplayNameGenerator customDisplayNameGenerator = new CustomDisplayNameGenerator();\n\t\twhen(delegate.getDefaultDisplayNameGenerator()).thenReturn(customDisplayNameGenerator);\n\n\t\t// call `cache.getDefaultDisplayNameGenerator()` twice to verify the delegate method is called only once.\n\t\tassertThat(cache.getDefaultDisplayNameGenerator()).isSameAs(customDisplayNameGenerator);\n\t\tassertThat(cache.getDefaultDisplayNameGenerator()).isSameAs(customDisplayNameGenerator);\n\n\t\tverify(delegate, only()).getDefaultDisplayNameGenerator();\n\t}\n\n\t@Test\n\tvoid cachesDefaultTestMethodOrderer() {\n\t\tfinal Optional<MethodOrderer> methodOrderer = Optional.of(new MethodOrderer.MethodName());\n\t\twhen(delegate.getDefaultTestMethodOrderer()).thenReturn(methodOrderer);\n\n\t\t// call `cache.getDefaultTestMethodOrderer()` twice to verify the delegate method is called only once.\n\t\tassertThat(cache.getDefaultTestMethodOrderer()).isSameAs(methodOrderer);\n\t\tassertThat(cache.getDefaultTestMethodOrderer()).isSameAs(methodOrderer);\n\n\t\tverify(delegate, only()).getDefaultTestMethodOrderer();\n\t}\n\n\t@Test\n\tvoid cachesDefaultTempDirCleanupMode() {\n\t\twhen(delegate.getDefaultTempDirCleanupMode()).thenReturn(NEVER);\n\n\t\t// call `cache.getDefaultTempStrategyDirCleanupMode()` twice to verify the delegate method is called only once.\n\t\tassertThat(cache.getDefaultTempDirCleanupMode()).isSameAs(NEVER);\n\t\tassertThat(cache.getDefaultTempDirCleanupMode()).isSameAs(NEVER);\n\n\t\tverify(delegate, only()).getDefaultTempDirCleanupMode();\n\t}\n\n\t@Test\n\tvoid cachesDefaultTempDirFactorySupplier() {\n\t\tSupplier<TempDirFactory> supplier = mock();\n\t\twhen(delegate.getDefaultTempDirFactorySupplier()).thenReturn(supplier);\n\n\t\t// call `cache.getDefaultTempDirFactorySupplier()` twice to verify the delegate method is called only once.\n\t\tassertThat(cache.getDefaultTempDirFactorySupplier()).isSameAs(supplier);\n\t\tassertThat(cache.getDefaultTempDirFactorySupplier()).isSameAs(supplier);\n\n\t\tverify(delegate, only()).getDefaultTempDirFactorySupplier();\n\t}\n\n\t@Test\n\tvoid doesNotCacheRawParameters() {\n\t\twhen(delegate.getRawConfigurationParameter(\"foo\")).thenReturn(Optional.of(\"bar\")).thenReturn(\n\t\t\tOptional.of(\"baz\"));\n\n\t\tassertThat(cache.getRawConfigurationParameter(\"foo\")).contains(\"bar\");\n\t\tassertThat(cache.getRawConfigurationParameter(\"foo\")).contains(\"baz\");\n\n\t\tverify(delegate, times(2)).getRawConfigurationParameter(\"foo\");\n\t\tverifyNoMoreInteractions(delegate);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/config/DefaultJupiterConfigurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.jupiter.api.io.CleanupMode.ALWAYS;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Supplier;\n\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.engine.descriptor.CustomDisplayNameGenerator;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.launcher.core.ConfigurationParametersFactoryForTests;\n\nclass DefaultJupiterConfigurationTests {\n\n\tprivate static final String KEY = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getDefaultTestInstanceLifecyclePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"ConfigurationParameters\",\n\t\t\t() -> new DefaultJupiterConfiguration(null, dummyOutputDirectoryCreator(), mock()));\n\t}\n\n\t@Test\n\tvoid getDefaultTestInstanceLifecycleWithNoConfigParamSet() {\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(configurationParameters(Map.of()),\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\t\tLifecycle lifecycle = configuration.getDefaultTestInstanceLifecycle();\n\t\tassertThat(lifecycle).isEqualTo(PER_METHOD);\n\t}\n\n\t@Test\n\tvoid getDefaultTempDirCleanupModeWithNoConfigParamSet() {\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(configurationParameters(Map.of()),\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\t\tCleanupMode cleanupMode = configuration.getDefaultTempDirCleanupMode();\n\t\tassertThat(cleanupMode).isEqualTo(ALWAYS);\n\t}\n\n\t@Test\n\tvoid getDefaultTestInstanceLifecycleWithConfigParamSet() {\n\t\tassertAll(//\n\t\t\t() -> assertDefaultConfigParam(null, PER_METHOD), //\n\t\t\t() -> assertThatThrownBy(() -> getDefaultTestInstanceLifecycleConfigParam(\"\")) //\n\t\t\t\t\t.hasMessage(\"Invalid test instance lifecycle mode '' set via the '%s' configuration parameter.\",\n\t\t\t\t\t\tDEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME), //\n\t\t\t() -> assertThatThrownBy(() -> getDefaultTestInstanceLifecycleConfigParam(\"bogus\")) //\n\t\t\t\t\t.hasMessage(\n\t\t\t\t\t\t\"Invalid test instance lifecycle mode 'BOGUS' set via the '%s' configuration parameter.\",\n\t\t\t\t\t\tDEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME), //\n\t\t\t() -> assertDefaultConfigParam(PER_METHOD.name(), PER_METHOD), //\n\t\t\t() -> assertDefaultConfigParam(PER_METHOD.name().toLowerCase(), PER_METHOD), //\n\t\t\t() -> assertDefaultConfigParam(\"  \" + PER_METHOD.name() + \"  \", PER_METHOD), //\n\t\t\t() -> assertDefaultConfigParam(PER_CLASS.name(), PER_CLASS), //\n\t\t\t() -> assertDefaultConfigParam(PER_CLASS.name().toLowerCase(), PER_CLASS), //\n\t\t\t() -> assertDefaultConfigParam(\"  \" + PER_CLASS.name() + \"  \", Lifecycle.PER_CLASS) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid shouldGetDefaultDisplayNameGeneratorWithConfigParamSet() {\n\t\tvar parameters = configurationParameters(\n\t\t\tMap.of(Constants.DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME, CustomDisplayNameGenerator.class.getName()));\n\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(parameters, dummyOutputDirectoryCreator(),\n\t\t\tmock());\n\n\t\tDisplayNameGenerator defaultDisplayNameGenerator = configuration.getDefaultDisplayNameGenerator();\n\n\t\tassertThat(defaultDisplayNameGenerator).isInstanceOf(CustomDisplayNameGenerator.class);\n\t}\n\n\t@Test\n\tvoid shouldGetStandardAsDefaultDisplayNameGeneratorWithoutConfigParamSet() {\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(configurationParameters(Map.of()),\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\n\t\tDisplayNameGenerator defaultDisplayNameGenerator = configuration.getDefaultDisplayNameGenerator();\n\n\t\tassertThat(defaultDisplayNameGenerator).isInstanceOf(DisplayNameGenerator.Standard.class);\n\t}\n\n\t@Test\n\tvoid shouldGetNothingAsDefaultTestMethodOrderWithoutConfigParamSet() {\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(configurationParameters(Map.of()),\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\n\t\tfinal Optional<MethodOrderer> defaultTestMethodOrder = configuration.getDefaultTestMethodOrderer();\n\n\t\tassertThat(defaultTestMethodOrder).isEmpty();\n\t}\n\n\t@Test\n\tvoid shouldGetDefaultTempDirFactorySupplierWithConfigParamSet() {\n\t\tvar parameters = configurationParameters(\n\t\t\tMap.of(Constants.DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME, CustomFactory.class.getName()));\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(parameters, dummyOutputDirectoryCreator(),\n\t\t\tmock());\n\n\t\tSupplier<TempDirFactory> supplier = configuration.getDefaultTempDirFactorySupplier();\n\n\t\tassertThat(supplier.get()).isInstanceOf(CustomFactory.class);\n\t}\n\n\t@Test\n\tvoid shouldGetStandardAsDefaultTempDirFactorySupplierWithoutConfigParamSet() {\n\t\tJupiterConfiguration configuration = new DefaultJupiterConfiguration(configurationParameters(Map.of()),\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\n\t\tSupplier<TempDirFactory> supplier = configuration.getDefaultTempDirFactorySupplier();\n\n\t\tassertThat(supplier.get()).isSameAs(TempDirFactory.Standard.INSTANCE);\n\t}\n\n\t@Test\n\tvoid doesNotReportAnyIssuesIfConfigurationParametersAreEmpty() {\n\t\tList<DiscoveryIssue> issues = new ArrayList<>();\n\n\t\tnew DefaultJupiterConfiguration(configurationParameters(Map.of()), dummyOutputDirectoryCreator(),\n\t\t\tDiscoveryIssueReporter.collecting(issues)).getDefaultTestInstanceLifecycle();\n\n\t\tassertThat(issues).isEmpty();\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(ParallelExecutorServiceType.class)\n\tvoid doesNotReportAnyIssuesIfParallelExecutionIsEnabledAndConfigurationParameterIsSet(\n\t\t\tParallelExecutorServiceType executorServiceType) {\n\t\tvar parameters = Map.of(Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, true, //\n\t\t\tConstants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME, executorServiceType);\n\t\tList<DiscoveryIssue> issues = new ArrayList<>();\n\n\t\tnew DefaultJupiterConfiguration(ConfigurationParametersFactoryForTests.create(parameters),\n\t\t\tdummyOutputDirectoryCreator(), DiscoveryIssueReporter.collecting(issues)).getDefaultTestInstanceLifecycle();\n\n\t\tassertThat(issues).isEmpty();\n\t}\n\n\t@Test\n\tvoid asksUsersToTryWorkerThreadPoolHierarchicalExecutorServiceIfParallelExecutionIsEnabled() {\n\t\tvar parameters = Map.of(Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, true);\n\t\tList<DiscoveryIssue> issues = new ArrayList<>();\n\n\t\tnew DefaultJupiterConfiguration(configurationParameters(parameters), dummyOutputDirectoryCreator(),\n\t\t\tDiscoveryIssueReporter.collecting(issues)).getDefaultTestInstanceLifecycle();\n\n\t\tassertThat(issues).containsExactly(DiscoveryIssue.create(Severity.INFO, \"\"\"\n\t\t\t\tParallel test execution is enabled but the default ForkJoinPool-based executor service will be used. \\\n\t\t\t\tPlease give the new implementation based on a regular thread pool a try by setting the \\\n\t\t\t\t'junit.jupiter.execution.parallel.config.executor-service' configuration parameter to \\\n\t\t\t\t'WORKER_THREAD_POOL' and report any issues to the JUnit team. Alternatively, set the configuration \\\n\t\t\t\tparameter to 'FORK_JOIN_POOL' to hide this message and keep using the original implementation.\"\"\"));\n\t}\n\n\tprivate void assertDefaultConfigParam(@Nullable String configValue, Lifecycle expected) {\n\t\tvar lifecycle = getDefaultTestInstanceLifecycleConfigParam(configValue);\n\t\tassertThat(lifecycle).isEqualTo(expected);\n\t}\n\n\tprivate static Lifecycle getDefaultTestInstanceLifecycleConfigParam(@Nullable String configValue) {\n\t\tvar configParams = configurationParameters(configValue == null ? Map.of() : Map.of(KEY, configValue));\n\t\treturn new DefaultJupiterConfiguration(configParams, dummyOutputDirectoryCreator(),\n\t\t\tmock()).getDefaultTestInstanceLifecycle();\n\t}\n\n\tprivate static ConfigurationParameters configurationParameters(Map<@NonNull String, ?> parameters) {\n\t\treturn ConfigurationParametersFactoryForTests.create(parameters);\n\t}\n\n\t@NullMarked\n\tprivate static class CustomFactory implements TempDirFactory {\n\n\t\t@Override\n\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/config/InstantiatingConfigurationParameterConverterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.config;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Constants.DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.engine.descriptor.CustomDisplayNameGenerator;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 5.5\n */\n@TrackLogRecords\nclass InstantiatingConfigurationParameterConverterTests {\n\n\tprivate static final String KEY = DEFAULT_DISPLAY_NAME_GENERATOR_PROPERTY_NAME;\n\n\t@Test\n\tvoid shouldInstantiateConfiguredClass(LogRecordListener listener) {\n\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.of(CustomDisplayNameGenerator.class.getName()));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tDisplayNameGenerator displayNameGenerator = converter.get(configurationParameters, KEY).orElseThrow();\n\n\t\tassertThat(displayNameGenerator).isInstanceOf(CustomDisplayNameGenerator.class);\n\t\tassertExpectedLogMessage(listener, Level.CONFIG,\n\t\t\t\"Using default display name generator \"\n\t\t\t\t\t+ \"'org.junit.jupiter.engine.descriptor.CustomDisplayNameGenerator' set via the \"\n\t\t\t\t\t+ \"'junit.jupiter.displayname.generator.default' configuration parameter.\");\n\t}\n\n\t@Test\n\tvoid shouldReturnEmptyOptionalIfNoConfigurationFound() {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.empty());\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tOptional<DisplayNameGenerator> displayNameGenerator = converter.get(configurationParameters, KEY);\n\n\t\tassertThat(displayNameGenerator).isEmpty();\n\t}\n\n\t@Test\n\tvoid shouldReturnEmptyOptionalIfConfigurationIsBlank() {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.of(\"\"));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tOptional<DisplayNameGenerator> displayNameGenerator = converter.get(configurationParameters, KEY);\n\n\t\tassertThat(displayNameGenerator).isEmpty();\n\t}\n\n\t@Test\n\tvoid shouldTrimAndInstantiateConfiguredClass(LogRecordListener listener) {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\tString classNameWithSpaces = \" \" + CustomDisplayNameGenerator.class.getName() + \"  \";\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.of(classNameWithSpaces));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tDisplayNameGenerator displayNameGenerator = converter.get(configurationParameters, KEY).orElseThrow();\n\n\t\tassertThat(displayNameGenerator).isInstanceOf(CustomDisplayNameGenerator.class);\n\t\tassertExpectedLogMessage(listener, Level.CONFIG,\n\t\t\t\"Using default display name generator \"\n\t\t\t\t\t+ \"'org.junit.jupiter.engine.descriptor.CustomDisplayNameGenerator' set via the \"\n\t\t\t\t\t+ \"'junit.jupiter.displayname.generator.default' configuration parameter.\");\n\t}\n\n\t@Test\n\tvoid shouldReturnEmptyOptionalIfNoClassFound(LogRecordListener listener) {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.of(\"random-string\"));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tOptional<DisplayNameGenerator> displayNameGenerator = converter.get(configurationParameters, KEY);\n\n\t\tassertThat(displayNameGenerator).isEmpty();\n\t\tassertExpectedLogMessage(listener, Level.WARNING,\n\t\t\t\"Failed to load default display name generator \"\n\t\t\t\t\t+ \"class 'random-string' set via the 'junit.jupiter.displayname.generator.default' \"\n\t\t\t\t\t+ \"configuration parameter. Falling back to default behavior.\");\n\t}\n\n\t@Test\n\tvoid shouldReturnEmptyOptionalIfClassFoundIsNotATypeOfExpectedType(LogRecordListener listener) {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(Optional.of(Object.class.getName()));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tOptional<DisplayNameGenerator> displayNameGenerator = converter.get(configurationParameters, KEY);\n\n\t\tassertThat(displayNameGenerator).isEmpty();\n\t\tassertExpectedLogMessage(listener, Level.WARNING,\n\t\t\t\"Failed to load default display name generator class 'java.lang.Object' \"\n\t\t\t\t\t+ \"set via the 'junit.jupiter.displayname.generator.default' configuration parameter. \"\n\t\t\t\t\t+ \"Falling back to default behavior.\");\n\t}\n\n\t@Test\n\tvoid shouldReturnEmptyOptionalIfClassNameIsNotFullyQualified(LogRecordListener listener) {\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(KEY)).thenReturn(\n\t\t\tOptional.of(CustomDisplayNameGenerator.class.getSimpleName()));\n\n\t\tInstantiatingConfigurationParameterConverter<DisplayNameGenerator> converter = new InstantiatingConfigurationParameterConverter<>(\n\t\t\tDisplayNameGenerator.class, \"display name generator\");\n\t\tOptional<DisplayNameGenerator> displayNameGenerator = converter.get(configurationParameters, KEY);\n\n\t\tassertThat(displayNameGenerator).isEmpty();\n\t\tassertExpectedLogMessage(listener, Level.WARNING,\n\t\t\t\"Failed to load default display name generator class 'CustomDisplayNameGenerator' \"\n\t\t\t\t\t+ \"set via the 'junit.jupiter.displayname.generator.default' configuration parameter. \"\n\t\t\t\t\t+ \"Falling back to default behavior.\");\n\t}\n\n\tprivate void assertExpectedLogMessage(LogRecordListener listener, Level level, String expectedMessage) {\n\t\t// @formatter:off\n\t\tassertTrue(listener.stream(level)\n\t\t\t\t.map(LogRecord::getMessage)\n\t\t\t\t.anyMatch(expectedMessage::equals));\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/CustomDisplayNameGenerator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.jupiter.api.DisplayNameGenerator;\n\npublic class CustomDisplayNameGenerator implements DisplayNameGenerator {\n\n\t@Override\n\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\treturn \"class-display-name\";\n\t}\n\n\t@Override\n\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\treturn \"nested-class-display-name\";\n\t}\n\n\t@Override\n\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\tMethod testMethod) {\n\t\treturn \"method-display-name\";\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/DisplayNameUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DisplayNameGeneration;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\n\n/**\n * Unit tests for {@link DisplayNameUtils}.\n *\n * @since 5.5\n */\nclass DisplayNameUtilsTests {\n\n\t@Nested\n\tclass ClassDisplayNameTests {\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromDisplayNameAnnotation() {\n\n\t\t\tString displayName = DisplayNameUtils.determineDisplayName(MyTestCase.class, () -> \"default-name\");\n\n\t\t\tassertThat(displayName).isEqualTo(\"my-test-case\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromSupplierIfDisplayNameAnnotationProvidesBlankString() {\n\n\t\t\tString displayName = DisplayNameUtils.determineDisplayName(BlankDisplayNameTestCase.class,\n\t\t\t\t() -> \"default-name\");\n\n\t\t\tassertThat(displayName).isEqualTo(\"default-name\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromSupplierIfNoDisplayNameAnnotationPresent() {\n\n\t\t\tString displayName = DisplayNameUtils.determineDisplayName(NotDisplayNameTestCase.class,\n\t\t\t\t() -> \"default-name\");\n\n\t\t\tassertThat(displayName).isEqualTo(\"default-name\");\n\t\t}\n\n\t\t@Nested\n\t\tclass ClassDisplayNameSupplierTests {\n\n\t\t\tprivate final JupiterConfiguration configuration = mock();\n\n\t\t\t@Test\n\t\t\tvoid shouldGetDisplayNameFromDisplayNameGenerationAnnotation() {\n\t\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForClass(\n\t\t\t\t\tStandardDisplayNameTestCase.class, configuration);\n\n\t\t\t\tString name = StandardDisplayNameTestCase.class.getName();\n\t\t\t\tString expectedClassName = name.substring(name.lastIndexOf(\".\") + 1);\n\t\t\t\tassertThat(displayName.get()).isEqualTo(expectedClassName);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid shouldGetUnderscoreDisplayNameFromDisplayNameGenerationAnnotation() {\n\t\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForClass(\n\t\t\t\t\tUnderscore_DisplayName_TestCase.class, configuration);\n\n\t\t\t\tassertThat(displayName.get()).isEqualTo(\"DisplayNameUtilsTests$Underscore DisplayName TestCase\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid shouldGetDisplayNameFromDefaultDisplayNameGenerator() {\n\t\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForClass(MyTestCase.class,\n\t\t\t\t\tconfiguration);\n\n\t\t\t\tassertThat(displayName.get()).isEqualTo(\"class-display-name\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid shouldFallbackOnDefaultDisplayNameGeneratorWhenNullIsGenerated() {\n\t\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForClass(\n\t\t\t\t\tNullDisplayNameTestCase.class, configuration);\n\n\t\t\t\tassertThat(displayName.get()).isEqualTo(\"class-display-name\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nested\n\tclass NestedClassDisplayNameTests {\n\n\t\tprivate final JupiterConfiguration configuration = mock();\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromDisplayNameGenerationAnnotation() {\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForNestedClass(List::of,\n\t\t\t\tStandardDisplayNameTestCase.class, configuration);\n\n\t\t\tassertThat(displayName.get()).isEqualTo(StandardDisplayNameTestCase.class.getSimpleName());\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromDefaultDisplayNameGenerator() {\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForNestedClass(List::of,\n\t\t\t\tNestedTestCase.class, configuration);\n\n\t\t\tassertThat(displayName.get()).isEqualTo(\"nested-class-display-name\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldFallbackOnDefaultDisplayNameGeneratorWhenNullIsGenerated() {\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\tSupplier<String> displayName = DisplayNameUtils.createDisplayNameSupplierForNestedClass(List::of,\n\t\t\t\tNullDisplayNameTestCase.NestedTestCase.class, configuration);\n\n\t\t\tassertThat(displayName.get()).isEqualTo(\"nested-class-display-name\");\n\t\t}\n\t}\n\n\t@Nested\n\tclass MethodDisplayNameTests {\n\n\t\tprivate final JupiterConfiguration configuration = mock();\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromDisplayNameGenerationAnnotation() throws Exception {\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\t\tMethod method = MyTestCase.class.getDeclaredMethod(\"test1\");\n\t\t\tString displayName = DisplayNameUtils.determineDisplayNameForMethod(List::of,\n\t\t\t\tStandardDisplayNameTestCase.class, method, configuration);\n\n\t\t\tassertThat(displayName).isEqualTo(\"test1()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldGetDisplayNameFromDefaultNameGenerator() throws Exception {\n\t\t\tMethod method = MyTestCase.class.getDeclaredMethod(\"test1\");\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\n\t\t\tString displayName = DisplayNameUtils.determineDisplayNameForMethod(List::of, NotDisplayNameTestCase.class,\n\t\t\t\tmethod, configuration);\n\n\t\t\tassertThat(displayName).isEqualTo(\"method-display-name\");\n\t\t}\n\n\t\t@Test\n\t\tvoid shouldFallbackOnDefaultDisplayNameGeneratorWhenNullIsGenerated() throws Exception {\n\t\t\tMethod method = NullDisplayNameTestCase.class.getDeclaredMethod(\"test\");\n\t\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\n\t\t\tString displayName = DisplayNameUtils.determineDisplayNameForMethod(List::of, NullDisplayNameTestCase.class,\n\t\t\t\tmethod, configuration);\n\n\t\t\tassertThat(displayName).isEqualTo(\"method-display-name\");\n\t\t}\n\t}\n\n\t@DisplayName(\"my-test-case\\t\")\n\t@DisplayNameGeneration(value = CustomDisplayNameGenerator.class)\n\tstatic class MyTestCase {\n\n\t\tvoid test1() {\n\t\t}\n\t}\n\n\t@DisplayName(\"\")\n\tstatic class BlankDisplayNameTestCase {\n\t}\n\n\t@DisplayNameGeneration(value = DisplayNameGenerator.Standard.class)\n\tstatic class StandardDisplayNameTestCase {\n\t}\n\n\t@DisplayNameGeneration(value = DisplayNameGenerator.ReplaceUnderscores.class)\n\tstatic class Underscore_DisplayName_TestCase {\n\t}\n\n\tstatic class NotDisplayNameTestCase {\n\t}\n\n\t@Nested\n\tclass NestedTestCase {\n\t}\n\n\t@DisplayNameGeneration(value = NullDisplayNameGenerator.class)\n\tstatic class NullDisplayNameTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tstatic class NullDisplayNameGenerator implements DisplayNameGenerator {\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForClass(Class<?> testClass) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForNestedClass(List<Class<?>> enclosingInstanceTypes, Class<?> nestedClass) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic String generateDisplayNameForMethod(List<Class<?>> enclosingInstanceTypes, Class<?> testClass,\n\t\t\t\tMethod testMethod) {\n\t\t\treturn null;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionContextTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.MediaType.TEXT_PLAIN;\nimport static org.junit.jupiter.api.MediaType.TEXT_PLAIN_UTF_8;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.hierarchicalOutputDirectoryCreator;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.PreInterruptCallback;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.config.DefaultJupiterConfiguration;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.DefaultTestInstances;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector;\nimport org.junit.platform.launcher.core.NamespacedHierarchicalStoreProviders;\nimport org.mockito.ArgumentCaptor;\n\n/**\n * Unit tests for concrete implementations of {@link ExtensionContext}:\n * {@link JupiterEngineExtensionContext}, {@link ClassExtensionContext}, and\n * {@link MethodExtensionContext}.\n *\n * @since 5.0\n */\npublic class ExtensionContextTests {\n\n\tprivate static final ThrowingConsumer<Path> failingAction = __ -> fail(\"should not be called\");\n\n\tprivate final JupiterConfiguration configuration = mock();\n\tprivate final ExtensionRegistry extensionRegistry = mock();\n\tprivate final LauncherStoreFacade launcherStoreFacade = new LauncherStoreFacade(\n\t\tNamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStore());\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\t\twhen(configuration.getDefaultExecutionMode()).thenReturn(ExecutionMode.SAME_THREAD);\n\t\twhen(configuration.getDefaultClassesExecutionMode()).thenReturn(ExecutionMode.SAME_THREAD);\n\t\twhen(configuration.getOutputDirectoryCreator()).thenReturn(dummyOutputDirectoryCreator());\n\t}\n\n\t@Test\n\tvoid fromJupiterEngineDescriptor() {\n\t\tvar engineTestDescriptor = new JupiterEngineDescriptor(UniqueId.root(\"engine\", \"junit-jupiter\"), configuration);\n\n\t\ttry (var engineContext = new JupiterEngineExtensionContext(mock(), engineTestDescriptor, configuration,\n\t\t\textensionRegistry, launcherStoreFacade)) {\n\t\t\t// @formatter:off\n\t\t\tassertAll(\"engineContext\",\n\t\t\t\t() -> assertThat(engineContext.getElement()).isEmpty(),\n\t\t\t\t() -> assertThat(engineContext.getTestClass()).isEmpty(),\n\t\t\t\t() -> assertThat(engineContext.getTestInstance()).isEmpty(),\n\t\t\t\t() -> assertThat(engineContext.getTestMethod()).isEmpty(),\n\t\t\t\t() -> assertPreconditionViolationFor(engineContext::getRequiredTestClass),\n\t\t\t\t() -> assertPreconditionViolationFor(engineContext::getRequiredTestInstance),\n\t\t\t\t() -> assertPreconditionViolationFor(engineContext::getRequiredTestMethod),\n\t\t\t\t() -> assertThat(engineContext.getDisplayName()).isEqualTo(engineTestDescriptor.getDisplayName()),\n\t\t\t\t() -> assertThat(engineContext.getParent()).isEmpty(),\n\t\t\t\t() -> assertThat(engineContext.getRoot()).isSameAs(engineContext),\n\t\t\t\t() -> assertThat(engineContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD),\n\t\t\t\t() -> assertThat(engineContext.getExtensions(PreInterruptCallback.class)).isEmpty()\n\t\t\t);\n\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid fromClassTestDescriptor() {\n\t\tvar nestedClassDescriptor = nestedClassDescriptor();\n\t\tvar outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor);\n\t\tvar doublyNestedClassDescriptor = doublyNestedClassDescriptor();\n\t\tvar methodTestDescriptor = nestedMethodDescriptor();\n\t\tnestedClassDescriptor.addChild(doublyNestedClassDescriptor);\n\t\tnestedClassDescriptor.addChild(methodTestDescriptor);\n\n\t\tvar parentExtensionContext = mock(AbstractExtensionContext.class);\n\n\t\tvar outerExtensionContext = new ClassExtensionContext(parentExtensionContext, mock(), outerClassDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\n\t\t// @formatter:off\n\t\tassertAll(\"outerContext\",\n\t\t\t() -> assertThat(outerExtensionContext.getElement()).contains(OuterClassTestCase.class),\n\t\t\t() -> assertThat(outerExtensionContext.getTestClass()).contains(OuterClassTestCase.class),\n\t\t\t() -> assertThat(outerExtensionContext.getTestInstance()).isEmpty(),\n\t\t\t() -> assertThat(outerExtensionContext.getTestMethod()).isEmpty(),\n\t\t\t() -> assertThat(outerExtensionContext.getRequiredTestClass()).isEqualTo(OuterClassTestCase.class),\n\t\t\t() -> assertPreconditionViolationFor(outerExtensionContext::getRequiredTestInstance),\n\t\t\t() -> assertPreconditionViolationFor(outerExtensionContext::getRequiredTestMethod),\n\t\t\t() -> assertThat(outerExtensionContext.getDisplayName()).isEqualTo(outerClassDescriptor.getDisplayName()),\n\t\t\t() -> assertThat(outerExtensionContext.getParent()).containsSame(parentExtensionContext),\n\t\t\t() -> assertThat(outerExtensionContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD),\n\t\t\t() -> assertThat(outerExtensionContext.getExtensions(PreInterruptCallback.class)).isEmpty(),\n\t\t\t() -> assertThat(outerExtensionContext.getEnclosingTestClasses()).isEmpty()\n\t\t);\n\t\t// @formatter:on\n\n\t\tvar nestedExtensionContext = new ClassExtensionContext(outerExtensionContext, mock(), nestedClassDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\t// @formatter:off\n\t\tassertAll(\"nestedContext\",\n\t\t\t() -> assertThat(nestedExtensionContext.getParent()).containsSame(outerExtensionContext),\n\t\t\t() -> assertThat(nestedExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.class),\n\t\t\t() -> assertThat(nestedExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class)\n\t\t);\n\t\t// @formatter:on\n\n\t\tvar doublyNestedExtensionContext = new ClassExtensionContext(nestedExtensionContext, mock(),\n\t\t\tdoublyNestedClassDescriptor, PER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\t// @formatter:off\n\t\tassertAll(\"doublyNestedContext\",\n\t\t\t\t() -> assertThat(doublyNestedExtensionContext.getParent()).containsSame(nestedExtensionContext),\n\t\t\t\t() -> assertThat(doublyNestedExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.DoublyNestedClass.class),\n\t\t\t\t() -> assertThat(doublyNestedExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class, OuterClassTestCase.NestedClass.class)\n\t\t);\n\t\t// @formatter:on\n\n\t\tvar methodExtensionContext = new MethodExtensionContext(nestedExtensionContext, mock(), methodTestDescriptor,\n\t\t\tconfiguration, extensionRegistry, launcherStoreFacade, new OpenTest4JAwareThrowableCollector());\n\t\t// @formatter:off\n\t\tassertAll(\"methodContext\",\n\t\t\t\t() -> assertThat(methodExtensionContext.getParent()).containsSame(nestedExtensionContext),\n\t\t\t\t() -> assertThat(methodExtensionContext.getTestClass()).contains(OuterClassTestCase.NestedClass.class),\n\t\t\t\t() -> assertThat(methodExtensionContext.getEnclosingTestClasses()).containsExactly(OuterClassTestCase.class)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid ExtensionContext_With_ExtensionRegistry_getExtensions() {\n\t\tvar classTestDescriptor = nestedClassDescriptor();\n\t\ttry (var ctx = new ClassExtensionContext(mock(AbstractExtensionContext.class), mock(), classTestDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock())) {\n\n\t\t\tExtension ext = mock();\n\t\t\twhen(extensionRegistry.getExtensions(Extension.class)).thenReturn(List.of(ext));\n\n\t\t\tassertThat(ctx.getExtensions(Extension.class)).isEqualTo(List.of(ext));\n\t\t}\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid tagsCanBeRetrievedInExtensionContext() {\n\t\tvar nestedClassDescriptor = nestedClassDescriptor();\n\t\tvar outerClassDescriptor = outerClassDescriptor(nestedClassDescriptor);\n\t\tvar methodTestDescriptor = methodDescriptor();\n\t\touterClassDescriptor.addChild(methodTestDescriptor);\n\n\t\tvar rootExtensionContext = new JupiterEngineExtensionContext(mock(), mock(), configuration, extensionRegistry,\n\t\t\tlauncherStoreFacade);\n\t\tassertThat(rootExtensionContext.getRoot()).isSameAs(rootExtensionContext);\n\n\t\tvar outerExtensionContext = new ClassExtensionContext(rootExtensionContext, mock(), outerClassDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\n\t\tassertThat(outerExtensionContext.getTags()).containsExactly(\"outer-tag\");\n\t\tassertThat(outerExtensionContext.getRoot()).isSameAs(rootExtensionContext);\n\n\t\tvar nestedExtensionContext = new ClassExtensionContext(outerExtensionContext, mock(), nestedClassDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\tassertThat(nestedExtensionContext.getTags()).containsExactlyInAnyOrder(\"outer-tag\", \"nested-tag\");\n\t\tassertThat(nestedExtensionContext.getRoot()).isSameAs(rootExtensionContext);\n\n\t\tvar methodExtensionContext = new MethodExtensionContext(outerExtensionContext, mock(), methodTestDescriptor,\n\t\t\tconfiguration, extensionRegistry, launcherStoreFacade, new OpenTest4JAwareThrowableCollector());\n\t\tmethodExtensionContext.setTestInstances(DefaultTestInstances.of(new OuterClassTestCase()));\n\t\tassertThat(methodExtensionContext.getTags()).containsExactlyInAnyOrder(\"outer-tag\", \"method-tag\");\n\t\tassertThat(methodExtensionContext.getRoot()).isSameAs(rootExtensionContext);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid fromMethodTestDescriptor() {\n\t\tvar methodTestDescriptor = methodDescriptor();\n\t\tvar classTestDescriptor = outerClassDescriptor(methodTestDescriptor);\n\t\tvar engineDescriptor = new JupiterEngineDescriptor(UniqueId.forEngine(\"junit-jupiter\"), configuration);\n\t\tengineDescriptor.addChild(classTestDescriptor);\n\n\t\tObject testInstance = new OuterClassTestCase();\n\t\tvar testMethod = methodTestDescriptor.getTestMethod();\n\n\t\tvar engineExtensionContext = new JupiterEngineExtensionContext(mock(), engineDescriptor, configuration,\n\t\t\textensionRegistry, launcherStoreFacade);\n\t\tvar classExtensionContext = new ClassExtensionContext(engineExtensionContext, mock(), classTestDescriptor,\n\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\tvar methodExtensionContext = new MethodExtensionContext(classExtensionContext, mock(), methodTestDescriptor,\n\t\t\tconfiguration, extensionRegistry, launcherStoreFacade, new OpenTest4JAwareThrowableCollector());\n\t\tmethodExtensionContext.setTestInstances(DefaultTestInstances.of(testInstance));\n\n\t\t// @formatter:off\n\t\tassertAll(\"methodContext\",\n\t\t\t() -> assertThat(methodExtensionContext.getElement()).contains(testMethod),\n\t\t\t() -> assertThat(methodExtensionContext.getTestClass()).contains(OuterClassTestCase.class),\n\t\t\t() -> assertThat(methodExtensionContext.getEnclosingTestClasses()).isEmpty(),\n\t\t\t() -> assertThat(methodExtensionContext.getTestInstance()).contains(testInstance),\n\t\t\t() -> assertThat(methodExtensionContext.getTestMethod()).contains(testMethod),\n\t\t\t() -> assertThat(methodExtensionContext.getRequiredTestClass()).isEqualTo(OuterClassTestCase.class),\n\t\t\t() -> assertThat(methodExtensionContext.getRequiredTestInstance()).isEqualTo(testInstance),\n\t\t\t() -> assertThat(methodExtensionContext.getRequiredTestMethod()).isEqualTo(testMethod),\n\t\t\t() -> assertThat(methodExtensionContext.getDisplayName()).isEqualTo(methodTestDescriptor.getDisplayName()),\n\t\t\t() -> assertThat(methodExtensionContext.getParent()).contains(classExtensionContext),\n\t\t\t() -> assertThat(methodExtensionContext.getRoot()).isSameAs(engineExtensionContext),\n\t\t\t() -> assertThat(methodExtensionContext.getExecutionMode()).isEqualTo(ExecutionMode.SAME_THREAD)\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid reportEntriesArePublishedToExecutionListener() {\n\t\tvar classTestDescriptor = outerClassDescriptor(mock());\n\t\tvar engineExecutionListener = spy(EngineExecutionListener.class);\n\t\tExtensionContext extensionContext = new ClassExtensionContext(mock(AbstractExtensionContext.class),\n\t\t\tengineExecutionListener, classTestDescriptor, PER_METHOD, configuration, extensionRegistry,\n\t\t\tlauncherStoreFacade, mock());\n\n\t\tvar map1 = Map.of(\"key\", \"value\");\n\t\tvar map2 = Map.of(\"other key\", \"other value\");\n\n\t\textensionContext.publishReportEntry(map1);\n\t\textensionContext.publishReportEntry(map2);\n\t\textensionContext.publishReportEntry(\"3rd key\", \"third value\");\n\t\textensionContext.publishReportEntry(\"status message\");\n\n\t\tvar entryCaptor = ArgumentCaptor.forClass(ReportEntry.class);\n\t\tverify(engineExecutionListener, times(4)) //\n\t\t\t\t.reportingEntryPublished(eq(classTestDescriptor), entryCaptor.capture());\n\n\t\tvar reportEntry1 = entryCaptor.getAllValues().get(0);\n\t\tvar reportEntry2 = entryCaptor.getAllValues().get(1);\n\t\tvar reportEntry3 = entryCaptor.getAllValues().get(2);\n\t\tvar reportEntry4 = entryCaptor.getAllValues().get(3);\n\n\t\tassertEquals(map1, reportEntry1.getKeyValuePairs());\n\t\tassertEquals(map2, reportEntry2.getKeyValuePairs());\n\t\tassertEquals(\"third value\", reportEntry3.getKeyValuePairs().get(\"3rd key\"));\n\t\tassertEquals(\"status message\", reportEntry4.getKeyValuePairs().get(\"value\"));\n\t}\n\n\t@Test\n\tvoid fileEntriesArePublishedToExecutionListener(@TempDir Path tempDir) {\n\t\tvar engineExecutionListener = mock(EngineExecutionListener.class);\n\t\tvar classTestDescriptor = outerClassDescriptor(null);\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir, engineExecutionListener,\n\t\t\tclassTestDescriptor);\n\n\t\textensionContext.publishFile(\"test1.txt\", TEXT_PLAIN_UTF_8, file -> Files.writeString(file, \"Test 1\"));\n\t\textensionContext.publishDirectory(\"test2\", dir -> {\n\t\t\tFiles.writeString(dir.resolve(\"nested1.txt\"), \"Nested content 1\");\n\t\t\tFiles.writeString(dir.resolve(\"nested2.txt\"), \"Nested content 2\");\n\t\t});\n\n\t\tvar entryCaptor = ArgumentCaptor.forClass(FileEntry.class);\n\t\tverify(engineExecutionListener, times(2)) //\n\t\t\t\t.fileEntryPublished(eq(classTestDescriptor), entryCaptor.capture());\n\t\tvar fileEntries = entryCaptor.getAllValues();\n\n\t\tvar fileEntry1 = fileEntries.getFirst();\n\t\tassertThat(fileEntry1.getPath()).isEqualTo(tempDir.resolve(\"OuterClass/test1.txt\"));\n\t\tassertThat(fileEntry1.getMediaType()).contains(TEXT_PLAIN_UTF_8.toString());\n\n\t\tvar fileEntry2 = fileEntries.get(1);\n\t\tassertThat(fileEntry2.getPath()).isEqualTo(tempDir.resolve(\"OuterClass/test2\"));\n\t\tassertThat(fileEntry2.getMediaType()).isEmpty();\n\t\tassertThat(fileEntry2.getPath().resolve(\"nested1.txt\")).usingCharset(UTF_8).hasContent(\"Nested content 1\");\n\t\tassertThat(fileEntry2.getPath().resolve(\"nested2.txt\")).usingCharset(UTF_8).hasContent(\"Nested content 2\");\n\t}\n\n\t@ParameterizedTest\n\t@NullAndEmptySource\n\t@ValueSource(strings = { \" \", \" \\t \" })\n\t@SuppressWarnings(\"DataFlowIssue\") // publishFile() parameters are not @Nullable\n\tvoid failsWhenAttemptingToPublishFileWithNullOrBlankName(@Nullable String name, @TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"name\",\n\t\t\t() -> extensionContext.publishFile(name, TEXT_PLAIN, failingAction));\n\t}\n\n\t@ParameterizedTest\n\t@NullAndEmptySource\n\t@ValueSource(strings = { \" \", \" \\t \" })\n\t@SuppressWarnings(\"DataFlowIssue\") // publishDirectory() parameters are not @Nullable\n\tvoid failsWhenAttemptingToPublishDirectoryWithNullOrBlankName(@Nullable String name, @TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"name\",\n\t\t\t() -> extensionContext.publishDirectory(name, failingAction));\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishFileWithPathSeparators(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\t\tvar name = \"test\" + File.separator + \"subDir\";\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishFile(name, TEXT_PLAIN, failingAction))//\n\t\t\t\t.withMessage(\"name must not contain path separators: \" + name);\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishDirectoryWithPathSeparators(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\t\tvar name = \"test\" + File.separator + \"subDir\";\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishDirectory(name, failingAction))//\n\t\t\t\t.withMessage(\"name must not contain path separators: \" + name);\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishMissingFiles(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishFile(\"test\", TEXT_PLAIN, Files::deleteIfExists)) //\n\t\t\t\t.withMessage(\"Published path must exist: \" + tempDir.resolve(\"OuterClass\").resolve(\"test\"));\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishMissingDirectory(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishDirectory(\"test\", Files::delete)) //\n\t\t\t\t.withMessage(\"Published path must exist: \" + tempDir.resolve(\"OuterClass\").resolve(\"test\"));\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishDirectoriesAsRegularFiles(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishFile(\"test\", TEXT_PLAIN, Files::createDirectory))//\n\t\t\t\t.withMessage(\"Published path must be a regular file: \" + tempDir.resolve(\"OuterClass\").resolve(\"test\"));\n\t}\n\n\t@Test\n\tvoid failsWhenAttemptingToPublishRegularFilesAsDirectories(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\tassertPreconditionViolationFor(() -> extensionContext.publishDirectory(\"test\", dir -> {\n\t\t\tFiles.delete(dir);\n\t\t\tFiles.createFile(dir);\n\t\t})).withMessage(\"Published path must be a directory: \" + tempDir.resolve(\"OuterClass\").resolve(\"test\"));\n\t}\n\n\t@Test\n\tvoid allowsPublishingToTheSameDirectoryTwice(@TempDir Path tempDir) {\n\t\tvar extensionContext = createExtensionContextForFilePublishing(tempDir);\n\n\t\textensionContext.publishDirectory(\"test\",\n\t\t\tdir -> Files.writeString(dir.resolve(\"nested1.txt\"), \"Nested content 1\"));\n\t\textensionContext.publishDirectory(\"test\",\n\t\t\tdir -> Files.writeString(dir.resolve(\"nested2.txt\"), \"Nested content 2\"));\n\n\t\tassertThat(tempDir.resolve(\"OuterClass/test/nested1.txt\")).hasContent(\"Nested content 1\");\n\t\tassertThat(tempDir.resolve(\"OuterClass/test/nested2.txt\")).hasContent(\"Nested content 2\");\n\t}\n\n\tprivate ExtensionContext createExtensionContextForFilePublishing(Path tempDir) {\n\t\treturn createExtensionContextForFilePublishing(tempDir, mock(EngineExecutionListener.class),\n\t\t\touterClassDescriptor(null));\n\t}\n\n\tprivate ExtensionContext createExtensionContextForFilePublishing(Path tempDir,\n\t\t\tEngineExecutionListener engineExecutionListener, ClassTestDescriptor classTestDescriptor) {\n\t\twhen(configuration.getOutputDirectoryCreator()) //\n\t\t\t\t.thenReturn(hierarchicalOutputDirectoryCreator(tempDir));\n\t\treturn new ClassExtensionContext(mock(AbstractExtensionContext.class), engineExecutionListener,\n\t\t\tclassTestDescriptor, PER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t}\n\n\t@Test\n\t@SuppressWarnings({ \"resource\", \"deprecation\" })\n\tvoid usingStore() {\n\t\tvar methodTestDescriptor = methodDescriptor();\n\t\tvar classTestDescriptor = outerClassDescriptor(methodTestDescriptor);\n\t\tExtensionContext parentContext = new ClassExtensionContext(mock(AbstractExtensionContext.class), mock(),\n\t\t\tclassTestDescriptor, PER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\tvar childContext = new MethodExtensionContext(parentContext, mock(), methodTestDescriptor, configuration,\n\t\t\textensionRegistry, launcherStoreFacade, new OpenTest4JAwareThrowableCollector());\n\t\tchildContext.setTestInstances(DefaultTestInstances.of(new OuterClassTestCase()));\n\n\t\tvar childStore = childContext.getStore(Namespace.GLOBAL);\n\t\tvar parentStore = parentContext.getStore(Namespace.GLOBAL);\n\n\t\tfinal Object key1 = \"key 1\";\n\t\tfinal var value1 = \"a value\";\n\t\tchildStore.put(key1, value1);\n\t\tassertEquals(value1, childStore.get(key1));\n\t\tassertEquals(value1, childStore.remove(key1));\n\t\tassertNull(childStore.get(key1));\n\n\t\tchildStore.put(key1, value1);\n\t\tassertEquals(value1, childStore.get(key1));\n\t\tassertEquals(value1, childStore.remove(key1, String.class));\n\t\tassertNull(childStore.get(key1));\n\n\t\tfinal Object key2 = \"key 2\";\n\t\tfinal var value2 = \"other value\";\n\t\tassertEquals(value2, childStore.computeIfAbsent(key2, key -> value2));\n\t\tassertEquals(value2, childStore.computeIfAbsent(key2, key -> \"a different value\", String.class));\n\t\tassertEquals(value2, childStore.getOrComputeIfAbsent(key2, key -> \"a different value\"));\n\t\tassertEquals(value2, childStore.getOrComputeIfAbsent(key2, key -> \"a different value\", String.class));\n\t\tassertEquals(value2, childStore.get(key2));\n\t\tassertEquals(value2, childStore.get(key2, String.class));\n\n\t\tfinal Object parentKey = \"parent key\";\n\t\tfinal var parentValue = \"parent value\";\n\t\tparentStore.put(parentKey, parentValue);\n\t\tassertEquals(parentValue, childStore.computeIfAbsent(parentKey, k -> \"a different value\"));\n\t\tassertEquals(parentValue, childStore.getOrComputeIfAbsent(parentKey, k -> \"a different value\"));\n\t\tassertEquals(parentValue, childStore.get(parentKey));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"extensionContextFactories\")\n\tvoid configurationParameter(Function<JupiterConfiguration, ? extends ExtensionContext> extensionContextFactory) {\n\n\t\tvar key = \"123\";\n\t\tvar expected = Optional.of(key);\n\n\t\tConfigurationParameters configurationParameters = mock();\n\t\twhen(configurationParameters.get(\"123\")).thenReturn(expected);\n\t\tJupiterConfiguration echo = new DefaultJupiterConfiguration(configurationParameters,\n\t\t\tdummyOutputDirectoryCreator(), mock());\n\n\t\tvar context = extensionContextFactory.apply(echo);\n\n\t\tassertEquals(expected, context.getConfigurationParameter(key));\n\t}\n\n\tstatic List<Named<Function<JupiterConfiguration, ? extends ExtensionContext>>> extensionContextFactories() {\n\t\tExtensionRegistry extensionRegistry = mock();\n\t\tLauncherStoreFacade launcherStoreFacade = mock();\n\t\tvar testClass = ExtensionContextTests.class;\n\t\treturn List.of( //\n\t\t\tnamed(\"engine\", (JupiterConfiguration configuration) -> {\n\t\t\t\tvar engineUniqueId = UniqueId.parse(\"[engine:junit-jupiter]\");\n\t\t\t\tvar engineDescriptor = new JupiterEngineDescriptor(engineUniqueId, configuration);\n\t\t\t\treturn new JupiterEngineExtensionContext(mock(), engineDescriptor, configuration, extensionRegistry,\n\t\t\t\t\tlauncherStoreFacade);\n\t\t\t}), //\n\t\t\tnamed(\"class\", (JupiterConfiguration configuration) -> {\n\t\t\t\tvar classUniqueId = UniqueId.parse(\"[engine:junit-jupiter]/[class:MyClass]\");\n\t\t\t\tvar classTestDescriptor = new ClassTestDescriptor(classUniqueId, testClass, configuration);\n\t\t\t\treturn new ClassExtensionContext(mock(AbstractExtensionContext.class), mock(), classTestDescriptor,\n\t\t\t\t\tPER_METHOD, configuration, extensionRegistry, launcherStoreFacade, mock());\n\t\t\t}), //\n\t\t\tnamed(\"method\", (JupiterConfiguration configuration) -> {\n\t\t\t\tvar method = ReflectionSupport.findMethod(testClass, \"extensionContextFactories\").orElseThrow();\n\t\t\t\tvar methodUniqueId = UniqueId.parse(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\");\n\t\t\t\tvar methodTestDescriptor = new TestMethodTestDescriptor(methodUniqueId, testClass, method, List::of,\n\t\t\t\t\tconfiguration);\n\t\t\t\treturn new MethodExtensionContext(mock(AbstractExtensionContext.class), mock(), methodTestDescriptor,\n\t\t\t\t\tconfiguration, extensionRegistry, launcherStoreFacade, mock());\n\t\t\t}) //\n\t\t);\n\t}\n\n\tprivate NestedClassTestDescriptor nestedClassDescriptor() {\n\t\treturn new NestedClassTestDescriptor(UniqueId.root(\"nested-class\", \"NestedClass\"),\n\t\t\tOuterClassTestCase.NestedClass.class, List::of, configuration);\n\t}\n\n\tprivate NestedClassTestDescriptor doublyNestedClassDescriptor() {\n\t\treturn new NestedClassTestDescriptor(UniqueId.root(\"nested-class\", \"DoublyNestedClass\"),\n\t\t\tOuterClassTestCase.NestedClass.DoublyNestedClass.class, List::of, configuration);\n\t}\n\n\tprivate ClassTestDescriptor outerClassDescriptor(@Nullable TestDescriptor child) {\n\t\tvar classTestDescriptor = new ClassTestDescriptor(UniqueId.root(\"class\", \"OuterClass\"),\n\t\t\tOuterClassTestCase.class, configuration);\n\t\tif (child != null) {\n\t\t\tclassTestDescriptor.addChild(child);\n\t\t}\n\t\treturn classTestDescriptor;\n\t}\n\n\tprivate TestMethodTestDescriptor methodDescriptor() {\n\t\ttry {\n\t\t\treturn new TestMethodTestDescriptor(UniqueId.root(\"method\", \"aMethod\"), OuterClassTestCase.class,\n\t\t\t\tOuterClassTestCase.class.getDeclaredMethod(\"aMethod\"), List::of, configuration);\n\t\t}\n\t\tcatch (NoSuchMethodException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate TestMethodTestDescriptor nestedMethodDescriptor() {\n\t\ttry {\n\t\t\treturn new TestMethodTestDescriptor(UniqueId.root(\"method\", \"nestedMethod\"),\n\t\t\t\tOuterClassTestCase.NestedClass.class, BaseNestedTestCase.class.getDeclaredMethod(\"nestedMethod\"),\n\t\t\t\tList::of, configuration);\n\t\t}\n\t\tcatch (NoSuchMethodException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tstatic abstract class BaseNestedTestCase {\n\t\t@Test\n\t\tvoid nestedMethod() {\n\t\t}\n\n\t\t@Nested\n\t\tclass DoublyNestedClass {\n\t\t}\n\t}\n\n\t@Tag(\"outer-tag\")\n\tstatic class OuterClassTestCase {\n\n\t\t@Tag(\"nested-tag\")\n\t\t@Nested\n\t\tclass NestedClass extends BaseNestedTestCase {\n\t\t}\n\n\t\t@Tag(\"method-tag\")\n\t\tvoid aMethod() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ExtensionsUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Field;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.engine.extension.ExtensionRegistrar;\n\n/**\n * Tests for {@link ExtensionUtils}.\n *\n * @since 5.11.3\n */\nclass ExtensionsUtilsTests {\n\n\t@Test\n\tvoid registerExtensionsViaStaticFields() throws Exception {\n\t\tField field = TestCase.class.getDeclaredField(\"staticField\");\n\t\tExtensionRegistrar registrar = mock();\n\t\tExtensionUtils.registerExtensionsFromStaticFields(registrar, TestCase.class);\n\t\tverify(registrar).registerExtension(Extension1.class);\n\t\tverify(registrar).registerExtension(Extension2.class);\n\t\tverify(registrar).registerExtension(TestCase.staticField, field);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tvoid registerExtensionsViaInstanceFields() throws Exception {\n\t\tClass<TestCase> testClass = TestCase.class;\n\t\tField field = testClass.getDeclaredField(\"instanceField\");\n\t\tExtensionRegistrar registrar = mock();\n\t\tExtensionUtils.registerExtensionsFromInstanceFields(registrar, testClass);\n\t\tverify(registrar).registerExtension(Extension1.class);\n\t\tverify(registrar).registerExtension(Extension2.class);\n\t\tverify(registrar).registerUninitializedExtension(eq(testClass), eq(field), any(Function.class));\n\t}\n\n\tstatic class Extension1 implements Extension {\n\t}\n\n\tstatic class Extension2 implements Extension {\n\t}\n\n\tstatic class Extension3 implements Extension {\n\t}\n\n\tstatic class Extension4 implements Extension {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ExtendWith(Extension1.class)\n\t@ExtendWith(Extension2.class)\n\t@interface UseCustomExtensions {\n\t}\n\n\tstatic class TestCase {\n\n\t\t@UseCustomExtensions\n\t\t@RegisterExtension\n\t\tstatic Extension3 staticField = new Extension3();\n\n\t\t@UseCustomExtensions\n\t\t@RegisterExtension\n\t\tExtension4 instanceField = new Extension4();\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.JupiterTestDescriptorTests.StaticTestCase.StaticTestCaseLevel2;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\n\n/**\n * Unit tests for {@link ClassTestDescriptor}, {@link NestedClassTestDescriptor},\n * and {@link TestMethodTestDescriptor}.\n *\n * @since 5.0\n * @see org.junit.jupiter.engine.descriptor.LifecycleMethodUtilsTests\n */\nclass JupiterTestDescriptorTests {\n\n\tprivate static final UniqueId uniqueId = UniqueId.root(\"enigma\", \"foo\");\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\t\twhen(configuration.getDefaultExecutionMode()).thenReturn(ExecutionMode.SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid constructFromClass() {\n\t\tClassTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, TestCase.class, configuration);\n\n\t\tassertEquals(TestCase.class, descriptor.getTestClass());\n\t\tassertThat(descriptor.getTags()).containsExactly(TestTag.create(\"inherited-class-level-tag\"),\n\t\t\tTestTag.create(\"classTag1\"), TestTag.create(\"classTag2\"));\n\t}\n\n\t@Test\n\tvoid constructFromClassWithInvalidBeforeAllDeclaration() {\n\t\t// Note: if we can instantiate the descriptor, then the invalid configuration\n\t\t// will not be reported during the test engine discovery phase.\n\t\tClassTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, TestCaseWithInvalidBeforeAllMethod.class,\n\t\t\tconfiguration);\n\n\t\tassertEquals(TestCaseWithInvalidBeforeAllMethod.class, descriptor.getTestClass());\n\t}\n\n\t@Test\n\tvoid constructFromClassWithInvalidAfterAllDeclaration() {\n\t\t// Note: if we can instantiate the descriptor, then the invalid configuration\n\t\t// will not be reported during the test engine discovery phase.\n\t\tClassTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, TestCaseWithInvalidAfterAllMethod.class,\n\t\t\tconfiguration);\n\n\t\tassertEquals(TestCaseWithInvalidAfterAllMethod.class, descriptor.getTestClass());\n\t}\n\n\t@Test\n\tvoid constructFromClassWithInvalidBeforeEachDeclaration() {\n\t\t// Note: if we can instantiate the descriptor, then the invalid configuration\n\t\t// will not be reported during the test engine discovery phase.\n\t\tClassTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, TestCaseWithInvalidBeforeEachMethod.class,\n\t\t\tconfiguration);\n\n\t\tassertEquals(TestCaseWithInvalidBeforeEachMethod.class, descriptor.getTestClass());\n\t}\n\n\t@Test\n\tvoid constructFromClassWithInvalidAfterEachDeclaration() {\n\t\t// Note: if we can instantiate the descriptor, then the invalid configuration\n\t\t// will not be reported during the test engine discovery phase.\n\t\tClassTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, TestCaseWithInvalidAfterEachMethod.class,\n\t\t\tconfiguration);\n\n\t\tassertEquals(TestCaseWithInvalidAfterEachMethod.class, descriptor.getTestClass());\n\t}\n\n\t@Test\n\tvoid constructFromMethod() throws Exception {\n\t\tClass<?> testClass = TestCase.class;\n\t\tMethod testMethod = testClass.getDeclaredMethod(\"test\");\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, testClass, testMethod, List::of,\n\t\t\tconfiguration);\n\n\t\tassertEquals(uniqueId, descriptor.getUniqueId());\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test()\", descriptor.getDisplayName(), \"display name:\");\n\t\tassertEquals(\"test()\", descriptor.getLegacyReportingName(), \"legacy name:\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithAnnotations() throws Exception {\n\t\tJupiterTestDescriptor classDescriptor = new ClassTestDescriptor(uniqueId, TestCase.class, configuration);\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"foo\");\n\t\tTestMethodTestDescriptor methodDescriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\t\tclassDescriptor.addChild(methodDescriptor);\n\n\t\tassertEquals(testMethod, methodDescriptor.getTestMethod());\n\t\tassertEquals(\"custom test name\", methodDescriptor.getDisplayName(), \"display name:\");\n\t\tassertEquals(\"foo()\", methodDescriptor.getLegacyReportingName(), \"legacy name:\");\n\n\t\tList<String> tags = methodDescriptor.getTags().stream().map(TestTag::getName).toList();\n\t\tassertThat(tags).containsExactlyInAnyOrder(\"inherited-class-level-tag\", \"classTag1\", \"classTag2\", \"methodTag1\",\n\t\t\t\"methodTag2\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithCustomTestAnnotation() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"customTestAnnotation\");\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"custom name\", descriptor.getDisplayName(), \"display name:\");\n\t\tassertEquals(\"customTestAnnotation()\", descriptor.getLegacyReportingName(), \"legacy name:\");\n\t\tassertThat(descriptor.getTags()).containsExactly(TestTag.create(\"custom-tag\"));\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithParameters() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"test\", String.class, BigDecimal.class);\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test(String, BigDecimal)\", descriptor.getDisplayName(), \"display name\");\n\t\tassertEquals(\"test(String, BigDecimal)\", descriptor.getLegacyReportingName(), \"legacy name\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithPrimitiveArrayParameter() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"test\", int[].class);\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test(int[])\", descriptor.getDisplayName(), \"display name\");\n\t\tassertEquals(\"test(int[])\", descriptor.getLegacyReportingName(), \"legacy name\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithObjectArrayParameter() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"test\", String[].class);\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test(String[])\", descriptor.getDisplayName(), \"display name\");\n\t\tassertEquals(\"test(String[])\", descriptor.getLegacyReportingName(), \"legacy name\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithMultidimensionalPrimitiveArrayParameter() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"test\", int[][][][][].class);\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test(int[][][][][])\", descriptor.getDisplayName(), \"display name\");\n\t\tassertEquals(\"test(int[][][][][])\", descriptor.getLegacyReportingName(), \"legacy name\");\n\t}\n\n\t@Test\n\tvoid constructFromMethodWithMultidimensionalObjectArrayParameter() throws Exception {\n\t\tMethod testMethod = TestCase.class.getDeclaredMethod(\"test\", String[][][][][].class);\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\t\tassertEquals(\"test(String[][][][][])\", descriptor.getDisplayName(), \"display name\");\n\t\tassertEquals(\"test(String[][][][][])\", descriptor.getLegacyReportingName(), \"legacy name\");\n\t}\n\n\t@Test\n\tvoid constructFromInheritedMethod() throws Exception {\n\t\tMethod testMethod = ConcreteTestCase.class.getMethod(\"theTest\");\n\t\tTestMethodTestDescriptor descriptor = new TestMethodTestDescriptor(uniqueId, ConcreteTestCase.class, testMethod,\n\t\t\tList::of, configuration);\n\n\t\tassertEquals(testMethod, descriptor.getTestMethod());\n\n\t\tOptional<TestSource> sourceOptional = descriptor.getSource();\n\t\tassertThat(sourceOptional).containsInstanceOf(MethodSource.class);\n\n\t\tMethodSource methodSource = (MethodSource) sourceOptional.orElseThrow();\n\t\tassertEquals(ConcreteTestCase.class.getName(), methodSource.getClassName());\n\t\tassertEquals(\"theTest\", methodSource.getMethodName());\n\t}\n\n\t@Test\n\tvoid shouldTakeCustomMethodNameDescriptorFromConfigurationIfPresent() {\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\n\t\tClassBasedTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, getClass(), configuration);\n\t\tassertEquals(\"class-display-name\", descriptor.getDisplayName());\n\t\tassertEquals(getClass().getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new NestedClassTestDescriptor(uniqueId, NestedTestCase.class, List::of, configuration);\n\t\tassertEquals(\"nested-class-display-name\", descriptor.getDisplayName());\n\t\tassertEquals(NestedTestCase.class.getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new ClassTestDescriptor(uniqueId, StaticTestCase.class, configuration);\n\t\tassertEquals(\"class-display-name\", descriptor.getDisplayName());\n\t\tassertEquals(StaticTestCase.class.getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new ClassTestDescriptor(uniqueId, StaticTestCaseLevel2.class, configuration);\n\t\tassertEquals(\"class-display-name\", descriptor.getDisplayName());\n\t\tassertEquals(StaticTestCaseLevel2.class.getName(), descriptor.getLegacyReportingName());\n\t}\n\n\t@Test\n\tvoid defaultDisplayNamesForTestClasses() {\n\t\tClassBasedTestDescriptor descriptor = new ClassTestDescriptor(uniqueId, getClass(), configuration);\n\t\tassertEquals(getClass().getSimpleName(), descriptor.getDisplayName());\n\t\tassertEquals(getClass().getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new NestedClassTestDescriptor(uniqueId, NestedTestCase.class, List::of, configuration);\n\t\tassertEquals(NestedTestCase.class.getSimpleName(), descriptor.getDisplayName());\n\t\tassertEquals(NestedTestCase.class.getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new ClassTestDescriptor(uniqueId, StaticTestCase.class, configuration);\n\t\tString staticDisplayName = getClass().getSimpleName() + \"$\" + StaticTestCase.class.getSimpleName();\n\t\tassertEquals(staticDisplayName, descriptor.getDisplayName());\n\t\tassertEquals(StaticTestCase.class.getName(), descriptor.getLegacyReportingName());\n\n\t\tdescriptor = new ClassTestDescriptor(uniqueId, StaticTestCaseLevel2.class, configuration);\n\t\tstaticDisplayName += \"$\" + StaticTestCaseLevel2.class.getSimpleName();\n\t\tassertEquals(staticDisplayName, descriptor.getDisplayName());\n\t\tassertEquals(StaticTestCaseLevel2.class.getName(), descriptor.getLegacyReportingName());\n\t}\n\n\t@Test\n\tvoid enclosingClassesAreDerivedFromParent() {\n\t\tClassBasedTestDescriptor parentDescriptor = new ClassTestDescriptor(uniqueId, StaticTestCase.class,\n\t\t\tconfiguration);\n\t\tClassBasedTestDescriptor nestedDescriptor = new NestedClassTestDescriptor(uniqueId, NestedTestCase.class,\n\t\t\tList::of, configuration);\n\t\tassertThat(parentDescriptor.getEnclosingTestClasses()).isEmpty();\n\t\tassertThat(nestedDescriptor.getEnclosingTestClasses()).isEmpty();\n\n\t\tparentDescriptor.addChild(nestedDescriptor);\n\t\tassertThat(parentDescriptor.getEnclosingTestClasses()).isEmpty();\n\t\tassertThat(nestedDescriptor.getEnclosingTestClasses()).containsExactly(StaticTestCase.class);\n\t}\n\n\t@Test\n\tvoid ancestorsAreConsistent() throws Exception {\n\t\tClassBasedTestDescriptor parentDescriptor = new ClassTestDescriptor(uniqueId, StaticTestCase.class,\n\t\t\tconfiguration);\n\n\t\tClassBasedTestDescriptor nestedDescriptor = new NestedClassTestDescriptor(uniqueId, NestedTestCase.class,\n\t\t\tList::of, configuration);\n\t\tparentDescriptor.addChild(nestedDescriptor);\n\n\t\tTestMethodTestDescriptor methodDescriptor = new TestMethodTestDescriptor(uniqueId, TestCase.class,\n\t\t\tNestedTestCase.class.getDeclaredMethod(\"test\"), List::of, configuration);\n\t\tnestedDescriptor.addChild(methodDescriptor);\n\n\t\tassertThat(methodDescriptor.ancestors()) //\n\t\t\t\t.containsExactlyElementsOf(methodDescriptor.getAncestors()) //\n\t\t\t\t.containsExactly(nestedDescriptor, parentDescriptor);\n\n\t\tassertThat(nestedDescriptor.ancestors()) //\n\t\t\t\t.containsExactlyElementsOf(nestedDescriptor.getAncestors()) //\n\t\t\t\t.containsExactly(parentDescriptor);\n\n\t\tassertThat(parentDescriptor.ancestors()) //\n\t\t\t\t.containsExactlyElementsOf(parentDescriptor.getAncestors()) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Test\n\t@DisplayName(\"custom name\")\n\t@Tag(\"  custom-tag  \")\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CustomTestAnnotation {\n\t}\n\n\t@Tag(\"inherited-class-level-tag\")\n\tprivate static abstract class AbstractTestCase {\n\t}\n\n\t@Tag(\"classTag1\")\n\t@Tag(\"classTag2\")\n\t@DisplayName(\"custom class name\")\n\t@SuppressWarnings(\"unused\")\n\tprivate static class TestCase extends AbstractTestCase {\n\n\t\tvoid test() {\n\t\t}\n\n\t\tvoid test(String txt, BigDecimal sum) {\n\t\t}\n\n\t\tvoid test(int[] nums) {\n\t\t}\n\n\t\tvoid test(int[][][][][] nums) {\n\t\t}\n\n\t\tvoid test(String[] info) {\n\t\t}\n\n\t\tvoid test(String[][][][][] info) {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"custom test name\")\n\t\t@Tag(\"methodTag1\")\n\t\t@Tag(\"methodTag2\")\n\t\t@Tag(\"tag containing whitespace\")\n\t\tvoid foo() {\n\t\t}\n\n\t\t@CustomTestAnnotation\n\t\tvoid customTestAnnotation() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tprivate static class TestCaseWithInvalidBeforeAllMethod {\n\n\t\t// must be static\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tprivate static class TestCaseWithInvalidAfterAllMethod {\n\n\t\t// must be static\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@AfterAll\n\t\tvoid afterAll() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tprivate static class TestCaseWithInvalidBeforeEachMethod {\n\n\t\t// must NOT be static\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@BeforeEach\n\t\tstatic void beforeEach() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tprivate static class TestCaseWithInvalidAfterEachMethod {\n\n\t\t// must NOT be static\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@AfterEach\n\t\tstatic void afterEach() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass NestedTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class StaticTestCase {\n\n\t\tstatic class StaticTestCaseLevel2 {\n\t\t}\n\t}\n\n\tprivate abstract static class AbstractTestBase {\n\n\t\t@Test\n\t\tpublic void theTest() {\n\t\t}\n\t}\n\n\tprivate static class ConcreteTestCase extends AbstractTestBase {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/LauncherStoreFacadeTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrowsExactly;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.execution.NamespaceAwareStore;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * Tests for {@link LauncherStoreFacade}.\n *\n * @since 5.13\n */\nclass LauncherStoreFacadeTest {\n\n\tprivate NamespacedHierarchicalStore<Namespace> requestLevelStore;\n\tprivate NamespacedHierarchicalStore<Namespace> sessionLevelStore;\n\tprivate ExtensionContext.Namespace extensionNamespace;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tsessionLevelStore = new NamespacedHierarchicalStore<>(null);\n\t\trequestLevelStore = new NamespacedHierarchicalStore<>(sessionLevelStore);\n\t\textensionNamespace = ExtensionContext.Namespace.create(\"foo\", \"bar\");\n\t}\n\n\t@Test\n\tvoid createsInstanceSuccessfullyWithValidStore() {\n\t\tassertDoesNotThrow(() -> new LauncherStoreFacade(requestLevelStore));\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenRequestLevelStoreHasNoParent() {\n\t\tassertThrowsExactly(JUnitException.class, () -> new LauncherStoreFacade(sessionLevelStore), () -> {\n\t\t\tthrow new JUnitException(\"Request-level store must have a parent\");\n\t\t});\n\t}\n\n\t@Test\n\tvoid returnsRequestLevelStore() {\n\t\tLauncherStoreFacade facade = new LauncherStoreFacade(requestLevelStore);\n\t\tassertEquals(requestLevelStore, facade.getRequestLevelStore());\n\t}\n\n\t@Test\n\tvoid returnsNamespaceAwareStoreWithRequestLevelStore() {\n\t\tLauncherStoreFacade facade = new LauncherStoreFacade(requestLevelStore);\n\t\tExtensionContext.Store store = facade.getRequestLevelStore(extensionNamespace);\n\n\t\tassertNotNull(store);\n\t\tassertInstanceOf(NamespaceAwareStore.class, store);\n\t}\n\n\t@Test\n\tvoid returnsNamespaceAwareStore() {\n\t\tLauncherStoreFacade facade = new LauncherStoreFacade(requestLevelStore);\n\t\tNamespaceAwareStore adapter = facade.getStoreAdapter(requestLevelStore, extensionNamespace);\n\n\t\tassertNotNull(adapter);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid throwsExceptionWhenNamespaceIsNull() {\n\t\tLauncherStoreFacade facade = new LauncherStoreFacade(requestLevelStore);\n\t\tassertPreconditionViolationFor(() -> facade.getStoreAdapter(requestLevelStore, null));\n\t}\n\n\t@Test\n\tvoid returnsNamespaceAwareStoreWithGlobalNamespace() {\n\t\trequestLevelStore.put(Namespace.GLOBAL, \"foo\", \"bar\");\n\n\t\tLauncherStoreFacade facade = new LauncherStoreFacade(requestLevelStore);\n\t\tExtensionContext.Store store = facade.getRequestLevelStore(ExtensionContext.Namespace.GLOBAL);\n\n\t\tassertEquals(\"bar\", store.get(\"foo\"));\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/LifecycleMethodUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterAllMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findAfterEachMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeAllMethods;\nimport static org.junit.jupiter.engine.descriptor.LifecycleMethodUtils.findBeforeEachMethods;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Unit tests for {@link LifecycleMethodUtils}.\n *\n * @since 5.0\n */\nclass LifecycleMethodUtilsTests {\n\n\tList<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\tDiscoveryIssueReporter issueReporter = DiscoveryIssueReporter.collecting(discoveryIssues);\n\n\t@Test\n\tvoid findNonVoidBeforeAllMethodsWithStandardLifecycle() throws Exception {\n\t\tvar methods = findBeforeAllMethods(TestCaseWithInvalidLifecycleMethods.class, true, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tvar methodSource = MethodSource.from(TestCaseWithInvalidLifecycleMethods.class.getDeclaredMethod(\"cc\"));\n\t\tvar notVoidIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@BeforeAll method 'private java.lang.Double org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.cc()' must not return a value.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar notStaticIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@BeforeAll method 'private java.lang.Double org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.cc()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar privateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\"@BeforeAll method 'private java.lang.Double org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.cc()' should not be private. This will be disallowed in a future release.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notVoidIssue, notStaticIssue, privateIssue);\n\t}\n\n\t@Test\n\tvoid findNonVoidAfterAllMethodsWithStandardLifecycle() throws Exception {\n\t\tvar methods = findAfterAllMethods(TestCaseWithInvalidLifecycleMethods.class, true, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tvar methodSource = MethodSource.from(TestCaseWithInvalidLifecycleMethods.class.getDeclaredMethod(\"dd\"));\n\t\tvar notVoidIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@AfterAll method 'private java.lang.String org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.dd()' must not return a value.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar notStaticIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@AfterAll method 'private java.lang.String org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.dd()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar privateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\"@AfterAll method 'private java.lang.String org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.dd()' should not be private. This will be disallowed in a future release.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notVoidIssue, notStaticIssue, privateIssue);\n\t}\n\n\t@Test\n\tvoid findNonVoidBeforeEachMethodsWithStandardLifecycle() throws Exception {\n\t\tvar methods = findBeforeEachMethods(TestCaseWithInvalidLifecycleMethods.class, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tvar methodSource = MethodSource.from(TestCaseWithInvalidLifecycleMethods.class.getDeclaredMethod(\"aa\"));\n\t\tvar notVoidIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@BeforeEach method 'private java.lang.String org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.aa()' must not return a value.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar privateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\"@BeforeEach method 'private java.lang.String org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.aa()' should not be private. This will be disallowed in a future release.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notVoidIssue, privateIssue);\n\t}\n\n\t@Test\n\tvoid findNonVoidAfterEachMethodsWithStandardLifecycle() throws Exception {\n\t\tvar methods = findAfterEachMethods(TestCaseWithInvalidLifecycleMethods.class, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tvar methodSource = MethodSource.from(TestCaseWithInvalidLifecycleMethods.class.getDeclaredMethod(\"bb\"));\n\t\tvar notVoidIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@AfterEach method 'private int org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.bb()' must not return a value.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tvar privateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\"@AfterEach method 'private int org.junit.jupiter.engine.descriptor.TestCaseWithInvalidLifecycleMethods.bb()' should not be private. This will be disallowed in a future release.\") //\n\t\t\t\t.source(methodSource) //\n\t\t\t\t.build();\n\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notVoidIssue, privateIssue);\n\t}\n\n\t@Test\n\tvoid findBeforeEachMethodsWithStandardLifecycle() {\n\t\tList<Method> methods = findBeforeEachMethods(TestCaseWithStandardLifecycle.class, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactlyInAnyOrder(\"nine\", \"ten\");\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAfterEachMethodsWithStandardLifecycle() {\n\t\tList<Method> methods = findAfterEachMethods(TestCaseWithStandardLifecycle.class, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactlyInAnyOrder(\"eleven\", \"twelve\");\n\t}\n\n\t@Test\n\tvoid findBeforeAllMethodsWithStandardLifecycleAndWithoutRequiringStatic() {\n\t\tList<Method> methods = findBeforeAllMethods(TestCaseWithStandardLifecycle.class, false, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactly(\"one\");\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid findBeforeAllMethodsWithStandardLifecycleAndRequiringStatic() throws Exception {\n\t\tvar methods = findBeforeAllMethods(TestCaseWithStandardLifecycle.class, true, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tvar expectedIssue = DiscoveryIssue.builder(Severity.ERROR,\n\t\t\t\"@BeforeAll method 'void org.junit.jupiter.engine.descriptor.TestCaseWithStandardLifecycle.one()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\") //\n\t\t\t\t.source(MethodSource.from(TestCaseWithStandardLifecycle.class.getDeclaredMethod(\"one\"))) //\n\t\t\t\t.build();\n\t\tassertThat(discoveryIssues).containsExactly(expectedIssue);\n\t}\n\n\t@Test\n\tvoid findBeforeAllMethodsWithLifeCyclePerClassAndRequiringStatic() {\n\t\tList<Method> methods = findBeforeAllMethods(TestCaseWithLifecyclePerClass.class, false, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactlyInAnyOrder(\"three\", \"four\");\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAfterAllMethodsWithStandardLifecycleAndWithoutRequiringStatic() {\n\t\tList<Method> methods = findAfterAllMethods(TestCaseWithStandardLifecycle.class, false, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactlyInAnyOrder(\"five\", \"six\");\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAfterAllMethodsWithStandardLifecycleAndRequiringStatic() {\n\t\tvar methods = findAfterAllMethods(TestCaseWithStandardLifecycle.class, true, issueReporter);\n\t\tassertThat(methods).isEmpty();\n\n\t\tassertThat(discoveryIssues) //\n\t\t\t\t.filteredOn(where(DiscoveryIssue::severity, isEqual(Severity.ERROR))) //\n\t\t\t\t.isNotEmpty();\n\t}\n\n\t@Test\n\tvoid findAfterAllMethodsWithLifeCyclePerClassAndRequiringStatic() {\n\t\tList<Method> methods = findAfterAllMethods(TestCaseWithLifecyclePerClass.class, false, issueReporter);\n\n\t\tassertThat(namesOf(methods)).containsExactlyInAnyOrder(\"seven\", \"eight\");\n\t}\n\n\tprivate static List<String> namesOf(List<Method> methods) {\n\t\treturn methods.stream().map(Method::getName).toList();\n\t}\n\n}\n\nclass TestCaseWithStandardLifecycle {\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@BeforeAll\n\tvoid one() {\n\t}\n\n\t@BeforeEach\n\tvoid nine() {\n\t}\n\n\t@BeforeEach\n\tvoid ten() {\n\t}\n\n\t@AfterEach\n\tvoid eleven() {\n\t}\n\n\t@AfterEach\n\tvoid twelve() {\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@AfterAll\n\tvoid five() {\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@AfterAll\n\tvoid six() {\n\t}\n\n}\n\n@TestInstance(Lifecycle.PER_CLASS)\nclass TestCaseWithLifecyclePerClass {\n\n\t@BeforeAll\n\tvoid three() {\n\t}\n\n\t@BeforeAll\n\tvoid four() {\n\t}\n\n\t@AfterAll\n\tvoid seven() {\n\t}\n\n\t@AfterAll\n\tvoid eight() {\n\t}\n\n}\n\n@SuppressWarnings(\"JUnitMalformedDeclaration\")\n@NullUnmarked\nclass TestCaseWithInvalidLifecycleMethods {\n\n\t@BeforeEach\n\tprivate String aa() {\n\t\treturn null;\n\t}\n\n\t@AfterEach\n\tprivate int bb() {\n\t\treturn 1;\n\t}\n\n\t@BeforeAll\n\tprivate Double cc() {\n\t\treturn null;\n\t}\n\n\t@AfterAll\n\tprivate String dd() {\n\t\treturn \"\";\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/NamespaceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\n\nclass NamespaceTests {\n\n\t@Test\n\tvoid namespacesEqualForSamePartsSequence() {\n\t\tNamespace ns1 = Namespace.create(\"part1\", \"part2\");\n\t\tNamespace ns2 = Namespace.create(\"part1\", \"part2\");\n\n\t\tassertEquals(ns1, ns2);\n\t}\n\n\t@Test\n\tvoid orderOfNamespacePartsDoesMatter() {\n\t\tNamespace ns1 = Namespace.create(\"part1\", \"part2\");\n\t\tNamespace ns2 = Namespace.create(\"part2\", \"part1\");\n\n\t\tassertNotEquals(ns1, ns2);\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/ResourceAutoClosingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.LauncherStoreFacade;\nimport org.junit.jupiter.engine.extension.ExtensionRegistry;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.launcher.core.NamespacedHierarchicalStoreProviders;\nimport org.junit.platform.testkit.engine.ExecutionRecorder;\n\nclass ResourceAutoClosingTests {\n\n\tprivate final JupiterConfiguration configuration = mock();\n\tprivate final ExtensionRegistry extensionRegistry = mock();\n\tprivate final JupiterEngineDescriptor testDescriptor = mock();\n\tprivate final LauncherStoreFacade launcherStoreFacade = new LauncherStoreFacade(\n\t\tNamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStore());\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid shouldCloseAutoCloseableWhenIsClosingStoredAutoCloseablesEnabledIsTrue() throws Exception {\n\t\tAutoCloseableResource resource = new AutoCloseableResource();\n\t\twhen(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(true);\n\n\t\tExtensionContext extensionContext = new JupiterEngineExtensionContext(mock(), testDescriptor, configuration,\n\t\t\textensionRegistry, launcherStoreFacade);\n\t\tExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);\n\t\tstore.put(\"resource\", resource);\n\n\t\t((AutoCloseable) extensionContext).close();\n\n\t\tassertThat(resource.closed).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid shouldNotCloseAutoCloseableWhenIsClosingStoredAutoCloseablesEnabledIsFalse() throws Exception {\n\t\tAutoCloseableResource resource = new AutoCloseableResource();\n\t\twhen(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(false);\n\n\t\tExtensionContext extensionContext = new JupiterEngineExtensionContext(mock(), testDescriptor, configuration,\n\t\t\textensionRegistry, launcherStoreFacade);\n\t\tExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);\n\t\tstore.put(\"resource\", resource);\n\n\t\t((AutoCloseable) extensionContext).close();\n\n\t\tassertThat(resource.closed).isFalse();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid shouldLogWarningWhenResourceImplementsCloseableResourceButNotAutoCloseableAndConfigIsTrue(\n\t\t\t@TrackLogRecords LogRecordListener listener) throws Exception {\n\t\tExecutionRecorder executionRecorder = new ExecutionRecorder();\n\t\tCloseableResource resource1 = new CloseableResource();\n\t\tCloseableResource resource2 = new CloseableResource();\n\t\tCloseableResource resource3 = new CloseableResource() {\n\t\t};\n\t\twhen(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(true);\n\n\t\tExtensionContext extensionContext = new JupiterEngineExtensionContext(executionRecorder, testDescriptor,\n\t\t\tconfiguration, extensionRegistry, launcherStoreFacade);\n\t\tExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);\n\t\tstore.put(\"resource1\", resource1);\n\t\tstore.put(\"resource2\", resource2);\n\t\tstore.put(\"resource3\", resource3);\n\n\t\t((AutoCloseable) extensionContext).close();\n\n\t\tassertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage) //\n\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\"Type implements CloseableResource but not AutoCloseable: \" + resource1.getClass().getName(),\n\t\t\t\t\t\"Type implements CloseableResource but not AutoCloseable: \" + resource3.getClass().getName());\n\t\tassertThat(resource1.closed).isTrue();\n\t\tassertThat(resource2.closed).isTrue();\n\t\tassertThat(resource3.closed).isTrue();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid shouldNotLogWarningWhenResourceImplementsCloseableResourceAndAutoCloseableAndConfigIsFalse(\n\t\t\t@TrackLogRecords LogRecordListener listener) throws Exception {\n\t\tExecutionRecorder executionRecorder = new ExecutionRecorder();\n\t\tCloseableResource resource = new CloseableResource();\n\t\twhen(configuration.isClosingStoredAutoCloseablesEnabled()).thenReturn(false);\n\n\t\tExtensionContext extensionContext = new JupiterEngineExtensionContext(executionRecorder, testDescriptor,\n\t\t\tconfiguration, extensionRegistry, launcherStoreFacade);\n\t\tExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.GLOBAL);\n\t\tstore.put(\"resource\", resource);\n\n\t\t((AutoCloseable) extensionContext).close();\n\n\t\tassertThat(listener.stream(Level.WARNING)).isEmpty();\n\t\tassertThat(resource.closed).isTrue();\n\t}\n\n\tstatic class AutoCloseableResource implements AutoCloseable {\n\t\tprivate boolean closed = false;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tclosed = true;\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\tstatic class CloseableResource implements ExtensionContext.Store.CloseableResource {\n\t\tprivate boolean closed = false;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tclosed = true;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestFactoryTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClasspathResourceSource;\nimport org.junit.platform.engine.support.descriptor.DirectorySource;\nimport org.junit.platform.engine.support.descriptor.FilePosition;\nimport org.junit.platform.engine.support.descriptor.FileSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.descriptor.UriSource;\nimport org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector;\n\n/**\n * Unit tests for {@link TestFactoryTestDescriptor}.\n *\n * @since 5.0\n */\nclass TestFactoryTestDescriptorTests {\n\n\t@Test\n\tvoid copyIncludesTransformedDynamicDescendantFilter() throws Exception {\n\t\tvar rootUniqueId = UniqueId.forEngine(\"engine\");\n\t\tvar parentUniqueId = rootUniqueId.append(\"class\", \"myClass\");\n\t\tvar originalUniqueId = parentUniqueId.append(\"old\", \"testFactory()\");\n\n\t\tvar configuration = mock(JupiterConfiguration.class);\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\t\tMethod testMethod = CustomStreamTestCase.class.getDeclaredMethod(\"customStream\");\n\t\tvar original = new TestFactoryTestDescriptor(originalUniqueId, CustomStreamTestCase.class, testMethod, List::of,\n\t\t\tconfiguration);\n\n\t\toriginal.getDynamicDescendantFilter().allowUniqueIdPrefix(originalUniqueId.append(\"foo\", \"bar\"));\n\t\toriginal.getDynamicDescendantFilter().allowIndex(42);\n\n\t\tvar newUniqueId = parentUniqueId.append(\"new\", \"testFactory()\");\n\n\t\tvar copy = original.withUniqueId(new UniqueIdPrefixTransformer(originalUniqueId, newUniqueId));\n\n\t\tassertThat(copy.getUniqueId()).isEqualTo(newUniqueId);\n\t\tassertThat(copy.getDynamicDescendantFilter().test(newUniqueId, 0)).isTrue();\n\t\tassertThat(copy.getDynamicDescendantFilter().test(newUniqueId, 42)).isTrue();\n\t\tassertThat(copy.getDynamicDescendantFilter().test(originalUniqueId, 1)).isFalse();\n\t}\n\n\t/**\n\t * @since 5.3\n\t */\n\t@Nested\n\tclass TestSources {\n\n\t\t@Test\n\t\tvoid classpathResourceSourceFromUriWithFilePosition() {\n\t\t\tFilePosition position = FilePosition.from(42, 21);\n\t\t\tURI uri = URI.create(\"classpath:/test.js?line=42&column=21\");\n\t\t\tTestSource testSource = TestFactoryTestDescriptor.fromUri(uri);\n\n\t\t\tassertThat(testSource).isInstanceOf(ClasspathResourceSource.class);\n\t\t\tClasspathResourceSource source = (ClasspathResourceSource) testSource;\n\t\t\tassertThat(source.getClasspathResourceName()).isEqualTo(\"test.js\");\n\t\t\tassertThat(source.getPosition()).hasValue(position);\n\t\t}\n\n\t\t@Test\n\t\tvoid fileSourceFromUriWithFilePosition() {\n\t\t\tFile file = new File(\"src/test/resources/log4j2-test.xml\");\n\t\t\tassertThat(file).isFile();\n\n\t\t\tFilePosition position = FilePosition.from(42, 21);\n\t\t\tURI uri = URI.create(file.toURI() + \"?line=42&column=21\");\n\t\t\tTestSource testSource = TestFactoryTestDescriptor.fromUri(uri);\n\n\t\t\tassertThat(testSource).isInstanceOf(FileSource.class);\n\t\t\tFileSource source = (FileSource) testSource;\n\t\t\tassertThat(source.getFile().getAbsolutePath()).isEqualTo(file.getAbsolutePath());\n\t\t\tassertThat(source.getPosition()).hasValue(position);\n\t\t}\n\n\t\t@Test\n\t\tvoid directorySourceFromUri() {\n\t\t\tFile file = new File(\"src/test/resources\");\n\t\t\tassertThat(file).isDirectory();\n\n\t\t\tURI uri = file.toURI();\n\t\t\tTestSource testSource = TestFactoryTestDescriptor.fromUri(uri);\n\n\t\t\tassertThat(testSource).isInstanceOf(DirectorySource.class);\n\t\t\tDirectorySource source = (DirectorySource) testSource;\n\t\t\tassertThat(source.getFile().getAbsolutePath()).isEqualTo(file.getAbsolutePath());\n\t\t}\n\n\t\t@Test\n\t\tvoid defaultUriSourceFromUri() {\n\t\t\tFile file = new File(\"src/test/resources\");\n\t\t\tassertThat(file).isDirectory();\n\n\t\t\tURI uri = URI.create(\"https://example.com?foo=bar&line=42\");\n\t\t\tTestSource testSource = TestFactoryTestDescriptor.fromUri(uri);\n\n\t\t\tassertThat(testSource).isInstanceOf(UriSource.class);\n\t\t\tassertThat(testSource.getClass().getSimpleName()).isEqualTo(\"DefaultUriSource\");\n\t\t\tUriSource source = (UriSource) testSource;\n\t\t\tassertThat(source.getUri()).isEqualTo(uri);\n\t\t}\n\n\t\t@Test\n\t\tvoid methodSourceFromUri() {\n\t\t\tURI uri = URI.create(\"method:org.junit.Foo#bar(java.lang.String,%20java.lang.String[])\");\n\t\t\tTestSource testSource = TestFactoryTestDescriptor.fromUri(uri);\n\n\t\t\tassertThat(testSource).isInstanceOf(MethodSource.class);\n\t\t\tassertThat(testSource.getClass().getSimpleName()).isEqualTo(\"MethodSource\");\n\t\t\tMethodSource source = (MethodSource) testSource;\n\t\t\tassertThat(source.getClassName()).isEqualTo(\"org.junit.Foo\");\n\t\t\tassertThat(source.getMethodName()).isEqualTo(\"bar\");\n\t\t\tassertThat(source.getMethodParameterTypes()).isEqualTo(\"java.lang.String, java.lang.String[]\");\n\t\t}\n\t}\n\n\t@Nested\n\tclass Streams {\n\n\t\tprivate JupiterEngineExecutionContext context;\n\t\tprivate ExtensionContext extensionContext;\n\t\tprivate TestFactoryTestDescriptor descriptor;\n\t\tprivate boolean isClosed;\n\n\t\t@BeforeEach\n\t\tvoid before() throws Exception {\n\t\t\tJupiterConfiguration jupiterConfiguration = mock();\n\t\t\twhen(jupiterConfiguration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\n\t\t\textensionContext = mock();\n\t\t\tisClosed = false;\n\n\t\t\tcontext = new JupiterEngineExecutionContext(mock(), mock(), mock()) //\n\t\t\t\t\t.extend() //\n\t\t\t\t\t.withThrowableCollector(new OpenTest4JAwareThrowableCollector()) //\n\t\t\t\t\t.withExtensionContext(extensionContext) //\n\t\t\t\t\t.withExtensionRegistry(mock()) //\n\t\t\t\t\t.build();\n\n\t\t\tMethod testMethod = CustomStreamTestCase.class.getDeclaredMethod(\"customStream\");\n\t\t\tdescriptor = new TestFactoryTestDescriptor(UniqueId.forEngine(\"engine\"), CustomStreamTestCase.class,\n\t\t\t\ttestMethod, List::of, jupiterConfiguration);\n\t\t\twhen(extensionContext.getTestMethod()).thenReturn(Optional.of(testMethod));\n\t\t}\n\n\t\t@Test\n\t\tvoid streamsFromTestFactoriesShouldBeClosed() {\n\t\t\tStream<DynamicTest> dynamicTestStream = Stream.empty();\n\t\t\tprepareMockForTestInstanceWithCustomStream(dynamicTestStream);\n\n\t\t\tdescriptor.invokeTestMethod(context, mock());\n\n\t\t\tassertTrue(isClosed);\n\t\t}\n\n\t\t@Test\n\t\tvoid streamsFromTestFactoriesShouldBeClosedWhenTheyThrow() {\n\t\t\tStream<Integer> integerStream = Stream.of(1, 2);\n\t\t\tprepareMockForTestInstanceWithCustomStream(integerStream);\n\n\t\t\tdescriptor.invokeTestMethod(context, mock());\n\n\t\t\tassertTrue(isClosed);\n\t\t}\n\n\t\tprivate void prepareMockForTestInstanceWithCustomStream(Stream<?> stream) {\n\t\t\tStream<?> mockStream = stream.onClose(() -> isClosed = true);\n\t\t\twhen(extensionContext.getRequiredTestInstance()).thenReturn(new CustomStreamTestCase(mockStream));\n\t\t}\n\n\t}\n\n\tprivate record CustomStreamTestCase(Stream<?> mockStream) {\n\n\t\t@TestFactory\n\t\tStream<?> customStream() {\n\t\t\treturn mockStream;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestInstanceLifecycleUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.jupiter.engine.descriptor.TestInstanceLifecycleUtils.getTestInstanceLifecycle;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.engine.config.DefaultJupiterConfiguration;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * Unit tests for {@link TestInstanceLifecycleUtils}.\n *\n * <p>NOTE: it doesn't make sense to unit test the JVM system property fallback\n * support in this test class since that feature is a concrete implementation\n * detail of {@code LauncherConfigurationParameters} which necessitates an\n * integration test via the {@code Launcher} API.\n *\n * @since 5.0\n */\nclass TestInstanceLifecycleUtilsTests {\n\n\tprivate static final String KEY = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getTestInstanceLifecyclePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"testClass\", () -> getTestInstanceLifecycle(null,\n\t\t\tnew DefaultJupiterConfiguration(mock(), dummyOutputDirectoryCreator(), mock())));\n\n\t\tassertPreconditionViolationNotNullFor(\"configuration\", () -> getTestInstanceLifecycle(getClass(), null));\n\t}\n\n\t@Test\n\tvoid getTestInstanceLifecycleWithNoConfigParamSet() {\n\t\tLifecycle lifecycle = getTestInstanceLifecycle(getClass(),\n\t\t\tnew DefaultJupiterConfiguration(mock(), dummyOutputDirectoryCreator(), mock()));\n\t\tassertThat(lifecycle).isEqualTo(PER_METHOD);\n\t}\n\n\t@Test\n\tvoid getTestInstanceLifecycleWithConfigParamSet() {\n\t\tConfigurationParameters configParams = mock();\n\t\twhen(configParams.get(KEY)).thenReturn(Optional.of(PER_CLASS.name().toLowerCase()));\n\t\tLifecycle lifecycle = getTestInstanceLifecycle(getClass(),\n\t\t\tnew DefaultJupiterConfiguration(configParams, dummyOutputDirectoryCreator(), mock()));\n\t\tassertThat(lifecycle).isEqualTo(PER_CLASS);\n\t}\n\n\t@Test\n\tvoid getTestInstanceLifecycleWithLocalConfigThatOverridesCustomDefaultSetViaConfigParam() {\n\t\tConfigurationParameters configParams = mock();\n\t\twhen(configParams.get(KEY)).thenReturn(Optional.of(PER_CLASS.name().toLowerCase()));\n\t\tLifecycle lifecycle = getTestInstanceLifecycle(TestCase.class,\n\t\t\tnew DefaultJupiterConfiguration(configParams, dummyOutputDirectoryCreator(), mock()));\n\t\tassertThat(lifecycle).isEqualTo(PER_METHOD);\n\t}\n\n\t@Test\n\tvoid getTestInstanceLifecycleFromMetaAnnotationWithNoConfigParamSet() {\n\t\tClass<?> testClass = BaseMetaAnnotatedTestCase.class;\n\t\tLifecycle lifecycle = getTestInstanceLifecycle(testClass,\n\t\t\tnew DefaultJupiterConfiguration(mock(), dummyOutputDirectoryCreator(), mock()));\n\t\tassertThat(lifecycle).isEqualTo(PER_CLASS);\n\t}\n\n\t@Test\n\tvoid getTestInstanceLifecycleFromSpecializedClassWithNoConfigParamSet() {\n\t\tClass<?> testClass = SpecializedTestCase.class;\n\t\tLifecycle lifecycle = getTestInstanceLifecycle(testClass,\n\t\t\tnew DefaultJupiterConfiguration(mock(), dummyOutputDirectoryCreator(), mock()));\n\t\tassertThat(lifecycle).isEqualTo(PER_CLASS);\n\t}\n\n\t@TestInstance(Lifecycle.PER_METHOD)\n\tprivate static class TestCase {\n\t}\n\n\t@Inherited\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.TYPE)\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tprivate @interface PerClassLifeCycle {\n\t}\n\n\t@PerClassLifeCycle\n\tprivate static class BaseMetaAnnotatedTestCase {\n\t}\n\n\tprivate static class SpecializedTestCase extends BaseMetaAnnotatedTestCase {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateInvocationTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.ArgumentMatchers.anyInt;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.engine.UniqueId;\n\nclass TestTemplateInvocationTestDescriptorTests {\n\n\t@Test\n\tvoid invocationsDoNotDeclareExclusiveResources() throws Exception {\n\t\tClass<MyTestCase> testClass = MyTestCase.class;\n\t\tMethod testTemplateMethod = testClass.getDeclaredMethod(\"testTemplate\");\n\t\tJupiterConfiguration configuration = mock();\n\t\twhen(configuration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\t\tTestTemplateTestDescriptor parent = new TestTemplateTestDescriptor(UniqueId.root(\"segment\", \"template\"),\n\t\t\ttestClass, testTemplateMethod, List::of, configuration);\n\t\tTestTemplateInvocationContext invocationContext = mock();\n\t\twhen(invocationContext.getDisplayName(anyInt())).thenReturn(\"invocation\");\n\n\t\tTestTemplateInvocationTestDescriptor testDescriptor = new TestTemplateInvocationTestDescriptor(\n\t\t\tparent.getUniqueId().append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"1\"), testClass,\n\t\t\ttestTemplateMethod, invocationContext, 1, configuration);\n\n\t\tassertThat(parent.getExclusiveResources()).hasSize(1);\n\t\tassertThat(testDescriptor.getExclusiveResources()).isEmpty();\n\t}\n\n\tstatic class MyTestCase {\n\t\t@TestTemplate\n\t\t@ResourceLock(\"a\")\n\t\tvoid testTemplate() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/TestTemplateTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\n\n/**\n * Unit tests for {@link TestTemplateTestDescriptor}.\n *\n * @since 5.0\n */\nclass TestTemplateTestDescriptorTests {\n\n\tprivate JupiterConfiguration jupiterConfiguration = mock();\n\n\t@BeforeEach\n\tvoid prepareJupiterConfiguration() {\n\t\twhen(jupiterConfiguration.getDefaultDisplayNameGenerator()).thenReturn(new DisplayNameGenerator.Standard());\n\t}\n\n\t@Test\n\tvoid inheritsTagsFromParent() throws Exception {\n\t\tvar rootUniqueId = UniqueId.root(\"segment\", \"template\");\n\t\tvar parentUniqueId = rootUniqueId.append(\"class\", \"myClass\");\n\t\tvar parent = containerTestDescriptorWithTags(parentUniqueId, Set.of(TestTag.create(\"foo\")));\n\n\t\tvar testDescriptor = new TestTemplateTestDescriptor(parentUniqueId.append(\"tmp\", \"testTemplate()\"),\n\t\t\tMyTestCase.class, MyTestCase.class.getDeclaredMethod(\"testTemplate\"), List::of, jupiterConfiguration);\n\t\tparent.addChild(testDescriptor);\n\n\t\tassertThat(testDescriptor.getTags()).containsExactlyInAnyOrder(TestTag.create(\"foo\"), TestTag.create(\"bar\"),\n\t\t\tTestTag.create(\"baz\"));\n\t}\n\n\t@Test\n\tvoid shouldUseCustomDisplayNameGeneratorIfPresentFromConfiguration() throws Exception {\n\t\tvar rootUniqueId = UniqueId.root(\"segment\", \"template\");\n\t\tvar parentUniqueId = rootUniqueId.append(\"class\", \"myClass\");\n\t\tvar parent = containerTestDescriptorWithTags(parentUniqueId, Set.of(TestTag.create(\"foo\")));\n\n\t\twhen(jupiterConfiguration.getDefaultDisplayNameGenerator()).thenReturn(new CustomDisplayNameGenerator());\n\n\t\tvar testDescriptor = new TestTemplateTestDescriptor(parentUniqueId.append(\"tmp\", \"testTemplate()\"),\n\t\t\tMyTestCase.class, MyTestCase.class.getDeclaredMethod(\"testTemplate\"), List::of, jupiterConfiguration);\n\t\tparent.addChild(testDescriptor);\n\n\t\tassertThat(testDescriptor.getDisplayName()).isEqualTo(\"method-display-name\");\n\t}\n\n\t@Test\n\tvoid shouldUseStandardDisplayNameGeneratorIfConfigurationNotPresent() throws Exception {\n\t\tvar rootUniqueId = UniqueId.root(\"segment\", \"template\");\n\t\tvar parentUniqueId = rootUniqueId.append(\"class\", \"myClass\");\n\t\tvar parent = containerTestDescriptorWithTags(parentUniqueId, Set.of(TestTag.create(\"foo\")));\n\n\t\tvar testDescriptor = new TestTemplateTestDescriptor(parentUniqueId.append(\"tmp\", \"testTemplate()\"),\n\t\t\tMyTestCase.class, MyTestCase.class.getDeclaredMethod(\"testTemplate\"), List::of, jupiterConfiguration);\n\t\tparent.addChild(testDescriptor);\n\n\t\tassertThat(testDescriptor.getDisplayName()).isEqualTo(\"testTemplate()\");\n\t}\n\n\t@Test\n\tvoid copyIncludesTransformedDynamicDescendantFilter() throws Exception {\n\t\tvar rootUniqueId = UniqueId.root(\"segment\", \"template\");\n\t\tvar parentUniqueId = rootUniqueId.append(\"class\", \"myClass\");\n\t\tvar originalUniqueId = parentUniqueId.append(\"old\", \"testTemplate()\");\n\n\t\tvar original = new TestTemplateTestDescriptor(originalUniqueId, MyTestCase.class,\n\t\t\tMyTestCase.class.getDeclaredMethod(\"testTemplate\"), List::of, jupiterConfiguration);\n\n\t\toriginal.getDynamicDescendantFilter().allowUniqueIdPrefix(originalUniqueId.append(\"foo\", \"bar\"));\n\t\toriginal.getDynamicDescendantFilter().allowIndex(42);\n\n\t\tvar newUniqueId = parentUniqueId.append(\"new\", \"testTemplate()\");\n\n\t\tvar copy = original.withUniqueId(new UniqueIdPrefixTransformer(originalUniqueId, newUniqueId));\n\n\t\tassertThat(copy.getUniqueId()).isEqualTo(newUniqueId);\n\t\tassertThat(copy.getDynamicDescendantFilter().test(newUniqueId, 0)).isTrue();\n\t\tassertThat(copy.getDynamicDescendantFilter().test(newUniqueId, 42)).isTrue();\n\t\tassertThat(copy.getDynamicDescendantFilter().test(originalUniqueId, 1)).isFalse();\n\t}\n\n\tprivate AbstractTestDescriptor containerTestDescriptorWithTags(UniqueId uniqueId, Set<TestTag> tags) {\n\t\treturn new AbstractTestDescriptor(uniqueId, \"testDescriptor with tags\") {\n\n\t\t\t@Override\n\t\t\tpublic Type getType() {\n\t\t\t\treturn Type.CONTAINER;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<TestTag> getTags() {\n\t\t\t\treturn tags;\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic class MyTestCase {\n\n\t\t@Tag(\"bar\")\n\t\t@Tag(\"baz\")\n\t\t@TestTemplate\n\t\tvoid testTemplate() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class1WithTestCases.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\npublic class Class1WithTestCases {\n\n\t@Test\n\tvoid test1() {\n\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/Class2WithTestCases.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\npublic class Class2WithTestCases {\n\n\t@Test\n\tvoid test2() {\n\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithStaticInnerTestCases.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\npublic class ClassWithStaticInnerTestCases {\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class ShouldBeDiscovered {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static class ShouldNotBeDiscovered {\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/descriptor/subpackage/ClassWithoutTestCases.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.descriptor.subpackage;\n\n/**\n * @since 5.0\n */\npublic class ClassWithoutTestCases {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DisplayNameGenerator.getDisplayNameGenerator;\nimport static org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.DYNAMIC_CONTAINER_SEGMENT_TYPE;\nimport static org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor.DYNAMIC_TEST_SEGMENT_TYPE;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.appendClassTemplateInvocationSegment;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.engineId;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForClass;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForMethod;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForStaticClass;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestFactoryMethod;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestTemplateMethod;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.FAILED;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.RESOLVED;\nimport static org.junit.platform.engine.SelectorResolutionResult.Status.UNRESOLVED;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathRoots;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.excludePackageNames;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.includePackageNames;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.DynamicDescendantFilter;\nimport org.junit.jupiter.engine.descriptor.Filterable;\nimport org.junit.jupiter.engine.descriptor.JupiterTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.subpackage.Class1WithTestCases;\nimport org.junit.jupiter.engine.descriptor.subpackage.Class2WithTestCases;\nimport org.junit.jupiter.engine.descriptor.subpackage.ClassWithStaticInnerTestCases;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.mockito.ArgumentCaptor;\n\n/**\n * @since 5.0\n */\nclass DiscoverySelectorResolverTests extends AbstractJupiterTestEngineTests {\n\n\tprivate final JupiterConfiguration configuration = mock();\n\tprivate final LauncherDiscoveryListener discoveryListener = mock();\n\n\tprivate @Nullable TestDescriptor engineDescriptor;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(configuration.getDefaultDisplayNameGenerator()) //\n\t\t\t\t.thenReturn(getDisplayNameGenerator(DisplayNameGenerator.Standard.class));\n\t\twhen(configuration.getDefaultExecutionMode()).thenReturn(ExecutionMode.SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid nonTestClassResolution() {\n\t\tresolve(request().selectors(selectClass(NonTestClass.class)));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t}\n\n\t@Test\n\tvoid doesNotAttemptToResolveMethodsForNonTestClasses() {\n\t\tvar methodSelector = selectMethod(NonTestClass.class, \"doesNotExist\");\n\t\tresolve(request().selectors(methodSelector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tassertUnresolved(methodSelector);\n\t}\n\n\t@Test\n\tvoid abstractClassResolution() {\n\t\tresolve(request().selectors(selectClass(AbstractTestClass.class)));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tassertUnresolved(selectClass(AbstractTestClass.class));\n\t}\n\n\t@Test\n\tvoid singleClassResolution() {\n\t\tClassSelector selector = selectClass(MyTestClass.class);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(4, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tassertUniqueIdsForMyTestClass(uniqueIds());\n\t}\n\n\t@Test\n\tvoid classResolutionForNonexistentClass() {\n\t\tClassSelector selector = selectClass(\"org.example.DoesNotExist\");\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tvar result = verifySelectorProcessed(selector);\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow()).hasMessageContaining(\"Could not load class with name\");\n\t}\n\n\t@Test\n\tvoid duplicateClassSelectorOnlyResolvesOnce() {\n\t\tresolve(request().selectors( //\n\t\t\tselectClass(MyTestClass.class), //\n\t\t\tselectClass(MyTestClass.class) //\n\t\t));\n\n\t\tassertEquals(4, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tassertUniqueIdsForMyTestClass(uniqueIds());\n\t}\n\n\t@Test\n\tvoid twoClassesResolution() {\n\t\tClassSelector selector1 = selectClass(MyTestClass.class);\n\t\tClassSelector selector2 = selectClass(YourTestClass.class);\n\n\t\tresolve(request().selectors(selector1, selector2));\n\n\t\tassertEquals(7, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertUniqueIdsForMyTestClass(uniqueIds);\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(YourTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(YourTestClass.class, \"test3()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(YourTestClass.class, \"test4()\"));\n\t}\n\n\tprivate void assertUniqueIdsForMyTestClass(List<UniqueId> uniqueIds) {\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(MyTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test1()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test2()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForTestFactoryMethod(MyTestClass.class, \"dynamicTest()\"));\n\t}\n\n\t@Test\n\tvoid classResolutionOfStaticNestedClass() {\n\t\tClassSelector selector = selectClass(OtherTestClass.NestedTestClass.class);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(3, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(OtherTestClass.NestedTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test5()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test6()\"));\n\t}\n\n\t@Test\n\tvoid classResolutionOfClassTemplate() {\n\t\tvar selector = selectClass(ClassTemplateTestCase.class);\n\n\t\tAtomicBoolean verified = new AtomicBoolean();\n\t\tPostDiscoveryFilter filter = descriptor -> {\n\t\t\tif (descriptor instanceof ClassTemplateTestDescriptor) {\n\t\t\t\tassertThat(descriptor.mayRegisterTests()).isFalse();\n\t\t\t\tassertThat(descriptor.getDescendants()).hasSize(1);\n\t\t\t\tverified.set(true);\n\t\t\t}\n\t\t\treturn FilterResult.included(\"included\");\n\t\t};\n\n\t\tresolve(request().selectors(selector).filters(filter));\n\n\t\tassertThat(verified.get()).describedAs(\"filter can see descendants\").isTrue();\n\n\t\tTestDescriptor classTemplateDescriptor = getOnlyElement(requireNonNull(engineDescriptor).getChildren());\n\t\tassertThat(classTemplateDescriptor.mayRegisterTests()).isTrue();\n\t\tassertThat(classTemplateDescriptor.getDescendants()).isEmpty();\n\n\t\tvar classTemplateSegment = classTemplateDescriptor.getUniqueId().getLastSegment();\n\t\tassertThat(classTemplateSegment.getType()).isEqualTo(\"class-template\");\n\t\tassertThat(classTemplateSegment.getValue()).isEqualTo(ClassTemplateTestCase.class.getName());\n\t}\n\n\t@Test\n\tvoid uniqueIdResolutionOfClassTemplateInvocation() {\n\t\tvar selector = selectUniqueId(\n\t\t\tappendClassTemplateInvocationSegment(uniqueIdForClass(ClassTemplateTestCase.class), 1));\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getChildren()).hasSize(1);\n\n\t\tTestDescriptor classTemplateDescriptor = getOnlyElement(requireNonNull(engineDescriptor).getChildren());\n\n\t\tclassTemplateDescriptor.prune();\n\t\tassertThat(requireNonNull(engineDescriptor).getChildren()).hasSize(1);\n\t\tassertThat(classTemplateDescriptor.mayRegisterTests()).isTrue();\n\t\tassertThat(classTemplateDescriptor.getDescendants()).isEmpty();\n\n\t\tclassTemplateDescriptor.prune();\n\t\tassertThat(requireNonNull(engineDescriptor).getChildren()).hasSize(1);\n\t\tassertThat(classTemplateDescriptor.mayRegisterTests()).isTrue();\n\t\tassertThat(classTemplateDescriptor.getDescendants()).isEmpty();\n\t}\n\n\t@Test\n\tvoid methodResolution() throws NoSuchMethodException {\n\t\tMethod test1 = MyTestClass.class.getDeclaredMethod(\"test1\");\n\t\tMethodSelector selector = selectMethod(test1.getDeclaringClass(), test1);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(MyTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid methodResolutionFromInheritedMethod() throws NoSuchMethodException {\n\t\tMethodSelector selector = selectMethod(HerTestClass.class, MyTestClass.class.getDeclaredMethod(\"test1\"));\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(HerTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(HerTestClass.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid resolvingSelectorOfNonTestMethodResolvesNothing() throws NoSuchMethodException {\n\t\tMethod notATest = MyTestClass.class.getDeclaredMethod(\"notATest\");\n\t\tMethodSelector selector = selectMethod(notATest.getDeclaringClass(), notATest);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t}\n\n\t@Test\n\tvoid methodResolutionForNonexistentClass() {\n\t\tString className = \"org.example.DoesNotExist\";\n\t\tString methodName = \"bogus\";\n\t\tMethodSelector selector = selectMethod(className, methodName, \"\");\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tvar result = verifySelectorProcessed(selector);\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow())//\n\t\t\t\t.isInstanceOf(PreconditionViolationException.class)//\n\t\t\t\t.hasMessageStartingWith(\"Could not load class with name: \" + className);\n\t}\n\n\t@Test\n\tvoid methodResolutionForNonexistentMethod() {\n\t\tMethodSelector selector = selectMethod(MyTestClass.class, \"bogus\", \"\");\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tvar result = verifySelectorProcessed(selector);\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow()).hasMessageContaining(\"Could not find method\");\n\t}\n\n\t@Test\n\tvoid classResolutionByUniqueId() {\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueIdForClass(MyTestClass.class).toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(4, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertUniqueIdsForMyTestClass(uniqueIds);\n\t}\n\n\t@Test\n\tvoid staticNestedClassResolutionByUniqueId() {\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueIdForClass(OtherTestClass.NestedTestClass.class).toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(3, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(OtherTestClass.NestedTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test5()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test6()\"));\n\t}\n\n\t@Test\n\tvoid methodOfInnerClassByUniqueId() {\n\t\tUniqueIdSelector selector = selectUniqueId(\n\t\t\tuniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test5()\").toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(OtherTestClass.NestedTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(OtherTestClass.NestedTestClass.class, \"test5()\"));\n\t}\n\n\t@Test\n\tvoid resolvingUniqueIdWithUnknownSegmentTypeResolvesNothing() {\n\t\tUniqueId uniqueId = engineId().append(\"bogus\", \"enigma\");\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueId);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tassertUnresolved(selector);\n\t}\n\n\t@Test\n\tvoid resolvingUniqueIdOfNonTestMethodResolvesNothing() {\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueIdForMethod(MyTestClass.class, \"notATest()\"));\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).isEmpty();\n\t\tassertUnresolved(selector);\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueIdWithMissingMethodName() {\n\t\tUniqueId uniqueId = uniqueIdForMethod(getClass(), \"()\");\n\n\t\tresolve(request().selectors(selectUniqueId(uniqueId)));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tvar result = verifySelectorProcessed(selectUniqueId(uniqueId));\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow())//\n\t\t\t\t.isInstanceOf(PreconditionViolationException.class)//\n\t\t\t\t.hasMessageStartingWith(\"Method [()] does not match pattern\");\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueIdWithMissingParameters() {\n\t\tUniqueId uniqueId = uniqueIdForMethod(getClass(), \"methodName\");\n\n\t\tresolve(request().selectors(selectUniqueId(uniqueId)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).isEmpty();\n\t\tvar result = verifySelectorProcessed(selectUniqueId(uniqueId));\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow())//\n\t\t\t\t.isInstanceOf(PreconditionViolationException.class)//\n\t\t\t\t.hasMessageStartingWith(\"Method [methodName] does not match pattern\");\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueIdWithBogusParameters() {\n\t\tUniqueId uniqueId = uniqueIdForMethod(getClass(), \"methodName(java.lang.String, junit.foo.Enigma)\");\n\n\t\tresolve(request().selectors(selectUniqueId(uniqueId)));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tvar result = verifySelectorProcessed(selectUniqueId(uniqueId));\n\t\tassertThat(result.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(result.getThrowable().orElseThrow())//\n\t\t\t\t.isInstanceOf(JUnitException.class)//\n\t\t\t\t.hasMessage(\"Failed to load parameter type [%s] for method [%s] in class [%s].\", \"junit.foo.Enigma\",\n\t\t\t\t\t\"methodName\", getClass().getName());\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueId() {\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueIdForMethod(MyTestClass.class, \"test1()\").toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(MyTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueIdFromInheritedClass() {\n\t\tUniqueIdSelector selector = selectUniqueId(uniqueIdForMethod(HerTestClass.class, \"test1()\").toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(HerTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(HerTestClass.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid methodResolutionByUniqueIdWithParams() {\n\t\tUniqueIdSelector selector = selectUniqueId(\n\t\t\tuniqueIdForMethod(HerTestClass.class, \"test7(java.lang.String)\").toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(2, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(HerTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(HerTestClass.class, \"test7(java.lang.String)\"));\n\n\t}\n\n\t@Test\n\tvoid resolvingUniqueIdWithWrongParamsResolvesNothing() {\n\t\tUniqueId uniqueId = uniqueIdForMethod(HerTestClass.class, \"test7(java.math.BigDecimal)\");\n\n\t\tresolve(request().selectors(selectUniqueId(uniqueId)));\n\n\t\tassertTrue(requireNonNull(engineDescriptor).getDescendants().isEmpty());\n\t\tassertUnresolved(selectUniqueId(uniqueId));\n\t}\n\n\t@Test\n\tvoid twoMethodResolutionsByUniqueId() {\n\t\tUniqueIdSelector selector1 = selectUniqueId(uniqueIdForMethod(MyTestClass.class, \"test1()\").toString());\n\t\tUniqueIdSelector selector2 = selectUniqueId(uniqueIdForMethod(MyTestClass.class, \"test2()\").toString());\n\n\t\t// adding same selector twice should have no effect\n\t\tresolve(request().selectors(selector1, selector2, selector2));\n\n\t\tassertEquals(3, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(MyTestClass.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test1()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(MyTestClass.class, \"test2()\"));\n\n\t\tTestDescriptor classFromMethod1 = descriptorByUniqueId(\n\t\t\tuniqueIdForMethod(MyTestClass.class, \"test1()\")).getParent().orElseThrow();\n\t\tTestDescriptor classFromMethod2 = descriptorByUniqueId(\n\t\t\tuniqueIdForMethod(MyTestClass.class, \"test2()\")).getParent().orElseThrow();\n\n\t\tassertEquals(classFromMethod1, classFromMethod2);\n\t\tassertSame(classFromMethod1, classFromMethod2);\n\t}\n\n\t@Test\n\tvoid packageResolutionUsingExplicitBasePackage() {\n\t\tPackageSelector selector = selectPackage(\"org.junit.jupiter.engine.descriptor.subpackage\");\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(6, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class1WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class1WithTestCases.class, \"test1()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class2WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class2WithTestCases.class, \"test2()\"));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(ClassWithStaticInnerTestCases.ShouldBeDiscovered.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid packageResolutionUsingDefaultPackage() throws Exception {\n\t\tresolve(request().selectors(selectPackage(\"\")));\n\n\t\t// 150 is completely arbitrary. The actual number is likely much higher.\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants())//\n\t\t\t\t.describedAs(\"Too few test descriptors in classpath\")//\n\t\t\t\t.hasSizeGreaterThan(150);\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds)//\n\t\t\t\t.describedAs(\"Failed to pick up DefaultPackageTestCase via classpath scanning\")//\n\t\t\t\t.contains(uniqueIdForClass(ReflectionSupport.tryToLoadClass(\"DefaultPackageTestCase\").get()));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class1WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class1WithTestCases.class, \"test1()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class2WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class2WithTestCases.class, \"test2()\"));\n\t}\n\n\t@Test\n\tvoid classpathResolution() throws Exception {\n\t\tPath classpath = Path.of(\n\t\t\tDiscoverySelectorResolverTests.class.getProtectionDomain().getCodeSource().getLocation().toURI());\n\n\t\tList<ClasspathRootSelector> selectors = selectClasspathRoots(Set.of(classpath));\n\n\t\tresolve(request().selectors(selectors));\n\n\t\t// 150 is completely arbitrary. The actual number is likely much higher.\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants())//\n\t\t\t\t.describedAs(\"Too few test descriptors in classpath\")//\n\t\t\t\t.hasSizeGreaterThan(150);\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds)//\n\t\t\t\t.describedAs(\"Failed to pick up DefaultPackageTestCase via classpath scanning\")//\n\t\t\t\t.contains(uniqueIdForClass(ReflectionSupport.tryToLoadClass(\"DefaultPackageTestCase\").getNonNull()));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class1WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class1WithTestCases.class, \"test1()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(Class2WithTestCases.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(Class2WithTestCases.class, \"test2()\"));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(ClassWithStaticInnerTestCases.ShouldBeDiscovered.class, \"test1()\"));\n\t}\n\n\t@Test\n\tvoid classpathResolutionForJarFiles() throws Exception {\n\t\tURL jarUrl = requireNonNull(getClass().getResource(\"/jupiter-testjar.jar\"));\n\t\tPath path = Path.of(jarUrl.toURI());\n\t\tList<ClasspathRootSelector> selectors = selectClasspathRoots(Set.of(path));\n\n\t\tClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\ttry (URLClassLoader classLoader = new URLClassLoader(new URL[] { jarUrl })) {\n\t\t\tThread.currentThread().setContextClassLoader(classLoader);\n\n\t\t\tresolve(request().selectors(selectors));\n\n\t\t\tassertThat(uniqueIds()) //\n\t\t\t\t\t.contains(uniqueIdForStaticClass(\"com.example.project.FirstTest\")) //\n\t\t\t\t\t.contains(uniqueIdForStaticClass(\"com.example.project.SecondTest\"));\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\t@Test\n\tvoid nestedTestResolutionFromBaseClass() {\n\t\tClassSelector selector = selectClass(TestCaseWithNesting.class);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).hasSize(6);\n\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(TestCaseWithNesting.class, \"testA()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(TestCaseWithNesting.NestedTestCase.class, \"testB()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class, \"testC()\"));\n\t}\n\n\t@Test\n\tvoid nestedTestResolutionFromNestedTestClass() {\n\t\tClassSelector selector = selectClass(TestCaseWithNesting.NestedTestCase.class);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).hasSize(5);\n\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(TestCaseWithNesting.NestedTestCase.class, \"testB()\"));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class, \"testC()\"));\n\t}\n\n\t@Test\n\tvoid nestedTestResolutionFromUniqueId() {\n\t\tUniqueIdSelector selector = selectUniqueId(\n\t\t\tuniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class).toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).hasSize(4);\n\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class, \"testC()\"));\n\t}\n\n\t@Test\n\tvoid doubleNestedTestResolutionFromClass() {\n\t\tClassSelector selector = selectClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class);\n\n\t\tresolve(request().selectors(selector));\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).hasSize(4);\n\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class, \"testC()\"));\n\t}\n\n\t@Test\n\tvoid methodResolutionInDoubleNestedTestClass() throws NoSuchMethodException {\n\t\tMethodSelector selector = selectMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class,\n\t\t\tTestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class.getDeclaredMethod(\"testC\"));\n\n\t\tresolve(request().selectors(selector));\n\n\t\tassertEquals(4, requireNonNull(engineDescriptor).getDescendants().size());\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.DoubleNestedTestCase.class, \"testC()\"));\n\t}\n\n\t@Test\n\tvoid nestedTestResolutionFromUniqueIdToMethod() {\n\t\tUniqueIdSelector selector = selectUniqueId(\n\t\t\tuniqueIdForMethod(TestCaseWithNesting.NestedTestCase.class, \"testB()\").toString());\n\n\t\tresolve(request().selectors(selector));\n\n\t\tList<UniqueId> uniqueIds = uniqueIds();\n\t\tassertThat(uniqueIds).hasSize(3);\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForClass(TestCaseWithNesting.NestedTestCase.class));\n\t\tassertThat(uniqueIds).contains(uniqueIdForMethod(TestCaseWithNesting.NestedTestCase.class, \"testB()\"));\n\t}\n\n\t@Test\n\tvoid testFactoryMethodResolutionByUniqueId() {\n\t\tClass<?> clazz = MyTestClass.class;\n\t\tUniqueId factoryUid = uniqueIdForTestFactoryMethod(clazz, \"dynamicTest()\");\n\n\t\tresolve(request().selectors(selectUniqueId(factoryUid)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), factoryUid);\n\t}\n\n\t@Test\n\tvoid testTemplateMethodResolutionByUniqueId() {\n\t\tClass<?> clazz = TestClassWithTemplate.class;\n\t\tUniqueId templateUid = uniqueIdForTestTemplateMethod(clazz, \"testTemplate()\");\n\n\t\tresolve(request().selectors(selectUniqueId(templateUid)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), templateUid);\n\t}\n\n\t@Test\n\tvoid resolvingDynamicTestByUniqueIdResolvesUpToParentTestFactory() {\n\t\tClass<?> clazz = MyTestClass.class;\n\t\tUniqueId factoryUid = uniqueIdForTestFactoryMethod(clazz, \"dynamicTest()\");\n\t\tUniqueId dynamicTestUid = factoryUid.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#1\");\n\t\tUniqueId differentDynamicTestUid = factoryUid.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#2\");\n\n\t\tresolve(request().selectors(selectUniqueId(dynamicTestUid)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), factoryUid);\n\t\tTestDescriptor testClassDescriptor = getOnlyElement(requireNonNull(engineDescriptor).getChildren());\n\n\t\tTestDescriptor testFactoryDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tDynamicDescendantFilter dynamicDescendantFilter = getDynamicDescendantFilter(testFactoryDescriptor);\n\t\tassertThat(dynamicDescendantFilter.test(dynamicTestUid, 42)).isTrue();\n\t\tassertThat(dynamicDescendantFilter.test(differentDynamicTestUid, 42)).isFalse();\n\n\t\tassertAllSelectorsResolved();\n\t}\n\n\t@Test\n\tvoid resolvingDynamicContainerByUniqueIdResolvesUpToParentTestFactory() {\n\t\tClass<?> clazz = MyTestClass.class;\n\t\tUniqueId factoryUid = uniqueIdForTestFactoryMethod(clazz, \"dynamicTest()\");\n\t\tUniqueId dynamicContainerUid = factoryUid.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#1\");\n\t\tUniqueId differentDynamicContainerUid = factoryUid.append(DYNAMIC_CONTAINER_SEGMENT_TYPE, \"#2\");\n\t\tUniqueId dynamicTestUid = dynamicContainerUid.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#1\");\n\t\tUniqueId differentDynamicTestUid = dynamicContainerUid.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#2\");\n\n\t\tresolve(request().selectors(selectUniqueId(dynamicTestUid)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), factoryUid);\n\t\tTestDescriptor testClassDescriptor = getOnlyElement(requireNonNull(engineDescriptor).getChildren());\n\n\t\tTestDescriptor testFactoryDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tDynamicDescendantFilter dynamicDescendantFilter = getDynamicDescendantFilter(testFactoryDescriptor);\n\t\tassertThat(dynamicDescendantFilter.test(dynamicTestUid, 42)).isTrue();\n\t\tassertThat(dynamicDescendantFilter.test(differentDynamicContainerUid, 42)).isFalse();\n\t\tassertThat(dynamicDescendantFilter.test(differentDynamicTestUid, 42)).isFalse();\n\n\t\tassertAllSelectorsResolved();\n\t}\n\n\t@Test\n\tvoid resolvingDynamicTestByUniqueIdAndTestFactoryByMethodSelectorResolvesTestFactory() {\n\t\tClass<?> clazz = MyTestClass.class;\n\t\tUniqueId factoryUid = uniqueIdForTestFactoryMethod(clazz, \"dynamicTest()\");\n\t\tUniqueId dynamicTestUid = factoryUid.append(DYNAMIC_TEST_SEGMENT_TYPE, \"#1\");\n\n\t\tresolve(request().selectors(selectUniqueId(dynamicTestUid), selectMethod(clazz, \"dynamicTest\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), factoryUid);\n\t\tTestDescriptor testClassDescriptor = getOnlyElement(requireNonNull(engineDescriptor).getChildren());\n\t\tTestDescriptor testFactoryDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tDynamicDescendantFilter dynamicDescendantFilter = getDynamicDescendantFilter(testFactoryDescriptor);\n\t\tassertThat(dynamicDescendantFilter.test(UniqueId.root(\"foo\", \"bar\"), 42)).isTrue();\n\t}\n\n\tprivate DynamicDescendantFilter getDynamicDescendantFilter(TestDescriptor testDescriptor) {\n\t\tassertThat(testDescriptor).isInstanceOf(JupiterTestDescriptor.class);\n\t\treturn ((Filterable) testDescriptor).getDynamicDescendantFilter();\n\t}\n\n\t@Test\n\tvoid resolvingTestTemplateInvocationByUniqueIdResolvesOnlyUpToParentTestTemplate() {\n\t\tClass<?> clazz = TestClassWithTemplate.class;\n\t\tUniqueId templateUid = uniqueIdForTestTemplateMethod(clazz, \"testTemplate()\");\n\t\tUniqueId invocationUid = templateUid.append(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE, \"#1\");\n\n\t\tresolve(request().selectors(selectUniqueId(invocationUid)));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(2);\n\t\tassertThat(uniqueIds()).containsSequence(uniqueIdForClass(clazz), templateUid);\n\t}\n\n\t@Test\n\tvoid includingPackageNameFilterExcludesClassesInNonMatchingPackages() {\n\t\tresolve(request().selectors(selectClass(MatchingClass.class)).filters(\n\t\t\tincludePackageNames(\"org.junit.jupiter.engine.unknown\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).isEmpty();\n\t}\n\n\t@Test\n\tvoid includingPackageNameFilterIncludesClassesInMatchingPackages() {\n\t\tresolve(request().selectors(selectClass(MatchingClass.class)).filters(\n\t\t\tincludePackageNames(\"org.junit.jupiter.engine\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(3);\n\t}\n\n\t@Test\n\tvoid excludingPackageNameFilterExcludesClassesInMatchingPackages() {\n\t\tresolve(request().selectors(selectClass(MatchingClass.class)).filters(\n\t\t\texcludePackageNames(\"org.junit.jupiter.engine\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).isEmpty();\n\t}\n\n\t@Test\n\tvoid excludingPackageNameFilterIncludesClassesInNonMatchingPackages() {\n\t\tresolve(request().selectors(selectClass(MatchingClass.class)).filters(\n\t\t\texcludePackageNames(\"org.junit.jupiter.engine.unknown\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(3);\n\t}\n\n\t@Test\n\tvoid classNamePatternFilterExcludesNonMatchingClasses() {\n\t\tresolve(request().selectors(selectClass(MatchingClass.class), selectClass(OtherClass.class)).filters(\n\t\t\tincludeClassNamePatterns(\".*MatchingClass\")));\n\n\t\tassertThat(requireNonNull(engineDescriptor).getDescendants()).hasSize(3);\n\t}\n\n\tprivate void resolve(LauncherDiscoveryRequestBuilder builder) {\n\t\tengineDescriptor = discoverTests(builder.build()).getEngineDescriptor();\n\t}\n\n\tprivate TestDescriptor descriptorByUniqueId(UniqueId uniqueId) {\n\t\treturn requireNonNull(engineDescriptor).getDescendants().stream().filter(\n\t\t\td -> d.getUniqueId().equals(uniqueId)).findFirst().orElseThrow();\n\t}\n\n\tprivate List<UniqueId> uniqueIds() {\n\t\treturn requireNonNull(engineDescriptor).getDescendants().stream().map(TestDescriptor::getUniqueId).toList();\n\t}\n\n\tprivate LauncherDiscoveryRequestBuilder request() {\n\t\treturn defaultRequest() //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.listeners(discoveryListener);\n\t}\n\n\tprivate void assertAllSelectorsResolved() {\n\t\tArgumentCaptor<SelectorResolutionResult> resultCaptor = ArgumentCaptor.forClass(SelectorResolutionResult.class);\n\t\tverify(discoveryListener).selectorProcessed(eq(UniqueId.forEngine(\"junit-jupiter\")), any(),\n\t\t\tresultCaptor.capture());\n\t\tassertThat(resultCaptor.getAllValues()) //\n\t\t\t\t.flatExtracting(SelectorResolutionResult::getStatus) //\n\t\t\t\t.allMatch(Predicate.isEqual(RESOLVED));\n\t}\n\n\tprivate void assertUnresolved(DiscoverySelector selector) {\n\t\tvar result = verifySelectorProcessed(selector);\n\t\tassertThat(result.getStatus()).isEqualTo(UNRESOLVED);\n\t}\n\n\tprivate SelectorResolutionResult verifySelectorProcessed(DiscoverySelector selector) {\n\t\tArgumentCaptor<SelectorResolutionResult> resultCaptor = ArgumentCaptor.forClass(SelectorResolutionResult.class);\n\t\tverify(discoveryListener).selectorProcessed(eq(UniqueId.forEngine(\"junit-jupiter\")), eq(selector),\n\t\t\tresultCaptor.capture());\n\t\treturn resultCaptor.getValue();\n\t}\n\n}\n\n// -----------------------------------------------------------------------------\n\nclass NonTestClass {\n}\n\nabstract class AbstractTestClass {\n\n\t@SuppressWarnings(\"unused\")\n\t@Test\n\tvoid test() {\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass MyTestClass {\n\n\t@Test\n\tvoid test1() {\n\t}\n\n\t@Test\n\tvoid test2() {\n\t}\n\n\tvoid notATest() {\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTest() {\n\t\treturn Stream.empty();\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass YourTestClass {\n\n\t@Test\n\tvoid test3() {\n\t}\n\n\t@Test\n\tvoid test4() {\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass HerTestClass extends MyTestClass {\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@Test\n\tvoid test7(@SuppressWarnings(\"unused\") String param) {\n\t}\n}\n\nclass OtherTestClass {\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class NestedTestClass {\n\n\t\t@Test\n\t\tvoid test5() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test6() {\n\t\t}\n\t}\n}\n\nclass TestCaseWithNesting {\n\n\t@Test\n\tvoid testA() {\n\t}\n\n\t@Nested\n\tclass NestedTestCase {\n\n\t\t@Test\n\t\tvoid testB() {\n\t\t}\n\n\t\t@Nested\n\t\tclass DoubleNestedTestCase {\n\n\t\t\t@Test\n\t\t\tvoid testC() {\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass TestClassWithTemplate {\n\n\t@TestTemplate\n\tvoid testTemplate() {\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass MatchingClass {\n\t@Nested\n\tclass NestedClass {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass OtherClass {\n\t@Test\n\tvoid test() {\n\t}\n}\n\n@ClassTemplate\nclass ClassTemplateTestCase {\n\t@Test\n\tvoid test() {\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/DiscoveryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery;\n\nimport static java.util.Comparator.comparing;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeFalse;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestTemplateMethod;\nimport static org.junit.jupiter.params.provider.Arguments.argumentSet;\nimport static org.junit.platform.commons.test.IdeUtils.runningInEclipse;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.includeClassNamePatterns;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.DisabledInEclipse;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * Test correct test discovery in simple test classes for the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass DiscoveryTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid discoverTestClass() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(LocalTestCase.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(7, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid doNotDiscoverAbstractTestClass() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(AbstractTestCase.class)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(0, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"org.junit.jupiter.engine.discovery.DiscoveryTests$InterfaceTestCase\",\n\t\t\t\"org.junit.jupiter.engine.kotlin.KotlinInterfaceTestCase\" })\n\tvoid doNotDiscoverTestInterface(String className) {\n\n\t\tassumeFalse(runningInEclipse() && className.contains(\".kotlin.\"));\n\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(className)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(0, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\t@DisabledInEclipse\n\tvoid doNotDiscoverGeneratedKotlinDefaultImplsClass() {\n\t\tLauncherDiscoveryRequest request = defaultRequest() //\n\t\t\t\t.selectors(selectClass(\"org.junit.jupiter.engine.kotlin.KotlinInterfaceTestCase$DefaultImpls\")) //\n\t\t\t\t.build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(0, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\t@DisabledInEclipse\n\tvoid discoverDeclaredKotlinDefaultImplsClass() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectClass(\"org.junit.jupiter.engine.kotlin.KotlinDefaultImplsTestCase$DefaultImpls\")).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.engine.discovery.DiscoveryTests$ConcreteImplementationOfInterfaceTestCase\",\n\t\t\t\"org.junit.jupiter.engine.kotlin.KotlinInterfaceImplementationTestCase\" })\n\tvoid discoverTestClassInheritingTestsFromInterface(String className) {\n\n\t\tassumeFalse(runningInEclipse() && className.contains(\".kotlin.\"));\n\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectClass(className)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverMethodByUniqueId() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectUniqueId(JupiterUniqueIdBuilder.uniqueIdForMethod(LocalTestCase.class, \"test1()\"))).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverMethodByUniqueIdForOverloadedMethod() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectUniqueId(JupiterUniqueIdBuilder.uniqueIdForMethod(LocalTestCase.class, \"test4()\"))).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverMethodByUniqueIdForOverloadedMethodVariantThatAcceptsArguments() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectUniqueId(JupiterUniqueIdBuilder.uniqueIdForMethod(LocalTestCase.class,\n\t\t\t\t\"test4(\" + TestInfo.class.getName() + \")\"))).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverMethodByMethodReference() throws NoSuchMethodException {\n\t\tMethod testMethod = LocalTestCase.class.getDeclaredMethod(\"test3\");\n\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(\n\t\t\tselectMethod(LocalTestCase.class, testMethod)).build();\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverMultipleMethodsOfSameClass() {\n\t\tLauncherDiscoveryRequest request = defaultRequest().selectors(selectMethod(LocalTestCase.class, \"test1\"),\n\t\t\tselectMethod(LocalTestCase.class, \"test2\")).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTestsWithoutIssues(request);\n\n\t\tassertThat(engineDescriptor.getChildren()).hasSize(1);\n\t\tTestDescriptor classDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(classDescriptor.getChildren()).hasSize(2);\n\t}\n\n\t@Test\n\tvoid discoverCompositeSpec() {\n\t\tLauncherDiscoveryRequest spec = defaultRequest().selectors(\n\t\t\tselectUniqueId(JupiterUniqueIdBuilder.uniqueIdForMethod(LocalTestCase.class, \"test2()\")),\n\t\t\tselectClass(LocalTestCase.class)).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTests(spec).getEngineDescriptor();\n\t\tassertEquals(7, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverTestTemplateMethodByUniqueId() {\n\t\tLauncherDiscoveryRequest spec = defaultRequest().selectors(\n\t\t\tselectUniqueId(uniqueIdForTestTemplateMethod(TestTemplateClass.class, \"testTemplate()\"))).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTests(spec).getEngineDescriptor();\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverTestTemplateMethodByMethodSelector() {\n\t\tLauncherDiscoveryRequest spec = defaultRequest().selectors(\n\t\t\tselectMethod(TestTemplateClass.class, \"testTemplate\")).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTests(spec).getEngineDescriptor();\n\t\tassertEquals(2, engineDescriptor.getDescendants().size(), \"# resolved test descriptors\");\n\t}\n\n\t@Test\n\tvoid discoverDeeplyNestedTestMethodByNestedMethodSelector() throws Exception {\n\t\tvar selector = selectNestedMethod(\n\t\t\tList.of(TestCaseWithExtendedNested.class, TestCaseWithExtendedNested.ConcreteInner1.class),\n\t\t\tAbstractSuperClass.NestedInAbstractClass.class,\n\t\t\tAbstractSuperClass.NestedInAbstractClass.class.getDeclaredMethod(\"test\"));\n\t\tLauncherDiscoveryRequest spec = defaultRequest().selectors(selector).build();\n\n\t\tTestDescriptor engineDescriptor = discoverTests(spec).getEngineDescriptor();\n\n\t\tClassTestDescriptor topLevelClassDescriptor = (ClassTestDescriptor) getOnlyElement(\n\t\t\tengineDescriptor.getChildren());\n\t\tassertThat(topLevelClassDescriptor.getTestClass()).isEqualTo(TestCaseWithExtendedNested.class);\n\n\t\tNestedClassTestDescriptor firstLevelNestedClassDescriptor = (NestedClassTestDescriptor) getOnlyElement(\n\t\t\ttopLevelClassDescriptor.getChildren());\n\t\tassertThat(firstLevelNestedClassDescriptor.getTestClass()).isEqualTo(\n\t\t\tTestCaseWithExtendedNested.ConcreteInner1.class);\n\n\t\tNestedClassTestDescriptor secondLevelNestedClassDescriptor = (NestedClassTestDescriptor) getOnlyElement(\n\t\t\tfirstLevelNestedClassDescriptor.getChildren());\n\t\tassertThat(secondLevelNestedClassDescriptor.getTestClass()).isEqualTo(\n\t\t\tAbstractSuperClass.NestedInAbstractClass.class);\n\n\t\tTestMethodTestDescriptor methodDescriptor = (TestMethodTestDescriptor) getOnlyElement(\n\t\t\tsecondLevelNestedClassDescriptor.getChildren());\n\t\tassertThat(methodDescriptor.getTestMethod().getName()).isEqualTo(\"test\");\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"requestsForTestClassWithInvalidTestMethod\")\n\tvoid reportsWarningForTestClassWithInvalidTestMethod(LauncherDiscoveryRequest request) throws Exception {\n\n\t\tvar method = InvalidTestCases.InvalidTestMethodTestCase.class.getDeclaredMethod(\"test\");\n\n\t\tvar results = discoverTests(request);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(3);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not be private. It will not be executed.\", method.toGenericString());\n\t\tassertThat(discoveryIssues.get(1).message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not be static. It will not be executed.\", method.toGenericString());\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not return a value. It will not be executed.\",\n\t\t\t\t\tmethod.toGenericString());\n\t}\n\n\tstatic List<Named<LauncherDiscoveryRequest>> requestsForTestClassWithInvalidTestMethod() {\n\t\treturn List.of( //\n\t\t\tnamed(\"directly selected\",\n\t\t\t\tdefaultRequest().selectors(selectClass(InvalidTestCases.InvalidTestMethodTestCase.class)).build()), //\n\t\t\tnamed(\"indirectly selected\", defaultRequest() //\n\t\t\t\t\t.selectors(selectPackage(InvalidTestCases.InvalidTestMethodTestCase.class.getPackageName())) //\n\t\t\t\t\t.filters(includeClassNamePatterns(\n\t\t\t\t\t\tPattern.quote(InvalidTestCases.InvalidTestMethodTestCase.class.getName()))).build()), //\n\t\t\tnamed(\"subclasses\", defaultRequest() //\n\t\t\t\t\t.selectors(selectClasses(InvalidTestCases.InvalidTestMethodSubclass1TestCase.class,\n\t\t\t\t\t\tInvalidTestCases.InvalidTestMethodSubclass2TestCase.class)) //\n\t\t\t\t\t.build()) //\n\t\t);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"requestsForTestClassWithInvalidStandaloneTestClass\")\n\tvoid reportsWarningForInvalidStandaloneTestClass(LauncherDiscoveryRequest request, Class<?> testClass) {\n\n\t\tvar results = discoverTests(request);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(2);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\",\n\t\t\t\t\ttestClass.getName());\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\"Test class '%s' must not be private. It will not be executed.\", testClass.getName());\n\t}\n\n\tstatic List<Arguments> requestsForTestClassWithInvalidStandaloneTestClass() {\n\t\treturn List.of( //\n\t\t\targumentSet(\"directly selected\",\n\t\t\t\tdefaultRequest().selectors(selectClass(InvalidTestCases.InvalidTestClassTestCase.class)).build(),\n\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.class), //\n\t\t\targumentSet(\"indirectly selected\", defaultRequest() //\n\t\t\t\t\t.selectors(selectPackage(InvalidTestCases.InvalidTestClassTestCase.class.getPackageName())) //\n\t\t\t\t\t.filters(includeClassNamePatterns(\n\t\t\t\t\t\tPattern.quote(InvalidTestCases.InvalidTestClassTestCase.class.getName()))).build(), //\n\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.class), //\n\t\t\targumentSet(\"subclass\", defaultRequest() //\n\t\t\t\t\t.selectors(selectClass(InvalidTestCases.InvalidTestClassSubclassTestCase.class)) //\n\t\t\t\t\t.build(), //\n\t\t\t\tInvalidTestCases.InvalidTestClassSubclassTestCase.class) //\n\t\t);\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"requestsForTestClassWithInvalidNestedTestClass\")\n\tvoid reportsWarningForInvalidNestedTestClass(LauncherDiscoveryRequest request) {\n\n\t\tvar results = discoverTests(request);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(2);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\"@Nested class '%s' must not be private. It will not be executed.\",\n\t\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.Inner.class.getName());\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.startsWith(\"@Nested class '%s' must not be static.\".formatted(\n\t\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.Inner.class.getName()));\n\t}\n\n\tstatic List<Named<LauncherDiscoveryRequest>> requestsForTestClassWithInvalidNestedTestClass() {\n\t\treturn List.of( //\n\t\t\tnamed(\"directly selected\",\n\t\t\t\tdefaultRequest().selectors(selectClass(InvalidTestCases.InvalidTestClassTestCase.Inner.class)).build()), //\n\t\t\tnamed(\"subclass\", defaultRequest() //\n\t\t\t\t\t.selectors(selectNestedClass(List.of(InvalidTestCases.InvalidTestClassSubclassTestCase.class),\n\t\t\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.Inner.class)) //\n\t\t\t\t\t.build()) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid reportsWarningForTestClassWithPotentialNestedTestClasses() {\n\n\t\tvar results = discoverTestsForClass(InvalidTestCases.class);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(2);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Inner class '%s' looks like it was intended to be a test class but will not be executed. It must be static or annotated with @Nested.\",\n\t\t\t\t\tInvalidTestCases.InvalidTestClassSubclassTestCase.class.getName());\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Inner class '%s' looks like it was intended to be a test class but will not be executed. It must be static or annotated with @Nested.\",\n\t\t\t\t\tInvalidTestCases.InvalidTestClassTestCase.class.getName());\n\t}\n\n\t@Test\n\tvoid ignoresUnrelatedClassDefinitionCycles() {\n\t\tvar results = discoverTestsForClass(UnrelatedRecursiveHierarchyTestCase.class);\n\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\t}\n\n\t@Test\n\tvoid ignoresRecursiveNonTestHierarchyCycles() {\n\t\tvar results = discoverTestsForClass(NonTestRecursiveHierarchyTestCase.class);\n\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\t}\n\n\t@Test\n\tvoid reportsMissingNestedAnnotationOnRecursiveHierarchy() {\n\t\tvar results = discoverTestsForClass(RecursiveHierarchyWithoutNestedTestCase.class);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\t\tassertThat(discoveryIssues.getFirst().severity()) //\n\t\t\t\t.isEqualTo(Severity.WARNING);\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Inner class '%s' looks like it was intended to be a test class but will not be executed. It must be static or annotated with @Nested.\",\n\t\t\t\t\tRecursiveHierarchyWithoutNestedTestCase.Inner.class.getName());\n\t}\n\n\t@Test\n\tvoid reportsWarningsForInvalidTags() throws Exception {\n\n\t\tvar results = discoverTestsForClass(InvalidTagsTestCase.class);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(2);\n\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\"Invalid tag syntax in @Tag(\\\"\\\") declaration on class '%s'. Tag will be ignored.\",\n\t\t\t\t\tInvalidTagsTestCase.class.getName());\n\t\tassertThat(discoveryIssues.getFirst().source()) //\n\t\t\t\t.contains(ClassSource.from(InvalidTagsTestCase.class));\n\n\t\tvar method = InvalidTagsTestCase.class.getDeclaredMethod(\"test\");\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\"Invalid tag syntax in @Tag(\\\"|\\\") declaration on method '%s'. Tag will be ignored.\",\n\t\t\t\t\tmethod.toGenericString());\n\t\tassertThat(discoveryIssues.getLast().source()) //\n\t\t\t\t.contains(org.junit.platform.engine.support.descriptor.MethodSource.from(method));\n\t}\n\n\t@Test\n\tvoid reportsWarningsForBlankDisplayNames() throws Exception {\n\n\t\tvar results = discoverTestsForClass(BlankDisplayNamesTestCase.class);\n\n\t\tvar discoveryIssues = results.getDiscoveryIssues().stream().sorted(comparing(DiscoveryIssue::message)).toList();\n\t\tassertThat(discoveryIssues).hasSize(2);\n\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\"@DisplayName on class '%s' must be declared with a non-blank value.\",\n\t\t\t\t\tBlankDisplayNamesTestCase.class.getName());\n\t\tassertThat(discoveryIssues.getFirst().source()) //\n\t\t\t\t.contains(ClassSource.from(BlankDisplayNamesTestCase.class));\n\n\t\tvar method = BlankDisplayNamesTestCase.class.getDeclaredMethod(\"test\");\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\"@DisplayName on method '%s' must be declared with a non-blank value.\",\n\t\t\t\t\tmethod.toGenericString());\n\t\tassertThat(discoveryIssues.getLast().source()) //\n\t\t\t\t.contains(org.junit.platform.engine.support.descriptor.MethodSource.from(method));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t@SuppressWarnings(\"unused\")\n\tstatic abstract class AbstractTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Test\n\t\tabstract void abstractTest();\n\t}\n\n\tstatic class LocalTestCase {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test4() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test4(TestInfo testInfo) {\n\t\t}\n\n\t\t@CustomTestAnnotation\n\t\tvoid customTestAnnotation() {\n\t\t\t/* no-op */\n\t\t}\n\n\t}\n\n\t@Test\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CustomTestAnnotation {\n\t}\n\n\tstatic class TestTemplateClass {\n\n\t\t@TestTemplate\n\t\tvoid testTemplate() {\n\t\t}\n\n\t}\n\n\tstatic abstract class AbstractSuperClass {\n\t\t@Nested\n\t\tclass NestedInAbstractClass {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TestCaseWithExtendedNested {\n\t\t@Nested\n\t\tclass ConcreteInner1 extends AbstractSuperClass {\n\t\t}\n\t}\n\n\tstatic class InvalidTestCases {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\tstatic class InvalidTestMethodTestCase {\n\t\t\t@Test\n\t\t\tprivate static int test() {\n\t\t\t\treturn fail(\"should not be called\");\n\t\t\t}\n\t\t}\n\n\t\tstatic class InvalidTestMethodSubclass1TestCase extends InvalidTestMethodTestCase {\n\t\t}\n\n\t\tstatic class InvalidTestMethodSubclass2TestCase extends InvalidTestMethodTestCase {\n\t\t}\n\n\t\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"InnerClassMayBeStatic\" })\n\t\tprivate class InvalidTestClassTestCase {\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tfail(\"should not be called\");\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tprivate static class Inner {\n\t\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tfail(\"should not be called\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\tprivate class InvalidTestClassSubclassTestCase extends InvalidTestClassTestCase {\n\t\t}\n\n\t}\n\n\tstatic class UnrelatedRecursiveHierarchyTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@SuppressWarnings({ \"InnerClassMayBeStatic\", \"unused\" })\n\t\tclass Inner {\n\t\t\tclass Recursive extends Inner {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class RecursiveHierarchyWithoutNestedTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@SuppressWarnings({ \"InnerClassMayBeStatic\", \"unused\" })\n\t\tclass Inner extends RecursiveHierarchyWithoutNestedTestCase {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tstatic class NonTestRecursiveHierarchyTestCase {\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tclass Inner extends NonTestRecursiveHierarchyTestCase {\n\t\t}\n\t}\n\n\t@Tag(\"\")\n\tstatic class InvalidTagsTestCase {\n\t\t@Test\n\t\t@Tag(\"|\")\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@DisplayName(\"\")\n\tstatic class BlankDisplayNamesTestCase {\n\t\t@Test\n\t\t@DisplayName(\"\\t\")\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tinterface InterfaceTestCase {\n\t\t@Test\n\t\tdefault void test() {\n\t\t}\n\t}\n\n\tstatic class ConcreteImplementationOfInterfaceTestCase implements InterfaceTestCase {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestFactoryMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Assertions;\nimport org.junit.jupiter.api.DynamicContainer;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Unit tests for {@link IsTestFactoryMethod}.\n *\n * @since 5.0\n */\nclass IsTestFactoryMethodTests {\n\n\tfinal List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\tfinal Predicate<Method> isTestFactoryMethod = new IsTestFactoryMethod(\n\t\tDiscoveryIssueReporter.collecting(discoveryIssues));\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"dynamicTestsFactoryFromCollection\", \"dynamicTestsFactoryFromStreamWithExtendsWildcard\",\n\t\t\t\"dynamicTestsFactoryFromNode\", \"dynamicTestsFactoryFromTest\", \"dynamicTestsFactoryFromContainer\",\n\t\t\t\"dynamicTestsFactoryFromNodeArray\", \"dynamicTestsFactoryFromTestArray\",\n\t\t\t\"dynamicTestsFactoryFromContainerArray\" })\n\tvoid validFactoryMethods(String methodName) {\n\t\tassertThat(isTestFactoryMethod).accepts(method(methodName));\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"bogusVoidFactory\", \"bogusStringsFactory\", \"bogusStringArrayFactory\",\n\t\t\t\"dynamicTestsFactoryFromStreamWithSuperWildcard\" })\n\tvoid invalidFactoryMethods(String methodName) {\n\t\tvar method = method(methodName);\n\n\t\tassertThat(isTestFactoryMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(DiscoveryIssue.Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\n\t\t\t\"@TestFactory method '%s' must return a single org.junit.jupiter.api.DynamicNode or a \"\n\t\t\t\t\t+ \"Stream, Collection, Iterable, Iterator, Iterator provider, or array of org.junit.jupiter.api.DynamicNode. \"\n\t\t\t\t\t+ \"It will not be executed.\",\n\t\t\tmethod.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"objectFactory\", \"objectArrayFactory\", \"rawCollectionFactory\", \"unboundStreamFactory\" })\n\tvoid suspiciousFactoryMethods(String methodName) {\n\t\tvar method = method(methodName);\n\n\t\tassertThat(isTestFactoryMethod).accepts(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(DiscoveryIssue.Severity.INFO);\n\t\tassertThat(issue.message()).isEqualTo(\n\t\t\t\"The declared return type of @TestFactory method '%s' does not support static validation. \"\n\t\t\t\t\t+ \"It must return a single org.junit.jupiter.api.DynamicNode or a \"\n\t\t\t\t\t+ \"Stream, Collection, Iterable, Iterator, Iterator provider, or array of org.junit.jupiter.api.DynamicNode.\",\n\t\t\tmethod.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\tprivate static Method method(String name) {\n\t\treturn ReflectionSupport.findMethod(ClassWithTestFactoryMethods.class, name).orElseThrow();\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static class ClassWithTestFactoryMethods {\n\n\t\t@TestFactory\n\t\tCollection<DynamicTest> dynamicTestsFactoryFromCollection() {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<? extends DynamicTest> dynamicTestsFactoryFromStreamWithExtendsWildcard() {\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest dynamicTestsFactoryFromNode() {\n\t\t\treturn dynamicTest(\"foo\", Assertions::fail);\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest dynamicTestsFactoryFromTest() {\n\t\t\treturn dynamicTest(\"foo\", Assertions::fail);\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicNode dynamicTestsFactoryFromContainer() {\n\t\t\treturn dynamicContainer(\"foo\", Stream.empty());\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicNode[] dynamicTestsFactoryFromNodeArray() {\n\t\t\treturn new DynamicNode[0];\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest[] dynamicTestsFactoryFromTestArray() {\n\t\t\treturn new DynamicTest[0];\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicContainer[] dynamicTestsFactoryFromContainerArray() {\n\t\t\treturn new DynamicContainer[0];\n\t\t}\n\n\t\t@TestFactory\n\t\tvoid bogusVoidFactory() {\n\t\t}\n\n\t\t@TestFactory\n\t\tCollection<String> bogusStringsFactory() {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\n\t\t@TestFactory\n\t\tString[] bogusStringArrayFactory() {\n\t\t\treturn new String[0];\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<? super DynamicTest> dynamicTestsFactoryFromStreamWithSuperWildcard() {\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@TestFactory\n\t\tObject objectFactory() {\n\t\t\treturn dynamicTest(\"foo\", Assertions::fail);\n\t\t}\n\n\t\t@TestFactory\n\t\tObject[] objectArrayFactory() {\n\t\t\treturn new DynamicNode[0];\n\t\t}\n\n\t\t@SuppressWarnings(\"rawtypes\")\n\t\t@TestFactory\n\t\tCollection rawCollectionFactory() {\n\t\t\treturn new ArrayList<>();\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<?> unboundStreamFactory() {\n\t\t\treturn Stream.of();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static java.util.Comparator.comparing;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Unit tests for {@link IsTestMethod}.\n *\n * @since 5.0\n */\nclass IsTestMethodTests {\n\n\tfinal List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\tfinal Predicate<Method> isTestMethod = new IsTestMethod(DiscoveryIssueReporter.collecting(discoveryIssues));\n\n\t@Test\n\tvoid publicTestMethod() {\n\t\tMethod method = method(\"publicTestMethod\");\n\t\t// Ensure that somebody doesn't accidentally delete the public modifier again.\n\t\tassertTrue(ModifierSupport.isPublic(method));\n\t\tassertThat(isTestMethod).accepts(method);\n\t}\n\n\t@Test\n\tvoid publicTestMethodWithArgument() {\n\t\tMethod method = method(\"publicTestMethodWithArgument\", TestInfo.class);\n\t\t// Ensure that somebody doesn't accidentally delete the public modifier again.\n\t\tassertTrue(ModifierSupport.isPublic(method));\n\t\tassertThat(isTestMethod).accepts(method);\n\t}\n\n\t@Test\n\tvoid protectedTestMethod() {\n\t\tassertThat(isTestMethod).accepts(method(\"protectedTestMethod\"));\n\t}\n\n\t@Test\n\tvoid packageVisibleTestMethod() {\n\t\tassertThat(isTestMethod).accepts(method(\"packageVisibleTestMethod\"));\n\t}\n\n\t@Test\n\tvoid bogusAbstractTestMethod() {\n\t\tvar method = abstractMethod(\"bogusAbstractTestMethod\");\n\n\t\tassertThat(isTestMethod).rejects(method);\n\t\tassertThat(discoveryIssues).isEmpty();\n\t}\n\n\t@Test\n\tvoid bogusAbstractNonVoidTestMethod() {\n\t\tvar method = abstractMethod(\"bogusAbstractNonVoidTestMethod\");\n\n\t\tassertThat(isTestMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not return a value. It will not be executed.\",\n\t\t\t\t\tmethod.toGenericString());\n\t}\n\n\t@Test\n\tvoid bogusStaticTestMethod() {\n\t\tvar method = method(\"bogusStaticTestMethod\");\n\n\t\tassertThat(isTestMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\"@Test method '%s' must not be static. It will not be executed.\",\n\t\t\tmethod.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\t@Test\n\tvoid bogusPrivateTestMethod() {\n\t\tvar method = method(\"bogusPrivateTestMethod\");\n\n\t\tassertThat(isTestMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\"@Test method '%s' must not be private. It will not be executed.\",\n\t\t\tmethod.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"bogusTestMethodReturningObject\", \"bogusTestMethodReturningVoidReference\",\n\t\t\t\"bogusTestMethodReturningPrimitive\" })\n\tvoid bogusNonVoidTestMethods(String methodName) {\n\t\tvar method = method(methodName);\n\n\t\tassertThat(isTestMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\"@Test method '%s' must not return a value. It will not be executed.\",\n\t\t\tmethod.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\t@Test\n\tvoid bogusStaticPrivateNonVoidTestMethod() {\n\t\tvar method = method(\"bogusStaticPrivateNonVoidTestMethod\");\n\n\t\tassertThat(isTestMethod).rejects(method);\n\n\t\tassertThat(discoveryIssues).hasSize(3);\n\t\tdiscoveryIssues.sort(comparing(DiscoveryIssue::message));\n\t\tassertThat(discoveryIssues.getFirst().message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not be private. It will not be executed.\", method.toGenericString());\n\t\tassertThat(discoveryIssues.get(1).message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not be static. It will not be executed.\", method.toGenericString());\n\t\tassertThat(discoveryIssues.getLast().message()) //\n\t\t\t\t.isEqualTo(\"@Test method '%s' must not return a value. It will not be executed.\",\n\t\t\t\t\tmethod.toGenericString());\n\t}\n\n\tprivate static Method method(String name, Class<?>... parameterTypes) {\n\t\treturn ReflectionSupport.findMethod(ClassWithTestMethods.class, name, parameterTypes).orElseThrow();\n\t}\n\n\tprivate Method abstractMethod(String name) {\n\t\treturn ReflectionSupport.findMethod(AbstractClassWithAbstractTestMethod.class, name).orElseThrow();\n\t}\n\n\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"unused\" })\n\tprivate static abstract class AbstractClassWithAbstractTestMethod {\n\n\t\t@Test\n\t\tabstract void bogusAbstractTestMethod();\n\n\t\t@Test\n\t\tabstract int bogusAbstractNonVoidTestMethod();\n\n\t}\n\n\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"unused\" })\n\tprivate static class ClassWithTestMethods {\n\n\t\t@Test\n\t\tstatic void bogusStaticTestMethod() {\n\t\t}\n\n\t\t@Test\n\t\tprivate void bogusPrivateTestMethod() {\n\t\t}\n\n\t\t@Test\n\t\tprivate static int bogusStaticPrivateNonVoidTestMethod() {\n\t\t\treturn 42;\n\t\t}\n\n\t\t@Test\n\t\tString bogusTestMethodReturningObject() {\n\t\t\treturn \"\";\n\t\t}\n\n\t\t@Test\n\t\tVoid bogusTestMethodReturningVoidReference() {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Test\n\t\tint bogusTestMethodReturningPrimitive() {\n\t\t\treturn 0;\n\t\t}\n\n\t\t@Test\n\t\tpublic void publicTestMethod() {\n\t\t}\n\n\t\t@Test\n\t\tpublic void publicTestMethodWithArgument(TestInfo info) {\n\t\t}\n\n\t\t@Test\n\t\tprotected void protectedTestMethod() {\n\t\t}\n\n\t\t@Test\n\t\tvoid packageVisibleTestMethod() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/IsTestTemplateMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\n/**\n * Unit tests for {@link IsTestTemplateMethod}.\n *\n * @since 5.0\n */\nclass IsTestTemplateMethodTests {\n\n\tfinal List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\tfinal IsTestTemplateMethod isTestTemplateMethod = new IsTestTemplateMethod(\n\t\tDiscoveryIssueReporter.collecting(discoveryIssues));\n\n\t@Test\n\tvoid testTemplateMethodReturningVoid() {\n\t\tassertThat(isTestTemplateMethod).accepts(method(\"templateReturningVoid\"));\n\t}\n\n\t@Test\n\tvoid bogusTestTemplateMethodReturningObject() {\n\t\tvar method = method(\"bogusTemplateReturningObject\");\n\n\t\tassertThat(isTestTemplateMethod).rejects(method);\n\n\t\tvar issue = getOnlyElement(discoveryIssues);\n\t\tassertThat(issue.severity()).isEqualTo(DiscoveryIssue.Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\n\t\t\t\"@TestTemplate method '%s' must not return a value. It will not be executed.\", method.toGenericString());\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\t}\n\n\tprivate static Method method(String name) {\n\t\treturn ReflectionSupport.findMethod(ClassWithTestTemplateMethods.class, name).orElseThrow();\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static class ClassWithTestTemplateMethods {\n\n\t\t@TestTemplate\n\t\tvoid templateReturningVoid() {\n\t\t}\n\n\t\t@TestTemplate\n\t\tString bogusTemplateReturningObject() {\n\t\t\treturn \"\";\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/discovery/predicates/TestClassPredicatesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.discovery.predicates;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\n\npublic class TestClassPredicatesTests {\n\n\tprivate final List<DiscoveryIssue> discoveryIssues = new ArrayList<>();\n\tprivate final TestClassPredicates predicates = new TestClassPredicates(\n\t\tDiscoveryIssueReporter.collecting(discoveryIssues));\n\n\t@Nested\n\tclass StandaloneTestClasses {\n\n\t\t@Test\n\t\tvoid classWithTestMethodEvaluatesToTrue() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(ClassWithTestMethod.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(ClassWithTestMethod.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid classWithTestFactoryEvaluatesToTrue() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(ClassWithTestFactory.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(ClassWithTestFactory.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid classWithTestTemplateEvaluatesToTrue() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(ClassWithTestTemplate.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(ClassWithTestTemplate.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid classWithNestedTestClassEvaluatesToTrue() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(ClassWithNestedTestClass.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(ClassWithNestedTestClass.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid staticTestClassEvaluatesToTrue() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(TestCases.StaticTestCase.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(TestCases.StaticTestCase.class));\n\t\t}\n\n\t\t// -------------------------------------------------------------------------\n\n\t\t@Test\n\t\tvoid abstractClassEvaluatesToFalse() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(AbstractClass.class));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(AbstractClass.class));\n\t\t\tassertThat(discoveryIssues).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid localClassEvaluatesToFalse() {\n\n\t\t\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"NewClassNamingConvention\" })\n\t\t\tclass LocalClass {\n\t\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar candidate = LocalClass.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be a local class. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactly(issue);\n\t\t}\n\n\t\t@Test\n\t\tvoid anonymousClassEvaluatesToFalse() {\n\n\t\t\tObject object = new Object() {\n\t\t\t\t@SuppressWarnings(\"unused\")\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tClass<?> candidate = object.getClass();\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be anonymous. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactly(issue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateClassWithTestMethodEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.PrivateClassWithTestMethod.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notPrivateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tvar notInnerClassIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\".formatted(\n\t\t\t\t\tcandidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notPrivateIssue, notInnerClassIssue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateClassWithTestFactoryEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.PrivateClassWithTestFactory.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notPrivateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tvar notInnerClassIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\".formatted(\n\t\t\t\t\tcandidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notPrivateIssue, notInnerClassIssue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateClassWithTestTemplateEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.PrivateClassWithTestTemplate.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notPrivateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tvar notInnerClassIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\".formatted(\n\t\t\t\t\tcandidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notPrivateIssue, notInnerClassIssue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateClassWithNestedTestCasesEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.PrivateClassWithNestedTestClass.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notPrivateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tvar notInnerClassIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\".formatted(\n\t\t\t\t\tcandidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactlyInAnyOrder(notPrivateIssue, notInnerClassIssue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateStaticTestClassEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.PrivateStaticTestCase.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notPrivateIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactly(notPrivateIssue);\n\t\t}\n\n\t\t/*\n\t\t * see https://github.com/junit-team/junit-framework/issues/2249\n\t\t */\n\t\t@Test\n\t\tvoid recursiveHierarchies() {\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(TestCases.OuterClass.class));\n\t\t\tassertTrue(predicates.isValidStandaloneTestClass(TestCases.OuterClass.class));\n\t\t\tassertThat(discoveryIssues).isEmpty();\n\n\t\t\tvar candidate = TestCases.OuterClass.RecursiveInnerClass.class;\n\n\t\t\tassertTrue(predicates.looksLikeIntendedTestClass(candidate));\n\t\t\tassertFalse(predicates.isValidStandaloneTestClass(candidate));\n\n\t\t\tvar notInnerClassIssue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"Test class '%s' must not be an inner class unless annotated with @Nested. It will not be executed.\".formatted(\n\t\t\t\t\tcandidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues).containsExactly(notInnerClassIssue);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass NestedTestClasses {\n\n\t\t@Test\n\t\tvoid innerClassEvaluatesToTrue() {\n\t\t\tvar candidate = TestCases.NestedClassesTestCase.InnerClass.class;\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertTrue(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).accepts(candidate);\n\t\t}\n\n\t\t@Test\n\t\tvoid staticNestedClassEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.NestedClassesTestCase.StaticNestedClass.class;\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertFalse(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).rejects(candidate);\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"@Nested class '%s' must not be static. \".formatted(candidate.getName())\n\t\t\t\t\t\t+ \"It will only be executed if discovered as a standalone test class. \"\n\t\t\t\t\t\t+ \"You should remove the annotation or make it non-static to resolve this warning.\") //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues.stream().distinct()).containsExactly(issue);\n\t\t}\n\n\t\t@Test\n\t\tvoid topLevelClassEvaluatesToFalse() {\n\t\t\tvar candidate = InvalidTopLevelNestedTestClass.class;\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertFalse(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).rejects(candidate);\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t(\"Top-level class '%s' must not be annotated with @Nested. \".formatted(candidate.getName())\n\t\t\t\t\t\t+ \"It will be executed anyway for backward compatibility. \"\n\t\t\t\t\t\t+ \"You should remove the @Nested annotation to resolve this warning.\")) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues.stream().distinct()).containsExactly(issue);\n\t\t}\n\n\t\t@Test\n\t\tvoid privateNestedClassEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.NestedClassesTestCase.PrivateInnerClass.class;\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertFalse(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).rejects(candidate);\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"@Nested class '%s' must not be private. It will not be executed.\".formatted(candidate.getName())) //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues.stream().distinct()).containsExactly(issue);\n\t\t}\n\n\t\t@Test\n\t\tvoid abstractInnerClassEvaluatesToFalse() {\n\t\t\tvar candidate = TestCases.NestedClassesTestCase.AbstractInnerClass.class;\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertFalse(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).rejects(candidate);\n\t\t\tassertThat(discoveryIssues).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid localClassEvaluatesToFalse() {\n\n\t\t\t@Nested\n\t\t\tclass LocalClass {\n\t\t\t}\n\n\t\t\tvar candidate = LocalClass.class;\n\n\t\t\tassertThat(predicates.isAnnotatedWithNested).accepts(candidate);\n\t\t\tassertFalse(predicates.isValidNestedTestClass(candidate));\n\t\t\tassertThat(predicates.isAnnotatedWithNestedAndValid).rejects(candidate);\n\n\t\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING,\n\t\t\t\t\"@Nested class '%s' must not be static. \".formatted(candidate.getName())\n\t\t\t\t\t\t+ \"It will only be executed if discovered as a standalone test class. \"\n\t\t\t\t\t\t+ \"You should remove the annotation or make it non-static to resolve this warning.\") //\n\t\t\t\t\t.source(ClassSource.from(candidate)) //\n\t\t\t\t\t.build();\n\t\t\tassertThat(discoveryIssues.stream().distinct()).containsExactly(issue);\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class TestCases {\n\n\t\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"InnerClassMayBeStatic\" })\n\t\tprivate class PrivateClassWithTestMethod {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\n\t\t}\n\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tprivate class PrivateClassWithTestFactory {\n\n\t\t\t@TestFactory\n\t\t\tCollection<DynamicTest> factory() {\n\t\t\t\treturn new ArrayList<>();\n\t\t\t}\n\n\t\t}\n\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tprivate class PrivateClassWithTestTemplate {\n\n\t\t\t@TestTemplate\n\t\t\tvoid template(int a) {\n\t\t\t}\n\n\t\t}\n\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tprivate class PrivateClassWithNestedTestClass {\n\n\t\t\t@Nested\n\t\t\tclass InnerClass {\n\n\t\t\t\t@Test\n\t\t\t\tvoid first() {\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid second() {\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\t// -------------------------------------------------------------------------\n\n\t\tstatic class StaticTestCase {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\tprivate static class PrivateStaticTestCase {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OuterClass {\n\n\t\t\t@Nested\n\t\t\tclass InnerClass {\n\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Intentionally commented out so that RecursiveInnerClass is NOT a candidate test class\n\t\t\t// @Nested\n\t\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\t\tclass RecursiveInnerClass extends OuterClass {\n\t\t\t}\n\n\t\t}\n\n\t\tprivate static class NestedClassesTestCase {\n\n\t\t\t@Nested\n\t\t\tclass InnerClass {\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Nested\n\t\t\tstatic class StaticNestedClass {\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Nested\n\t\t\tprivate class PrivateInnerClass {\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tprivate abstract class AbstractInnerClass {\n\t\t\t}\n\n\t\t}\n\t}\n\n}\n\n// -----------------------------------------------------------------------------\n\nabstract class AbstractClass {\n\t@SuppressWarnings(\"unused\")\n\t@Test\n\tvoid test() {\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass ClassWithTestMethod {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass ClassWithTestFactory {\n\n\t@TestFactory\n\tCollection<DynamicTest> factory() {\n\t\treturn new ArrayList<>();\n\t}\n\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass ClassWithTestTemplate {\n\n\t@TestTemplate\n\tvoid template(int a) {\n\t}\n\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\nclass ClassWithNestedTestClass {\n\n\t@Nested\n\tclass InnerClass {\n\n\t\t@Test\n\t\tvoid first() {\n\t\t}\n\n\t\t@Test\n\t\tvoid second() {\n\t\t}\n\n\t}\n}\n\n@SuppressWarnings(\"NewClassNamingConvention\")\n@Nested\nclass InvalidTopLevelNestedTestClass {\n\t@Test\n\tvoid test() {\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/AbstractExecutableInvokerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.execution.ParameterResolutionUtilsTests.ConfigurableParameterResolver;\nimport org.junit.jupiter.engine.execution.ParameterResolutionUtilsTests.ConstructorInjectionTestCase;\nimport org.junit.jupiter.engine.execution.ParameterResolutionUtilsTests.MethodSource;\nimport org.junit.jupiter.engine.execution.ParameterResolutionUtilsTests.NumberParameterResolver;\nimport org.junit.jupiter.engine.execution.ParameterResolutionUtilsTests.StringParameterResolver;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.9\n */\nabstract class AbstractExecutableInvokerTests {\n\n\tprivate static final String ENIGMA = \"enigma\";\n\n\tprotected final MethodSource instance = mock();\n\n\tprotected @Nullable Method method;\n\n\tprotected final ExtensionContext extensionContext = mock();\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\tprotected final MutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions(\n\t\tconfiguration);\n\n\t@Test\n\tvoid constructorInjection() {\n\t\tregister(new StringParameterResolver(), new NumberParameterResolver());\n\n\t\tClass<ConstructorInjectionTestCase> outerClass = ConstructorInjectionTestCase.class;\n\t\tConstructor<ConstructorInjectionTestCase> constructor = ReflectionUtils.getDeclaredConstructor(outerClass);\n\t\tConstructorInjectionTestCase outer = invokeConstructor(constructor, null);\n\n\t\tassertNotNull(outer);\n\t\tassertEquals(ENIGMA, outer.str);\n\n\t\tClass<ConstructorInjectionTestCase.NestedTestCase> innerClass = ConstructorInjectionTestCase.NestedTestCase.class;\n\t\tConstructor<ConstructorInjectionTestCase.NestedTestCase> innerConstructor = ReflectionUtils.getDeclaredConstructor(\n\t\t\tinnerClass);\n\t\tConstructorInjectionTestCase.NestedTestCase inner = invokeConstructor(innerConstructor, outer);\n\n\t\tassertNotNull(inner);\n\t\tassertEquals(42, inner.num);\n\t}\n\n\t@Test\n\tvoid resolveArgumentsViaParameterResolver() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(\"argument\");\n\n\t\tinvokeMethod();\n\n\t\tverify(instance).singleStringParameter(\"argument\");\n\t}\n\n\tprivate void thereIsAParameterResolverThatResolvesTheParameterTo(Object argument) {\n\t\tregister(ConfigurableParameterResolver.supportsAndResolvesTo(parameterContext -> argument));\n\t}\n\n\tprivate void testMethodWithASingleStringParameter() {\n\t\tthis.method = ReflectionSupport.findMethod(this.instance.getClass(), \"singleStringParameter\",\n\t\t\tString.class).orElseThrow();\n\t}\n\n\tprivate void register(ParameterResolver... resolvers) {\n\t\tfor (ParameterResolver resolver : resolvers) {\n\t\t\textensionRegistry.registerExtension(resolver, this);\n\t\t}\n\t}\n\n\tabstract void invokeMethod();\n\n\tabstract <T> T invokeConstructor(Constructor<T> constructor, @Nullable Object outerInstance);\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultExecutableInvokerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.lang.reflect.Constructor;\n\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Unit tests for {@link DefaultExecutableInvoker}.\n *\n * @since 5.9\n */\nclass DefaultExecutableInvokerTests extends AbstractExecutableInvokerTests {\n\n\t@Override\n\tvoid invokeMethod() {\n\t\tnewInvoker().invoke(requireNonNull(this.method), this.instance);\n\t}\n\n\t@Override\n\t<T> T invokeConstructor(Constructor<T> constructor, @Nullable Object outerInstance) {\n\t\treturn newInvoker().invoke(constructor, outerInstance);\n\t}\n\n\tprivate DefaultExecutableInvoker newInvoker() {\n\t\treturn new DefaultExecutableInvoker(this.extensionContext, this.extensionRegistry);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DefaultTestInstancesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.Test;\n\nclass DefaultTestInstancesTests {\n\n\t@Test\n\tvoid topLevelClass() {\n\t\tDefaultTestInstances instances = DefaultTestInstances.of(this);\n\n\t\tassertThat(instances.getInnermostInstance()).isSameAs(this);\n\t\tassertThat(instances.getAllInstances()).containsExactly(this);\n\t\tassertThat(instances.getEnclosingInstances()).isEmpty();\n\t\tassertThat(instances.findInstance(Object.class)).contains(this);\n\t\tassertThat(instances.findInstance(String.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid nestedLevelClass() {\n\t\tDefaultTestInstancesTests outermost = this;\n\t\tNested innermost = new Nested();\n\t\tDefaultTestInstances instances = DefaultTestInstances.of(DefaultTestInstances.of(outermost), innermost);\n\n\t\tassertThat(instances.getInnermostInstance()).isSameAs(innermost);\n\t\tassertThat(instances.getAllInstances()).containsExactly(outermost, innermost);\n\t\tassertThat(instances.getEnclosingInstances()).containsExactly(outermost);\n\t\tassertThat(instances.findInstance(Object.class)).contains(innermost);\n\t\tassertThat(instances.findInstance(Nested.class)).contains(innermost);\n\t\tassertThat(instances.findInstance(DefaultTestInstancesTests.class)).contains(outermost);\n\t\tassertThat(instances.findInstance(String.class)).isEmpty();\n\t}\n\n\tclass Nested {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/DynamicTestIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\n/**\n * Integration tests for dynamic tests.\n *\n * @since 5.5\n */\nclass DynamicTestIntegrationTests {\n\n\tprivate static final int TEN_MB = 10 * 1024 * 1024;\n\n\t/**\n\t * Without the fix in {@code DynamicTestTestDescriptor}, setting the\n\t * {@code -mx200m} VM argument will cause an {@link OutOfMemoryError} before\n\t * the 200 limit is reached.\n\t *\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1865\">Issue 1865</a>\n\t */\n\t@TestFactory\n\tStream<DynamicTest> generateDynamicTestsThatReferenceLargeAmountsOfMemory() {\n\t\treturn Stream.generate(() -> new byte[TEN_MB])//\n\t\t\t\t// The lambda Executable in the following line *must* reference\n\t\t\t\t// the `bytes` array in order to hold onto the allocated memory.\n\t\t\t\t.map(bytes -> dynamicTest(\"test\", () -> assertNotNull(bytes)))//\n\t\t\t\t.limit(200);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreConcurrencyTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * Concurrency tests for {@link NamespaceAwareStore} and {@link NamespacedHierarchicalStore}.\n *\n * @since 5.0\n */\nclass ExtensionContextStoreConcurrencyTests {\n\n\tprivate final AtomicInteger count = new AtomicInteger();\n\n\t@Test\n\tvoid concurrentAccessToDefaultStoreWithoutParentStore() {\n\t\t// Run the actual test 100 times \"for good measure\".\n\t\tIntStream.range(1, 100).forEach(i -> {\n\t\t\tStore store = reset();\n\t\t\t// Simulate 100 extensions interacting concurrently with the Store.\n\t\t\tIntStream.range(1, 100).parallel().forEach(j -> store.computeIfAbsent(\"key\", this::newValue));\n\t\t\tassertEquals(1, count.get(), () -> \"number of times newValue() was invoked in run #\" + i);\n\t\t});\n\t}\n\n\tprivate String newValue(String key) {\n\t\tcount.incrementAndGet();\n\t\treturn \"value\";\n\t}\n\n\tprivate Store reset() {\n\t\tcount.set(0);\n\t\treturn new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null), Namespace.GLOBAL);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ExtensionContextStoreTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.ExtensionContextException;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStoreException;\n\n/**\n * Unit tests for {@link NamespaceAwareStore} and {@link NamespacedHierarchicalStore}.\n *\n * @since 5.5\n * @see ExtensionContextStoreConcurrencyTests\n * @see ExtensionValuesStoreTests\n */\nclass ExtensionContextStoreTests {\n\n\tprivate static final String KEY = \"key\";\n\tprivate static final String VALUE = \"value\";\n\n\tprivate final NamespacedHierarchicalStore<Namespace> parentStore = new NamespacedHierarchicalStore<>(null);\n\tprivate final NamespacedHierarchicalStore<Namespace> localStore = new NamespacedHierarchicalStore<>(parentStore);\n\tprivate final Store store = new NamespaceAwareStore(localStore, Namespace.GLOBAL);\n\n\t@Test\n\tvoid getOrDefaultWithNoValuePresent() {\n\t\tassertThat(store.get(KEY)).isNull();\n\n\t\tassertThat(store.getOrDefault(KEY, boolean.class, true)).isTrue();\n\t\tassertThat(store.getOrDefault(KEY, String.class, VALUE)).isEqualTo(VALUE);\n\t}\n\n\t@Test\n\tvoid getOrDefaultRequestingIncompatibleType() {\n\t\tlocalStore.put(Namespace.GLOBAL, KEY, VALUE);\n\t\tassertThat(store.get(KEY)).isEqualTo(VALUE);\n\n\t\tException exception = assertThrows(ExtensionContextException.class,\n\t\t\t() -> store.getOrDefault(KEY, boolean.class, true));\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessageContaining(\"is not of required type\") //\n\t\t\t\t.hasCauseInstanceOf(NamespacedHierarchicalStoreException.class) //\n\t\t\t\t.hasStackTraceContaining(NamespacedHierarchicalStore.class.getName());\n\t}\n\n\t@Test\n\tvoid getOrDefaultWithValueInLocalStore() {\n\t\tlocalStore.put(Namespace.GLOBAL, KEY, VALUE);\n\t\tassertThat(store.get(KEY)).isEqualTo(VALUE);\n\n\t\tassertThat(store.getOrDefault(KEY, String.class, VALUE)).isEqualTo(VALUE);\n\t}\n\n\t@Test\n\tvoid getOrDefaultWithValueInParentStore() {\n\t\tparentStore.put(Namespace.GLOBAL, KEY, VALUE);\n\t\tassertThat(store.get(KEY)).isEqualTo(VALUE);\n\n\t\tassertThat(store.getOrDefault(KEY, String.class, VALUE)).isEqualTo(VALUE);\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\tvoid getOrComputeIfAbsentWithFailingCreator() {\n\t\tvar invocations = new AtomicInteger();\n\n\t\tvar e1 = assertThrows(RuntimeException.class, () -> store.getOrComputeIfAbsent(KEY, __ -> {\n\t\t\tinvocations.incrementAndGet();\n\t\t\tthrow new RuntimeException();\n\t\t}));\n\t\tvar e2 = assertThrows(RuntimeException.class, () -> store.get(KEY));\n\t\tassertSame(e1, e2);\n\n\t\tassertDoesNotThrow(localStore::close);\n\t\tassertThat(invocations).hasValue(1);\n\t}\n\n\t@Test\n\tvoid computeIfAbsentWithFailingCreator() {\n\t\tassertThrows(RuntimeException.class, () -> store.computeIfAbsent(KEY, __ -> {\n\t\t\tthrow new RuntimeException();\n\t\t}));\n\t\tassertNull(store.get(KEY));\n\t\tassertDoesNotThrow(localStore::close);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/InterceptingExecutableInvokerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.ReflectiveInterceptorCall;\n\n/**\n * Unit tests for {@link InterceptingExecutableInvoker}.\n *\n * @since 5.0\n */\nclass InterceptingExecutableInvokerTests extends AbstractExecutableInvokerTests {\n\n\t@Override\n\tvoid invokeMethod() {\n\t\tnewInvoker().invoke(requireNonNull(this.method), this.instance, this.extensionContext, this.extensionRegistry,\n\t\t\tpassthroughInterceptor());\n\t}\n\n\t@Override\n\t<T> T invokeConstructor(Constructor<T> constructor, @Nullable Object outerInstance) {\n\t\treturn newInvoker().invoke(constructor, Optional.ofNullable(outerInstance), __ -> extensionContext,\n\t\t\textensionRegistry, passthroughInterceptor());\n\t}\n\n\tprivate InterceptingExecutableInvoker newInvoker() {\n\t\treturn new InterceptingExecutableInvoker();\n\t}\n\n\tprivate static <E extends Executable, T> ReflectiveInterceptorCall<E, T> passthroughInterceptor() {\n\t\treturn (interceptor, invocation, invocationContext, extensionContext) -> invocation.proceed();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/JupiterEngineExecutionContextTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.withSettings;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.engine.EngineExecutionListener;\n\n/**\n * Unit tests for {@link JupiterEngineExecutionContext}.\n *\n * @since 5.0\n */\nclass JupiterEngineExecutionContextTests {\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\tprivate final EngineExecutionListener engineExecutionListener = mock();\n\n\tprivate final LauncherStoreFacade launcherStoreFacade = mock();\n\n\tprivate final JupiterEngineExecutionContext originalContext = new JupiterEngineExecutionContext(\n\t\tengineExecutionListener, configuration, launcherStoreFacade);\n\n\t@Test\n\tvoid executionListenerIsHandedOnWhenContextIsExtended() {\n\t\tassertSame(engineExecutionListener, originalContext.getExecutionListener());\n\t\tJupiterEngineExecutionContext newContext = originalContext.extend().build();\n\t\tassertSame(engineExecutionListener, newContext.getExecutionListener());\n\t}\n\n\t@Test\n\tvoid extendWithAllAttributes() {\n\t\tExtensionContext extensionContext = mock();\n\t\tMutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions(\n\t\t\tconfiguration);\n\t\tTestInstancesProvider testInstancesProvider = mock();\n\t\tJupiterEngineExecutionContext newContext = originalContext.extend() //\n\t\t\t\t.withExtensionContext(extensionContext) //\n\t\t\t\t.withExtensionRegistry(extensionRegistry) //\n\t\t\t\t.withTestInstancesProvider(testInstancesProvider) //\n\t\t\t\t.build();\n\n\t\tassertSame(extensionContext, newContext.getExtensionContext());\n\t\tassertSame(extensionRegistry, newContext.getExtensionRegistry());\n\t\tassertSame(testInstancesProvider, newContext.getTestInstancesProvider());\n\t}\n\n\t@Test\n\tvoid canOverrideAttributeWhenContextIsExtended() {\n\t\tExtensionContext extensionContext = mock();\n\t\tMutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions(\n\t\t\tconfiguration);\n\t\tTestInstancesProvider testInstancesProvider = mock();\n\t\tExtensionContext newExtensionContext = mock();\n\n\t\tJupiterEngineExecutionContext newContext = originalContext.extend() //\n\t\t\t\t.withExtensionContext(extensionContext) //\n\t\t\t\t.withExtensionRegistry(extensionRegistry) //\n\t\t\t\t.withTestInstancesProvider(testInstancesProvider) //\n\t\t\t\t.build() //\n\t\t\t\t.extend() //\n\t\t\t\t.withExtensionContext(newExtensionContext) //\n\t\t\t\t.build();\n\n\t\tassertSame(newExtensionContext, newContext.getExtensionContext());\n\t\tassertSame(extensionRegistry, newContext.getExtensionRegistry());\n\t\tassertSame(testInstancesProvider, newContext.getTestInstancesProvider());\n\t}\n\n\t@Test\n\tvoid closeAttemptExceptionWillBeThrownDownTheCallStack() throws Exception {\n\t\tExtensionContext failingExtensionContext = mock(ExtensionContext.class,\n\t\t\twithSettings().extraInterfaces(AutoCloseable.class));\n\t\tException expectedCause = new Exception(\"test message\");\n\t\tdoThrow(expectedCause).when(((AutoCloseable) failingExtensionContext)).close();\n\n\t\tJupiterEngineExecutionContext newContext = originalContext.extend() //\n\t\t\t\t.withExtensionContext(failingExtensionContext) //\n\t\t\t\t.build();\n\n\t\tException actualException = assertThrows(Exception.class, newContext::close);\n\n\t\tassertThat(actualException) //\n\t\t\t\t.hasMessage(\"Failed to close extension context\") //\n\t\t\t\t.cause().isSameAs(expectedCause);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/ParameterResolutionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.Mockito.mock;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.function.Predicate;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Unit tests for {@link ParameterResolutionUtils}.\n *\n * @since 5.9\n */\nclass ParameterResolutionUtilsTests {\n\n\tprivate static final String ENIGMA = \"enigma\";\n\n\tprivate final MethodSource instance = mock();\n\n\tprivate @Nullable Method method;\n\n\tprivate final ExtensionContext extensionContext = mock();\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\tprivate final MutableExtensionRegistry extensionRegistry = MutableExtensionRegistry.createRegistryWithDefaultExtensions(\n\t\tconfiguration);\n\n\t@Test\n\tvoid resolveConstructorArguments() {\n\t\tregister(new StringParameterResolver());\n\n\t\tClass<ConstructorInjectionTestCase> topLevelClass = ConstructorInjectionTestCase.class;\n\t\t@Nullable\n\t\tObject[] arguments = resolveConstructorParameters(topLevelClass, null);\n\n\t\tassertThat(arguments).containsExactly(ENIGMA);\n\t}\n\n\t@Test\n\tvoid resolveNestedConstructorArguments() {\n\t\tregister(new NumberParameterResolver());\n\n\t\tClass<ConstructorInjectionTestCase> outerClass = ConstructorInjectionTestCase.class;\n\t\tConstructorInjectionTestCase outer = ReflectionSupport.newInstance(outerClass, \"str\");\n\n\t\tClass<ConstructorInjectionTestCase.NestedTestCase> innerClass = ConstructorInjectionTestCase.NestedTestCase.class;\n\t\t@Nullable\n\t\tObject[] arguments = resolveConstructorParameters(innerClass, outer);\n\n\t\tassertThat(arguments).containsExactly(outer, 42);\n\t}\n\n\t@Test\n\tvoid resolveConstructorArgumentsWithMissingResolver() {\n\t\tConstructor<ConstructorInjectionTestCase> constructor = ReflectionUtils.getDeclaredConstructor(\n\t\t\tConstructorInjectionTestCase.class);\n\n\t\tException exception = assertThrows(ParameterResolutionException.class,\n\t\t\t() -> ParameterResolutionUtils.resolveParameters(constructor, Optional.empty(), Optional.empty(),\n\t\t\t\textensionContext, extensionRegistry));\n\n\t\tassertThat(exception.getMessage())//\n\t\t\t\t.contains(\"No ParameterResolver registered for parameter [java.lang.String\")//\n\t\t\t\t.contains(\"in constructor\")//\n\t\t\t\t.contains(ConstructorInjectionTestCase.class.getName());\n\t}\n\n\t@Test\n\tvoid resolvingArgumentsForMethodsWithoutParameterDoesNotDependOnParameterResolvers() {\n\t\ttestMethodWithNoParameters();\n\t\tthrowDuringParameterResolution(new RuntimeException(\"boom!\"));\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).isEmpty();\n\t}\n\n\t@Test\n\tvoid resolveArgumentsViaParameterResolver() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(\"argument\");\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).containsExactly(\"argument\");\n\t}\n\n\t@Test\n\tvoid resolveMultipleArguments() {\n\t\ttestMethodWith(\"multipleParameters\", String.class, Integer.class, Double.class);\n\t\tregister(ConfigurableParameterResolver.supportsAndResolvesTo(parameterContext -> {\n\t\t\treturn switch (parameterContext.getIndex()) {\n\t\t\t\tcase 0 -> \"0\";\n\t\t\t\tcase 1 -> 1;\n\t\t\t\tdefault -> 2.0;\n\t\t\t};\n\t\t}));\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).containsExactly(\"0\", 1, 2.0);\n\t}\n\n\t@Test\n\tvoid onlyConsiderParameterResolversThatSupportAParticularParameter() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatDoesNotSupportThisParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(\"something\");\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).containsExactly(\"something\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid passContextInformationToParameterResolverMethods() {\n\t\tanyTestMethodWithAtLeastOneParameter();\n\t\tArgumentRecordingParameterResolver extension = new ArgumentRecordingParameterResolver();\n\t\tregister(extension);\n\n\t\tresolveMethodParameters();\n\n\t\tassertSame(extensionContext, extension.supportsArguments.extensionContext);\n\t\tassertEquals(0, extension.supportsArguments.parameterContext.getIndex());\n\t\tassertSame(instance, extension.supportsArguments.parameterContext.getTarget().orElseThrow());\n\t\tassertSame(extensionContext, extension.resolveArguments.extensionContext);\n\t\tassertEquals(0, extension.resolveArguments.parameterContext.getIndex());\n\t\tassertSame(instance, extension.resolveArguments.parameterContext.getTarget().orElseThrow());\n\t\tassertThat(extension.resolveArguments.parameterContext.toString())//\n\t\t\t\t.contains(\"parameter\", String.class.getTypeName(), \"index\", \"0\", \"target\", \"Mock\");\n\t}\n\n\t@Test\n\tvoid resolvingArgumentsForMethodsWithPrimitiveTypesIsSupported() {\n\t\ttestMethodWithASinglePrimitiveIntParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(42);\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).containsExactly(42);\n\t}\n\n\t@Test\n\tvoid nullIsAViableArgumentIfAReferenceTypeParameterIsExpected() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(null);\n\n\t\t@Nullable\n\t\tObject[] arguments = resolveMethodParameters();\n\n\t\tassertThat(arguments).hasSize(1);\n\t\tassertNull(arguments[0]);\n\t}\n\n\t@Test\n\tvoid reportThatNullIsNotAViableArgumentIfAPrimitiveTypeIsExpected() {\n\t\ttestMethodWithASinglePrimitiveIntParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(null);\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\t// @formatter:off\n\t\tassertThat(caught.getMessage())\n\t\t\t\t.contains(\"in method\")\n\t\t\t\t.contains(\"resolved a null value for parameter [int\")\n\t\t\t\t.contains(\"but a primitive of type [int] is required.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid reportIfThereIsNoParameterResolverThatSupportsTheParameter() {\n\t\ttestMethodWithASingleStringParameter();\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\tassertThat(caught.getMessage()).contains(\"parameter [java.lang.String\").contains(\"in method\");\n\t}\n\n\t@Test\n\tvoid reportIfThereAreMultipleParameterResolversThatSupportTheParameter() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(\"one\");\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(\"two\");\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\tString className = Pattern.quote(ConfigurableParameterResolver.class.getName());\n\n\t\t// @formatter:off\n\t\tassertThat(caught.getMessage())\n\t\t\t\t.matches(\"Discovered multiple competing ParameterResolvers for parameter \\\\[java.lang.String .+?\\\\] \" +\n\t\t\t\t\t\t\"in method .+: \" + className + \"@.+, \" + className + \"@.+\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid reportTypeMismatchBetweenParameterAndResolvedParameter() {\n\t\ttestMethodWithASingleStringParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(BigDecimal.ONE);\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\t// @formatter:off\n\t\tassertThat(caught.getMessage())\n\t\t\t\t.contains(\"resolved a value of type [java.math.BigDecimal] for parameter [java.lang.String\")\n\t\t\t\t.contains(\"in method\")\n\t\t\t\t.contains(\"but a value assignment compatible with [java.lang.String] is required.\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid reportTypeMismatchBetweenParameterAndResolvedParameterWithArrayTypes() {\n\t\ttestMethodWithASingleStringArrayParameter();\n\t\tthereIsAParameterResolverThatResolvesTheParameterTo(new int[][] {});\n\n\t\tassertThatExceptionOfType(ParameterResolutionException.class)//\n\t\t\t\t.isThrownBy(this::resolveMethodParameters)//\n\t\t\t\t.withMessageContaining(//\n\t\t\t\t\t\"resolved a value of type [int[][]] for parameter [java.lang.String[]\", //\n\t\t\t\t\t\"in method\", //\n\t\t\t\t\t\"but a value assignment compatible with [java.lang.String[]] is required.\" //\n\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid wrapAllExceptionsThrownDuringParameterResolutionIntoAParameterResolutionException() {\n\t\tanyTestMethodWithAtLeastOneParameter();\n\t\tIllegalArgumentException cause = anyExceptionButParameterResolutionException();\n\t\tthrowDuringParameterResolution(cause);\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\tassertSame(cause, caught.getCause(), () -> \"cause should be present\");\n\t\tassertThat(caught.getMessage())//\n\t\t\t\t.matches(\"^Failed to resolve parameter \\\\[java.lang.String .+?\\\\] in method \\\\[.+?\\\\]$\");\n\t}\n\n\t@Test\n\tvoid exceptionMessageContainsMessageFromExceptionThrownDuringParameterResolution() {\n\t\tanyTestMethodWithAtLeastOneParameter();\n\t\tRuntimeException cause = new RuntimeException(\"boom!\");\n\t\tthrowDuringParameterResolution(cause);\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\tassertSame(cause, caught.getCause(), () -> \"cause should be present\");\n\t\tassertThat(caught.getMessage())//\n\t\t\t\t.matches(\"^Failed to resolve parameter \\\\[java.lang.String .+?\\\\] in method \\\\[.+?\\\\]: boom!$\");\n\t}\n\n\t@Test\n\tvoid doNotWrapThrownExceptionIfItIsAlreadyAParameterResolutionException() {\n\t\tanyTestMethodWithAtLeastOneParameter();\n\t\tParameterResolutionException cause = new ParameterResolutionException(\"custom message\");\n\t\tthrowDuringParameterResolution(cause);\n\n\t\tParameterResolutionException caught = assertThrows(ParameterResolutionException.class,\n\t\t\tthis::resolveMethodParameters);\n\n\t\tassertSame(cause, caught);\n\t}\n\n\tprivate IllegalArgumentException anyExceptionButParameterResolutionException() {\n\t\treturn new IllegalArgumentException();\n\t}\n\n\tprivate void throwDuringParameterResolution(RuntimeException parameterResolutionException) {\n\t\tregister(ConfigurableParameterResolver.onAnyCallThrow(parameterResolutionException));\n\t}\n\n\tprivate void thereIsAParameterResolverThatResolvesTheParameterTo(@Nullable Object argument) {\n\t\tregister(ConfigurableParameterResolver.supportsAndResolvesTo(parameterContext -> argument));\n\t}\n\n\tprivate void thereIsAParameterResolverThatDoesNotSupportThisParameter() {\n\t\tregister(ConfigurableParameterResolver.withoutSupport());\n\t}\n\n\tprivate void anyTestMethodWithAtLeastOneParameter() {\n\t\ttestMethodWithASingleStringParameter();\n\t}\n\n\tprivate void testMethodWithNoParameters() {\n\t\ttestMethodWith(\"noParameter\");\n\t}\n\n\tprivate void testMethodWithASingleStringParameter() {\n\t\ttestMethodWith(\"singleStringParameter\", String.class);\n\t}\n\n\tprivate void testMethodWithASingleStringArrayParameter() {\n\t\ttestMethodWith(\"singleStringArrayParameter\", String[].class);\n\t}\n\n\tprivate void testMethodWithASinglePrimitiveIntParameter() {\n\t\ttestMethodWith(\"primitiveParameterInt\", int.class);\n\t}\n\n\tprivate void testMethodWith(String methodName, Class<?>... parameterTypes) {\n\t\tthis.method = ReflectionSupport.findMethod(this.instance.getClass(), methodName, parameterTypes).get();\n\t}\n\n\tprivate void register(ParameterResolver... resolvers) {\n\t\tfor (ParameterResolver resolver : resolvers) {\n\t\t\textensionRegistry.registerExtension(resolver, this);\n\t\t}\n\t}\n\n\tprivate <T> @Nullable Object[] resolveConstructorParameters(Class<T> clazz, @Nullable Object outerInstance) {\n\t\tConstructor<T> constructor = ReflectionUtils.getDeclaredConstructor(clazz);\n\t\treturn ParameterResolutionUtils.resolveParameters(constructor, Optional.empty(),\n\t\t\tOptional.ofNullable(outerInstance), extensionContext, extensionRegistry);\n\t}\n\n\tprivate @Nullable Object[] resolveMethodParameters() {\n\t\treturn ParameterResolutionUtils.resolveParameters(requireNonNull(this.method), Optional.of(this.instance),\n\t\t\tthis.extensionContext, this.extensionRegistry);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class ArgumentRecordingParameterResolver implements ParameterResolver {\n\n\t\tArgumentRecordingParameterResolver.@Nullable Arguments supportsArguments;\n\t\tArgumentRecordingParameterResolver.@Nullable Arguments resolveArguments;\n\n\t\trecord Arguments(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\tsupportsArguments = new ArgumentRecordingParameterResolver.Arguments(parameterContext, extensionContext);\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\tresolveArguments = new ArgumentRecordingParameterResolver.Arguments(parameterContext, extensionContext);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tstatic class ConfigurableParameterResolver implements ParameterResolver {\n\n\t\tstatic ParameterResolver onAnyCallThrow(RuntimeException runtimeException) {\n\t\t\treturn new ConfigurableParameterResolver(parameterContext -> {\n\t\t\t\tthrow runtimeException;\n\t\t\t}, parameterContext -> {\n\t\t\t\tthrow runtimeException;\n\t\t\t});\n\t\t}\n\n\t\tstatic ParameterResolver supportsAndResolvesTo(Function<ParameterContext, @Nullable Object> resolve) {\n\t\t\treturn new ConfigurableParameterResolver(parameterContext -> true, resolve);\n\t\t}\n\n\t\tstatic ParameterResolver withoutSupport() {\n\t\t\treturn new ConfigurableParameterResolver(parameterContext -> false, parameter -> {\n\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t});\n\t\t}\n\n\t\tprivate final Predicate<ParameterContext> supports;\n\t\tprivate final Function<ParameterContext, @Nullable Object> resolve;\n\n\t\tprivate ConfigurableParameterResolver(Predicate<ParameterContext> supports,\n\t\t\t\tFunction<ParameterContext, @Nullable Object> resolve) {\n\t\t\tthis.supports = supports;\n\t\t\tthis.resolve = resolve;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn supports.test(parameterContext);\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn resolve.apply(parameterContext);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tinterface MethodSource {\n\n\t\tvoid noParameter();\n\n\t\tvoid singleStringParameter(String parameter);\n\n\t\tvoid singleStringArrayParameter(String[] parameter);\n\n\t\tvoid primitiveParameterInt(int parameter);\n\n\t\tvoid multipleParameters(String first, Integer second, Double third);\n\t}\n\n\tstatic class StringParameterResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == String.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn ENIGMA;\n\t\t}\n\t}\n\n\tstatic class NumberParameterResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == Number.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn 42;\n\t\t}\n\t}\n\n\tstatic class ConstructorInjectionTestCase {\n\n\t\tfinal String str;\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tConstructorInjectionTestCase(String str) {\n\t\t\tthis.str = str;\n\t\t}\n\n\t\tclass NestedTestCase {\n\n\t\t\tfinal Number num;\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tNestedTestCase(Number num) {\n\t\t\t\tthis.num = num;\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/UniqueIdParsingForArrayParameterIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.execution.injection.sample.PrimitiveArrayParameterResolver;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Event;\n\n/**\n * Integration tests for {@link UniqueId#parse(String)} for methods\n * with array type parameters.\n *\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/810\">#810</a>\n * @see org.junit.platform.engine.UniqueIdTests\n *\n * @since 5.0\n */\nclass UniqueIdParsingForArrayParameterIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid executeTestsForPrimitiveArrayMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(PrimitiveArrayMethodInjectionTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tUniqueId uniqueId = executionResults.allEvents()\n\t\t\t\t.map(Event::getTestDescriptor)\n\t\t\t\t.distinct()\n\t\t\t\t.skip(2)\n\t\t\t\t.map(TestDescriptor::getUniqueId)\n\t\t\t\t.findFirst()\n\t\t\t\t.orElseThrow(AssertionError::new);\n\t\t// @formatter:on\n\n\t\tassertThat(UniqueId.parse(uniqueId.toString())).isEqualTo(uniqueId);\n\t}\n\n\t@ExtendWith(PrimitiveArrayParameterResolver.class)\n\tstatic class PrimitiveArrayMethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid primitiveArray(int... ints) {\n\t\t\tassertArrayEquals(new int[] { 1, 2, 3 }, ints);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * @since 5.0\n */\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface CustomAnnotation {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomAnnotationParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * @since 5.0\n */\npublic class CustomAnnotationParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t// We invoke parameterContext.isAnnotated() instead of parameterContext.getParameter().isAnnotationPresent()\n\t\t// in order to verify support for the convenience method in the ParameterContext API.\n\t\treturn parameterContext.isAnnotated(CustomAnnotation.class);\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn ReflectionSupport.newInstance(parameterContext.getParameter().getType());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport java.util.Date;\n\n/**\n * @since 5.0\n */\npublic class CustomType {\n\n\tprivate final Date date = new Date();\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"CustomType: \" + this.date;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/CustomTypeParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * @since 5.0\n */\npublic class CustomTypeParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn parameterContext.getParameter().getType().equals(CustomType.class);\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn new CustomType();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/DoubleParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that always resolves a {@link Double}\n * parameter to {@code 42.0}.\n *\n * @since 5.0\n */\npublic class DoubleParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn parameterContext.getParameter().getType() == Double.class;\n\t}\n\n\t@Override\n\tpublic Double resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn 42.0;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/LongParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that always resolves a {@link Long}\n * parameter to {@code 42}.\n *\n * <p>This resolver also <em>attempts</em> to support generic parameter type\n * declarations if the generic type is defined at the class level in a\n * superclass or interface (extended or implemented by the test class) and\n * is assignable from {@link Long}.\n *\n * @since 5.0\n */\npublic class LongParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t// Exact match?\n\t\tif (parameterContext.getParameter().getType() == Long.class) {\n\t\t\treturn true;\n\t\t}\n\n\t\tType typeInMethod = parameterContext.getParameter().getParameterizedType();\n\n\t\t// Type variables in parameterized class\n\t\tfor (TypeVariable<?> typeVariable : parameterContext.getDeclaringExecutable().getDeclaringClass().getTypeParameters()) {\n\t\t\tboolean namesMatch = typeInMethod.getTypeName().equals(typeVariable.getName());\n\t\t\tboolean typesAreCompatible = typeVariable.getBounds().length == 1 && //\n\t\t\t\t\ttypeVariable.getBounds()[0] instanceof Class<?> clazz && //\n\t\t\t\t\tclazz.isAssignableFrom(Long.class);\n\n\t\t\tif (namesMatch && typesAreCompatible) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\t@Override\n\tpublic Long resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn 42L;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfListsTypeBasedParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.support.TypeBasedParameterResolver;\n\n/**\n * @since 5.6\n */\npublic class MapOfListsTypeBasedParameterResolver extends TypeBasedParameterResolver<Map<String, List<Integer>>> {\n\n\t@Override\n\tpublic Map<String, List<Integer>> resolveParameter(ParameterContext parameterContext,\n\t\t\tExtensionContext extensionContext) {\n\n\t\treturn Map.of(\"ids\", List.of(1, 42));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/MapOfStringsParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.Map;\nimport java.util.TreeMap;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that resolves {@code Map<String, String>} types.\n *\n * @since 5.0\n */\npublic class MapOfStringsParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\tType type = parameterContext.getParameter().getParameterizedType();\n\t\tif (!(type instanceof ParameterizedType parameterizedType)) {\n\t\t\treturn false;\n\t\t}\n\t\tType[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n\t\tif (actualTypeArguments.length != 2) {\n\t\t\treturn false;\n\t\t}\n\t\treturn actualTypeArguments[0] == String.class && actualTypeArguments[1] == String.class;\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\tMap<String, String> map = new TreeMap<>();\n\t\tmap.put(\"key\", \"value\");\n\t\treturn map;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NullIntegerParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that always resolves an\n * {@link Integer} or {@code int} parameter to a {@code null} value.\n *\n * @since 5.0\n */\npublic class NullIntegerParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn Integer.class == parameterContext.getParameter().getType()\n\t\t\t\t|| int.class == parameterContext.getParameter().getType();\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/NumberParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * This is a non-realistic {@link ParameterResolver} that claims to\n * resolve any {@link Number}, when in fact it always resolves an {@link Integer}.\n *\n * <p>This may appear nonsensical; however, there are use cases for which a\n * resolver may think that it can support a particular parameter only to\n * discover later that the actual resolved value is not assignment compatible.\n *\n * <p>For example, consider the case with Spring: the {@code SpringExtension} can\n * theoretically resolve any type of {@code ApplicationContext}, but if the\n * required parameter type is {@code WebApplicationContext} and the user\n * neglects to annotate the test class with {@code @WebAppConfiguration} then\n * the {@code ApplicationContext} loaded by Spring's testing support will in\n * fact be an {@code ApplicationContext} but not a {@code WebApplicationContext}.\n * Since Spring does not determine this in advance, such a scenario would lead to\n * an {@link IllegalArgumentException} with the message \"argument type mismatch\"\n * when JUnit attempts to invoke the test method.\n *\n * @since 5.0\n */\npublic class NumberParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn Number.class.isAssignableFrom(parameterContext.getParameter().getType());\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn 42;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveArrayParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that resolves arrays of primitive integers.\n *\n * @since 5.0\n */\npublic class PrimitiveArrayParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn int[].class == parameterContext.getParameter().getType();\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn new int[] { 1, 2, 3 };\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/execution/injection/sample/PrimitiveIntegerParameterResolver.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.execution.injection.sample;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\n\n/**\n * Example {@link ParameterResolver} that resolves primitive integers.\n *\n * @since 5.0\n */\npublic class PrimitiveIntegerParameterResolver implements ParameterResolver {\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn int.class == parameterContext.getParameter().getType();\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn 42;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/AutoCloseTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalStateException;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.io.InputStream;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.AutoClose;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\nimport org.junit.platform.testkit.engine.Execution;\n\n/**\n * Integration tests for {@link AutoClose @AutoClose} and the {@link AutoCloseExtension}.\n *\n * @since 5.11\n */\nclass AutoCloseTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> recorder = new ArrayList<>();\n\n\t@BeforeEach\n\t@AfterEach\n\tvoid resetTracking() {\n\t\tInstancePerClassTestCase.closed = false;\n\t\trecorder.clear();\n\t}\n\n\t@Test\n\tvoid blankCloseMethodName() {\n\t\tClass<?> testClass = BlankCloseMethodNameTestCase.class;\n\t\tString msg = \"@AutoClose on field %s.field must specify a method name.\".formatted(testClass.getCanonicalName());\n\t\tEvents tests = executeTestsForClass(testClass).testEvents();\n\t\tassertFailingWithMessage(tests, msg);\n\t}\n\n\t@Test\n\tvoid primitiveTypeCannotBeClosed() {\n\t\tClass<?> testClass = PrimitiveFieldTestCase.class;\n\t\tString msg = \"@AutoClose is not supported on primitive field %s.x.\".formatted(testClass.getCanonicalName());\n\t\tEvents tests = executeTestsForClass(testClass).testEvents();\n\t\tassertFailingWithMessage(tests, msg);\n\t}\n\n\t@Test\n\tvoid arrayCannotBeClosed() {\n\t\tClass<?> testClass = ArrayFieldTestCase.class;\n\t\tString msg = \"@AutoClose is not supported on array field %s.x.\".formatted(testClass.getCanonicalName());\n\t\tEvents tests = executeTestsForClass(testClass).testEvents();\n\t\tassertFailingWithMessage(tests, msg);\n\t}\n\n\t@Test\n\tvoid nullCannotBeClosed(@TrackLogRecords LogRecordListener listener) {\n\t\tClass<?> testClass = NullCloseableFieldTestCase.class;\n\t\tString msg = \"Cannot @AutoClose field %s.field because it is null.\".formatted(testClass.getCanonicalName());\n\t\tEvents tests = executeTestsForClass(testClass).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(1).failed(0));\n\t\tassertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).anyMatch(msg::equals);\n\t}\n\n\t@Test\n\tvoid noCloseMethod() {\n\t\tassertMissingCloseMethod(NoCloseMethodTestCase.class, \"close\");\n\t}\n\n\t@Test\n\tvoid noShutdownMethod() {\n\t\tassertMissingCloseMethod(NoShutdownMethodTestCase.class, \"shutdown\");\n\t}\n\n\t/**\n\t * Tests prerequisites for the {@link AutoCloseSpy} implementation.\n\t */\n\t@Test\n\tvoid spyPermitsOnlyASingleAction() {\n\t\t@SuppressWarnings(\"resource\")\n\t\tAutoCloseSpy spy = new AutoCloseSpy(\"preconditions\");\n\n\t\tspy.close();\n\n\t\tassertThatIllegalStateException().isThrownBy(spy::run).withMessage(\"Already closed via close()\");\n\t\tassertThatIllegalStateException().isThrownBy(spy::close).withMessage(\"Already closed via close()\");\n\t\tassertThat(recorder).containsExactly(\"AutoCloseTests.preconditions.close()\");\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3684\">#3684</a>\n\t */\n\t@Test\n\tvoid fieldsAreProperlyClosedViaInterfaceMethods() {\n\t\t// If the test method succeeds, that means there was no issue invoking\n\t\t// the @AutoClose fields. No need to assert anything else for this use case.\n\t\texecuteTestsForClass(CloseMethodMustBeInvokedViaInterfaceTestCase.class)//\n\t\t\t\t.testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(1));\n\t}\n\n\t@Test\n\tvoid fieldsAreProperlyClosedWithInstancePerMethodTestClass() {\n\t\tEvents tests = executeTestsForClass(InstancePerMethodTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(2));\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t// test1()\n\t\t\t\"InstancePerMethodTestCase.runnable.run()\", //\n\t\t\t\"InstancePerMethodTestCase.closable.close()\", //\n\t\t\t// test2()\n\t\t\t\"InstancePerMethodTestCase.runnable.run()\", //\n\t\t\t\"InstancePerMethodTestCase.closable.close()\", //\n\t\t\t// Class-level cleanup\n\t\t\t\"InstancePerMethodTestCase.staticClosable.close()\"//\n\t\t);\n\t}\n\n\t@Test\n\tvoid fieldsAreProperlyClosedWithInstancePerClassTestClass() {\n\t\tEvents tests = executeTestsForClass(InstancePerClassTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(2));\n\t\tassertThat(InstancePerClassTestCase.closed).isTrue();\n\t}\n\n\t@Test\n\tvoid fieldsAreProperlyClosedWithNestedTestClassesWithInstancePerMethod() {\n\t\tEvents tests = executeTestsForClass(InstancePerMethodEnclosingTestCase.NestedTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(1));\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t\"NestedTestCase.nestedClosable.close()\", //\n\t\t\t\"InstancePerMethodEnclosingTestCase.enclosingClosable.close()\", //\n\t\t\t\"NestedTestCase.nestedStaticClosable.close()\", //\n\t\t\t\"InstancePerMethodEnclosingTestCase.enclosingStaticClosable.close()\"//\n\t\t);\n\n\t\t// Reset tracking\n\t\tresetTracking();\n\n\t\ttests = executeTestsForClass(InstancePerMethodEnclosingTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(2));\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t\"InstancePerMethodEnclosingTestCase.enclosingClosable.close()\", //\n\t\t\t\"NestedTestCase.nestedClosable.close()\", //\n\t\t\t\"InstancePerMethodEnclosingTestCase.enclosingClosable.close()\", //\n\t\t\t\"NestedTestCase.nestedStaticClosable.close()\", //\n\t\t\t\"InstancePerMethodEnclosingTestCase.enclosingStaticClosable.close()\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid fieldsAreProperlyClosedWithNestedTestClassesWithInstancePerClass() {\n\t\t// With test instance lifecycle \"per class\" mode, we actually expect the\n\t\t// same behavior for the closing of all fields when the nested test class\n\t\t// is run standalone AND when it's run along with its enclosing class.\n\t\tString[] expected = { //\n\t\t\t\t\"NestedTestCase.nestedStaticClosable.close()\", //\n\t\t\t\t\"NestedTestCase.nestedClosable.close()\", //\n\t\t\t\t\"InstancePerClassEnclosingTestCase.enclosingStaticClosable.close()\", //\n\t\t\t\t\"InstancePerClassEnclosingTestCase.enclosingClosable.close()\" //\n\t\t};\n\n\t\tEvents tests = executeTestsForClass(InstancePerClassEnclosingTestCase.NestedTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(1));\n\t\tassertThat(recorder).containsExactly(expected);\n\n\t\t// Reset tracking\n\t\tresetTracking();\n\n\t\ttests = executeTestsForClass(InstancePerClassEnclosingTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(2));\n\t\tassertThat(recorder).containsExactly(expected);\n\t}\n\n\t@Test\n\tvoid fieldsAreProperlyClosedWithinTestClassHierarchy() {\n\t\tEvents tests = executeTestsForClass(SuperTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(1));\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t// superTest()\n\t\t\t\"SuperTestCase.superClosable.close()\", //\n\t\t\t// Class-level cleanup\n\t\t\t\"SuperTestCase.superStaticClosable.close()\" //\n\t\t);\n\n\t\t// Reset tracking\n\t\tresetTracking();\n\n\t\ttests = executeTestsForClass(SubTestCase.class).testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(2));\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t// superTest()\n\t\t\t\"SubTestCase.subClosable.close()\", //\n\t\t\t\"SuperTestCase.superClosable.close()\", //\n\t\t\t// subTest()\n\t\t\t\"SubTestCase.subClosable.close()\", //\n\t\t\t\"SuperTestCase.superClosable.close()\", //\n\t\t\t// Class-level cleanup in subclass\n\t\t\t\"SubTestCase.subStaticClosable.close()\", //\n\t\t\t// Class-level cleanup in superclass\n\t\t\t\"SuperTestCase.superStaticClosable.close()\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid allFieldsAreClosedIfAnyFieldThrowsAnException() {\n\t\t// Prerequisites to ensure fields are \"ordered\" as expected (based on the hash codes for their names).\n\t\tassertThat(\"staticField1\".hashCode()).isLessThan(\"staticField2\".hashCode()).isLessThan(\n\t\t\t\"staticField3\".hashCode());\n\t\tassertThat(\"field1\".hashCode()).isLessThan(\"field2\".hashCode()).isLessThan(\"field3\".hashCode());\n\n\t\tClass<?> testClass = FailingFieldsTestCase.class;\n\t\tEngineExecutionResults allEvents = executeTestsForClass(testClass);\n\n\t\tEvents tests = allEvents.testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(0).failed(1));\n\n\t\t// Verify that ALL fields were closed in the proper order.\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t\"FailingFieldsTestCase.field1.close()\", //\n\t\t\t\"FailingFieldsTestCase.field2.close()\", //\n\t\t\t\"FailingFieldsTestCase.field3.close()\", //\n\t\t\t\"FailingFieldsTestCase.staticField1.close()\", //\n\t\t\t\"FailingFieldsTestCase.staticField2.close()\", //\n\t\t\t\"FailingFieldsTestCase.staticField3.close()\" //\n\t\t);\n\n\t\t// Test-level failures\n\t\tassertThat(findFailure(tests, \"test()\")) //\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class) //\n\t\t\t\t.hasMessage(\"FailingFieldsTestCase.field1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasSuppressedException(new RuntimeException(\"FailingFieldsTestCase.field2.close()\"));\n\n\t\tEvents containers = allEvents.containerEvents();\n\t\tcontainers.assertStatistics(stats -> stats.succeeded(1).failed(1));\n\n\t\t// Container-level failures\n\t\tassertThat(findFailure(containers, testClass.getSimpleName())) //\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class) //\n\t\t\t\t.hasMessage(\"FailingFieldsTestCase.staticField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasSuppressedException(new RuntimeException(\"FailingFieldsTestCase.staticField2.close()\"));\n\t}\n\n\t@Test\n\tvoid allFieldsAreClosedIfAnyFieldThrowsAnExceptionWithNestedTestClassesWithInstancePerMethod() {\n\t\tClass<?> enclosingTestClass = FailingFieldsEnclosingTestCase.class;\n\t\tClass<?> nestedTestClass = FailingFieldsEnclosingTestCase.NestedTestCase.class;\n\n\t\tEngineExecutionResults allEvents = executeTestsForClass(nestedTestClass);\n\t\tEvents tests = allEvents.testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(0).failed(1));\n\n\t\t// Verify that ALL fields were closed in the proper order.\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t// Results from NestedTestCase instance\n\t\t\t\"NestedTestCase.nestedField1.close()\", //\n\t\t\t\"NestedTestCase.nestedField2.close()\", //\n\t\t\t// Results from FailingFieldsEnclosingTestCase instance\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField1.close()\", //\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField2.close()\", //\n\t\t\t// Results from NestedTestCase class\n\t\t\t\"NestedTestCase.nestedStaticField1.close()\", //\n\t\t\t\"NestedTestCase.nestedStaticField2.close()\", //\n\t\t\t// Results from FailingFieldsEnclosingTestCase class\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingStaticField1.close()\", //\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingStaticField2.close()\"//\n\t\t);\n\n\t\t// Test-level failures\n\t\tassertThat(findFailure(tests, \"nestedTest()\"))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"NestedTestCase.nestedField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasSuppressedException(new RuntimeException(\"FailingFieldsEnclosingTestCase.enclosingField1.close()\"));\n\n\t\tEvents containers = allEvents.containerEvents();\n\t\tcontainers.assertStatistics(stats -> stats.succeeded(1).failed(2));\n\n\t\t// Container-level failures\n\t\tassertThat(findFailure(containers, nestedTestClass.getSimpleName()))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"NestedTestCase.nestedStaticField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasNoSuppressedExceptions();\n\t\tassertThat(findFailure(containers, enclosingTestClass.getSimpleName()))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"FailingFieldsEnclosingTestCase.enclosingStaticField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasNoSuppressedExceptions();\n\n\t\t// Reset tracking\n\t\tresetTracking();\n\n\t\tallEvents = executeTestsForClass(enclosingTestClass);\n\t\ttests = allEvents.testEvents();\n\t\ttests.assertStatistics(stats -> stats.succeeded(0).failed(2));\n\n\t\t// Verify that ALL fields were closed in the proper order.\n\t\tassertThat(recorder).containsExactly(//\n\t\t\t// Results from FailingFieldsEnclosingTestCase instance\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField1.close()\", //\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField2.close()\", //\n\n\t\t\t// Results from NestedTestCase instance\n\t\t\t\"NestedTestCase.nestedField1.close()\", //\n\t\t\t\"NestedTestCase.nestedField2.close()\", //\n\t\t\t// Results from FailingFieldsEnclosingTestCase instance\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField1.close()\", //\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingField2.close()\", //\n\t\t\t// Results from NestedTestCase class\n\t\t\t\"NestedTestCase.nestedStaticField1.close()\", //\n\t\t\t\"NestedTestCase.nestedStaticField2.close()\", //\n\t\t\t// Results from FailingFieldsEnclosingTestCase class\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingStaticField1.close()\", //\n\t\t\t\"FailingFieldsEnclosingTestCase.enclosingStaticField2.close()\"//\n\t\t);\n\n\t\t// Test-level failures\n\t\tassertThat(findFailure(tests, \"enclosingTest()\"))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"FailingFieldsEnclosingTestCase.enclosingField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasNoSuppressedExceptions();\n\t\tassertThat(findFailure(tests, \"nestedTest()\"))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"NestedTestCase.nestedField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasSuppressedException(new RuntimeException(\"FailingFieldsEnclosingTestCase.enclosingField1.close()\"));\n\n\t\tcontainers = allEvents.containerEvents();\n\t\tcontainers.assertStatistics(stats -> stats.succeeded(1).failed(2));\n\n\t\t// Container-level failures\n\t\tassertThat(findFailure(containers, nestedTestClass.getSimpleName()))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"NestedTestCase.nestedStaticField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasNoSuppressedExceptions();\n\t\tassertThat(findFailure(containers, enclosingTestClass.getSimpleName()))//\n\t\t\t\t.isExactlyInstanceOf(RuntimeException.class)//\n\t\t\t\t.hasMessage(\"FailingFieldsEnclosingTestCase.enclosingStaticField1.close()\")//\n\t\t\t\t.hasNoCause()//\n\t\t\t\t.hasNoSuppressedExceptions();\n\t}\n\n\tprivate Throwable findFailure(Events tests, String displayName) {\n\t\treturn findExecution(tests, displayName)//\n\t\t\t\t.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow();\n\t}\n\n\tprivate static Execution findExecution(Events events, String displayName) {\n\t\treturn events.executions()//\n\t\t\t\t.filter(execution -> execution.getTestDescriptor().getDisplayName().contains(displayName))//\n\t\t\t\t.findFirst().get();\n\t}\n\n\tprivate static void assertFailingWithMessage(Events testEvents, String msg) {\n\t\ttestEvents//\n\t\t\t\t.assertStatistics(stats -> stats.failed(1))//\n\t\t\t\t.assertThatEvents().haveExactly(1, finishedWithFailure(message(msg)));\n\t}\n\n\tprivate void assertMissingCloseMethod(Class<?> testClass, String methodName) {\n\t\tString msg = \"Cannot @AutoClose field %s.field because %s does not define method %s().\".formatted(\n\t\t\ttestClass.getCanonicalName(), String.class.getName(), methodName);\n\t\tEvents tests = executeTestsForClass(testClass).testEvents();\n\t\tassertFailingWithMessage(tests, msg);\n\t}\n\n\tinterface TestInterface {\n\n\t\t@Test\n\t\tdefault void test() {\n\t\t}\n\t}\n\n\tstatic class BlankCloseMethodNameTestCase implements TestInterface {\n\n\t\t@AutoClose(\"\")\n\t\tfinal String field = \"blank\";\n\t}\n\n\tstatic class PrimitiveFieldTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tfinal int x = 0;\n\t}\n\n\tstatic class ArrayFieldTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tfinal int[] x = {};\n\t}\n\n\t@NullUnmarked\n\tstatic class NullCloseableFieldTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable field = null;\n\t}\n\n\tstatic class NoCloseMethodTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tprivate final String field = \"\";\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@AutoClose(\"shutdown\")\n\t@interface AutoShutdown {\n\t}\n\n\tstatic class NoShutdownMethodTestCase implements TestInterface {\n\n\t\t@AutoShutdown\n\t\tprivate final String field = \"\";\n\t}\n\n\tstatic class CloseMethodMustBeInvokedViaInterfaceTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tfinal InputStream inputStream = InputStream.nullInputStream();\n\n\t\t@AutoClose(\"shutdown\")\n\t\tfinal ExecutorService service = Executors.newSingleThreadExecutor();\n\t}\n\n\t@TestInstance(PER_METHOD)\n\t@NullUnmarked\n\tstatic class InstancePerMethodTestCase {\n\n\t\t@AutoClose\n\t\tprivate static AutoCloseable staticClosable;\n\n\t\t@AutoClose\n\t\tprivate static final AutoCloseable nullStatic = null;\n\n\t\t@AutoClose\n\t\tprivate final AutoCloseable closable = new AutoCloseSpy(\"closable\");\n\n\t\t@AutoClose(\"   run      \") // intentionally contains extra whitespace.\n\t\tprivate final Runnable runnable = new AutoCloseSpy(\"runnable\");\n\n\t\t@AutoClose\n\t\tprivate final AutoCloseable nullField = null;\n\n\t\t@BeforeAll\n\t\tstatic void setup() {\n\t\t\tstaticClosable = new AutoCloseSpy(\"staticClosable\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class InstancePerClassTestCase {\n\n\t\tstatic boolean closed = false;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable field = () -> closed = true;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertThat(closed).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertThat(closed).isFalse();\n\t\t}\n\t}\n\n\t@TestInstance(PER_METHOD)\n\tstatic class InstancePerMethodEnclosingTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseSpy enclosingStaticClosable;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable enclosingClosable = new AutoCloseSpy(\"enclosingClosable\");\n\n\t\t@BeforeAll\n\t\tstatic void setup() {\n\t\t\tenclosingStaticClosable = new AutoCloseSpy(\"enclosingStaticClosable\");\n\t\t}\n\n\t\t@Nested\n\t\t@TestInstance(PER_METHOD)\n\t\tclass NestedTestCase implements TestInterface {\n\n\t\t\t@AutoClose\n\t\t\tstatic AutoCloseSpy nestedStaticClosable;\n\n\t\t\t@AutoClose\n\t\t\tfinal AutoCloseable nestedClosable = new AutoCloseSpy(\"nestedClosable\");\n\n\t\t\t@BeforeAll\n\t\t\tstatic void setup() {\n\t\t\t\tnestedStaticClosable = new AutoCloseSpy(\"nestedStaticClosable\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class InstancePerClassEnclosingTestCase implements TestInterface {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseSpy enclosingStaticClosable;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable enclosingClosable = new AutoCloseSpy(\"enclosingClosable\");\n\n\t\t@BeforeAll\n\t\tstatic void setup() {\n\t\t\tenclosingStaticClosable = new AutoCloseSpy(\"enclosingStaticClosable\");\n\t\t}\n\n\t\t@Nested\n\t\t@TestInstance(PER_CLASS)\n\t\tclass NestedTestCase implements TestInterface {\n\n\t\t\t@AutoClose\n\t\t\tstatic AutoCloseSpy nestedStaticClosable;\n\n\t\t\t@AutoClose\n\t\t\tfinal AutoCloseable nestedClosable = new AutoCloseSpy(\"nestedClosable\");\n\n\t\t\t@BeforeAll\n\t\t\tstatic void setup() {\n\t\t\t\tnestedStaticClosable = new AutoCloseSpy(\"nestedStaticClosable\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class SuperTestCase {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable superStaticClosable;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable superClosable = new AutoCloseSpy(\"superClosable\");\n\n\t\t@BeforeAll\n\t\t// WARNING: if this method is named setup() AND the @BeforeAll method in\n\t\t// SubTestCase is also named setup(), the latter will \"hide\" the former.\n\t\tstatic void superSetup() {\n\t\t\tsuperStaticClosable = new AutoCloseSpy(\"superStaticClosable\");\n\t\t}\n\n\t\t@Test\n\t\tvoid superTest() {\n\t\t}\n\t}\n\n\tstatic class SubTestCase extends SuperTestCase {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable subStaticClosable;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable subClosable = new AutoCloseSpy(\"subClosable\");\n\n\t\t@BeforeAll\n\t\tstatic void subSetup() {\n\t\t\tsubStaticClosable = new AutoCloseSpy(\"subStaticClosable\");\n\t\t}\n\n\t\t@Test\n\t\tvoid subTest() {\n\t\t}\n\t}\n\n\tstatic class FailingFieldsTestCase {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable staticField1;\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable staticField2;\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable staticField3;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable field1 = new AutoCloseSpy(\"field1\", true);\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable field2 = new AutoCloseSpy(\"field2\", true);\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable field3 = new AutoCloseSpy(\"field3\", false);\n\n\t\t@BeforeAll\n\t\tstatic void setup() {\n\t\t\tstaticField1 = new AutoCloseSpy(\"staticField1\", true);\n\t\t\tstaticField2 = new AutoCloseSpy(\"staticField2\", true);\n\t\t\tstaticField3 = new AutoCloseSpy(\"staticField3\", false);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class FailingFieldsEnclosingTestCase {\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable enclosingStaticField1;\n\n\t\t@AutoClose\n\t\tstatic AutoCloseable enclosingStaticField2;\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable enclosingField1 = new AutoCloseSpy(\"enclosingField1\", true);\n\n\t\t@AutoClose\n\t\tfinal AutoCloseable enclosingField2 = new AutoCloseSpy(\"enclosingField2\", false);\n\n\t\t@BeforeAll\n\t\tstatic void setup() {\n\t\t\tenclosingStaticField1 = new AutoCloseSpy(\"enclosingStaticField1\", true);\n\t\t\tenclosingStaticField2 = new AutoCloseSpy(\"enclosingStaticField2\", false);\n\t\t}\n\n\t\t@Test\n\t\tvoid enclosingTest() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\t@AutoClose\n\t\t\tstatic AutoCloseable nestedStaticField1;\n\n\t\t\t@AutoClose\n\t\t\tstatic AutoCloseable nestedStaticField2;\n\n\t\t\t@AutoClose\n\t\t\tfinal AutoCloseable nestedField1 = new AutoCloseSpy(\"nestedField1\", true);\n\n\t\t\t@AutoClose\n\t\t\tfinal AutoCloseable nestedField2 = new AutoCloseSpy(\"nestedField2\", false);\n\n\t\t\t@BeforeAll\n\t\t\tstatic void setup() {\n\t\t\t\tnestedStaticField1 = new AutoCloseSpy(\"nestedStaticField1\", true);\n\t\t\t\tnestedStaticField2 = new AutoCloseSpy(\"nestedStaticField2\", false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid nestedTest() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tstatic class AutoCloseSpy implements AutoCloseable, Runnable {\n\n\t\tprivate final String prefix;\n\t\tprivate final boolean fail;\n\t\tprivate String invokedMethod = null;\n\n\t\tAutoCloseSpy(String prefix) {\n\t\t\tClass<?> callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();\n\t\t\tthis.fail = false;\n\t\t\tthis.prefix = callerClass.getSimpleName() + \".\" + prefix + \".\";\n\t\t}\n\n\t\tAutoCloseSpy(String prefix, boolean fail) {\n\t\t\tClass<?> callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();\n\t\t\tthis.prefix = callerClass.getSimpleName() + \".\" + prefix + \".\";\n\t\t\tthis.fail = fail;\n\t\t}\n\n\t\t@Override\n\t\tpublic void run() {\n\t\t\trecordInvocation(\"run()\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\trecordInvocation(\"close()\");\n\t\t}\n\n\t\tprivate void recordInvocation(String methodName) {\n\t\t\tif (this.invokedMethod != null) {\n\t\t\t\tthrow new IllegalStateException(\"Already closed via \" + this.invokedMethod);\n\t\t\t}\n\t\t\tthis.invokedMethod = methodName;\n\t\t\tString invocation = this.prefix + this.invokedMethod;\n\t\t\trecorder.add(invocation);\n\t\t\tif (this.fail) {\n\t\t\t\tthrow new RuntimeException(invocation);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterAllTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\n\n/**\n * Integration tests that verify support for {@link BeforeAll}, {@link AfterAll},\n * {@link BeforeAllCallback}, and {@link AfterAllCallback} in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass BeforeAndAfterAllTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\tprivate static @Nullable Optional<Throwable> actualExceptionInAfterAllCallback;\n\n\t@Test\n\tvoid beforeAllAndAfterAllCallbacks() {\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(TopLevelTestCase.class,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\"barBeforeAllCallback\",\n\t\t\t\t\"beforeAllMethod-1\",\n\t\t\t\t\t\"test-1\",\n\t\t\t\t\"afterAllMethod-1\",\n\t\t\t\"barAfterAllCallback\",\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).isEmpty();\n\t}\n\n\t@Test\n\tvoid beforeAllAndAfterAllCallbacksInSubclass() {\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(SecondLevelTestCase.class,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\"barBeforeAllCallback\",\n\t\t\t\t\"bazBeforeAllCallback\",\n\t\t\t\t\t\"beforeAllMethod-1\",\n\t\t\t\t\t\t\"beforeAllMethod-2\",\n\t\t\t\t\t\t\t\"test-2\",\n\t\t\t\t\t\t\"afterAllMethod-2\",\n\t\t\t\t\t\"afterAllMethod-1\",\n\t\t\t\t\"bazAfterAllCallback\",\n\t\t\t\"barAfterAllCallback\",\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).isEmpty();\n\t}\n\n\t@Test\n\tvoid beforeAllAndAfterAllCallbacksInSubSubclass() {\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(ThirdLevelTestCase.class,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\"barBeforeAllCallback\",\n\t\t\t\t\"bazBeforeAllCallback\",\n\t\t\t\t\t\"quuxBeforeAllCallback\",\n\t\t\t\t\t\t\"beforeAllMethod-1\",\n\t\t\t\t\t\t\t\"beforeAllMethod-2\",\n\t\t\t\t\t\t\t\t\"beforeAllMethod-3\",\n\t\t\t\t\t\t\t\t\t\"test-3\",\n\t\t\t\t\t\t\t\t\"afterAllMethod-3\",\n\t\t\t\t\t\t\t\"afterAllMethod-2\",\n\t\t\t\t\t\t\"afterAllMethod-1\",\n\t\t\t\t\t\"quuxAfterAllCallback\",\n\t\t\t\t\"bazAfterAllCallback\",\n\t\t\t\"barAfterAllCallback\",\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).isEmpty();\n\t}\n\n\t/**\n\t * Since static methods cannot be overridden, \"static hiding\" no longer occurs since 5.11.\n\t */\n\t@Test\n\tvoid beforeAllAndAfterAllCallbacksInSubSubclassWithoutStaticMethodHiding() {\n\t\t// NOTE: both the @BeforeAll AND the @AfterAll methods in level 3 are\n\t\t// executed as 1/2/3 due to the \"stable\" method sort order on the Platform.\n\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(ThirdLevelStaticHidingTestCase.class,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\"barBeforeAllCallback\",\n\t\t\t\t\"bazBeforeAllCallback\",\n\t\t\t\t\t\"quuxBeforeAllCallback\",\n\t\t\t\t\t\t\"beforeAllMethod-1\",\n\t\t\t\t\t\t\t\"beforeAllMethod-2\",\n\t\t\t\t\t\t\t\t\"beforeAllMethod-1-level3\",\n\t\t\t\t\t\t\t\t\"beforeAllMethod-2-level3\",\n\t\t\t\t\t\t\t\t\"beforeAllMethod-3-level3\",\n\t\t\t\t\t\t\t\t\t\"test-3\",\n\t\t\t\t\t\t\t\t\"afterAllMethod-1-level3\",\n\t\t\t\t\t\t\t\t\"afterAllMethod-2-level3\",\n\t\t\t\t\t\t\t\t\"afterAllMethod-3-level3\",\n\t\t\t\t\t\t\t\"afterAllMethod-2\",\n\t\t\t\t\t\t\"afterAllMethod-1\",\n\t\t\t\t\t\"quuxAfterAllCallback\",\n\t\t\t\t\"bazAfterAllCallback\",\n\t\t\t\"barAfterAllCallback\",\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).isEmpty();\n\t}\n\n\t@Test\n\tvoid beforeAllMethodThrowsAnException() {\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(ExceptionInBeforeAllMethodTestCase.class, 0, 0,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\t\"beforeAllMethod\", // throws an exception.\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t\"afterAllMethod\",\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t@Test\n\tvoid beforeAllCallbackThrowsAnException() {\n\t\t// @formatter:off\n\t\tassertBeforeAllAndAfterAllCallbacks(ExceptionInBeforeAllCallbackTestCase.class, 0, 0,\n\t\t\t\"fooBeforeAllCallback\",\n\t\t\t\"exceptionThrowingBeforeAllCallback\", // throws an exception.\n\t\t\t\t// beforeAllMethod should not get invoked.\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t// afterAllMethod should not get invoked.\n\t\t\t\"fooAfterAllCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterAllCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\tprivate void assertBeforeAllAndAfterAllCallbacks(Class<?> testClass, String... expectedCalls) {\n\t\tassertBeforeAllAndAfterAllCallbacks(testClass, 1, 1, expectedCalls);\n\t}\n\n\tprivate void assertBeforeAllAndAfterAllCallbacks(Class<?> testClass, int testsStarted, int testsSuccessful,\n\t\t\tString... expectedCalls) {\n\n\t\tcallSequence.clear();\n\n\t\texecuteTestsForClass(testClass).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(testsStarted).succeeded(testsSuccessful));\n\n\t\tassertEquals(asList(expectedCalls), callSequence, () -> \"wrong call sequence for \" + testClass.getName());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t// Must NOT be private; otherwise, the @Test method gets discovered but never executed.\n\t@ExtendWith({ FooClassLevelCallbacks.class, BarClassLevelCallbacks.class })\n\tstatic class TopLevelTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll1() {\n\t\t\tcallSequence.add(\"beforeAllMethod-1\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll1() {\n\t\t\tcallSequence.add(\"afterAllMethod-1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-1\");\n\t\t}\n\t}\n\n\t// Must NOT be private; otherwise, the @Test method gets discovered but never executed.\n\t@ExtendWith(BazClassLevelCallbacks.class)\n\tstatic class SecondLevelTestCase extends TopLevelTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll2() {\n\t\t\tcallSequence.add(\"beforeAllMethod-2\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll2() {\n\t\t\tcallSequence.add(\"afterAllMethod-2\");\n\t\t}\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-2\");\n\t\t}\n\t}\n\n\t@ExtendWith(QuuxClassLevelCallbacks.class)\n\tstatic class ThirdLevelTestCase extends SecondLevelTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll3() {\n\t\t\tcallSequence.add(\"beforeAllMethod-3\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll3() {\n\t\t\tcallSequence.add(\"afterAllMethod-3\");\n\t\t}\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-3\");\n\t\t}\n\t}\n\n\t@ExtendWith(QuuxClassLevelCallbacks.class)\n\tstatic class ThirdLevelStaticHidingTestCase extends SecondLevelTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll1() {\n\t\t\tcallSequence.add(\"beforeAllMethod-1-level3\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll2() {\n\t\t\tcallSequence.add(\"beforeAllMethod-2-level3\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll3() {\n\t\t\tcallSequence.add(\"beforeAllMethod-3-level3\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll1() {\n\t\t\tcallSequence.add(\"afterAllMethod-1-level3\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll2() {\n\t\t\tcallSequence.add(\"afterAllMethod-2-level3\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll3() {\n\t\t\tcallSequence.add(\"afterAllMethod-3-level3\");\n\t\t}\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-3\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooClassLevelCallbacks.class)\n\tstatic class ExceptionInBeforeAllMethodTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tcallSequence.add(\"beforeAllMethod\");\n\t\t\tthrow new EnigmaException(\"@BeforeAll\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tcallSequence.add(\"afterAllMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith({ FooClassLevelCallbacks.class, ExceptionThrowingBeforeAllCallback.class })\n\tstatic class ExceptionInBeforeAllCallbackTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tcallSequence.add(\"beforeAllMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tcallSequence.add(\"afterAllMethod\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class FooClassLevelCallbacks implements BeforeAllCallback, AfterAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooBeforeAllCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooAfterAllCallback\");\n\t\t\tactualExceptionInAfterAllCallback = context.getExecutionException();\n\t\t}\n\t}\n\n\tstatic class BarClassLevelCallbacks implements BeforeAllCallback, AfterAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barBeforeAllCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barAfterAllCallback\");\n\t\t}\n\t}\n\n\tstatic class BazClassLevelCallbacks implements BeforeAllCallback, AfterAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"bazBeforeAllCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"bazAfterAllCallback\");\n\t\t}\n\t}\n\n\tstatic class QuuxClassLevelCallbacks implements BeforeAllCallback, AfterAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"quuxBeforeAllCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"quuxAfterAllCallback\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingBeforeAllCallback implements BeforeAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingBeforeAllCallback\");\n\t\t\tthrow new EnigmaException(\"BeforeAllCallback\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterEachTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests that verify support for {@link BeforeEach}, {@link AfterEach},\n * {@link BeforeEachCallback}, and {@link AfterEachCallback} in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n * @see BeforeAndAfterTestExecutionCallbackTests\n */\nclass BeforeAndAfterEachTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\tprivate static final List<String> beforeEachMethodCallSequence = new ArrayList<>();\n\n\tprivate static @Nullable Optional<Throwable> actualExceptionInAfterEachCallback;\n\n\t@SuppressWarnings(\"OptionalAssignedToNull\")\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t\tbeforeEachMethodCallSequence.clear();\n\t\tactualExceptionInAfterEachCallback = null;\n\t}\n\n\t@Test\n\tvoid beforeEachAndAfterEachCallbacks() {\n\t\tEvents testEvents = executeTestsForClass(OuterTestCase.class).testEvents();\n\n\t\tassertEquals(2, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(2, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\n\t\t\t// OuterTestCase\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"testOuter\",\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\",\n\n\t\t\t// InnerTestCase\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\"fizzBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"beforeEachInnerMethod\",\n\t\t\t\t\t\t\"testInner\",\n\t\t\t\t\t\"afterEachInnerMethod\",\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"fizzAfterEachCallback\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\"\n\n\t\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeEachAndAfterEachCallbacksDeclaredOnSuperclassAndSubclass() {\n\t\tEvents testEvents = executeTestsForClass(ChildTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(1, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\t\"testChild\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeEachAndAfterEachCallbacksDeclaredOnInterfaceAndClass() {\n\t\tEvents testEvents = executeTestsForClass(TestInterfaceTestCase.class).testEvents();\n\n\t\tassertEquals(2, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(2, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\n\t\t\t// Test Interface\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\t\"defaultTestMethod\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\",\n\n\t\t\t// Test Class\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\t\"localTestMethod\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\"\n\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeEachCallbackThrowsAnException() {\n\t\tEvents testEvents = executeTestsForClass(ExceptionInBeforeEachCallbackTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(0, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"exceptionThrowingBeforeEachCallback\", // throws an exception.\n\t\t\t// barBeforeEachCallback should not get invoked.\n\t\t\t\t// beforeEachMethod should not get invoked.\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t// afterEachMethod should not get invoked.\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"fooAfterEachCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterEachCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t@Test\n\tvoid afterEachCallbackThrowsAnException() {\n\t\tEvents testEvents = executeTestsForClass(ExceptionInAfterEachCallbackTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(0, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\"barBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"test\",\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"barAfterEachCallback\",\n\t\t\t\"exceptionThrowingAfterEachCallback\", // throws an exception.\n\t\t\t\"fooAfterEachCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterEachCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t@Test\n\tvoid beforeEachMethodThrowsAnException() {\n\t\tEvents testEvents = executeTestsForClass(ExceptionInBeforeEachMethodTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(0, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// Since the JVM does not guarantee the order in which methods are\n\t\t// returned via reflection (and since JUnit Jupiter does not yet\n\t\t// support ordering of @BeforeEach methods), we have to figure out\n\t\t// which @BeforeEach method got executed first in order to determine\n\t\t// the expected call sequence.\n\n\t\t// @formatter:off\n\t\tList<String> list1 = asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod1\", // throws an exception.\n\t\t\t\t// \"beforeEachMethod2\" should not get invoked\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"fooAfterEachCallback\"\n\t\t);\n\t\tList<String> list2 = asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod2\",\n\t\t\t\t\"beforeEachMethod1\", // throws an exception.\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"fooAfterEachCallback\"\n\t\t);\n\t\t// @formatter:on\n\n\t\tList<String> expected = beforeEachMethodCallSequence.getFirst().equals(\"beforeEachMethod1\") ? list1 : list2;\n\n\t\tassertEquals(expected, callSequence, \"wrong call sequence\");\n\n\t\tassertThat(actualExceptionInAfterEachCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t@Test\n\tvoid afterEachMethodThrowsAnException() {\n\t\tEvents testEvents = executeTestsForClass(ExceptionInAfterEachMethodTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(0, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"test\",\n\t\t\t\t\"afterEachMethod\", // throws an exception.\n\t\t\t\"fooAfterEachCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterEachCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t@Test\n\tvoid testMethodThrowsAnException() {\n\t\tEvents testEvents = executeTestsForClass(ExceptionInTestMethodTestCase.class).testEvents();\n\n\t\tassertEquals(1, testEvents.started().count(), \"# tests started\");\n\t\tassertEquals(0, testEvents.succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, testEvents.skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, testEvents.aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, testEvents.failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeEachCallback\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"test\", // throws an exception.\n\t\t\t\t\"afterEachMethod\",\n\t\t\t\"fooAfterEachCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertThat(actualExceptionInAfterEachCallback).containsInstanceOf(EnigmaException.class);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith(FooMethodLevelCallbacks.class)\n\tstatic class ParentTestCase {\n\t}\n\n\t@ExtendWith(BarMethodLevelCallbacks.class)\n\tstatic class ChildTestCase extends ParentTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"testChild\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooMethodLevelCallbacks.class)\n\tprivate interface TestInterface {\n\n\t\t@Test\n\t\tdefault void defaultTest() {\n\t\t\tcallSequence.add(\"defaultTestMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(BarMethodLevelCallbacks.class)\n\tstatic class TestInterfaceTestCase implements TestInterface {\n\n\t\t@Test\n\t\tvoid localTest() {\n\t\t\tcallSequence.add(\"localTestMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith({ FooMethodLevelCallbacks.class, BarMethodLevelCallbacks.class })\n\tstatic class OuterTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testOuter() {\n\t\t\tcallSequence.add(\"testOuter\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(FizzMethodLevelCallbacks.class)\n\t\tclass InnerTestCase {\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEachInnerMethod() {\n\t\t\t\tcallSequence.add(\"beforeEachInnerMethod\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid testInner() {\n\t\t\t\tcallSequence.add(\"testInner\");\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEachInnerMethod() {\n\t\t\t\tcallSequence.add(\"afterEachInnerMethod\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith({ FooMethodLevelCallbacks.class, ExceptionThrowingBeforeEachCallback.class,\n\t\t\tBarMethodLevelCallbacks.class })\n\tstatic class ExceptionInBeforeEachCallbackTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith({ FooMethodLevelCallbacks.class, ExceptionThrowingAfterEachCallback.class,\n\t\t\tBarMethodLevelCallbacks.class })\n\tstatic class ExceptionInAfterEachCallbackTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooMethodLevelCallbacks.class)\n\tstatic class ExceptionInBeforeEachMethodTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach1() {\n\t\t\tbeforeEachMethodCallSequence.add(\"beforeEachMethod1\");\n\t\t\tcallSequence.add(\"beforeEachMethod1\");\n\t\t\tthrow new EnigmaException(\"@BeforeEach\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach2() {\n\t\t\tbeforeEachMethodCallSequence.add(\"beforeEachMethod2\");\n\t\t\tcallSequence.add(\"beforeEachMethod2\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooMethodLevelCallbacks.class)\n\tstatic class ExceptionInAfterEachMethodTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t\tthrow new EnigmaException(\"@AfterEach\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooMethodLevelCallbacks.class)\n\tstatic class ExceptionInTestMethodTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t\tthrow new EnigmaException(\"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class FooMethodLevelCallbacks implements BeforeEachCallback, AfterEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooBeforeEachCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooAfterEachCallback\");\n\t\t\tactualExceptionInAfterEachCallback = context.getExecutionException();\n\t\t}\n\t}\n\n\tstatic class BarMethodLevelCallbacks implements BeforeEachCallback, AfterEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barBeforeEachCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barAfterEachCallback\");\n\t\t}\n\t}\n\n\tstatic class FizzMethodLevelCallbacks implements BeforeEachCallback, AfterEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fizzBeforeEachCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fizzAfterEachCallback\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingBeforeEachCallback implements BeforeEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingBeforeEachCallback\");\n\t\t\tthrow new EnigmaException(\"BeforeEachCallback\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingAfterEachCallback implements AfterEachCallback {\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingAfterEachCallback\");\n\t\t\tthrow new EnigmaException(\"AfterEachCallback\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/BeforeAndAfterTestExecutionCallbackTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for {@link BeforeTestExecutionCallback},\n * {@link AfterTestExecutionCallback}, {@link BeforeEach}, and {@link AfterEach}\n * in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n * @see BeforeAndAfterEachTests\n */\nclass BeforeAndAfterTestExecutionCallbackTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\tprivate static @Nullable Optional<Throwable> actualExceptionInAfterTestExecution;\n\n\t@SuppressWarnings(\"OptionalAssignedToNull\")\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t\tactualExceptionInAfterTestExecution = null;\n\t}\n\n\t@Test\n\tvoid beforeAndAfterTestExecutionCallbacks() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(OuterTestCase.class);\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\n\t\t\t// OuterTestCase\n\t\t\t\"beforeEachMethodOuter\",\n\t\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\t\"testOuter\",\n\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\t\"fooAfterTestExecutionCallback\",\n\t\t\t\"afterEachMethodOuter\",\n\n\t\t\t// InnerTestCase\n\t\t\t\"beforeEachMethodOuter\",\n\t\t\t\t\"beforeEachMethodInner\",\n\t\t\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\t\t\"fizzBeforeTestExecutionCallback\",\n\t\t\t\t\t\t\t\"testInner\",\n\t\t\t\t\t\t\"fizzAfterTestExecutionCallback\",\n\t\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\t\t\"fooAfterTestExecutionCallback\",\n\t\t\t\t\"afterEachMethodInner\",\n\t\t\t\"afterEachMethodOuter\"\n\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeAndAfterTestExecutionCallbacksDeclaredOnSuperclassAndSubclass() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ChildTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\"testChild\",\n\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\"fooAfterTestExecutionCallback\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeAndAfterTestExecutionCallbacksDeclaredOnInterfaceAndClass() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestInterfaceTestCase.class);\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\n\t\t\t// Test Interface\n\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\t\"defaultTestMethod\",\n\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\"fooAfterTestExecutionCallback\",\n\n\t\t\t// Test Class\n\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\t\"localTestMethod\",\n\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\"fooAfterTestExecutionCallback\"\n\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeEachMethodThrowsAnException() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ExceptionInBeforeEachMethodTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"beforeEachMethod\", // throws an exception.\n\t\t\t\t// fooBeforeTestExecutionCallback should not get invoked.\n\t\t\t\t\t// test should not get invoked.\n\t\t\t\t// fooAfterTestExecutionCallback should not get invoked.\n\t\t\t\"afterEachMethod\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertNull(actualExceptionInAfterTestExecution,\n\t\t\t\"test exception (fooAfterTestExecutionCallback should not have been called)\");\n\t}\n\n\t@Test\n\tvoid beforeTestExecutionCallbackThrowsAnException() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(\n\t\t\tExceptionInBeforeTestExecutionCallbackTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\"exceptionThrowingBeforeTestExecutionCallback\", // throws an exception.\n\t\t\t\t// barBeforeTestExecutionCallback should not get invoked.\n\t\t\t\t\t// test() should not get invoked.\n\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\t\"fooAfterTestExecutionCallback\",\n\t\t\t\"afterEachMethod\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertNotNull(actualExceptionInAfterTestExecution, \"test exception\");\n\t\tassertTrue(actualExceptionInAfterTestExecution.isPresent(), \"test exception should be present\");\n\t\tassertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());\n\t}\n\n\t@Test\n\tvoid afterTestExecutionCallbackThrowsAnException() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(\n\t\t\tExceptionInAfterTestExecutionCallbackTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\"barBeforeTestExecutionCallback\",\n\t\t\t\t\t\"test\",\n\t\t\t\t\"barAfterTestExecutionCallback\",\n\t\t\t\t\"exceptionThrowingAfterTestExecutionCallback\", // throws an exception.\n\t\t\t\t\"fooAfterTestExecutionCallback\",\n\t\t\t\"afterEachMethod\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertNotNull(actualExceptionInAfterTestExecution, \"test exception\");\n\t\tassertTrue(actualExceptionInAfterTestExecution.isPresent(), \"test exception should be present\");\n\t\tassertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());\n\t}\n\n\t@Test\n\tvoid testMethodThrowsAnException() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ExceptionInTestMethodTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tassertEquals(asList(\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"fooBeforeTestExecutionCallback\",\n\t\t\t\t\t\"test\", // throws an exception.\n\t\t\t\t\"fooAfterTestExecutionCallback\",\n\t\t\t\"afterEachMethod\"\n\t\t), callSequence, \"wrong call sequence\");\n\t\t// @formatter:on\n\n\t\tassertNotNull(actualExceptionInAfterTestExecution, \"test exception\");\n\t\tassertTrue(actualExceptionInAfterTestExecution.isPresent(), \"test exception should be present\");\n\t\tassertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith(FooTestExecutionCallbacks.class)\n\tstatic class ParentTestCase {\n\t}\n\n\t@ExtendWith(BarTestExecutionCallbacks.class)\n\tstatic class ChildTestCase extends ParentTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"testChild\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooTestExecutionCallbacks.class)\n\tprivate interface TestInterface {\n\n\t\t@Test\n\t\tdefault void defaultTest() {\n\t\t\tcallSequence.add(\"defaultTestMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(BarTestExecutionCallbacks.class)\n\tstatic class TestInterfaceTestCase implements TestInterface {\n\n\t\t@Test\n\t\tvoid localTest() {\n\t\t\tcallSequence.add(\"localTestMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith({ FooTestExecutionCallbacks.class, BarTestExecutionCallbacks.class })\n\tstatic class OuterTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethodOuter\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testOuter() {\n\t\t\tcallSequence.add(\"testOuter\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethodOuter\");\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(FizzTestExecutionCallbacks.class)\n\t\tclass InnerTestCase {\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeInnerMethod() {\n\t\t\t\tcallSequence.add(\"beforeEachMethodInner\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid testInner() {\n\t\t\t\tcallSequence.add(\"testInner\");\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterInnerMethod() {\n\t\t\t\tcallSequence.add(\"afterEachMethodInner\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith({ FooTestExecutionCallbacks.class, ExceptionThrowingBeforeTestExecutionCallback.class,\n\t\t\tBarTestExecutionCallbacks.class })\n\tstatic class ExceptionInBeforeTestExecutionCallbackTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith({ FooTestExecutionCallbacks.class, ExceptionThrowingAfterTestExecutionCallback.class,\n\t\t\tBarTestExecutionCallbacks.class })\n\tstatic class ExceptionInAfterTestExecutionCallbackTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooTestExecutionCallbacks.class)\n\tstatic class ExceptionInBeforeEachMethodTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t\tthrow new EnigmaException(\"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooTestExecutionCallbacks.class)\n\tstatic class ExceptionInTestMethodTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t\tthrow new EnigmaException(\"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tcallSequence.add(\"afterEachMethod\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class FooTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooBeforeTestExecutionCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fooAfterTestExecutionCallback\");\n\t\t\tactualExceptionInAfterTestExecution = context.getExecutionException();\n\t\t}\n\t}\n\n\tstatic class BarTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barBeforeTestExecutionCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"barAfterTestExecutionCallback\");\n\t\t}\n\t}\n\n\tstatic class FizzTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fizzBeforeTestExecutionCallback\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"fizzAfterTestExecutionCallback\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingBeforeTestExecutionCallback implements BeforeTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingBeforeTestExecutionCallback\");\n\t\t\tthrow new EnigmaException(\"BeforeTestExecutionCallback\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingAfterTestExecutionCallback implements AfterTestExecutionCallback {\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingAfterTestExecutionCallback\");\n\t\t\tthrow new EnigmaException(\"AfterTestExecutionCallback\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/CloseablePathTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static com.google.common.jimfs.Configuration.unix;\nimport static java.lang.annotation.ElementType.METHOD;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\nimport static java.nio.file.Files.createDirectory;\nimport static java.nio.file.Files.createFile;\nimport static java.nio.file.Files.createSymbolicLink;\nimport static java.nio.file.Files.createTempDirectory;\nimport static java.nio.file.Files.delete;\nimport static java.nio.file.Files.deleteIfExists;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\nimport static org.junit.jupiter.api.io.CleanupMode.ALWAYS;\nimport static org.junit.jupiter.api.io.CleanupMode.NEVER;\nimport static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.reset;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport com.google.common.jimfs.Jimfs;\n\nimport org.assertj.core.api.ThrowableAssert.ThrowingCallable;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.execution.NamespaceAwareStore;\nimport org.junit.jupiter.engine.extension.TempDirectory.CloseablePath;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * Integration tests for the creation and cleanup of the {@link TempDirectory}.\n *\n * @since 5.9\n */\n@DisplayName(\"Temporary directory\")\nclass CloseablePathTests extends AbstractJupiterTestEngineTests {\n\n\tprivate final AnnotatedElementContext elementContext = mock();\n\tprivate final ExtensionContext extensionContext = mock();\n\n\tprivate @Nullable CloseablePath closeablePath;\n\n\t@Target(METHOD)\n\t@Retention(RUNTIME)\n\t@ValueSource(classes = { File.class, Path.class })\n\tprivate @interface ElementTypeSource {\n\t}\n\n\t@BeforeEach\n\tvoid setUpExtensionContext() {\n\t\tvar store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null), Namespace.GLOBAL);\n\t\twhen(extensionContext.getStore(any())).thenReturn(store);\n\t}\n\n\t/**\n\t * Integration tests for the creation of the {@link TempDirectory} based on the different result\n\t * that {@link TempDirFactory#createTempDirectory(AnnotatedElementContext, ExtensionContext)} may provide.\n\t *\n\t * @since 5.11\n\t * @see TempDirFactory\n\t */\n\t@Nested\n\t@DisplayName(\"creation\")\n\tclass Creation {\n\n\t\tprivate Path root;\n\n\t\t@BeforeEach\n\t\tvoid setUpRootFolder() throws IOException {\n\t\t\troot = createTempDirectory(\"root\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid cleanupRoot() throws IOException {\n\t\t\tdelete(root);\n\t\t}\n\n\t\t@DisplayName(\"succeeds if the factory returns a directory\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid factoryReturnsDirectoryDynamic(Class<?> elementType) throws IOException {\n\t\t\tTempDirFactory factory = (_, _) -> createDirectory(root.resolve(\"directory\"));\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ALWAYS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tdelete(closeablePath.get());\n\t\t}\n\n\t\t@DisplayName(\"succeeds if the factory returns a symbolic link to a directory\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\t@DisabledOnOs(WINDOWS)\n\t\tvoid factoryReturnsSymbolicLinkToDirectory(Class<?> elementType) throws IOException {\n\t\t\tPath directory = createDirectory(root.resolve(\"directory\"));\n\t\t\tTempDirFactory factory = (_, _) -> createSymbolicLink(root.resolve(\"symbolicLink\"), directory);\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ALWAYS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tdelete(closeablePath.get());\n\t\t\tdelete(directory);\n\t\t}\n\n\t\t@DisplayName(\"succeeds if the factory returns a directory on a non-default file system for a Path annotated element\")\n\t\t@Test\n\t\tvoid factoryReturnsDirectoryOnNonDefaultFileSystemWithPath() throws IOException {\n\t\t\tTempDirFactory factory = new JimfsFactory();\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ALWAYS), Path.class, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tdelete(closeablePath.get());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@DisplayName(\"fails if the factory returns null\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid factoryReturnsNull(Class<?> elementType) throws IOException {\n\t\t\tTempDirFactory factory = spy(new Factory(null));\n\n\t\t\tassertThatExtensionConfigurationExceptionIsThrownBy(() -> TempDirectory.createTempDir(factory,\n\t\t\t\tcleanup(ALWAYS), elementType, elementContext, extensionContext));\n\n\t\t\tverify(factory).close();\n\t\t}\n\n\t\t@DisplayName(\"fails if the factory returns a file\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid factoryReturnsFile(Class<?> elementType) throws IOException {\n\t\t\tPath file = createFile(root.resolve(\"file\"));\n\t\t\tTempDirFactory factory = spy(new Factory(file));\n\n\t\t\tassertThatExtensionConfigurationExceptionIsThrownBy(() -> TempDirectory.createTempDir(factory,\n\t\t\t\tcleanup(ALWAYS), elementType, elementContext, extensionContext));\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(file).doesNotExist();\n\t\t}\n\n\t\t@DisplayName(\"fails if the factory returns a symbolic link to a file\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\t@DisabledOnOs(WINDOWS)\n\t\tvoid factoryReturnsSymbolicLinkToFile(Class<?> elementType) throws IOException {\n\t\t\tPath file = createFile(root.resolve(\"file\"));\n\t\t\tPath symbolicLink = createSymbolicLink(root.resolve(\"symbolicLink\"), file);\n\t\t\tTempDirFactory factory = spy(new Factory(symbolicLink));\n\n\t\t\tassertThatExtensionConfigurationExceptionIsThrownBy(() -> TempDirectory.createTempDir(factory,\n\t\t\t\tcleanup(ALWAYS), elementType, elementContext, extensionContext));\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(symbolicLink).doesNotExist();\n\n\t\t\tdelete(file);\n\t\t}\n\n\t\t@DisplayName(\"fails if the factory returns a directory on a non-default file system for a File annotated element\")\n\t\t@Test\n\t\tvoid factoryReturnsDirectoryOnNonDefaultFileSystemWithFile() throws IOException {\n\t\t\tTempDirFactory factory = spy(new JimfsFactory());\n\n\t\t\tassertThatExceptionOfType(ExtensionConfigurationException.class)//\n\t\t\t\t\t.isThrownBy(() -> TempDirectory.createTempDir(factory, cleanup(ALWAYS), File.class, elementContext,\n\t\t\t\t\t\textensionContext))//\n\t\t\t\t\t.withMessage(\"Failed to create default temp directory\")//\n\t\t\t\t\t.withCauseInstanceOf(PreconditionViolationException.class)//\n\t\t\t\t\t.havingCause().withMessage(\"temp directory with non-default file system cannot be injected into \"\n\t\t\t\t\t\t\t+ File.class.getName() + \" target\");\n\n\t\t\tverify(factory).close();\n\t\t}\n\n\t\t// Mockito spying a lambda fails with: VM does not support modification of given type\n\t\t@NullMarked\n\t\tprivate record Factory(Path path) implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) {\n\t\t\t\treturn path;\n\t\t\t}\n\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class JimfsFactory implements TempDirFactory {\n\n\t\t\tprivate final FileSystem fileSystem = Jimfs.newFileSystem(unix());\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn createDirectory(fileSystem.getPath(\"/\").resolve(\"directory\"));\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\tfileSystem.close();\n\t\t\t}\n\t\t}\n\n\t\tprivate static void assertThatExtensionConfigurationExceptionIsThrownBy(ThrowingCallable callable) {\n\t\t\tassertThatExceptionOfType(ExtensionConfigurationException.class)//\n\t\t\t\t\t.isThrownBy(callable)//\n\t\t\t\t\t.withMessage(\"Failed to create default temp directory\")//\n\t\t\t\t\t.withCauseInstanceOf(PreconditionViolationException.class)//\n\t\t\t\t\t.havingCause().withMessage(\"temp directory must be a directory\");\n\t\t}\n\n\t}\n\n\t/**\n\t * Integration tests for cleanup of the {@link TempDirectory} when the {@link CleanupMode} is\n\t * set to {@link CleanupMode#ALWAYS}, {@link CleanupMode#NEVER}, or {@link CleanupMode#ON_SUCCESS}.\n\t *\n\t * @since 5.9\n\t * @see TempDir\n\t * @see CleanupMode\n\t */\n\t@Nested\n\t@DisplayName(\"cleanup\")\n\tclass Cleanup {\n\n\t\tprivate final TempDirFactory factory = spy(TempDirFactory.Standard.INSTANCE);\n\n\t\t@AfterEach\n\t\tvoid cleanupTempDirectory() throws IOException {\n\t\t\tif (closeablePath != null) {\n\t\t\t\tdeleteIfExists(closeablePath.get());\n\t\t\t}\n\t\t}\n\n\t\t@DisplayName(\"is done for a cleanup mode of ALWAYS\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid always(Class<?> elementType, @TrackLogRecords LogRecordListener listener) throws IOException {\n\t\t\treset(factory);\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ALWAYS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tcloseablePath.close();\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(closeablePath.get()).doesNotExist();\n\t\t\tassertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//\n\t\t\t\t\t.noneMatch(m -> m.startsWith(\"Skipping cleanup of temp dir\"));\n\t\t}\n\n\t\t@DisplayName(\"is not done for a cleanup mode of NEVER\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid never(Class<?> elementType, @TrackLogRecords LogRecordListener listener) throws Exception {\n\t\t\treset(factory);\n\n\t\t\twhen(elementContext.getAnnotatedElement()).thenReturn(TestCase.class.getDeclaredField(\"tempDir\"));\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(NEVER), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tcloseablePath.close();\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(closeablePath.get()).exists();\n\t\t\tassertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//\n\t\t\t\t\t.anyMatch(m -> m.startsWith(\"Skipping cleanup of temp dir \")\n\t\t\t\t\t\t\t&& m.endsWith(\" for field TestCase.tempDir due to CleanupMode.NEVER.\"));\n\t\t}\n\n\t\t@DisplayName(\"is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated field)\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid onSuccessWithExceptionForAnnotatedField(Class<?> elementType, @TrackLogRecords LogRecordListener listener)\n\t\t\t\tthrows Exception {\n\n\t\t\tField field = TestCase.class.getDeclaredField(\"tempDir\");\n\n\t\t\tonSuccessWithException(elementType, listener, field,\n\t\t\t\t\" for field TestCase.tempDir due to CleanupMode.ON_SUCCESS.\");\n\t\t}\n\n\t\t@DisplayName(\"is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated method parameter)\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid onSuccessWithExceptionForAnnotatedMethodParameter(Class<?> elementType,\n\t\t\t\t@TrackLogRecords LogRecordListener listener) throws Exception {\n\n\t\t\tMethod method = TestCase.class.getDeclaredMethod(\"test\", TestInfo.class, Path.class);\n\t\t\tParameter parameter = method.getParameters()[1];\n\n\t\t\tonSuccessWithException(elementType, listener, parameter,\n\t\t\t\t\"for parameter 'tempDir' in method test(TestInfo, Path) due to CleanupMode.ON_SUCCESS.\");\n\t\t}\n\n\t\t@DisplayName(\"is not done for a cleanup mode of ON_SUCCESS, if there is an exception (for annotated constructor parameter)\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid onSuccessWithExceptionForAnnotatedConstructorParameter(Class<?> elementType,\n\t\t\t\t@TrackLogRecords LogRecordListener listener) throws Exception {\n\n\t\t\tConstructor<?> constructor = TestCase.class.getDeclaredConstructor(TestInfo.class, Path.class);\n\t\t\tParameter parameter = constructor.getParameters()[1];\n\n\t\t\tonSuccessWithException(elementType, listener, parameter,\n\t\t\t\t\"for parameter 'tempDir' in constructor TestCase(TestInfo, Path) due to CleanupMode.ON_SUCCESS.\");\n\t\t}\n\n\t\tprivate void onSuccessWithException(Class<?> elementType, @TrackLogRecords LogRecordListener listener,\n\t\t\t\tAnnotatedElement annotatedElement, String expectedMessage) throws Exception {\n\n\t\t\treset(factory);\n\n\t\t\twhen(extensionContext.getExecutionException()).thenReturn(Optional.of(new Exception()));\n\t\t\twhen(elementContext.getAnnotatedElement()).thenReturn(annotatedElement);\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ON_SUCCESS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tcloseablePath.close();\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(closeablePath.get()).exists();\n\t\t\tassertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//\n\t\t\t\t\t.anyMatch(m -> m.startsWith(\"Skipping cleanup of temp dir \") && m.endsWith(expectedMessage));\n\t\t}\n\n\t\t@DisplayName(\"is done for a cleanup mode of ON_SUCCESS, if there is no exception\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\tvoid onSuccessWithNoException(Class<?> elementType, @TrackLogRecords LogRecordListener listener)\n\t\t\t\tthrows IOException {\n\n\t\t\treset(factory);\n\n\t\t\twhen(extensionContext.getExecutionException()).thenReturn(Optional.empty());\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ON_SUCCESS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tassertThat(closeablePath.get()).isDirectory();\n\n\t\t\tcloseablePath.close();\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(closeablePath.get()).doesNotExist();\n\t\t\tassertThat(listener.stream(Level.INFO)).map(LogRecord::getMessage)//\n\t\t\t\t\t.noneMatch(m -> m.startsWith(\"Skipping cleanup of temp dir\"));\n\t\t}\n\n\t\t@DisplayName(\"deletes symbolic links targeting directory inside temp dir\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\t@DisabledOnOs(WINDOWS)\n\t\tvoid deletesSymbolicLinksTargetingDirInsideTempDir(Class<?> elementType,\n\t\t\t\t@TrackLogRecords LogRecordListener listener) throws IOException {\n\n\t\t\treset(factory);\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ON_SUCCESS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tvar rootDir = closeablePath.get();\n\t\t\tassertThat(rootDir).isDirectory();\n\n\t\t\tvar subDir = createDirectory(rootDir.resolve(\"subDir\"));\n\t\t\tFiles.createFile(subDir.resolve(\"file\"));\n\t\t\tFiles.createSymbolicLink(rootDir.resolve(\"symbolicLink\"), subDir);\n\n\t\t\tcloseablePath.close();\n\n\t\t\tverify(factory).close();\n\t\t\tassertThat(rootDir).doesNotExist();\n\t\t\tassertThat(listener.stream(Level.WARNING)).map(LogRecord::getMessage).isEmpty();\n\n\t\t}\n\n\t\t@DisplayName(\"deletes symbolic links targeting directory outside temp dir\")\n\t\t@ParameterizedTest\n\t\t@ElementTypeSource\n\t\t@DisabledOnOs(WINDOWS)\n\t\tvoid deletesSymbolicLinksTargetingDirOutsideTempDir(Class<?> elementType,\n\t\t\t\t@TrackLogRecords LogRecordListener listener) throws IOException {\n\n\t\t\treset(factory);\n\n\t\t\tcloseablePath = TempDirectory.createTempDir(factory, cleanup(ON_SUCCESS), elementType, elementContext,\n\t\t\t\textensionContext);\n\t\t\tvar rootDir = closeablePath.get();\n\t\t\tassertThat(rootDir).isDirectory();\n\n\t\t\tvar directoryOutsideTempDir = createTempDirectory(\"junit-\");\n\t\t\ttry {\n\t\t\t\tvar symbolicLink = createSymbolicLink(rootDir.resolve(\"symbolicLink\"), directoryOutsideTempDir);\n\n\t\t\t\tcloseablePath.close();\n\n\t\t\t\tverify(factory).close();\n\t\t\t\tassertThat(rootDir).doesNotExist();\n\t\t\t\tassertThat(directoryOutsideTempDir).isDirectory();\n\t\t\t\tassertThat(listener.stream(Level.WARNING)) //\n\t\t\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t\t\t.contains((\"Deleting symbolic link from location inside of temp dir (%s) \"\n\t\t\t\t\t\t\t\t+ \"to location outside of temp dir (%s) but not the target file/directory\").formatted(\n\t\t\t\t\t\t\t\t\tsymbolicLink, directoryOutsideTempDir.toRealPath()));\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tFiles.deleteIfExists(directoryOutsideTempDir);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static TempDirectory.Cleanup cleanup(CleanupMode cleanupMode) {\n\t\treturn new TempDirectory.Cleanup(cleanupMode, () -> TempDirDeletionStrategy.Standard.INSTANCE);\n\t}\n\n\t@NullUnmarked\n\tstatic class TestCase {\n\n\t\tPath tempDir;\n\n\t\tTestCase(TestInfo testInfo, Path tempDir) {\n\t\t}\n\n\t\tvoid test(TestInfo testInfo, Path tempDir) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ConfigLoaderExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Demo extension for auto-detection of extensions loaded via Java's\n * {@link java.util.ServiceLoader} mechanism.\n *\n * @since 5.11\n */\npublic class ConfigLoaderExtension implements BeforeAllCallback {\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/DefaultTestReporterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.verify;\n\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n@MockitoSettings\nclass DefaultTestReporterTests {\n\n\t@TempDir\n\tPath tempDir;\n\n\t@Mock\n\tExtensionContext extensionContext;\n\n\t@Captor\n\tArgumentCaptor<ThrowingConsumer<Path>> actionCaptor;\n\n\t@InjectMocks\n\tDefaultTestReporter testReporter;\n\n\t@Test\n\tvoid copiesExistingFileToTarget() throws Throwable {\n\t\ttestReporter.publishFile(Files.writeString(tempDir.resolve(\"source\"), \"content\"), MediaType.TEXT_PLAIN_UTF_8);\n\n\t\tverify(extensionContext).publishFile(eq(\"source\"), eq(MediaType.TEXT_PLAIN_UTF_8), actionCaptor.capture());\n\t\tactionCaptor.getValue().accept(tempDir.resolve(\"target\"));\n\n\t\tassertThat(tempDir.resolve(\"target\")).usingCharset(UTF_8).hasContent(\"content\");\n\t}\n\n\t@Test\n\tvoid executesCustomActionWithTargetFile() throws Throwable {\n\t\ttestReporter.publishFile(\"target\", MediaType.APPLICATION_OCTET_STREAM,\n\t\t\tfile -> Files.write(file, \"content\".getBytes()));\n\n\t\tverify(extensionContext).publishFile(eq(\"target\"), eq(MediaType.APPLICATION_OCTET_STREAM),\n\t\t\tactionCaptor.capture());\n\t\tactionCaptor.getValue().accept(tempDir.resolve(\"target\"));\n\n\t\tassertThat(tempDir.resolve(\"target\")).hasContent(\"content\");\n\t}\n\n\t@Test\n\tvoid copiesExistingDirectoryToTarget() throws Throwable {\n\t\tvar source = Files.createDirectory(tempDir.resolve(\"source\"));\n\t\tFiles.writeString(source.resolve(\"source1\"), \"content1\");\n\t\tvar sourceSubDir = Files.createDirectory(source.resolve(\"subDir\"));\n\t\tFiles.writeString(sourceSubDir.resolve(\"source2\"), \"content2\");\n\t\tFiles.writeString(Files.createDirectory(sourceSubDir.resolve(\"subSubDir\")).resolve(\"source3\"), \"content3\");\n\n\t\ttestReporter.publishDirectory(source);\n\n\t\tverify(extensionContext).publishDirectory(eq(\"source\"), actionCaptor.capture());\n\n\t\tvar target = tempDir.resolve(\"target\");\n\t\tactionCaptor.getValue().accept(target);\n\n\t\tassertThat(target).isDirectory();\n\t\tassertThat(target.resolve(\"source1\")).usingCharset(UTF_8).hasContent(\"content1\");\n\t\tvar targetSubDir = target.resolve(\"subDir\");\n\t\tassertThat(targetSubDir.resolve(\"source2\")).usingCharset(UTF_8).hasContent(\"content2\");\n\t\tassertThat(targetSubDir.resolve(\"subSubDir\").resolve(\"source3\")).usingCharset(UTF_8).hasContent(\"content3\");\n\t}\n\n\t@Test\n\tvoid executesCustomActionWithTargetDirectory() throws Throwable {\n\t\ttestReporter.publishDirectory(\"target\",\n\t\t\tdir -> Files.writeString(Files.createDirectory(dir).resolve(\"file\"), \"content\", Charset.defaultCharset()));\n\n\t\tverify(extensionContext).publishDirectory(eq(\"target\"), actionCaptor.capture());\n\n\t\tvar target = tempDir.resolve(\"target\");\n\t\tactionCaptor.getValue().accept(target);\n\n\t\tassertThat(target.resolve(\"file\")).hasContent(\"content\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"DataFlowIssue\") // publishFile() parameters are not @Nullable\n\tvoid failsWhenPublishingNullFile() {\n\t\tassertPreconditionViolationNotNullFor(\"file\", () -> testReporter.publishFile(null, MediaType.TEXT_PLAIN));\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"DataFlowIssue\") // publishFile() parameters are not @Nullable\n\tvoid failsWhenPublishingFileWithNullMediaType() {\n\t\tassertPreconditionViolationNotNullFor(\"mediaType\",\n\t\t\t() -> testReporter.publishFile(Path.of(\"test\"), (MediaType) null));\n\t}\n\n\t@Test\n\tvoid failsWhenPublishingMissingFile() {\n\t\tvar missingFile = tempDir.resolve(\"missingFile\");\n\n\t\tassertPreconditionViolationFor(() -> testReporter.publishFile(missingFile, MediaType.APPLICATION_OCTET_STREAM))//\n\t\t\t\t.withMessage(\"file must exist: \" + missingFile);\n\t}\n\n\t@Test\n\tvoid failsWhenPublishingDirectoryAsFile() {\n\t\tassertPreconditionViolationFor(() -> testReporter.publishFile(tempDir, MediaType.APPLICATION_OCTET_STREAM))//\n\t\t\t\t.withMessage(\"file must be a regular file: \" + tempDir);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"DataFlowIssue\") // publishDirectory() parameters are not @Nullable\n\tvoid failsWhenPublishingNullDirectory() {\n\t\tassertPreconditionViolationNotNullFor(\"directory\", () -> testReporter.publishDirectory(null));\n\t}\n\n\t@Test\n\tvoid failsWhenPublishingMissingDirectory() {\n\t\tvar missingDir = tempDir.resolve(\"missingDir\");\n\n\t\tassertPreconditionViolationFor(() -> testReporter.publishDirectory(missingDir))//\n\t\t\t\t.withMessage(\"directory must exist: \" + missingDir);\n\t}\n\n\t@Test\n\tvoid failsWhenPublishingFileAsDirectory() throws Exception {\n\t\tvar dir = Files.createFile(tempDir.resolve(\"source\"));\n\n\t\tassertPreconditionViolationFor(() -> testReporter.publishDirectory(dir))//\n\t\t\t\t.withMessage(\"path must represent a directory: \" + dir);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EnigmaException.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\n@SuppressWarnings(\"serial\")\nclass EnigmaException extends RuntimeException {\n\n\tEnigmaException(String message) {\n\t\tsuper(message);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/EventuallyInterruptibleInvocation.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\n\n/**\n * @since 5.5\n */\nclass EventuallyInterruptibleInvocation implements Invocation<Void> {\n\n\t@Override\n\tpublic Void proceed() {\n\t\twhile (!Thread.currentThread().isInterrupted()) {\n\t\t\tassertThat(IntStream.range(1, 1_000_000).sum()).isGreaterThan(0);\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExecutionConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Constants.DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.engine.extension.sub.AlwaysDisabledCondition;\nimport org.junit.jupiter.engine.extension.sub.AnotherAlwaysDisabledCondition;\nimport org.junit.jupiter.engine.extension.sub.SystemPropertyCondition;\nimport org.junit.jupiter.engine.extension.sub.SystemPropertyCondition.SystemProperty;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests that verify support for the {@link ExecutionCondition}\n * extension point in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\n@SetSystemProperty(key = ExecutionConditionTests.FOO, value = ExecutionConditionTests.BAR)\nclass ExecutionConditionTests extends AbstractJupiterTestEngineTests {\n\n\tstatic final String FOO = \"DisabledTests.foo\";\n\tstatic final String BAR = \"DisabledTests.bar\";\n\tprivate static final String BOGUS = \"DisabledTests.bogus\";\n\tprivate static final String DEACTIVATE = \"*AnotherAlwaysDisable*, org.junit.jupiter.engine.extension.sub.AlwaysDisable*\";\n\n\t@Test\n\tvoid conditionWorksOnContainer() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(TestCaseWithExecutionConditionOnClass.class);\n\n\t\texecutionResults.containerEvents().assertStatistics(stats -> stats.skipped(1));\n\t\texecutionResults.testEvents().assertStatistics(stats -> stats.started(0));\n\t}\n\n\t@Test\n\tvoid conditionWorksOnTest() {\n\t\tEvents tests = executeTestsForClass(TestCaseWithExecutionConditionOnMethods.class).testEvents();\n\n\t\ttests.assertStatistics(stats -> stats.started(2).succeeded(2).skipped(3));\n\t}\n\n\t@Test\n\tvoid overrideConditionsUsingFullyQualifiedClassName() {\n\t\tString deactivatePattern = SystemPropertyCondition.class.getName() + \",\" + DEACTIVATE;\n\t\tassertExecutionConditionOverride(deactivatePattern, 1, 1);\n\t\tassertExecutionConditionOverride(deactivatePattern, 4, 2, 2);\n\t}\n\n\t@Test\n\tvoid overrideConditionsUsingStar() {\n\t\t// \"*\" should deactivate DisabledCondition and SystemPropertyCondition\n\t\tString deactivatePattern = \"*\";\n\t\tassertExecutionConditionOverride(deactivatePattern, 2, 2);\n\t\tassertExecutionConditionOverride(deactivatePattern, 5, 2, 3);\n\t}\n\n\t@Test\n\tvoid overrideConditionsUsingStarPlusSimpleClassName() {\n\t\t// DisabledCondition should remain activated\n\t\tString deactivatePattern = \"*\" + SystemPropertyCondition.class.getSimpleName() + \", \" + DEACTIVATE;\n\t\tassertExecutionConditionOverride(deactivatePattern, 1, 1);\n\t\tassertExecutionConditionOverride(deactivatePattern, 4, 2, 2);\n\t}\n\n\t@Test\n\tvoid overrideConditionsUsingPackageNamePlusDotStar() {\n\t\t// DisabledCondition should remain activated\n\t\tString deactivatePattern = DEACTIVATE + \", \" + SystemPropertyCondition.class.getPackage().getName() + \".*\";\n\t\tassertExecutionConditionOverride(deactivatePattern, 1, 1);\n\t\tassertExecutionConditionOverride(deactivatePattern, 4, 2, 2);\n\t}\n\n\t@Test\n\tvoid overrideConditionsUsingMultipleWildcards() {\n\t\t// DisabledCondition should remain activated\n\t\tString deactivatePattern = \"org.junit.jupiter.*.System*Condition\" + \",\" + DEACTIVATE;\n\t\tassertExecutionConditionOverride(deactivatePattern, 1, 1);\n\t\tassertExecutionConditionOverride(deactivatePattern, 4, 2, 2);\n\t}\n\n\t@Test\n\tvoid deactivateAllConditions() {\n\t\t// DisabledCondition should remain activated\n\t\tString deactivatePattern = \"org.junit.jupiter.*.System*Condition\" + \", \" + DEACTIVATE;\n\t\tassertExecutionConditionOverride(deactivatePattern, 1, 1);\n\t\tassertExecutionConditionOverride(deactivatePattern, 4, 2, 2);\n\t}\n\n\tprivate void assertExecutionConditionOverride(String deactivatePattern, int testStartedCount, int testFailedCount) {\n\t\t// @formatter:off\n\t\tLauncherDiscoveryRequest request = request()\n\t\t\t\t.selectors(selectClass(TestCaseWithExecutionConditionOnClass.class))\n\t\t\t\t.configurationParameter(DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME, deactivatePattern)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tcontainers.assertStatistics(stats -> stats.skipped(0).started(2));\n\t\ttests.assertStatistics(stats -> stats.started(testStartedCount).failed(testFailedCount));\n\t}\n\n\tprivate void assertExecutionConditionOverride(String deactivatePattern, int started, int succeeded, int failed) {\n\t\t// @formatter:off\n\t\tLauncherDiscoveryRequest request = request()\n\t\t\t\t.selectors(selectClass(TestCaseWithExecutionConditionOnMethods.class))\n\t\t\t\t.configurationParameter(DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME, deactivatePattern)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\n\t\texecuteTests(request).testEvents().assertStatistics(\n\t\t\tstats -> stats.started(started).succeeded(succeeded).failed(failed));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t@SystemProperty(key = FOO, value = BOGUS)\n\t@DeactivatedConditions\n\tstatic class TestCaseWithExecutionConditionOnClass {\n\n\t\t@Test\n\t\tvoid disabledTest() {\n\t\t\tfail(\"this should be disabled\");\n\t\t}\n\n\t\t@Test\n\t\t@Disabled\n\t\tvoid atDisabledTest() {\n\t\t\tfail(\"this should be @Disabled\");\n\t\t}\n\t}\n\n\tstatic class TestCaseWithExecutionConditionOnMethods {\n\n\t\t@Test\n\t\tvoid enabledTest() {\n\t\t}\n\n\t\t@Test\n\t\t@Disabled\n\t\t@DeactivatedConditions\n\t\tvoid atDisabledTest() {\n\t\t\tfail(\"this should be @Disabled\");\n\t\t}\n\n\t\t@Test\n\t\t@SystemProperty(key = FOO, value = BAR)\n\t\tvoid systemPropertyEnabledTest() {\n\t\t}\n\n\t\t@Test\n\t\t@DeactivatedConditions\n\t\t@SystemProperty(key = FOO, value = BOGUS)\n\t\tvoid systemPropertyWithIncorrectValueTest() {\n\t\t\tfail(\"this should be disabled\");\n\t\t}\n\n\t\t@Test\n\t\t@DeactivatedConditions\n\t\t@SystemProperty(key = BOGUS, value = \"doesn't matter\")\n\t\tvoid systemPropertyNotSetTest() {\n\t\t\tfail(\"this should be disabled\");\n\t\t}\n\n\t}\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ExtendWith({ AlwaysDisabledCondition.class, AnotherAlwaysDisabledCondition.class })\n\t@interface DeactivatedConditions {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionContextExecutionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport java.lang.reflect.Method;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContextParameterResolver;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\nclass ExtensionContextExecutionTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\t@ExtendWith(ExtensionContextParameterResolver.class)\n\tvoid extensionContextHierarchy(ExtensionContext methodExtensionContext) {\n\t\tassertThat(methodExtensionContext).isNotNull();\n\t\tassertThat(methodExtensionContext.getElement()).containsInstanceOf(Method.class);\n\n\t\tOptional<ExtensionContext> classExtensionContext = methodExtensionContext.getParent();\n\t\tassertThat(classExtensionContext).isNotEmpty();\n\t\tassertThat(classExtensionContext.orElseThrow().getElement()).contains(ExtensionContextExecutionTests.class);\n\n\t\tOptional<ExtensionContext> engineExtensionContext = classExtensionContext.orElseThrow().getParent();\n\t\tassertThat(engineExtensionContext).isNotEmpty();\n\t\tassertThat(engineExtensionContext.orElseThrow().getElement()).isEmpty();\n\n\t\tassertThat(engineExtensionContext.orElseThrow().getParent()).isEmpty();\n\t}\n\n\t@Test\n\tvoid twoTestClassesCanShareStateViaEngineExtensionContext() {\n\t\tParent.counter.set(0);\n\n\t\texecuteTests(selectClass(A.class), selectClass(B.class)).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2));\n\n\t\tassertThat(Parent.counter).hasValue(1);\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@ExtendWith(OnlyIncrementCounterOnce.class)\n\tstatic class Parent {\n\t\tstatic final AtomicInteger counter = new AtomicInteger(0);\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class A extends Parent {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class B extends Parent {\n\t}\n\n\t@NullMarked\n\tstatic class OnlyIncrementCounterOnce implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tExtensionContext.Store store = getRoot(context).getStore(ExtensionContext.Namespace.GLOBAL);\n\t\t\tstore.computeIfAbsent(\"counter\", key -> Parent.counter.incrementAndGet());\n\t\t}\n\n\t\tprivate ExtensionContext getRoot(ExtensionContext context) {\n\t\t\treturn context.getParent().map(this::getRoot).orElse(context);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistrationViaParametersAndFieldsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;\nimport static org.junit.platform.commons.support.ReflectionSupport.makeAccessible;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Executable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.TestTemplate;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.execution.injection.sample.LongParameterResolver;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.support.ModifierSupport;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for extension registration via\n * {@link ExtendWith @ExtendWith} on parameters and fields.\n *\n * @since 5.8\n */\nclass ExtensionRegistrationViaParametersAndFieldsTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid constructorParameter() {\n\t\tassertOneTestSucceeded(ConstructorParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid constructorParameterForNestedTestClass() {\n\t\tassertTestsSucceeded(NestedConstructorParameterTestCase.class, 2);\n\t}\n\n\t@Test\n\tvoid beforeAllMethodParameter() {\n\t\tassertOneTestSucceeded(BeforeAllParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid afterAllMethodParameter() {\n\t\tassertOneTestSucceeded(AfterAllParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid beforeEachMethodParameter() {\n\t\tassertOneTestSucceeded(BeforeEachParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid afterEachMethodParameter() {\n\t\tassertOneTestSucceeded(AfterEachParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid testMethodParameter() {\n\t\tassertOneTestSucceeded(TestMethodParameterTestCase.class);\n\t}\n\n\t@Test\n\tvoid testFactoryMethodParameter() {\n\t\tassertTestsSucceeded(TestFactoryMethodParameterTestCase.class, 2);\n\t}\n\n\t@Test\n\tvoid testTemplateMethodParameter() {\n\t\tassertTestsSucceeded(TestTemplateMethodParameterTestCase.class, 2);\n\t}\n\n\t@Test\n\tvoid multipleRegistrationsViaParameter(@TrackLogRecords LogRecordListener listener) {\n\t\tassertOneTestSucceeded(MultipleRegistrationsViaParameterTestCase.class);\n\t\tassertThat(getRegisteredLocalExtensions(listener)).containsExactly(\"LongParameterResolver\", \"DummyExtension\");\n\t}\n\n\t@Test\n\tvoid staticField() {\n\t\tassertOneTestSucceeded(StaticFieldTestCase.class);\n\t}\n\n\t@Test\n\tvoid instanceField() {\n\t\tassertOneTestSucceeded(InstanceFieldTestCase.class);\n\t}\n\n\t@Test\n\tvoid fieldsWithTestInstancePerClass() {\n\t\tassertOneTestSucceeded(TestInstancePerClassFieldTestCase.class);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { MultipleMixedRegistrationsViaFieldTestCase.class,\n\t\t\tMultipleExtendWithRegistrationsViaFieldTestCase.class })\n\tvoid multipleRegistrationsViaField(Class<?> testClass, @TrackLogRecords LogRecordListener listener) {\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(getRegisteredLocalExtensions(listener)).containsExactly(\"LongParameterResolver\", \"DummyExtension\");\n\t}\n\n\t@Test\n\tvoid duplicateRegistrationViaField() {\n\t\tClass<?> testClass = DuplicateRegistrationViaFieldTestCase.class;\n\t\tString expectedMessage = \"Failed to register extension via field \"\n\t\t\t\t+ \"[org.junit.jupiter.api.extension.Extension \"\n\t\t\t\t+ \"org.junit.jupiter.engine.extension.ExtensionRegistrationViaParametersAndFieldsTests$DuplicateRegistrationViaFieldTestCase.dummy]. \"\n\t\t\t\t+ \"The field registers an extension of type [org.junit.jupiter.engine.extension.DummyExtension] \"\n\t\t\t\t+ \"via @RegisterExtension and @ExtendWith, but only one registration of a given extension type is permitted.\";\n\n\t\texecuteTestsForClass(testClass).testEvents().assertThatEvents().haveExactly(1,\n\t\t\tfinishedWithFailure(instanceOf(PreconditionViolationException.class), message(expectedMessage)));\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@ValueSource(classes = { AllInOneWithTestInstancePerMethodTestCase.class,\n\t\t\tAllInOneWithTestInstancePerClassTestCase.class })\n\tvoid registrationOrder(Class<?> testClass, @TrackLogRecords LogRecordListener listener) {\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(getRegisteredLocalExtensions(listener))//\n\t\t\t\t.containsExactly(//\n\t\t\t\t\t\"ClassLevelExtension2\", // @RegisterExtension on static field\n\t\t\t\t\t\"StaticField2\", // @ExtendWith on static field\n\t\t\t\t\t\"ClassLevelExtension1\", // @RegisterExtension on static field\n\t\t\t\t\t\"StaticField1\", // @ExtendWith on static field\n\t\t\t\t\t\"ConstructorParameter\", // @ExtendWith on parameter in constructor\n\t\t\t\t\t\"BeforeAllParameter\", // @ExtendWith on parameter in static @BeforeAll method\n\t\t\t\t\t\"BeforeEachParameter\", // @ExtendWith on parameter in @BeforeEach method\n\t\t\t\t\t\"AfterEachParameter\", // @ExtendWith on parameter in @AfterEach method\n\t\t\t\t\t\"AfterAllParameter\", // @ExtendWith on parameter in static @AfterAll method\n\t\t\t\t\t\"InstanceLevelExtension1\", // @RegisterExtension on instance field\n\t\t\t\t\t\"InstanceField1\", // @ExtendWith on instance field\n\t\t\t\t\t\"InstanceLevelExtension2\", // @RegisterExtension on instance field\n\t\t\t\t\t\"InstanceField2\", // @ExtendWith on instance field\n\t\t\t\t\t\"TestParameter\" // @ExtendWith on parameter in @Test method\n\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid registersProgrammaticTestInstancePostProcessors() {\n\t\tassertOneTestSucceeded(ProgrammaticTestInstancePostProcessorTestCase.class);\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(ParallelExecutorServiceType.class)\n\tvoid createsExtensionPerInstance(\n\t\t\tParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType executorServiceType) {\n\t\tvar results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(InitializationPerInstanceTestCase.class)) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, \"true\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME,\n\t\t\t\t\texecutorServiceType.name()) //\n\t\t);\n\t\tassertTestsSucceeded(results, 100);\n\t}\n\n\tprivate List<String> getRegisteredLocalExtensions(LogRecordListener listener) {\n\t\treturn listener.stream(MutableExtensionRegistry.class, Level.FINER) //\n\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t.filter(message -> message.contains(\"local extension\")) //\n\t\t\t\t.map(message -> {\n\t\t\t\t\tmessage = message.replaceAll(\" from source .+\", \"\");\n\t\t\t\t\tint beginIndex = message.lastIndexOf('.') + 1;\n\t\t\t\t\tif (message.contains(\"late-init\")) {\n\t\t\t\t\t\treturn message.substring(beginIndex, message.indexOf(\"]\"));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tint indexOfDollarSign = message.indexOf(\"$\");\n\t\t\t\t\t\tint indexOfAtSign = message.indexOf(\"@\");\n\t\t\t\t\t\tint endIndex = (indexOfDollarSign > 1 ? indexOfDollarSign : indexOfAtSign);\n\t\t\t\t\t\treturn message.substring(beginIndex, endIndex);\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.toList();\n\t}\n\n\tprivate void assertOneTestSucceeded(Class<?> testClass) {\n\t\tassertTestsSucceeded(testClass, 1);\n\t}\n\n\tprivate void assertTestsSucceeded(Class<?> testClass, int expected) {\n\t\tassertTestsSucceeded(executeTestsForClass(testClass), expected);\n\t}\n\n\tprivate static void assertTestsSucceeded(EngineExecutionResults results, int expected) {\n\t\tresults.testEvents().assertStatistics(\n\t\t\tstats -> stats.started(expected).succeeded(expected).skipped(0).aborted(0).failed(0));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the constructor\n\t * and then used for lifecycle and test methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class ConstructorParameterTestCase {\n\n\t\tConstructorParameterTestCase(@MagicParameter(\"constructor\") String text) {\n\t\t\tassertThat(text).isEqualTo(\"ConstructorParameterTestCase-0-constructor\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"beforeEach-0-enigma\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t/**\n\t\t * Redeclaring {@code @MagicParameter} should not result in a\n\t\t * {@link ParameterResolutionException}.\n\t\t */\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the constructor\n\t * and then used for lifecycle and test methods.\n\t */\n\t@Nested\n\t@ExtendWith(LongParameterResolver.class)\n\tclass NestedConstructorParameterTestCase {\n\n\t\tNestedConstructorParameterTestCase(TestInfo testInfo, @MagicParameter(\"constructor\") String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\t// Index is 2 instead of 1, since constructors for non-static nested classes\n\t\t\t// receive a reference to the enclosing instance as the first argument: this$0\n\t\t\tassertThat(text).isEqualTo(\"NestedConstructorParameterTestCase-2-constructor\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"beforeEach-0-enigma\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t/**\n\t\t * Redeclaring {@code @MagicParameter} should not result in a\n\t\t * {@link ParameterResolutionException}.\n\t\t */\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t}\n\n\t\t@Nested\n\t\tclass DoublyNestedConstructorParameterTestCase {\n\n\t\t\tDoublyNestedConstructorParameterTestCase(TestInfo testInfo, String text) {\n\t\t\t\tassertThat(testInfo).isNotNull();\n\t\t\t\t// Index is 2 instead of 1, since constructors for non-static nested classes\n\t\t\t\t// receive a reference to the enclosing instance as the first argument: this$0\n\t\t\t\tassertThat(text).isEqualTo(\"DoublyNestedConstructorParameterTestCase-2-enigma\");\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEach(String text, TestInfo testInfo) {\n\t\t\t\tassertThat(text).isEqualTo(\"beforeEach-0-enigma\");\n\t\t\t\tassertThat(testInfo).isNotNull();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\t\tassertThat(testInfo).isNotNull();\n\t\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * Redeclaring {@code @MagicParameter} should not result in a\n\t\t\t * {@link ParameterResolutionException}.\n\t\t\t */\n\t\t\t@AfterEach\n\t\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\t\tassertThat(testInfo).isNotNull();\n\t\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @BeforeAll}\n\t * method and then used for other lifecycle methods and test methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class BeforeAllParameterTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(@MagicParameter(\"method\") String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"beforeAll-0-method\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"beforeEach-0-enigma\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t/**\n\t\t * Redeclaring {@code @MagicParameter} should not result in a\n\t\t * {@link ParameterResolutionException}.\n\t\t */\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"afterAll-0-enigma\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @AfterAll}\n\t * method, but that registration occurs before the test method is invoked, which\n\t * allows the string parameters in the after-each and test methods to be resolved\n\t * by the {@link MagicParameter.Extension} as well.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class AfterAllParameterTestCase {\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-enigma\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterAll-2-method\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @BeforeEach}\n\t * method and then used for other lifecycle methods and test methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class BeforeEachParameterTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(@MagicParameter(\"method\") String text, TestInfo testInfo) {\n\t\t\tassertThat(text).isEqualTo(\"beforeEach-0-method\");\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t/**\n\t\t * Redeclaring {@code @MagicParameter} should not result in a\n\t\t * {@link ParameterResolutionException}.\n\t\t */\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @AfterEach}\n\t * method, but that registration occurs before the test method is invoked, which\n\t * allows the test method's parameter to be resolved by the {@link MagicParameter.Extension}\n\t * as well.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class AfterEachParameterTestCase {\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-enigma\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-method\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @Test}\n\t * method and then used for after-each lifecycle methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class TestMethodParameterTestCase {\n\n\t\t@Test\n\t\tvoid test(TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"test-1-method\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-enigma\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @TestFactory}\n\t * method and then used for after-each lifecycle methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class TestFactoryMethodParameterTestCase {\n\n\t\t@SuppressWarnings(\"ConstantValue\")\n\t\t@TestFactory\n\t\tStream<DynamicTest> testFactory(TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"testFactory-1-method\");\n\n\t\t\treturn IntStream.of(2, 4).mapToObj(num -> dynamicTest(\"\" + num, () -> assertEquals(0, num % 2)));\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-enigma\");\n\t\t}\n\n\t}\n\n\t/**\n\t * The {@link MagicParameter.Extension} is first registered for the {@code @TestTemplate}\n\t * method and then used for after-each lifecycle methods.\n\t */\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class TestTemplateMethodParameterTestCase {\n\n\t\t@TestTemplate\n\t\t@ExtendWith(TwoInvocationsContextProvider.class)\n\t\tvoid testTemplate(TestInfo testInfo, @MagicParameter(\"method\") String text) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"testTemplate-1-method\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(Long number, TestInfo testInfo, String text) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tassertThat(text).isEqualTo(\"afterEach-2-enigma\");\n\t\t}\n\n\t}\n\n\t@NullMarked\n\tprivate static class TwoInvocationsContextProvider implements TestTemplateInvocationContextProvider {\n\n\t\t@Override\n\t\tpublic boolean supportsTestTemplate(ExtensionContext context) {\n\t\t\treturn true;\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {\n\t\t\treturn Stream.of(emptyTestTemplateInvocationContext(), emptyTestTemplateInvocationContext());\n\t\t}\n\n\t\tprivate static TestTemplateInvocationContext emptyTestTemplateInvocationContext() {\n\t\t\treturn new TestTemplateInvocationContext() {\n\t\t\t};\n\t\t}\n\t}\n\n\t@ExtendWith(LongParameterResolver.class)\n\tstatic class MultipleRegistrationsViaParameterTestCase {\n\n\t\t@Test\n\t\tvoid test(@ExtendWith(DummyExtension.class) @ExtendWith(LongParameterResolver.class) Long number) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t}\n\t}\n\n\tstatic class MultipleMixedRegistrationsViaFieldTestCase {\n\n\t\t@ExtendWith(LongParameterResolver.class)\n\t\t@RegisterExtension\n\t\tDummyExtension dummy = new DummyExtension();\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@Test\n\t\tvoid test(Long number) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tstatic class MultipleExtendWithRegistrationsViaFieldTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ExtendWith(LongParameterResolver.class)\n\t\t@ExtendWith(DummyExtension.class)\n\t\tObject field;\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@Test\n\t\tvoid test(Long number) {\n\t\t\tassertThat(number).isEqualTo(42L);\n\t\t}\n\t}\n\n\tstatic class DuplicateRegistrationViaFieldTestCase {\n\n\t\t@ExtendWith(DummyExtension.class)\n\t\t@RegisterExtension\n\t\tExtension dummy = new DummyExtension();\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t/**\n\t * The {@link MagicField.Extension} is registered via a static field.\n\t */\n\t@NullUnmarked\n\tstatic class StaticFieldTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@MagicField\n\t\tprivate static String staticField1;\n\n\t\t@MagicField\n\t\tstatic String staticField2;\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t}\n\t}\n\n\t/**\n\t * The {@link MagicField.Extension} is registered via an instance field.\n\t */\n\t@NullUnmarked\n\tstatic class InstanceFieldTestCase {\n\n\t\t@MagicField\n\t\tString instanceField1;\n\n\t\t@MagicField\n\t\tprivate String instanceField2;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertThat(instanceField1).isEqualTo(\"postProcessTestInstance - instanceField1\");\n\t\t\tassertThat(instanceField2).isEqualTo(\"postProcessTestInstance - instanceField2\");\n\t\t}\n\t}\n\n\t/**\n\t * The {@link MagicField.Extension} is registered via a static field and\n\t * an instance field.\n\t */\n\t@NullUnmarked\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tstatic class TestInstancePerClassFieldTestCase {\n\n\t\t@MagicField\n\t\tstatic String staticField;\n\n\t\t@MagicField\n\t\tString instanceField;\n\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t\tassertThat(staticField).isEqualTo(\"beforeAll - staticField\");\n\t\t\tassertThat(instanceField).isEqualTo(\"postProcessTestInstance - instanceField\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertThat(staticField).isEqualTo(\"beforeAll - staticField\");\n\t\t\tassertThat(instanceField).isEqualTo(\"postProcessTestInstance - instanceField\");\n\t\t}\n\t}\n\n\t@NullUnmarked\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@TestInstance(Lifecycle.PER_METHOD)\n\tstatic class AllInOneWithTestInstancePerMethodTestCase {\n\n\t\t@StaticField1\n\t\t@Order(Integer.MAX_VALUE)\n\t\tstatic String staticField1;\n\n\t\t@StaticField2\n\t\t@ExtendWith(StaticField2.Extension.class)\n\t\t@Order(3)\n\t\tstatic String staticField2;\n\n\t\t@RegisterExtension\n\t\tprivate static final Extension classLevelExtension1 = new ClassLevelExtension1();\n\n\t\t@RegisterExtension\n\t\t@Order(1)\n\t\tstatic Extension classLevelExtension2 = new ClassLevelExtension2();\n\n\t\t@InstanceField1\n\t\t@Order(2)\n\t\tString instanceField1;\n\n\t\t@InstanceField2\n\t\t@ExtendWith(InstanceField2.Extension.class)\n\t\tString instanceField2;\n\n\t\t@RegisterExtension\n\t\t@Order(1)\n\t\tprivate final InstanceLevelExtension1 instanceLevelExtension1 = new InstanceLevelExtension1();\n\n\t\t@RegisterExtension\n\t\t@Order(3)\n\t\tInstanceLevelExtension2 instanceLevelExtension2 = new InstanceLevelExtension2();\n\n\t\tAllInOneWithTestInstancePerMethodTestCase(@ConstructorParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(@ExtendWith(BeforeAllParameter.Extension.class) @BeforeAllParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(@BeforeEachParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t\tassertThat(instanceField1).isEqualTo(\"postProcessTestInstance - instanceField1\");\n\t\t\tassertThat(instanceField2).isEqualTo(\"postProcessTestInstance - instanceField2\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(@TestParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t\tassertThat(instanceField1).isEqualTo(\"postProcessTestInstance - instanceField1\");\n\t\t\tassertThat(instanceField2).isEqualTo(\"postProcessTestInstance - instanceField2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(@AfterEachParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t\tassertThat(instanceField1).isEqualTo(\"postProcessTestInstance - instanceField1\");\n\t\t\tassertThat(instanceField2).isEqualTo(\"postProcessTestInstance - instanceField2\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(@AfterAllParameter String text) {\n\t\t\tassertThat(text).isEqualTo(\"enigma\");\n\t\t\tassertThat(staticField1).isEqualTo(\"beforeAll - staticField1\");\n\t\t\tassertThat(staticField2).isEqualTo(\"beforeAll - staticField2\");\n\t\t}\n\n\t}\n\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tstatic class AllInOneWithTestInstancePerClassTestCase extends AllInOneWithTestInstancePerMethodTestCase {\n\n\t\tAllInOneWithTestInstancePerClassTestCase(@ConstructorParameter String text) {\n\t\t\tsuper(text);\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tstatic class ProgrammaticTestInstancePostProcessorTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension resolver = new InstanceField2.Extension();\n\n\t\t@InstanceField2\n\t\tString instanceField2;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertThat(instanceField2).isEqualTo(\"postProcessTestInstance - instanceField2\");\n\t\t}\n\t}\n\n\t@Execution(CONCURRENT)\n\tstatic class InitializationPerInstanceTestCase {\n\t\t@RegisterExtension\n\t\tExtension extension = new InstanceParameterResolver<>(this);\n\n\t\t@Nested\n\t\tclass Wrapper {\n\n\t\t\t@RegisterExtension\n\t\t\tExtension extension = new InstanceParameterResolver<>(this);\n\n\t\t\t@RepeatedTest(100)\n\t\t\tvoid test(InitializationPerInstanceTestCase outerInstance, Wrapper innerInstance) {\n\t\t\t\tassertSame(InitializationPerInstanceTestCase.this, outerInstance);\n\t\t\t\tassertSame(Wrapper.this, innerInstance);\n\t\t\t}\n\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate record InstanceParameterResolver<T>(T instance) implements ParameterResolver {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\treturn instance.getClass().equals(parameterContext.getParameter().getType());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\treturn instance;\n\t\t\t}\n\t\t}\n\t}\n\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(MagicParameter.Extension.class)\n@interface MagicParameter {\n\n\tString value();\n\n\t@NullMarked\n\tclass Extension implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == String.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\tString text = parameterContext.findAnnotation(MagicParameter.class)//\n\t\t\t\t\t.map(MagicParameter::value)//\n\t\t\t\t\t.orElse(\"enigma\");\n\t\t\tExecutable declaringExecutable = parameterContext.getDeclaringExecutable();\n\t\t\tString name = declaringExecutable instanceof Constructor\n\t\t\t\t\t? declaringExecutable.getDeclaringClass().getSimpleName()\n\t\t\t\t\t: declaringExecutable.getName();\n\t\t\treturn \"%s-%d-%s\".formatted(name, parameterContext.getIndex(), text);\n\t\t}\n\t}\n}\n\n@SuppressWarnings(\"unused\")\n@NullMarked\nclass BaseParameterExtension<T extends Annotation> implements ParameterResolver {\n\n\tprivate final Class<T> annotationType;\n\n\t@SuppressWarnings(\"unchecked\")\n\tBaseParameterExtension() {\n\t\tType genericSuperclass = getClass().getGenericSuperclass();\n\t\tthis.annotationType = (Class<T>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];\n\t}\n\n\t@Override\n\tpublic final boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn parameterContext.isAnnotated(this.annotationType)\n\t\t\t\t&& parameterContext.getParameter().getType() == String.class;\n\t}\n\n\t@Override\n\tpublic final Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn \"enigma\";\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(ConstructorParameter.Extension.class)\n@interface ConstructorParameter {\n\tclass Extension extends BaseParameterExtension<ConstructorParameter> {\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n// Intentionally NOT annotated as follows\n// @ExtendWith(BeforeAllParameter.Extension.class)\n@interface BeforeAllParameter {\n\tclass Extension extends BaseParameterExtension<BeforeAllParameter> {\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(AfterAllParameter.Extension.class)\n@interface AfterAllParameter {\n\tclass Extension extends BaseParameterExtension<AfterAllParameter> {\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(BeforeEachParameter.Extension.class)\n@interface BeforeEachParameter {\n\tclass Extension extends BaseParameterExtension<BeforeEachParameter> {\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(AfterEachParameter.Extension.class)\n@interface AfterEachParameter {\n\tclass Extension extends BaseParameterExtension<AfterEachParameter> {\n\t}\n}\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(TestParameter.Extension.class)\n@interface TestParameter {\n\tclass Extension extends BaseParameterExtension<TestParameter> {\n\t}\n}\n\nclass DummyExtension implements Extension {\n}\n\n@NullMarked\nclass BaseFieldExtension<T extends Annotation> implements BeforeAllCallback, TestInstancePostProcessor {\n\n\tprivate final Class<T> annotationType;\n\n\t@SuppressWarnings(\"unchecked\")\n\tBaseFieldExtension() {\n\t\tType genericSuperclass = getClass().getGenericSuperclass();\n\t\tthis.annotationType = (Class<T>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];\n\t}\n\n\t@Override\n\tpublic final void beforeAll(ExtensionContext context) {\n\t\tinjectFields(\"beforeAll\", context.getRequiredTestClass(), null, ModifierSupport::isStatic);\n\t}\n\n\t@Override\n\tpublic final void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\tinjectFields(\"postProcessTestInstance\", context.getRequiredTestClass(), testInstance,\n\t\t\tModifierSupport::isNotStatic);\n\t}\n\n\tprivate void injectFields(String trigger, Class<?> testClass, @Nullable Object instance,\n\t\t\tPredicate<Field> predicate) {\n\t\tfindAnnotatedFields(testClass, this.annotationType, predicate).forEach(field -> {\n\t\t\ttry {\n\t\t\t\tmakeAccessible(field).set(instance, trigger + \" - \" + field.getName());\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t}\n\t\t});\n\t}\n}\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(MagicField.Extension.class)\n@interface MagicField {\n\tclass Extension extends BaseFieldExtension<MagicField> {\n\t}\n}\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(InstanceField1.Extension.class)\n@interface InstanceField1 {\n\tclass Extension extends BaseFieldExtension<InstanceField1> {\n\t}\n}\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n// Intentionally NOT annotated as follows\n// @ExtendWith(InstanceField2.Extension.class)\n@interface InstanceField2 {\n\tclass Extension extends BaseFieldExtension<InstanceField2> {\n\t}\n}\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(StaticField1.Extension.class)\n@interface StaticField1 {\n\tclass Extension extends BaseFieldExtension<StaticField1> {\n\t}\n}\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\n// Intentionally NOT annotated as follows\n// @ExtendWith(StaticField2.Extension.class)\n@interface StaticField2 {\n\tclass Extension extends BaseFieldExtension<StaticField2> {\n\t}\n}\n\nclass ClassLevelExtension1 implements Extension {\n}\n\nclass ClassLevelExtension2 implements Extension {\n}\n\nclass InstanceLevelExtension1 implements Extension {\n}\n\nclass InstanceLevelExtension2 implements Extension {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ExtensionRegistryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryFrom;\nimport static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryWithDefaultExtensions;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;\nimport org.junit.jupiter.engine.config.JupiterConfiguration;\nimport org.junit.platform.commons.util.ClassNamePatternFilterUtils;\n\n/**\n * Tests for the {@link MutableExtensionRegistry}.\n *\n * @since 5.0\n */\nclass ExtensionRegistryTests {\n\n\tprivate static final int NUM_CORE_EXTENSIONS = 7;\n\tprivate static final int NUM_DEMO_EXTENSIONS = 2;\n\n\tprivate final JupiterConfiguration configuration = mock();\n\n\tprivate MutableExtensionRegistry registry = createRegistryWithDefaultExtensions(configuration);\n\n\t@Test\n\tvoid newRegistryWithoutParentHasDefaultExtensions() {\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS, extensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered();\n\t}\n\n\t@Test\n\tvoid newRegistryWithoutParentHasDefaultExtensionsPlusAutodetectedExtensionsLoadedViaServiceLoader() {\n\n\t\twhen(configuration.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\t\twhen(configuration.getFilterForAutoDetectedExtensions()).thenReturn(__ -> true);\n\t\tregistry = createRegistryWithDefaultExtensions(configuration);\n\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS + NUM_DEMO_EXTENSIONS, extensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered(4);\n\n\t\tassertExtensionRegistered(registry, ServiceLoaderExtension.class);\n\t\tassertEquals(4, countExtensions(registry, BeforeAllCallback.class));\n\t}\n\n\t@Test\n\tvoid registryIncludesAndExcludesSpecificAutoDetectedExtensions() {\n\t\twhen(configuration.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\t\twhen(configuration.getFilterForAutoDetectedExtensions()).thenReturn(\n\t\t\textensionFilter(ServiceLoaderExtension.class.getName(), ConfigLoaderExtension.class.getName()));\n\t\tregistry = createRegistryWithDefaultExtensions(configuration);\n\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS + NUM_DEMO_EXTENSIONS - 1 /* exlcuded ConfigLoaderExtension */,\n\t\t\textensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered(3);\n\n\t\tassertExtensionRegistered(registry, ServiceLoaderExtension.class);\n\t\tassertEquals(3, countExtensions(registry, BeforeAllCallback.class));\n\t}\n\n\t@Test\n\tvoid registryIncludesAllAutoDetectedExtensionsAndExcludesNone() {\n\t\twhen(configuration.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\t\twhen(configuration.getFilterForAutoDetectedExtensions()).thenReturn(extensionFilter(\"*\", \"\"));\n\t\tregistry = createRegistryWithDefaultExtensions(configuration);\n\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS + NUM_DEMO_EXTENSIONS, extensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered(4);\n\n\t\tassertExtensionRegistered(registry, ServiceLoaderExtension.class);\n\t\tassertExtensionRegistered(registry, ConfigLoaderExtension.class);\n\t\tassertEquals(4, countExtensions(registry, BeforeAllCallback.class));\n\t}\n\n\t@Test\n\tvoid registryIncludesSpecificAutoDetectedExtensionsAndExcludesAll() {\n\t\twhen(configuration.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\t\twhen(configuration.getFilterForAutoDetectedExtensions()).thenReturn(\n\t\t\textensionFilter(ServiceLoaderExtension.class.getName(), \"*\"));\n\t\tregistry = createRegistryWithDefaultExtensions(configuration);\n\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS, extensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered(2);\n\n\t\tassertExtensionNotRegistered(registry, ServiceLoaderExtension.class);\n\t\tassertEquals(2, countExtensions(registry, BeforeAllCallback.class));\n\t}\n\n\t@Test\n\tvoid registryIncludesAndExcludesSameAutoDetectedExtension() {\n\t\twhen(configuration.isExtensionAutoDetectionEnabled()).thenReturn(true);\n\t\twhen(configuration.getFilterForAutoDetectedExtensions()).thenReturn(\n\t\t\textensionFilter(ServiceLoaderExtension.class.getName(), ServiceLoaderExtension.class.getName()));\n\t\tregistry = createRegistryWithDefaultExtensions(configuration);\n\n\t\tList<Extension> extensions = registry.getExtensions(Extension.class);\n\n\t\tassertEquals(NUM_CORE_EXTENSIONS, extensions.size());\n\t\tassertDefaultGlobalExtensionsAreRegistered(2);\n\n\t\tassertExtensionNotRegistered(registry, ServiceLoaderExtension.class);\n\t\tassertEquals(2, countExtensions(registry, BeforeAllCallback.class));\n\t}\n\n\t@Test\n\tvoid registerExtensionByImplementingClass() {\n\t\tregistry.registerExtension(MyExtension.class);\n\n\t\tassertExtensionRegistered(registry, MyExtension.class);\n\n\t\tregistry.registerExtension(MyExtension.class);\n\t\tregistry.registerExtension(MyExtension.class);\n\t\tregistry.registerExtension(MyExtension.class);\n\n\t\tassertEquals(1, registry.getExtensions(MyExtension.class).size());\n\t\tassertExtensionRegistered(registry, MyExtension.class);\n\t\tassertEquals(1, countExtensions(registry, MyExtensionApi.class));\n\n\t\tregistry.registerExtension(YourExtension.class);\n\t\tassertExtensionRegistered(registry, YourExtension.class);\n\t\tassertEquals(2, countExtensions(registry, MyExtensionApi.class));\n\t}\n\n\t@Test\n\tvoid registerExtensionThatImplementsMultipleExtensionApis() {\n\t\tregistry.registerExtension(MultipleExtension.class);\n\n\t\tassertExtensionRegistered(registry, MultipleExtension.class);\n\n\t\tassertEquals(1, countExtensions(registry, MyExtensionApi.class));\n\t\tassertEquals(1, countExtensions(registry, AnotherExtensionApi.class));\n\t}\n\n\t@Test\n\tvoid extensionsAreInheritedFromParent() {\n\t\tMutableExtensionRegistry parent = registry;\n\t\tparent.registerExtension(MyExtension.class);\n\n\t\tMutableExtensionRegistry child = createRegistryFrom(parent, Stream.of(YourExtension.class));\n\t\tassertExtensionRegistered(child, MyExtension.class);\n\t\tassertExtensionRegistered(child, YourExtension.class);\n\t\tassertEquals(2, countExtensions(child, MyExtensionApi.class));\n\n\t\tExtensionRegistry grandChild = createRegistryFrom(child, Stream.empty());\n\t\tassertExtensionRegistered(grandChild, MyExtension.class);\n\t\tassertExtensionRegistered(grandChild, YourExtension.class);\n\t\tassertEquals(2, countExtensions(grandChild, MyExtensionApi.class));\n\t}\n\n\t@Test\n\tvoid registeringSameExtensionImplementationInParentAndChildDoesNotResultInDuplicate() {\n\t\tMutableExtensionRegistry parent = registry;\n\t\tparent.registerExtension(MyExtension.class);\n\t\tassertEquals(1, countExtensions(parent, MyExtensionApi.class));\n\n\t\tMutableExtensionRegistry child = createRegistryFrom(parent, Stream.of(MyExtension.class, YourExtension.class));\n\t\tassertExtensionRegistered(child, MyExtension.class);\n\t\tassertExtensionRegistered(child, YourExtension.class);\n\t\tassertEquals(2, countExtensions(child, MyExtensionApi.class));\n\n\t\tExtensionRegistry grandChild = createRegistryFrom(child, Stream.of(MyExtension.class, YourExtension.class));\n\t\tassertExtensionRegistered(grandChild, MyExtension.class);\n\t\tassertExtensionRegistered(grandChild, YourExtension.class);\n\t\tassertEquals(2, countExtensions(grandChild, MyExtensionApi.class));\n\t}\n\n\t@Test\n\tvoid canStreamOverRegisteredExtension() {\n\t\tregistry.registerExtension(MyExtension.class);\n\n\t\tAtomicBoolean hasRun = new AtomicBoolean(false);\n\n\t\tregistry.getExtensions(MyExtensionApi.class).forEach(extension -> {\n\t\t\tassertEquals(MyExtension.class.getName(), extension.getClass().getName());\n\t\t\thasRun.set(true);\n\t\t});\n\n\t\tassertTrue(hasRun.get());\n\t}\n\n\tprivate long countExtensions(ExtensionRegistry registry, Class<? extends Extension> extensionType) {\n\t\treturn registry.stream(extensionType).count();\n\t}\n\n\tprivate void assertExtensionRegistered(ExtensionRegistry registry, Class<? extends Extension> extensionType) {\n\t\tassertFalse(registry.getExtensions(extensionType).isEmpty(),\n\t\t\t() -> extensionType.getSimpleName() + \" should be present\");\n\t}\n\n\tprivate void assertExtensionNotRegistered(ExtensionRegistry registry, Class<? extends Extension> extensionType) {\n\t\tassertTrue(registry.getExtensions(extensionType).isEmpty(),\n\t\t\t() -> extensionType.getSimpleName() + \" should not be present\");\n\t}\n\n\tprivate void assertDefaultGlobalExtensionsAreRegistered() {\n\t\tassertDefaultGlobalExtensionsAreRegistered(2);\n\t}\n\n\tprivate void assertDefaultGlobalExtensionsAreRegistered(long bacCount) {\n\t\tassertExtensionRegistered(registry, DisabledCondition.class);\n\t\tassertExtensionRegistered(registry, TempDirectory.class);\n\t\tassertExtensionRegistered(registry, TimeoutExtension.class);\n\t\tassertExtensionRegistered(registry, RepeatedTestExtension.class);\n\t\tassertExtensionRegistered(registry, TestInfoParameterResolver.class);\n\t\tassertExtensionRegistered(registry, TestReporterParameterResolver.class);\n\n\t\tassertEquals(bacCount, countExtensions(registry, BeforeAllCallback.class));\n\t\tassertEquals(2, countExtensions(registry, BeforeEachCallback.class));\n\t\tassertEquals(3, countExtensions(registry, ParameterResolver.class));\n\t\tassertEquals(1, countExtensions(registry, ExecutionCondition.class));\n\t\tassertEquals(1, countExtensions(registry, TestTemplateInvocationContextProvider.class));\n\t\tassertEquals(1, countExtensions(registry, InvocationInterceptor.class));\n\t}\n\n\tprivate static Predicate<Class<? extends Extension>> extensionFilter(String includes, String excludes) {\n\t\tvar nameFilter = ClassNamePatternFilterUtils.includeMatchingClassNames(includes) //\n\t\t\t\t.and(ClassNamePatternFilterUtils.excludeMatchingClassNames(excludes));\n\t\treturn clazz -> nameFilter.test(clazz.getName());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tinterface MyExtensionApi extends Extension {\n\n\t\tvoid doNothing(String test);\n\t}\n\n\tinterface AnotherExtensionApi extends Extension {\n\n\t\tvoid doMore();\n\t}\n\n\tstatic class MyExtension implements MyExtensionApi {\n\n\t\t@Override\n\t\tpublic void doNothing(String test) {\n\t\t}\n\t}\n\n\tstatic class YourExtension implements MyExtensionApi {\n\n\t\t@Override\n\t\tpublic void doNothing(String test) {\n\t\t}\n\t}\n\n\tstatic class MultipleExtension implements MyExtensionApi, AnotherExtensionApi {\n\n\t\t@Override\n\t\tpublic void doNothing(String test) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void doMore() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.function.Function.identity;\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.DynamicTestInvocationContext;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.extension.ReflectiveInvocationContext;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\nclass InvocationInterceptorTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid failsTestWhenInterceptorChainDoesNotCallInvocation() {\n\t\tvar results = executeTestsForClass(InvocationIgnoringInterceptorTestCase.class);\n\n\t\tvar tests = results.testEvents().assertStatistics(stats -> stats.failed(1).succeeded(0));\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(\"test\"), finishedWithFailure(instanceOf(JUnitException.class),\n\t\t\t\tmessage(it -> it.startsWith(\"Chain of InvocationInterceptors never called invocation\")))));\n\t}\n\n\t@NullMarked\n\tstatic class InvocationIgnoringInterceptorTestCase {\n\t\t@RegisterExtension\n\t\tExtension interceptor = new InvocationInterceptor() {\n\t\t\t@Override\n\t\t\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) {\n\t\t\t\t// do nothing\n\t\t\t}\n\t\t};\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\t// never called\n\t\t}\n\t}\n\n\t@Test\n\tvoid successTestWhenInterceptorChainSkippedInvocation() {\n\t\tvar results = executeTestsForClass(InvocationSkippedTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.failed(0).succeeded(1));\n\t}\n\n\t@NullMarked\n\tstatic class InvocationSkippedTestCase {\n\t\t@RegisterExtension\n\t\tExtension interceptor = new InvocationInterceptor() {\n\t\t\t@Override\n\t\t\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext) {\n\t\t\t\tinvocation.skip();\n\t\t\t}\n\t\t};\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid failsTestWhenInterceptorChainCallsInvocationMoreThanOnce() {\n\t\tvar results = executeTestsForClass(DoubleInvocationInterceptorTestCase.class);\n\n\t\tvar tests = results.testEvents().assertStatistics(stats -> stats.failed(1).succeeded(0));\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(\"test\"), finishedWithFailure(instanceOf(JUnitException.class), message(it -> it.startsWith(\n\t\t\t\t\"Chain of InvocationInterceptors called invocation multiple times instead of just once\")))));\n\t}\n\n\t@NullMarked\n\tstatic class DoubleInvocationInterceptorTestCase {\n\t\t@RegisterExtension\n\t\tExtension interceptor = new InvocationInterceptor() {\n\t\t\t@Override\n\t\t\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Throwable {\n\t\t\t\tinvocation.proceed();\n\t\t\t\tinvocation.proceed();\n\t\t\t}\n\t\t};\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\t// called twice\n\t\t}\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> callsInterceptors() {\n\t\tvar results = executeTestsForClass(TestCaseWithThreeInterceptors.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.failed(0).succeeded(3));\n\n\t\treturn Arrays.stream(InvocationType.values()) //\n\t\t\t\t.map(it -> dynamicTest(it.name(), () -> verifyEvents(results, it)));\n\t}\n\n\tprivate void verifyEvents(EngineExecutionResults results, InvocationType invocationType) {\n\t\tvar beforeEvents = List.of(\"before:foo\", \"before:bar\", \"before:baz\");\n\t\tvar testEvent = List.of(\"test\");\n\t\tvar afterEvents = List.of(\"after:baz\", \"after:bar\", \"after:foo\");\n\t\tvar allEvents = Stream.of(beforeEvents, testEvent, afterEvents).flatMap(Collection::stream).toList();\n\t\tString testClassName = TestCaseWithThreeInterceptors.class.getName();\n\n\t\tvar expectedElements = switch (invocationType) {\n\t\t\tcase BEFORE_ALL, AFTER_ALL -> prefixed(allEvents, testClassName);\n\t\t\tcase CONSTRUCTOR -> concatStreams(\n\t\t\t\tprefixed(allEvents, it -> it.endsWith(\":bar\") ? testClassName : \"test(TestReporter)\"),\n\t\t\t\tprefixed(allEvents, it -> it.endsWith(\":bar\") ? testClassName : \"testTemplate(TestReporter)[1]\"),\n\t\t\t\tprefixed(allEvents, it -> it.endsWith(\":bar\") ? testClassName : \"testFactory(TestReporter)\"));\n\t\t\tcase BEFORE_EACH, AFTER_EACH -> concatStreams(prefixed(allEvents, \"test(TestReporter)\"),\n\t\t\t\tprefixed(allEvents, \"testTemplate(TestReporter)[1]\"), prefixed(allEvents, \"testFactory(TestReporter)\"));\n\t\t\tcase TEST_METHOD -> prefixed(allEvents, \"test(TestReporter)\");\n\t\t\tcase TEST_TEMPLATE_METHOD -> prefixed(allEvents, \"testTemplate(TestReporter)[1]\");\n\t\t\tcase TEST_FACTORY_METHOD -> prefixed(allEvents, \"testFactory(TestReporter)\");\n\t\t\tcase DYNAMIC_TEST -> concatStreams(prefixed(beforeEvents, \"testFactory(TestReporter)[1]\"),\n\t\t\t\tprefixed(testEvent, \"testFactory(TestReporter)\"),\n\t\t\t\tprefixed(afterEvents, \"testFactory(TestReporter)[1]\"));\n\t\t};\n\n\t\tassertThat(getEvents(results, invocationType)) //\n\t\t\t\t.containsExactlyElementsOf(expectedElements.toList());\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate static <T> Stream<T> concatStreams(Stream<T>... items) {\n\t\treturn Stream.of(items).flatMap(identity());\n\t}\n\n\tprivate static Stream<String> prefixed(List<String> values, String prefix) {\n\t\treturn prefixed(values, __ -> prefix);\n\t}\n\n\tprivate static Stream<String> prefixed(List<String> values, UnaryOperator<String> prefixGenerator) {\n\t\treturn values.stream() //\n\t\t\t\t.map(it -> \"[%s] %s\".formatted(prefixGenerator.apply(it), it));\n\t}\n\n\tprivate Stream<String> getEvents(EngineExecutionResults results, InvocationType invocationType) {\n\t\treturn results.allEvents().reportingEntryPublished().stream() //\n\t\t\t\t.flatMap(event -> {\n\t\t\t\t\tvar reportEntry = event.getPayload(ReportEntry.class).orElseThrow();\n\t\t\t\t\tvar keyValuePairs = reportEntry.getKeyValuePairs();\n\t\t\t\t\tif (keyValuePairs.keySet().stream() //\n\t\t\t\t\t\t\t.map(InvocationType::valueOf) //\n\t\t\t\t\t\t\t.anyMatch(isEqual(invocationType))) {\n\t\t\t\t\t\treturn keyValuePairs.values().stream() //\n\t\t\t\t\t\t\t\t.map(it -> \"[%s] %s\".formatted(event.getTestDescriptor().getLegacyReportingName(), it));\n\t\t\t\t\t}\n\t\t\t\t\treturn Stream.empty();\n\t\t\t\t});\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ExtendWith({ FooInvocationInterceptor.class, BarInvocationInterceptor.class, BazInvocationInterceptor.class })\n\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\tstatic class TestCaseWithThreeInterceptors {\n\n\t\tTestCaseWithThreeInterceptors(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.CONSTRUCTOR);\n\t\t}\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.BEFORE_ALL);\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.BEFORE_EACH);\n\t\t}\n\n\t\t@Order(1)\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.TEST_METHOD);\n\t\t}\n\n\t\t@Order(2)\n\t\t@RepeatedTest(1)\n\t\tvoid testTemplate(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.TEST_TEMPLATE_METHOD);\n\t\t}\n\n\t\t@Order(3)\n\t\t@TestFactory\n\t\tDynamicTest testFactory(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.TEST_FACTORY_METHOD);\n\t\t\treturn dynamicTest(\"dynamicTest\", () -> publish(reporter, InvocationType.DYNAMIC_TEST));\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.AFTER_EACH);\n\t\t}\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@AfterAll\n\t\tstatic void afterAll(TestReporter reporter) {\n\t\t\tpublish(reporter, InvocationType.AFTER_ALL);\n\t\t}\n\n\t\tstatic void publish(TestReporter reporter, InvocationType type) {\n\t\t\treporter.publishEntry(type.name(), \"test\");\n\t\t}\n\n\t}\n\n\tenum InvocationType {\n\t\tBEFORE_ALL,\n\t\tCONSTRUCTOR,\n\t\tBEFORE_EACH,\n\t\tTEST_METHOD,\n\t\tTEST_TEMPLATE_METHOD,\n\t\tTEST_FACTORY_METHOD,\n\t\tDYNAMIC_TEST,\n\t\tAFTER_EACH,\n\t\tAFTER_ALL\n\t}\n\n\t@NullMarked\n\tabstract static class ReportingInvocationInterceptor implements InvocationInterceptor {\n\t\tprivate final Class<TestCaseWithThreeInterceptors> testClass = TestCaseWithThreeInterceptors.class;\n\t\tprivate final String name;\n\n\t\tReportingInvocationInterceptor(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn ExtensionContextScope.TEST_METHOD;\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptBeforeAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).isEmpty();\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"beforeAll\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.BEFORE_ALL);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T> T interceptTestClassConstructor(Invocation<T> invocation,\n\t\t\t\tReflectiveInvocationContext<Constructor<T>> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertEquals(testClass.getDeclaredConstructor(TestReporter.class), invocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\treturn reportAndProceed(invocation, extensionContext, InvocationType.CONSTRUCTOR);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptBeforeEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).containsInstanceOf(testClass);\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"beforeEach\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.BEFORE_EACH);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptTestMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).containsInstanceOf(testClass);\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"test\", TestReporter.class), invocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.TEST_METHOD);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptTestTemplateMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).containsInstanceOf(testClass);\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"testTemplate\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.TEST_TEMPLATE_METHOD);\n\t\t}\n\n\t\t@Override\n\t\tpublic <T> T interceptTestFactoryMethod(Invocation<T> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).containsInstanceOf(testClass);\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"testFactory\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\treturn reportAndProceed(invocation, extensionContext, InvocationType.TEST_FACTORY_METHOD);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptDynamicTest(Invocation<@Nullable Void> invocation,\n\t\t\t\tDynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {\n\t\t\tassertThat(invocationContext.getExecutable()).isNotNull();\n\t\t\tassertThat(extensionContext.getUniqueId()).isNotBlank();\n\t\t\tassertThat(extensionContext.getElement()).isEmpty();\n\t\t\tassertThat(extensionContext.getParent().flatMap(ExtensionContext::getTestMethod)) //\n\t\t\t\t\t.contains(testClass.getDeclaredMethod(\"testFactory\", TestReporter.class));\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.DYNAMIC_TEST);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptAfterEachMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).containsInstanceOf(testClass);\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"afterEach\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.AFTER_EACH);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptAfterAllMethod(Invocation<@Nullable Void> invocation,\n\t\t\t\tReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)\n\t\t\t\tthrows Throwable {\n\t\t\tassertEquals(testClass, invocationContext.getTargetClass());\n\t\t\tassertThat(invocationContext.getTarget()).isEmpty();\n\t\t\tassertEquals(testClass.getDeclaredMethod(\"afterAll\", TestReporter.class),\n\t\t\t\tinvocationContext.getExecutable());\n\t\t\tassertThat(invocationContext.getArguments()).hasSize(1).hasOnlyElementsOfType(TestReporter.class);\n\t\t\tthis.<@Nullable Void> reportAndProceed(invocation, extensionContext, InvocationType.AFTER_ALL);\n\t\t}\n\n\t\tprivate <T extends @Nullable Object> T reportAndProceed(Invocation<T> invocation,\n\t\t\t\tExtensionContext extensionContext, InvocationType type) throws Throwable {\n\t\t\textensionContext.publishReportEntry(type.name(), \"before:\" + name);\n\t\t\ttry {\n\t\t\t\treturn invocation.proceed();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\textensionContext.publishReportEntry(type.name(), \"after:\" + name);\n\t\t\t}\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class FooInvocationInterceptor extends ReportingInvocationInterceptor {\n\t\tFooInvocationInterceptor() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class BarInvocationInterceptor extends ReportingInvocationInterceptor {\n\t\tBarInvocationInterceptor() {\n\t\t\tsuper(\"bar\");\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn ExtensionContextScope.DEFAULT;\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class BazInvocationInterceptor extends ReportingInvocationInterceptor {\n\t\tBazInvocationInterceptor() {\n\t\t\tsuper(\"baz\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/LifecycleMethodExecutionExceptionHandlerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.LifecycleMethodExecutionExceptionHandler;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Tests that verify the support for lifecycle method execution exception handling\n * via {@link LifecycleMethodExecutionExceptionHandler}\n *\n * @since 5.5\n */\nclass LifecycleMethodExecutionExceptionHandlerTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static List<String> handlerCalls = new ArrayList<>();\n\tprivate static boolean throwExceptionBeforeAll;\n\tprivate static boolean throwExceptionBeforeEach;\n\tprivate static boolean throwExceptionAfterEach;\n\tprivate static boolean throwExceptionAfterAll;\n\n\t@BeforeEach\n\tvoid resetStatics() {\n\t\tthrowExceptionBeforeAll = true;\n\t\tthrowExceptionBeforeEach = true;\n\t\tthrowExceptionAfterEach = true;\n\t\tthrowExceptionAfterAll = true;\n\t\thandlerCalls.clear();\n\n\t\tSwallowExceptionHandler.callCounter.reset();\n\n\t\tRethrowExceptionHandler.callCounter.reset();\n\n\t\tConvertExceptionHandler.callCounter.reset();\n\n\t\tUnrecoverableExceptionHandler.callCounter.reset();\n\n\t\tShouldNotBeCalledHandler.callCounter.reset();\n\t}\n\n\t@Test\n\tvoid classLevelExceptionHandlersRethrowException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(RethrowingTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertEquals(1, RethrowExceptionHandler.callCounter.beforeAllCalls, \"Exception should handled in @BeforeAll\");\n\t\tassertEquals(1, RethrowExceptionHandler.callCounter.afterAllCalls, \"Exception should handled in @AfterAll\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(RethrowingTestCase.class), started()), //\n\t\t\tevent(container(RethrowingTestCase.class), finishedWithFailure(instanceOf(RuntimeException.class))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid testLevelExceptionHandlersRethrowException() {\n\t\tthrowExceptionBeforeAll = false;\n\t\tthrowExceptionAfterAll = false;\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(RethrowingTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertEquals(1, RethrowExceptionHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should be handled in @BeforeEach\");\n\t\tassertEquals(1, RethrowExceptionHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should be handled in @AfterEach\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(RethrowingTestCase.class), started()), //\n\t\t\tevent(test(\"aTest\"), started()), //\n\t\t\tevent(test(\"aTest\"), finishedWithFailure(instanceOf(RuntimeException.class))), //\n\t\t\tevent(container(RethrowingTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid classLevelExceptionHandlersConvertException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(ConvertingTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertEquals(1, ConvertExceptionHandler.callCounter.beforeAllCalls, \"Exception should handled in @BeforeAll\");\n\t\tassertEquals(1, ConvertExceptionHandler.callCounter.afterAllCalls, \"Exception should handled in @AfterAll\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ConvertingTestCase.class), started()), //\n\t\t\tevent(container(ConvertingTestCase.class), finishedWithFailure(instanceOf(IOException.class))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid testLevelExceptionHandlersConvertException() {\n\t\tthrowExceptionBeforeAll = false;\n\t\tthrowExceptionAfterAll = false;\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(ConvertingTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertEquals(1, ConvertExceptionHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should be handled in @BeforeEach\");\n\t\tassertEquals(1, ConvertExceptionHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should be handled in @AfterEach\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ConvertingTestCase.class), started()), //\n\t\t\tevent(test(\"aTest\"), started()), //\n\t\t\tevent(test(\"aTest\"), finishedWithFailure(instanceOf(IOException.class))), //\n\t\t\tevent(container(ConvertingTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionHandlersSwallowException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(SwallowingTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.beforeAllCalls,\n\t\t\t\"Exception should be handled in @BeforeAll\");\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should be handled in @BeforeEach\");\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should be handled in @AfterEach\");\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.afterAllCalls, \"Exception should be handled in @AfterAll\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(SwallowingTestCase.class), started()), //\n\t\t\tevent(test(\"aTest\"), started()), //\n\t\t\tevent(test(\"aTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(SwallowingTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid perClassLifecycleMethodsAreHandled() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(PerClassLifecycleTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\t\tassertEquals(2, SwallowExceptionHandler.callCounter.beforeAllCalls,\n\t\t\t\"Exception should be handled in @BeforeAll\");\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should be handled in @BeforeEach\");\n\t\tassertEquals(1, SwallowExceptionHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should be handled in @AfterEach\");\n\t\tassertEquals(2, SwallowExceptionHandler.callCounter.afterAllCalls, \"Exception should be handled in @AfterAll\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(PerClassLifecycleTestCase.class), started()), //\n\t\t\tevent(test(\"aTest\"), started()), //\n\t\t\tevent(test(\"aTest\"), finishedSuccessfully()), //\n\t\t\tevent(container(PerClassLifecycleTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid multipleHandlersAreCalledInOrder() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectClass(MultipleHandlersTestCase.class)).build();\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(MultipleHandlersTestCase.class), started()), //\n\t\t\tevent(test(\"aTest\"), started()), //\n\t\t\tevent(test(\"aTest\"), finishedSuccessfully()), //\n\t\t\tevent(test(\"aTest2\"), started()), //\n\t\t\tevent(test(\"aTest2\"), finishedSuccessfully()), //\n\t\t\tevent(container(MultipleHandlersTestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully())); //\n\n\t\tassertEquals(Arrays.asList(\n\t\t\t// BeforeAll chain (class level only)\n\t\t\t\"RethrowExceptionBeforeAll\", \"SwallowExceptionBeforeAll\",\n\t\t\t// BeforeEach chain for aTest (test + class level)\n\t\t\t\"ConvertExceptionBeforeEach\", \"RethrowExceptionBeforeEach\", \"SwallowExceptionBeforeEach\",\n\t\t\t// AfterEach chain for aTest  (test + class level)\n\t\t\t\"ConvertExceptionAfterEach\", \"RethrowExceptionAfterEach\", \"SwallowExceptionAfterEach\",\n\t\t\t// BeforeEach chain for aTest2 (class level only)\n\t\t\t\"RethrowExceptionBeforeEach\", \"SwallowExceptionBeforeEach\",\n\t\t\t// AfterEach chain for aTest2 (class level only)\n\t\t\t\"RethrowExceptionAfterEach\", \"SwallowExceptionAfterEach\",\n\t\t\t// AfterAll chain (class level only)\n\t\t\t\"RethrowExceptionAfterAll\", \"SwallowExceptionAfterAll\" //\n\t\t), handlerCalls, \"Wrong order of handler calls\");\n\t}\n\n\t@Test\n\tvoid unrecoverableExceptionsAreNotPropagatedInBeforeAll() {\n\t\tthrowExceptionBeforeAll = true;\n\t\tthrowExceptionBeforeEach = false;\n\t\tthrowExceptionAfterEach = false;\n\t\tthrowExceptionAfterAll = false;\n\n\t\tboolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException();\n\t\tassertTrue(unrecoverableExceptionThrown, \"Unrecoverable Exception should be thrown\");\n\t\tassertEquals(1, UnrecoverableExceptionHandler.callCounter.beforeAllCalls,\n\t\t\t\"Exception should be handled in @BeforeAll\");\n\t\tassertEquals(0, ShouldNotBeCalledHandler.callCounter.beforeAllCalls,\n\t\t\t\"Exception should not propagate in @BeforeAll\");\n\t}\n\n\t@Test\n\tvoid unrecoverableExceptionsAreNotPropagatedInBeforeEach() {\n\t\tthrowExceptionBeforeAll = false;\n\t\tthrowExceptionBeforeEach = true;\n\t\tthrowExceptionAfterEach = false;\n\t\tthrowExceptionAfterAll = false;\n\n\t\tboolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException();\n\t\tassertTrue(unrecoverableExceptionThrown, \"Unrecoverable Exception should be thrown\");\n\t\tassertEquals(1, UnrecoverableExceptionHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should be handled in @BeforeEach\");\n\t\tassertEquals(0, ShouldNotBeCalledHandler.callCounter.beforeEachCalls,\n\t\t\t\"Exception should not propagate in @BeforeEach\");\n\t}\n\n\t@Test\n\tvoid unrecoverableExceptionsAreNotPropagatedInAfterEach() {\n\t\tthrowExceptionBeforeAll = false;\n\t\tthrowExceptionBeforeEach = false;\n\t\tthrowExceptionAfterEach = true;\n\t\tthrowExceptionAfterAll = false;\n\n\t\tboolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException();\n\t\tassertTrue(unrecoverableExceptionThrown, \"Unrecoverable Exception should be thrown\");\n\t\tassertEquals(1, UnrecoverableExceptionHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should be handled in @AfterEach\");\n\t\tassertEquals(0, ShouldNotBeCalledHandler.callCounter.afterEachCalls,\n\t\t\t\"Exception should not propagate in @AfterEach\");\n\t}\n\n\t@Test\n\tvoid unrecoverableExceptionsAreNotPropagatedInAfterAll() {\n\t\tthrowExceptionBeforeAll = false;\n\t\tthrowExceptionBeforeEach = false;\n\t\tthrowExceptionAfterEach = false;\n\t\tthrowExceptionAfterAll = true;\n\n\t\tboolean unrecoverableExceptionThrown = executeThrowingOutOfMemoryException();\n\t\tassertTrue(unrecoverableExceptionThrown, \"Unrecoverable Exception should be thrown\");\n\t\tassertEquals(1, UnrecoverableExceptionHandler.callCounter.afterAllCalls,\n\t\t\t\"Exception should be handled in @AfterAll\");\n\t\tassertEquals(0, ShouldNotBeCalledHandler.callCounter.afterAllCalls,\n\t\t\t\"Exception should not propagate in @AfterAll\");\n\t}\n\n\tprivate boolean executeThrowingOutOfMemoryException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(\n\t\t\tselectClass(UnrecoverableExceptionTestCase.class)).build();\n\t\ttry {\n\t\t\texecuteTests(request);\n\t\t}\n\t\tcatch (OutOfMemoryError expected) {\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t// ------------------------------------------\n\n\tstatic class BaseTestCase {\n\t\t@BeforeAll\n\t\tstatic void throwBeforeAll() {\n\t\t\tif (throwExceptionBeforeAll) {\n\t\t\t\tthrow new RuntimeException(\"BeforeAllEx\");\n\t\t\t}\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid throwBeforeEach() {\n\t\t\tif (throwExceptionBeforeEach) {\n\t\t\t\tthrow new RuntimeException(\"BeforeEachEx\");\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid aTest() {\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid throwAfterEach() {\n\t\t\tif (throwExceptionAfterEach) {\n\t\t\t\tthrow new RuntimeException(\"AfterEachEx\");\n\t\t\t}\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void throwAfterAll() {\n\t\t\tif (throwExceptionAfterAll) {\n\t\t\t\tthrow new RuntimeException(\"AfterAllEx\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(RethrowExceptionHandler.class)\n\tstatic class RethrowingTestCase extends BaseTestCase {\n\t}\n\n\t@ExtendWith(ConvertExceptionHandler.class)\n\tstatic class ConvertingTestCase extends BaseTestCase {\n\t}\n\n\t@ExtendWith(SwallowExceptionHandler.class)\n\tstatic class SwallowingTestCase extends BaseTestCase {\n\t}\n\n\t@ExtendWith(ShouldNotBeCalledHandler.class)\n\t@ExtendWith(UnrecoverableExceptionHandler.class)\n\tstatic class UnrecoverableExceptionTestCase extends BaseTestCase {\n\t}\n\n\t@ExtendWith(ShouldNotBeCalledHandler.class)\n\t@ExtendWith(SwallowExceptionHandler.class)\n\t@ExtendWith(RethrowExceptionHandler.class)\n\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\tstatic class MultipleHandlersTestCase extends BaseTestCase {\n\n\t\t@Override\n\t\t@ExtendWith(ConvertExceptionHandler.class)\n\t\t@Order(1)\n\t\t@Test\n\t\tvoid aTest() {\n\t\t}\n\n\t\t@Order(2)\n\t\t@Test\n\t\tvoid aTest2() {\n\t\t}\n\t}\n\n\t@ExtendWith(SwallowExceptionHandler.class)\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\tstatic class PerClassLifecycleTestCase extends BaseTestCase {\n\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t\tthrow new RuntimeException(\"nonStaticBeforeAllEx\");\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll() {\n\t\t\tthrow new RuntimeException(\"nonStaticAfterAllEx\");\n\t\t}\n\t}\n\n\t// ------------------------------------------\n\n\tstatic class RethrowExceptionHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\tstatic HandlerCallCounter callCounter = new HandlerCallCounter();\n\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementBeforeAllCalls();\n\t\t\thandlerCalls.add(\"RethrowExceptionBeforeAll\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementBeforeEachCalls();\n\t\t\thandlerCalls.add(\"RethrowExceptionBeforeEach\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterEachCalls();\n\t\t\thandlerCalls.add(\"RethrowExceptionAfterEach\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterAllCalls();\n\t\t\thandlerCalls.add(\"RethrowExceptionAfterAll\");\n\t\t\tthrow throwable;\n\t\t}\n\t}\n\n\tstatic class SwallowExceptionHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\tstatic HandlerCallCounter callCounter = new HandlerCallCounter();\n\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementBeforeAllCalls();\n\t\t\thandlerCalls.add(\"SwallowExceptionBeforeAll\");\n\t\t\t// Do not rethrow\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementBeforeEachCalls();\n\t\t\thandlerCalls.add(\"SwallowExceptionBeforeEach\");\n\t\t\t// Do not rethrow\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementAfterEachCalls();\n\t\t\thandlerCalls.add(\"SwallowExceptionAfterEach\");\n\t\t\t// Do not rethrow\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementAfterAllCalls();\n\t\t\thandlerCalls.add(\"SwallowExceptionAfterAll\");\n\t\t\t// Do not rethrow\n\t\t}\n\t}\n\n\tstatic class ConvertExceptionHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\tstatic HandlerCallCounter callCounter = new HandlerCallCounter();\n\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementBeforeAllCalls();\n\t\t\thandlerCalls.add(\"ConvertExceptionBeforeAll\");\n\t\t\tthrow new IOException(throwable);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementBeforeEachCalls();\n\t\t\thandlerCalls.add(\"ConvertExceptionBeforeEach\");\n\t\t\tthrow new IOException(throwable);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterEachCalls();\n\t\t\thandlerCalls.add(\"ConvertExceptionAfterEach\");\n\t\t\tthrow new IOException(throwable);\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterAllCalls();\n\t\t\thandlerCalls.add(\"ConvertExceptionAfterAll\");\n\t\t\tthrow new IOException(throwable);\n\t\t}\n\t}\n\n\tstatic class UnrecoverableExceptionHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\tstatic HandlerCallCounter callCounter = new HandlerCallCounter();\n\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementBeforeAllCalls();\n\t\t\thandlerCalls.add(\"UnrecoverableExceptionBeforeAll\");\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementBeforeEachCalls();\n\t\t\thandlerCalls.add(\"UnrecoverableExceptionBeforeEach\");\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementAfterEachCalls();\n\t\t\thandlerCalls.add(\"UnrecoverableExceptionAfterEach\");\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tcallCounter.incrementAfterAllCalls();\n\t\t\thandlerCalls.add(\"UnrecoverableExceptionAfterAll\");\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\t}\n\n\tstatic class ShouldNotBeCalledHandler implements LifecycleMethodExecutionExceptionHandler {\n\t\tstatic HandlerCallCounter callCounter = new HandlerCallCounter();\n\n\t\t@Override\n\t\tpublic void handleBeforeAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementBeforeAllCalls();\n\t\t\thandlerCalls.add(\"ShouldNotBeCalledBeforeAll\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleBeforeEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tShouldNotBeCalledHandler.callCounter.incrementBeforeEachCalls();\n\t\t\thandlerCalls.add(\"ShouldNotBeCalledBeforeEach\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterEachMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterEachCalls();\n\t\t\thandlerCalls.add(\"ShouldNotBeCalledAfterEach\");\n\t\t\tthrow throwable;\n\t\t}\n\n\t\t@Override\n\t\tpublic void handleAfterAllMethodExecutionException(ExtensionContext context, Throwable throwable)\n\t\t\t\tthrows Throwable {\n\t\t\tcallCounter.incrementAfterAllCalls();\n\t\t\thandlerCalls.add(\"ShouldNotBeCalledAfterAll\");\n\t\t\tthrow throwable;\n\t\t}\n\t}\n\n\tstatic class HandlerCallCounter {\n\t\tprivate int beforeAllCalls;\n\t\tprivate int beforeEachCalls;\n\t\tprivate int afterEachCalls;\n\t\tprivate int afterAllCalls;\n\n\t\tHandlerCallCounter() {\n\t\t\treset();\n\t\t}\n\n\t\tpublic void reset() {\n\t\t\tthis.beforeAllCalls = 0;\n\t\t\tthis.beforeEachCalls = 0;\n\t\t\tthis.afterEachCalls = 0;\n\t\t\tthis.afterAllCalls = 0;\n\t\t}\n\n\t\tpublic void incrementBeforeAllCalls() {\n\t\t\tbeforeAllCalls++;\n\t\t}\n\n\t\tpublic void incrementBeforeEachCalls() {\n\t\t\tbeforeEachCalls++;\n\t\t}\n\n\t\tpublic void incrementAfterEachCalls() {\n\t\t\tafterEachCalls++;\n\t\t}\n\n\t\tpublic void incrementAfterAllCalls() {\n\t\t\tafterAllCalls++;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.ClassOrderer;\nimport org.junit.jupiter.api.ClassTemplate;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestClassOrder;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContext;\nimport org.junit.jupiter.api.extension.ClassTemplateInvocationContextProvider;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.testkit.engine.EngineDiscoveryResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for {@link ClassOrderer} support.\n *\n * @since 5.8\n */\nclass OrderedClassTests {\n\n\tprivate static final List<String> callSequence = Collections.synchronizedList(new ArrayList<>());\n\n\t@BeforeEach\n\t@AfterEach\n\tvoid clearCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid noOrderer() {\n\t\tvar discoveryIssues = discoverTests(null).getDiscoveryIssues();\n\t\tassertIneffectiveOrderAnnotationIssues(discoveryIssues);\n\n\t\texecuteTests(null)//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactlyInAnyOrder(\"A_TestCase\", \"B_TestCase\", \"C_TestCase\");\n\t}\n\n\t@Test\n\tvoid className() {\n\t\tvar discoveryIssues = discoverTests(ClassOrderer.ClassName.class).getDiscoveryIssues();\n\t\tassertIneffectiveOrderAnnotationIssues(discoveryIssues);\n\n\t\texecuteTests(ClassOrderer.ClassName.class)//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"A_TestCase\", \"B_TestCase\", \"C_TestCase\");\n\t}\n\n\t@Test\n\tvoid classNameAcrossPackages() {\n\t\ttry {\n\t\t\texample.B_TestCase.callSequence = callSequence;\n\n\t\t\t// @formatter:off\n\t\t\texecuteTests(ClassOrderer.ClassName.class, selectClasses(B_TestCase.class, example.B_TestCase.class))\n\t\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\t\t\t// @formatter:on\n\n\t\t\tassertThat(callSequence)//\n\t\t\t\t\t.containsExactly(\"example.B_TestCase\", \"B_TestCase\");\n\t\t}\n\t\tfinally {\n\t\t\texample.B_TestCase.callSequence = null;\n\t\t}\n\t}\n\n\t@Test\n\tvoid displayName() {\n\t\tvar discoveryIssues = discoverTests(ClassOrderer.DisplayName.class).getDiscoveryIssues();\n\t\tassertIneffectiveOrderAnnotationIssues(discoveryIssues);\n\n\t\texecuteTests(ClassOrderer.DisplayName.class)//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"C_TestCase\", \"B_TestCase\", \"A_TestCase\");\n\t}\n\n\t@Test\n\tvoid orderAnnotation() {\n\t\tvar discoveryIssues = discoverTests(ClassOrderer.OrderAnnotation.class).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).isEmpty();\n\n\t\texecuteTests(ClassOrderer.OrderAnnotation.class)//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"A_TestCase\", \"C_TestCase\", \"B_TestCase\");\n\t}\n\n\t@Test\n\tvoid orderAnnotationOnNestedTestClassesWithGlobalConfig() {\n\t\texecuteTests(ClassOrderer.OrderAnnotation.class, selectClass(OuterWithGlobalConfig.class))//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"Inner2\", \"Inner1\", \"Inner0\", \"Inner3\");\n\t}\n\n\t@Test\n\tvoid orderAnnotationOnNestedTestClassesWithLocalConfig(@TrackLogRecords LogRecordListener listener) {\n\t\texecuteTests(ClassOrderer.class, selectClass(OuterWithLocalConfig.class))//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\t// Ensure that supplying the ClassOrderer interface instead of an implementation\n\t\t// class results in a WARNING log message. This also lets us know the local\n\t\t// config is used.\n\t\tassertTrue(listener.stream(Level.WARNING)//\n\t\t\t\t.map(LogRecord::getMessage)//\n\t\t\t\t.anyMatch(m -> m.startsWith(\n\t\t\t\t\t\"Failed to load default class orderer class 'org.junit.jupiter.api.ClassOrderer'\")));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"Inner2\", \"Inner1\", \"Inner1Inner1\", \"Inner1Inner0\", \"Inner0\", \"Inner3\");\n\t}\n\n\t@Test\n\tvoid random() {\n\t\tvar discoveryIssues = discoverTests(ClassOrderer.Random.class).getDiscoveryIssues();\n\t\tassertIneffectiveOrderAnnotationIssues(discoveryIssues);\n\n\t\texecuteTests(ClassOrderer.Random.class)//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\t}\n\n\t@Test\n\tvoid classTemplateWithLocalConfig() {\n\t\tvar classTemplate = ClassTemplateWithLocalConfigTestCase.class;\n\t\tvar inner0 = ClassTemplateWithLocalConfigTestCase.Inner0.class;\n\t\tvar inner1 = ClassTemplateWithLocalConfigTestCase.Inner1.class;\n\t\tvar inner1Inner1 = ClassTemplateWithLocalConfigTestCase.Inner1.Inner1Inner1.class;\n\t\tvar inner1Inner0 = ClassTemplateWithLocalConfigTestCase.Inner1.Inner1Inner0.class;\n\n\t\texecuteTests(ClassOrderer.Random.class, selectClass(classTemplate))//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tvar inner1InvocationCallSequence = Stream.of(inner1, inner1Inner1, inner1Inner0, inner1Inner0).toList();\n\t\tvar inner1CallSequence = twice(inner1InvocationCallSequence).toList();\n\t\tvar outerCallSequence = Stream.concat(Stream.of(classTemplate),\n\t\t\tStream.concat(inner1CallSequence.stream(), Stream.of(inner0))).toList();\n\t\tvar expectedCallSequence = twice(outerCallSequence).map(Class::getSimpleName).toList();\n\n\t\tassertThat(callSequence).containsExactlyElementsOf(expectedCallSequence);\n\t}\n\n\tprivate static <T> Stream<T> twice(List<T> values) {\n\t\treturn Stream.concat(values.stream(), values.stream());\n\t}\n\n\t@Test\n\tvoid classTemplateWithGlobalConfig() {\n\t\tvar classTemplate = ClassTemplateWithLocalConfigTestCase.class;\n\t\tvar otherClass = A_TestCase.class;\n\n\t\texecuteTests(ClassOrderer.OrderAnnotation.class, selectClasses(otherClass, classTemplate))//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsSubsequence(classTemplate.getSimpleName(), otherClass.getSimpleName());\n\t}\n\n\t@Test\n\tvoid nestedClassedCanUseDefaultOrder(@TrackLogRecords LogRecordListener logRecords) {\n\t\texecuteTests(null, selectClass(RevertingBackToDefaultOrderTestCase.Inner.class));\n\t\tassertThat(callSequence).containsExactly(\"Test1\", \"Test2\", \"Test3\", \"Test4\");\n\t\tcallSequence.clear();\n\n\t\texecuteTests(ClassOrderer.OrderAnnotation.class, selectClass(RevertingBackToDefaultOrderTestCase.Inner.class));\n\t\tassertThat(callSequence).containsExactly(\"Test4\", \"Test2\", \"Test1\", \"Test3\");\n\t\tcallSequence.clear();\n\n\t\texecuteTests(ClassOrderer.Default.class, selectClass(RevertingBackToDefaultOrderTestCase.Inner.class));\n\t\tassertThat(callSequence).containsExactly(\"Test1\", \"Test2\", \"Test3\", \"Test4\");\n\t\tassertThat(logRecords.stream()) //\n\t\t\t\t.filteredOn(it -> it.getLevel().intValue() >= Level.WARNING.intValue()) //\n\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t.isEmpty();\n\t}\n\n\tprivate static void assertIneffectiveOrderAnnotationIssues(List<DiscoveryIssue> discoveryIssues) {\n\t\tassertThat(discoveryIssues).hasSize(2);\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::severity).containsOnly(Severity.INFO);\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::message) //\n\t\t\t\t.allMatch(it -> it.startsWith(\"Ineffective @Order annotation on class\")\n\t\t\t\t\t\t&& it.contains(\"It will not be applied because ClassOrderer.OrderAnnotation is not in use.\")\n\t\t\t\t\t\t&& it.endsWith(\n\t\t\t\t\t\t\t\"Note that the annotation may be either directly present or meta-present on the class.\"));\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::source).extracting(Optional::orElseThrow) //\n\t\t\t\t.containsExactlyInAnyOrder(ClassSource.from(A_TestCase.class), ClassSource.from(C_TestCase.class));\n\t}\n\n\tprivate Events executeTests(@Nullable Class<? extends ClassOrderer> classOrderer) {\n\t\treturn executeTests(classOrderer, selectClasses(A_TestCase.class, B_TestCase.class, C_TestCase.class));\n\t}\n\n\tprivate Events executeTests(@Nullable Class<? extends ClassOrderer> classOrderer, DiscoverySelector... selectors) {\n\t\treturn executeTests(classOrderer, List.of(selectors));\n\t}\n\n\tprivate Events executeTests(@Nullable Class<? extends ClassOrderer> classOrderer,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\t\t// @formatter:off\n\t\treturn testKit(classOrderer, selectors)\n\t\t\t\t.execute()\n\t\t\t\t.testEvents();\n\t\t// @formatter:on\n\t}\n\n\tprivate EngineDiscoveryResults discoverTests(@Nullable Class<? extends ClassOrderer> classOrderer) {\n\t\treturn discoverTests(classOrderer, selectClasses(A_TestCase.class, B_TestCase.class, C_TestCase.class));\n\t}\n\n\tprivate EngineDiscoveryResults discoverTests(@Nullable Class<? extends ClassOrderer> classOrderer,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\t\treturn testKit(classOrderer, selectors).discover();\n\t}\n\n\tprivate static EngineTestKit.Builder testKit(@Nullable Class<? extends ClassOrderer> classOrderer,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\n\t\tvar testKit = EngineTestKit.engine(\"junit-jupiter\");\n\t\tif (classOrderer != null) {\n\t\t\ttestKit.configurationParameter(DEFAULT_TEST_CLASS_ORDER_PROPERTY_NAME, classOrderer.getName());\n\t\t}\n\t\treturn testKit.selectors(selectors);\n\t}\n\n\tstatic abstract class BaseTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tvar testClass = testInfo.getTestClass().orElseThrow();\n\n\t\t\tcallSequence.add(testClass.getSimpleName());\n\t\t}\n\n\t\t@Test\n\t\tvoid a() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@Order(2)\n\t@DisplayName(\"Z\")\n\tstatic class A_TestCase extends BaseTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class B_TestCase extends BaseTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@Order(10)\n\t@DisplayName(\"A\")\n\tstatic class C_TestCase extends BaseTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class OuterWithGlobalConfig {\n\n\t\t@Nested\n\t\tclass Inner0 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(2)\n\t\tclass Inner1 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\tclass Inner2 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(Integer.MAX_VALUE)\n\t\tclass Inner3 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\tstatic class OuterWithLocalConfig {\n\n\t\t@Nested\n\t\tclass Inner0 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(2)\n\t\tclass Inner1 {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(2)\n\t\t\tclass Inner1Inner0 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(1)\n\t\t\tclass Inner1Inner1 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\tclass Inner2 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@Order(Integer.MAX_VALUE)\n\t\tclass Inner3 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Order(1)\n\t@TestClassOrder(ClassOrderer.OrderAnnotation.class)\n\t@ClassTemplate\n\t@ExtendWith(ClassTemplateWithLocalConfigTestCase.Twice.class)\n\tstatic class ClassTemplateWithLocalConfigTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(ClassTemplateWithLocalConfigTestCase.class.getSimpleName());\n\t\t}\n\n\t\t@Nested\n\t\t@Order(1)\n\t\tclass Inner0 {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\t@ClassTemplate\n\t\t@Order(0)\n\t\tclass Inner1 {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@ClassTemplate\n\t\t\t@Order(2)\n\t\t\tclass Inner1Inner0 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(1)\n\t\t\tclass Inner1Inner1 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Twice implements ClassTemplateInvocationContextProvider {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsClassTemplate(ExtensionContext context) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Stream<ClassTemplateInvocationContext> provideClassTemplateInvocationContexts(\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.of(new Ctx(), new Ctx());\n\t\t\t}\n\n\t\t\tprivate record Ctx() implements ClassTemplateInvocationContext {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestClassOrder(ClassOrderer.DisplayName.class)\n\tstatic class RevertingBackToDefaultOrderTestCase {\n\n\t\t@Nested\n\t\t@TestClassOrder(ClassOrderer.Default.class)\n\t\tclass Inner {\n\n\t\t\t@Nested\n\t\t\t@Order(3)\n\t\t\tclass Test1 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(2)\n\t\t\tclass Test2 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(4)\n\t\t\tclass Test3 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Order(1)\n\t\t\tclass Test4 {\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t\tcallSequence.add(getClass().getSimpleName());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Comparator.comparing;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.MethodOrderer.Random.RANDOM_SEED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Order.DEFAULT;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.regex.Pattern;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodDescriptor;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.MethodOrderer.Default;\nimport org.junit.jupiter.api.MethodOrderer.MethodName;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.MethodOrderer.Random;\nimport org.junit.jupiter.api.MethodOrdererContext;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.testkit.engine.EngineDiscoveryResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\nimport org.mockito.Mockito;\n\n/**\n * Integration tests that verify support for custom test method execution order\n * in the {@link JupiterTestEngine}.\n *\n * @since 5.4\n */\n@ParameterizedClass\n@EnumSource(ParallelExecutorServiceType.class)\nrecord OrderedMethodTests(ParallelExecutorServiceType executorServiceType) {\n\n\tprivate static final Set<String> callSequence = Collections.synchronizedSet(new LinkedHashSet<>());\n\tprivate static final Set<String> threadNames = Collections.synchronizedSet(new LinkedHashSet<>());\n\n\t@BeforeEach\n\tvoid clearCallSequence() {\n\t\tcallSequence.clear();\n\t\tthreadNames.clear();\n\t}\n\n\t@Test\n\tvoid methodName() {\n\t\tClass<?> testClass = AMethodNameTestCase.class;\n\n\t\t// The name of the base class MUST start with a letter alphanumerically\n\t\t// greater than \"A\" so that BaseTestCase comes after AMethodNameTestCase\n\t\t// if methods are sorted by class name for the fallback ordering if two\n\t\t// methods have the same name but different parameter lists. Note, however,\n\t\t// that MethodName actually does not order methods like that, but we want\n\t\t// this check to remain in place to ensure that the ordering does not rely\n\t\t// on the class names.\n\t\tassertThat(testClass.getSuperclass().getName()).isGreaterThan(testClass.getName());\n\n\t\tvar tests = executeTestsInParallel(testClass, Random.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence).containsExactly(\"$()\", \"AAA()\", \"AAA(org.junit.jupiter.api.TestInfo)\",\n\t\t\t\"AAA(org.junit.jupiter.api.TestReporter)\", \"ZZ_Top()\", \"___()\", \"a1()\", \"a2()\", \"b()\", \"c()\", \"zzz()\");\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@Test\n\tvoid displayName() {\n\t\tvar tests = executeTestsInParallel(DisplayNameTestCase.class, Random.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"$\", \"AAA\", \"No_display_name_attribute_1_caps()\", \"No_display_name_attribute_2_caps()\",\n\t\t\t\t\t\"ZZ_Top\", \"___\", \"a1\", \"a2\", \"b()\", \"no_display_name_attribute_1()\",\n\t\t\t\t\t\"no_display_name_attribute_2()\", \"repetition 1 of 1\", \"⑦ϼ\\uD83D\\uDC69\\u200D⚕\\uD83E\\uDDD8\\u200D♂\");\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@Test\n\tvoid orderAnnotation() {\n\t\tassertOrderAnnotationSupport(OrderAnnotationTestCase.class);\n\t}\n\n\t@Test\n\tvoid orderAnnotationInNestedTestClass() {\n\t\tassertOrderAnnotationSupport(OuterTestCase.class);\n\t}\n\n\t@Test\n\tvoid orderAnnotationWithNestedTestClass() {\n\t\tvar tests = executeTestsInParallel(OrderAnnotationWithNestedClassTestCase.class, Random.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"test1\", \"test2\", \"test3\", \"test4\", \"test5\", \"test6\", \"test7\", \"test8\", \"nestedTest1\",\n\t\t\t\t\t\"nestedTest2\");\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\tprivate void assertOrderAnnotationSupport(Class<?> testClass) {\n\t\tvar tests = executeTestsInParallel(testClass, Random.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence)//\n\t\t\t\t.containsExactly(\"test1\", \"test2\", \"test3\", \"test4\", \"test5\", \"test6\", \"test7\", \"test8\");\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@Test\n\tvoid random() {\n\t\tvar tests = executeTestsInParallel(RandomTestCase.class, Random.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { WithoutTestMethodOrderTestCase.class, ClassTemplateTestCase.class })\n\tvoid defaultOrderer(Class<?> testClass) {\n\t\tvar tests = executeTestsInParallel(testClass, OrderAnnotation.class);\n\n\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\tassertThat(callSequence).containsExactly(\"test1()\", \"test2()\", \"test3()\");\n\t\tassertThat(threadNames).hasSize(1);\n\t}\n\n\t@Test\n\tvoid randomWithBogusSeedRepeatedly(@TrackLogRecords LogRecordListener listener) {\n\t\tvar seed = \"explode\";\n\t\tvar expectedMessagePattern = Pattern.compile(\n\t\t\t\"Failed to convert configuration parameter \\\\[\" + Pattern.quote(Random.RANDOM_SEED_PROPERTY_NAME)\n\t\t\t\t\t+ \"] with value \\\\[\" + seed + \"] to a long\\\\. Using default seed \\\\[\\\\d+] as fallback\\\\.\");\n\n\t\tSet<String> uniqueSequences = new HashSet<>();\n\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\tcallSequence.clear();\n\t\t\tlistener.clear();\n\n\t\t\tvar tests = executeRandomTestCaseInParallelWithRandomSeed(seed);\n\n\t\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\t\tuniqueSequences.add(String.join(\",\", callSequence));\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(listener.stream(Random.class, Level.WARNING)\n\t\t\t\t\t.map(LogRecord::getMessage))\n\t\t\t\t\t.anyMatch(expectedMessagePattern.asMatchPredicate());\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tassertThat(uniqueSequences).size().isEqualTo(1);\n\t}\n\n\t@Test\n\tvoid randomWithDifferentSeedConsecutively(@TrackLogRecords LogRecordListener listener) {\n\t\tSet<String> uniqueSequences = new HashSet<>();\n\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\tvar seed = String.valueOf(i);\n\t\t\tvar expectedMessage = \"Using custom seed for configuration parameter [\" + Random.RANDOM_SEED_PROPERTY_NAME\n\t\t\t\t\t+ \"] with value [\" + seed + \"].\";\n\t\t\tcallSequence.clear();\n\t\t\tlistener.clear();\n\n\t\t\tvar tests = executeRandomTestCaseInParallelWithRandomSeed(seed);\n\n\t\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\t\tuniqueSequences.add(String.join(\",\", callSequence));\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(listener.stream(Random.class, Level.CONFIG)\n\t\t\t\t\t.map(LogRecord::getMessage))\n\t\t\t\t\t.contains(expectedMessage);\n\t\t\t// @formatter:on\n\n\t\t\tassertThat(threadNames).hasSize(i + 1);\n\t\t}\n\n\t\t// We assume that at least 3 out of 10 are different...\n\t\tassertThat(uniqueSequences).size().isGreaterThanOrEqualTo(3);\n\t}\n\n\t@Test\n\tvoid randomWithCustomSeed(@TrackLogRecords LogRecordListener listener) {\n\t\tvar seed = \"42\";\n\t\tvar expectedMessage = \"Using custom seed for configuration parameter [\" + Random.RANDOM_SEED_PROPERTY_NAME\n\t\t\t\t+ \"] with value [\" + seed + \"].\";\n\n\t\tfor (var i = 0; i < 10; i++) {\n\t\t\tcallSequence.clear();\n\t\t\tlistener.clear();\n\n\t\t\tvar tests = executeRandomTestCaseInParallelWithRandomSeed(seed);\n\n\t\t\ttests.assertStatistics(stats -> stats.succeeded(callSequence.size()));\n\n\t\t\t// With a custom seed, the \"randomness\" must be the same for every iteration.\n\t\t\tassertThat(callSequence).containsExactly(\"test2()\", \"test3()\", \"test4()\", \"repetition 1 of 1\", \"test1()\");\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(listener.stream(Random.class, Level.CONFIG)\n\t\t\t\t\t.map(LogRecord::getMessage))\n\t\t\t\t\t.contains(expectedMessage);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tassertThat(threadNames).size().isGreaterThanOrEqualTo(3);\n\t}\n\n\t@Test\n\tvoid reportsDiscoveryIssuesForIneffectiveOrderAnnotations() throws Exception {\n\t\tvar results = discoverTests(WithoutTestMethodOrderTestCase.class, OrderAnnotation.class);\n\t\tassertThat(results.getDiscoveryIssues()).isEmpty();\n\n\t\tresults = discoverTests(WithoutTestMethodOrderTestCase.class, null);\n\t\tassertIneffectiveOrderAnnotationIssues(results.getDiscoveryIssues());\n\n\t\tresults = discoverTests(WithoutTestMethodOrderTestCase.class, Random.class);\n\t\tassertIneffectiveOrderAnnotationIssues(results.getDiscoveryIssues());\n\t}\n\n\t@Test\n\tvoid misbehavingMethodOrdererThatAddsElements() {\n\t\tClass<?> testClass = MisbehavingByAddingTestCase.class;\n\n\t\tvar discoveryIssues = discoverTests(testClass, null).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\n\t\tvar issue = discoveryIssues.getFirst();\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\n\t\t\t\"MethodOrderer [%s] added 2 MethodDescriptor(s) for test class [%s] which will be ignored.\",\n\t\t\tMisbehavingByAdding.class.getName(), testClass.getName());\n\t\tassertThat(issue.source()).contains(ClassSource.from(testClass));\n\n\t\texecuteTestsInParallel(testClass, null, Severity.ERROR) //\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(2));\n\n\t\tassertThat(callSequence).containsExactly(\"test1()\", \"test2()\");\n\t}\n\n\t@Test\n\tvoid misbehavingMethodOrdererThatImpersonatesElements() {\n\t\tClass<?> testClass = MisbehavingByImpersonatingTestCase.class;\n\n\t\texecuteTestsInParallel(testClass, Random.class).assertStatistics(stats -> stats.succeeded(2));\n\n\t\tassertThat(callSequence).containsExactlyInAnyOrder(\"test1()\", \"test2()\");\n\t}\n\n\t@Test\n\tvoid misbehavingMethodOrdererThatRemovesElements() {\n\t\tClass<?> testClass = MisbehavingByRemovingTestCase.class;\n\n\t\tvar discoveryIssues = discoverTests(testClass, null).getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\n\t\tvar issue = discoveryIssues.getFirst();\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\n\t\t\t\"MethodOrderer [%s] removed 2 MethodDescriptor(s) for test class [%s] which will be retained with arbitrary ordering.\",\n\t\t\tMisbehavingByRemoving.class.getName(), testClass.getName());\n\t\tassertThat(issue.source()).contains(ClassSource.from(testClass));\n\n\t\texecuteTestsInParallel(testClass, null, Severity.ERROR) //\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(4));\n\n\t\tassertThat(callSequence) //\n\t\t\t\t.containsExactlyInAnyOrder(\"test1()\", \"test2()\", \"test3()\", \"test4()\") //\n\t\t\t\t.containsSubsequence(\"test3()\", \"test4()\") // ordered in MisbehavingByRemoving\n\t\t\t\t.containsSubsequence(\"test1()\", \"test3()\") // removed item is re-added before ordered item\n\t\t\t\t.containsSubsequence(\"test1()\", \"test4()\") // removed item is re-added before ordered item\n\t\t\t\t.containsSubsequence(\"test2()\", \"test3()\") // removed item is re-added before ordered item\n\t\t\t\t.containsSubsequence(\"test2()\", \"test4()\");// removed item is re-added before ordered item\n\t}\n\n\t@Test\n\tvoid nestedClassedCanUseDefaultOrder(@TrackLogRecords LogRecordListener logRecords) {\n\t\texecuteTestsInParallel(NestedClassWithDefaultOrderTestCase.NestedTests.class, null, Severity.WARNING);\n\t\tassertThat(callSequence).containsExactly(\"test1()\", \"test2()\", \"test3()\", \"test4()\");\n\t\tcallSequence.clear();\n\n\t\texecuteTestsInParallel(NestedClassWithDefaultOrderTestCase.NestedTests.class, OrderAnnotation.class);\n\t\tassertThat(callSequence).containsExactly(\"test4()\", \"test2()\", \"test1()\", \"test3()\");\n\t\tcallSequence.clear();\n\n\t\texecuteTestsInParallel(NestedClassWithDefaultOrderTestCase.NestedTests.class, Default.class, Severity.WARNING);\n\t\tassertThat(callSequence).containsExactly(\"test1()\", \"test2()\", \"test3()\", \"test4()\");\n\t\tassertThat(logRecords.stream()) //\n\t\t\t\t.filteredOn(it -> it.getLevel().intValue() >= Level.WARNING.intValue()) //\n\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t.isEmpty();\n\t}\n\n\tprivate EngineDiscoveryResults discoverTests(Class<?> testClass,\n\t\t\t@Nullable Class<? extends MethodOrderer> defaultOrderer) {\n\t\treturn testKit(testClass, defaultOrderer, Severity.INFO).discover();\n\t}\n\n\tprivate Events executeTestsInParallel(Class<?> testClass, @Nullable Class<? extends MethodOrderer> defaultOrderer) {\n\t\treturn executeTestsInParallel(testClass, defaultOrderer, Severity.INFO);\n\t}\n\n\tprivate Events executeTestsInParallel(Class<?> testClass, @Nullable Class<? extends MethodOrderer> defaultOrderer,\n\t\t\tSeverity criticalSeverity) {\n\t\treturn testKit(testClass, defaultOrderer, criticalSeverity) //\n\t\t\t\t.execute() //\n\t\t\t\t.testEvents();\n\t}\n\n\tprivate EngineTestKit.Builder testKit(Class<?> testClass, @Nullable Class<? extends MethodOrderer> defaultOrderer,\n\t\t\tSeverity criticalSeverity) {\n\n\t\tvar testKit = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, \"true\") //\n\t\t\t\t.configurationParameter(DEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\") //\n\t\t\t\t.configurationParameter(PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME, executorServiceType.name()) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, criticalSeverity.name());\n\t\tif (defaultOrderer != null) {\n\t\t\ttestKit.configurationParameter(DEFAULT_TEST_METHOD_ORDER_PROPERTY_NAME, defaultOrderer.getName());\n\t\t}\n\t\treturn testKit.selectors(selectClass(testClass));\n\t}\n\n\tprivate Events executeRandomTestCaseInParallelWithRandomSeed(String seed) {\n\t\tvar configurationParameters = Map.of(//\n\t\t\tPARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, \"true\", //\n\t\t\tDEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\", //\n\t\t\tRANDOM_SEED_PROPERTY_NAME, seed //\n\t\t);\n\n\t\t// @formatter:off\n\t\treturn EngineTestKit\n\t\t\t\t.engine(\"junit-jupiter\")\n\t\t\t\t.configurationParameters(configurationParameters)\n\t\t\t\t.selectors(selectClass(RandomTestCase.class))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents();\n\t\t// @formatter:on\n\t}\n\n\tprivate static void assertIneffectiveOrderAnnotationIssues(List<DiscoveryIssue> discoveryIssues) throws Exception {\n\t\tassertThat(discoveryIssues).hasSize(3);\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::severity).containsOnly(Severity.INFO);\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::message) //\n\t\t\t\t.allMatch(it -> it.startsWith(\"Ineffective @Order annotation on method\")\n\t\t\t\t\t\t&& it.contains(\"It will not be applied because MethodOrderer.OrderAnnotation is not in use.\")\n\t\t\t\t\t\t&& it.endsWith(\n\t\t\t\t\t\t\t\"Note that the annotation may be either directly present or meta-present on the method.\"));\n\t\tvar testClass = WithoutTestMethodOrderTestCase.class;\n\t\tassertThat(discoveryIssues).extracting(DiscoveryIssue::source).extracting(Optional::orElseThrow) //\n\t\t\t\t.containsExactlyInAnyOrder(MethodSource.from(testClass.getDeclaredMethod(\"test1\")),\n\t\t\t\t\tMethodSource.from(testClass.getDeclaredMethod(\"test2\")),\n\t\t\t\t\tMethodSource.from(testClass.getDeclaredMethod(\"test3\")));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class BaseTestCase {\n\n\t\t@Test\n\t\tvoid AAA() {\n\t\t}\n\n\t\t@Test\n\t\tvoid c() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\t@TestMethodOrder(MethodName.class)\n\tstatic class AMethodNameTestCase extends BaseTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tvar method = testInfo.getTestMethod().orElseThrow(AssertionError::new);\n\t\t\tvar signature = \"%s(%s)\".formatted(method.getName(),\n\t\t\t\tClassUtils.nullSafeToString(method.getParameterTypes()));\n\n\t\t\tcallSequence.add(signature);\n\t\t\tthreadNames.add(Thread.currentThread().getName());\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest b() {\n\t\t\treturn dynamicTest(\"dynamic\", () -> {\n\t\t\t});\n\t\t}\n\n\t\t@Test\n\t\tvoid $() {\n\t\t}\n\n\t\t@Test\n\t\tvoid ___() {\n\t\t}\n\n\t\t@Test\n\t\tvoid AAA(TestReporter testReporter) {\n\t\t}\n\n\t\t@Test\n\t\tvoid AAA(TestInfo testInfo) {\n\t\t}\n\n\t\t@Test\n\t\tvoid ZZ_Top() {\n\t\t}\n\n\t\t@Test\n\t\tvoid a1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid a2() {\n\t\t}\n\n\t\t@RepeatedTest(1)\n\t\tvoid zzz() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(MethodOrderer.DisplayName.class)\n\tstatic class DisplayNameTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\tthreadNames.add(Thread.currentThread().getName());\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest b() {\n\t\t\treturn dynamicTest(\"dynamic\", () -> {\n\t\t\t});\n\t\t}\n\n\t\t@DisplayName(\"$\")\n\t\t@Test\n\t\tvoid $() {\n\t\t}\n\n\t\t@DisplayName(\"___\")\n\t\t@Test\n\t\tvoid ___() {\n\t\t}\n\n\t\t@DisplayName(\"AAA\")\n\t\t@Test\n\t\tvoid AAA() {\n\t\t}\n\n\t\t@DisplayName(\"ZZ_Top\")\n\t\t@Test\n\t\tvoid ZZ_Top() {\n\t\t}\n\n\t\t@DisplayName(\"a1\")\n\t\t@Test\n\t\tvoid a1() {\n\t\t}\n\n\t\t@DisplayName(\"a2\")\n\t\t@Test\n\t\tvoid a2() {\n\t\t}\n\n\t\t@DisplayName(\"zzz\")\n\t\t@RepeatedTest(1)\n\t\tvoid zzz() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"⑦ϼ\\uD83D\\uDC69\\u200D⚕\\uD83E\\uDDD8\\u200D♂\")\n\t\tvoid special_characters() {\n\t\t}\n\n\t\t@Test\n\t\tvoid no_display_name_attribute_1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid no_display_name_attribute_2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid No_display_name_attribute_1_caps() {\n\t\t}\n\n\t\t@Test\n\t\tvoid No_display_name_attribute_2_caps() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class OrderAnnotationTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\tthreadNames.add(Thread.currentThread().getName());\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test8\")\n\t\t@Order(Integer.MAX_VALUE)\n\t\tvoid maxInteger() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test7\")\n\t\t@Order(DEFAULT + 1)\n\t\tvoid defaultOrderValuePlusOne() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test6\")\n\t\t// @Order(DEFAULT)\n\t\tvoid defaultOrderValue() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test3\")\n\t\t@Order(3)\n\t\tvoid $() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test5\")\n\t\t@Order(5)\n\t\tvoid AAA() {\n\t\t}\n\n\t\t@TestFactory\n\t\t@DisplayName(\"test4\")\n\t\t@Order(4)\n\t\tDynamicTest aaa() {\n\t\t\treturn dynamicTest(\"test4\", () -> {\n\t\t\t});\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"test1\")\n\t\t@Order(1)\n\t\tvoid zzz() {\n\t\t}\n\n\t\t@RepeatedTest(value = 1, name = \"{displayName}\")\n\t\t@DisplayName(\"test2\")\n\t\t@Order(2)\n\t\tvoid ___() {\n\t\t}\n\t}\n\n\tstatic class OuterTestCase {\n\n\t\t@Nested\n\t\tclass InnerTestCase {\n\n\t\t\t@Nested\n\t\t\tclass NestedOrderAnnotationTestCase extends OrderAnnotationTestCase {\n\t\t\t}\n\t\t}\n\t}\n\n\t@TestMethodOrder(Random.class)\n\tstatic class RandomTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\tthreadNames.add(Thread.currentThread().getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest test4() {\n\t\t\treturn dynamicTest(\"dynamic\", () -> {\n\t\t\t});\n\t\t}\n\n\t\t@RepeatedTest(1)\n\t\tvoid test5() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(MisbehavingByAdding.class)\n\tstatic class MisbehavingByAddingTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(MisbehavingByImpersonating.class)\n\tstatic class MisbehavingByImpersonatingTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(MisbehavingByRemoving.class)\n\tstatic class MisbehavingByRemovingTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test4() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t}\n\t}\n\n\tstatic class OrderAnnotationWithNestedClassTestCase extends OrderAnnotationTestCase {\n\t\t@Nested\n\t\tclass NestedTests {\n\n\t\t\t@BeforeEach\n\t\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\t@DisplayName(\"nestedTest1\")\n\t\t\tvoid nestedTest1() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\t@DisplayName(\"nestedTest2\")\n\t\t\tvoid nestedTest2() {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class MisbehavingByAdding implements MethodOrderer {\n\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName));\n\t\t\tcontext.getMethodDescriptors().add(mockMethodDescriptor());\n\t\t\tcontext.getMethodDescriptors().add(mockMethodDescriptor());\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tstatic <T> T mockMethodDescriptor() {\n\t\t\treturn (T) Mockito.mock((Class<? super T>) MethodDescriptor.class);\n\t\t}\n\n\t}\n\n\tstatic class MisbehavingByImpersonating implements MethodOrderer {\n\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName));\n\t\t\tMethodDescriptor method1 = context.getMethodDescriptors().get(0);\n\t\t\tMethodDescriptor method2 = context.getMethodDescriptors().get(1);\n\n\t\t\tcontext.getMethodDescriptors().set(0, createMethodDescriptorImpersonator(method1));\n\t\t\tcontext.getMethodDescriptors().set(1, createMethodDescriptorImpersonator(method2));\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tstatic <T> T createMethodDescriptorImpersonator(MethodDescriptor method) {\n\t\t\t@NullMarked\n\t\t\tclass Stub implements MethodDescriptor {\n\t\t\t\t@Override\n\t\t\t\tpublic Method getMethod() {\n\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String getDisplayName() {\n\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean isAnnotated(Class<? extends Annotation> annotationType) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic <A extends Annotation> Optional<A> findAnnotation(Class<A> annotationType) {\n\t\t\t\t\treturn Optional.empty();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic <A extends Annotation> List<A> findRepeatableAnnotations(Class<A> annotationType) {\n\t\t\t\t\treturn List.of();\n\t\t\t\t}\n\n\t\t\t\t@SuppressWarnings(\"EqualsWhichDoesntCheckParameterClass\")\n\t\t\t\t@Override\n\t\t\t\tpublic boolean equals(Object obj) {\n\t\t\t\t\treturn method.equals(obj);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int hashCode() {\n\t\t\t\t\treturn method.hashCode();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (T) new Stub();\n\t\t}\n\t}\n\n\tstatic class MisbehavingByRemoving implements MethodOrderer {\n\n\t\t@Override\n\t\tpublic void orderMethods(MethodOrdererContext context) {\n\t\t\tcontext.getMethodDescriptors().sort(comparing(MethodDescriptor::getDisplayName));\n\t\t\tcontext.getMethodDescriptors().removeFirst();\n\t\t\tcontext.getMethodDescriptors().removeFirst();\n\t\t}\n\t}\n\n\tstatic class WithoutTestMethodOrderTestCase {\n\n\t\t@BeforeEach\n\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\tthreadNames.add(Thread.currentThread().getName());\n\t\t}\n\n\t\t@Test\n\t\t@Order(2)\n\t\tvoid test2() {\n\t\t}\n\n\t\t@Test\n\t\t@Order(3)\n\t\tvoid test3() {\n\t\t}\n\n\t\t@Test\n\t\t@Order(1)\n\t\tvoid test1() {\n\t\t}\n\n\t}\n\n\tstatic class ClassTemplateTestCase extends WithoutTestMethodOrderTestCase {\n\t}\n\n\tstatic class NestedClassWithDefaultOrderTestCase extends OrderAnnotationTestCase {\n\n\t\t@Nested\n\t\t@TestMethodOrder(Default.class)\n\t\t@Execution(ExecutionMode.SAME_THREAD)\n\t\tclass NestedTests {\n\n\t\t\t@BeforeEach\n\t\t\tvoid trackInvocations(TestInfo testInfo) {\n\t\t\t\tcallSequence.add(testInfo.getDisplayName());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(3)\n\t\t\tvoid test1() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\tvoid test2() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(4)\n\t\t\tvoid test3() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\tvoid test4() {\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/OrderedProgrammaticExtensionRegistrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Order.DEFAULT;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.AfterEachCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\n\n/**\n * Integration tests that verify support for {@linkplain Order ordered} programmatic\n * extension registration via {@link RegisterExtension @RegisterExtension} in the\n * {@link JupiterTestEngine}.\n *\n * @since 5.4\n * @see ProgrammaticExtensionRegistrationTests\n */\nclass OrderedProgrammaticExtensionRegistrationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t/**\n\t * This method basically verifies the implementation of\n\t * {@link java.lang.String#hashCode()} (which needn't really be tested)\n\t * in order to make reasonable assumptions about how fields are sorted\n\t * in {@link org.junit.platform.commons.util.ReflectionUtils#defaultFieldSorter(Field, Field)}.\n\t *\n\t * <p>In other words, this method is just a sanity check for the chosen\n\t * field names in the test cases used in these tests.\n\t */\n\t@BeforeAll\n\tstatic void assertAssumptionsAboutDefaultOrderingAlgorithm() {\n\t\tString fieldName1 = \"extension1\";\n\t\tString fieldName2 = \"extension2\";\n\t\tString fieldName3 = \"extension3\";\n\n\t\tassertThat(fieldName1.hashCode()).isLessThan(fieldName2.hashCode());\n\t\tassertThat(fieldName2.hashCode()).isLessThan(fieldName3.hashCode());\n\t}\n\n\t@BeforeEach\n\tvoid clearCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid instanceLevelWithDefaultOrder() {\n\t\tClass<?> testClass = DefaultOrderInstanceLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension1 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\", //\n\t\t\ttestClassName + \" :: extension3 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithExplicitOrder() {\n\t\tClass<?> testClass = ExplicitOrderInstanceLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension3 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\", //\n\t\t\ttestClassName + \" :: extension1 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithDefaultOrderAndExplicitOrder() {\n\t\tClass<?> testClass = DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension3 :: before test\", //\n\t\t\ttestClassName + \" :: extension1 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\t/**\n\t * Verify that an \"after\" callback can be registered first relative to other\n\t * non-annotated \"after\" callbacks.\n\t *\n\t * @since 5.6\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1924\">gh-1924</a>\n\t */\n\t@Test\n\tvoid instanceLevelWithDefaultOrderPlusOneAndDefaultOrder() {\n\t\tClass<?> testClass = DefaultOrderPlusOneAndDefaultOrderInstanceLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension1 :: after test\", //\n\t\t\ttestClassName + \" :: extension3 :: after test\", //\n\t\t\ttestClassName + \" :: extension2 :: after test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithDefaultOrderAndExplicitOrderWithTestInstancePerClassLifecycle() {\n\t\tClass<?> testClass = DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension3 :: before test\", //\n\t\t\ttestClassName + \" :: extension1 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithDefaultOrderAndExplicitOrder() {\n\t\tClass<?> testClass = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\ttestClassName + \" :: extension3 :: before test\", //\n\t\t\ttestClassName + \" :: extension1 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithDefaultOrderAndExplicitOrderInheritedFromSuperclass() {\n\t\tClass<?> testClass = InheritedDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tClass<?> parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString parentName = parent.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\tparentName + \" :: extension3 :: before test\", //\n\t\t\tparentName + \" :: extension1 :: before test\", //\n\t\t\tparentName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithDefaultOrderDoesNotShadowExtensionFromSuperclass() {\n\t\tClass<?> testClass = DefaultOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tClass<?> parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString parentName = parent.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\tparentName + \" :: extension3 :: before test\", //\n\t\t\tparentName + \" :: extension1 :: before test\", //\n\t\t\tparentName + \" :: extension2 :: before test\", //\n\t\t\ttestClassName + \" :: extension3 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithExplicitOrderDoesNotShadowExtensionFromSuperclass() {\n\t\tClass<?> testClass = ExplicitOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString testClassName = testClass.getSimpleName();\n\t\tClass<?> parent = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase.class;\n\t\tString parentName = parent.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\tparentName + \" :: extension3 :: before test\", //\n\t\t\ttestClassName + \" :: extension2 :: before test\", //\n\t\t\tparentName + \" :: extension1 :: before test\", //\n\t\t\tparentName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithDefaultOrderAndExplicitOrderFromInterface() {\n\t\tClass<?> testClass = DefaultOrderAndExplicitOrderExtensionRegistrationFromInterfaceTestCase.class;\n\t\tClass<?> testInterface = DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationInterface.class;\n\t\tString interfaceName = testInterface.getSimpleName();\n\t\tassertOutcome(testClass, //\n\t\t\tinterfaceName + \" :: extension3 :: before test\", //\n\t\t\tinterfaceName + \" :: extension1 :: before test\", //\n\t\t\tinterfaceName + \" :: extension2 :: before test\" //\n\t\t);\n\t}\n\n\tprivate void assertOutcome(Class<?> testClass, String... values) {\n\t\texecuteTestsForClass(testClass).testEvents().assertStatistics(stats -> stats.succeeded(1));\n\t\tassertThat(callSequence).containsExactly(values);\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static class AbstractTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\tstatic class DefaultOrderInstanceLevelExtensionRegistrationTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t}\n\n\tstatic class ExplicitOrderInstanceLevelExtensionRegistrationTestCase extends AbstractTestCase {\n\n\t\t@Order(3)\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t@Order(2)\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@Order(1)\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationTestCase extends AbstractTestCase {\n\n\t\t// @Order(3)\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t// @Order(2)\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@Order(1)\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class DefaultOrderPlusOneAndDefaultOrderInstanceLevelExtensionRegistrationTestCase extends AbstractTestCase {\n\n\t\t@Order(DEFAULT + 1)\n\t\t@RegisterExtension\n\t\tExtension extension1 = new AfterEachExtension(1);\n\n\t\t@RegisterExtension\n\t\tExtension extension2 = new AfterEachExtension(2);\n\n\t\t@RegisterExtension\n\t\tExtension extension3 = new AfterEachExtension(3);\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class DefaultOrderAndExplicitOrderInstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase\n\t\t\textends AbstractTestCase {\n\n\t\t// @Order(3)\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t// @Order(2)\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@Order(1)\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase extends AbstractTestCase {\n\n\t\t// @Order(3)\n\t\t@RegisterExtension\n\t\tstatic Extension extension1 = new BeforeEachExtension(1);\n\n\t\t// @Order(2)\n\t\t@RegisterExtension\n\t\tstatic Extension extension2 = new BeforeEachExtension(2);\n\n\t\t@Order(1)\n\t\t@RegisterExtension\n\t\tstatic Extension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class InheritedDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase\n\t\t\textends DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase {\n\t}\n\n\tstatic class DefaultOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase\n\t\t\textends DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase {\n\n\t\t// @Order(1)\n\t\t@RegisterExtension\n\t\tstatic Extension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class ExplicitOrderShadowingDefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase\n\t\t\textends DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationTestCase {\n\n\t\t@Order(2)\n\t\t@RegisterExtension\n\t\tstatic Extension extension2 = new BeforeEachExtension(2);\n\n\t}\n\n\tinterface DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationInterface {\n\n\t\t// @Order(3)\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t// @Order(2)\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@Order(1)\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class DefaultOrderAndExplicitOrderExtensionRegistrationFromInterfaceTestCase extends AbstractTestCase\n\t\t\timplements DefaultOrderAndExplicitOrderClassLevelExtensionRegistrationInterface {\n\t}\n\n\tprivate static class BeforeEachExtension implements BeforeEachCallback {\n\n\t\tprivate final String prefix;\n\n\t\tBeforeEachExtension(int id) {\n\t\t\tClass<?> callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();\n\t\t\tthis.prefix = callerClass.getSimpleName() + \" :: extension\" + id + \" :: before \";\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(this.prefix + context.getRequiredTestMethod().getName());\n\t\t}\n\n\t}\n\n\tprivate static class AfterEachExtension implements AfterEachCallback {\n\n\t\tprivate final String prefix;\n\n\t\tAfterEachExtension(int id) {\n\t\t\tClass<?> callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();\n\t\t\tthis.prefix = callerClass.getSimpleName() + \" :: extension\" + id + \" :: after \";\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterEach(ExtensionContext context) {\n\t\t\tcallSequence.add(this.prefix + context.getRequiredTestMethod().getName());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ParameterResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.engine.execution.injection.sample.CustomAnnotation;\nimport org.junit.jupiter.engine.execution.injection.sample.CustomAnnotationParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.CustomType;\nimport org.junit.jupiter.engine.execution.injection.sample.CustomTypeParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.MapOfListsTypeBasedParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.MapOfStringsParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.NullIntegerParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.NumberParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.PrimitiveArrayParameterResolver;\nimport org.junit.jupiter.engine.execution.injection.sample.PrimitiveIntegerParameterResolver;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests that verify support for {@link ParameterResolver}\n * extensions in the {@link JupiterTestEngine}.\n *\n * @since 5.0\n */\nclass ParameterResolverTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid constructorInjection() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ConstructorInjectionTestCase.class);\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid constructorInjectionWithAnnotatedParameter() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(\n\t\t\tAnnotatedParameterConstructorInjectionTestCase.class);\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(MethodInjectionTestCase.class);\n\n\t\tassertEquals(7, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(6, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForNullValuedMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(NullMethodInjectionTestCase.class);\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tPredicate<String> expectations = s ->\n\t\t\t\ts.contains(\"NullIntegerParameterResolver\") &&\n\t\t\t\ts.contains(\"resolved a null value for parameter\") &&\n\t\t\t\ts.contains(\"but a primitive of type [int] is required\");\n\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(\n\t\t\t\ttest(\"injectPrimitive\"),\n\t\t\t\tfinishedWithFailure(instanceOf(ParameterResolutionException.class), message(expectations))\n\t\t\t));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestsForPrimitiveIntegerMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(PrimitiveIntegerMethodInjectionTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForPrimitiveArrayMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(PrimitiveArrayMethodInjectionTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForPotentiallyIncompatibleTypeMethodInjectionCases() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(\n\t\t\tPotentiallyIncompatibleTypeMethodInjectionTestCase.class);\n\t\tEvents tests = executionResults.testEvents();\n\n\t\tassertEquals(3, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests failed\");\n\n\t\t// @formatter:off\n\t\tPredicate<String> expectations = s ->\n\t\t\t\ts.contains(\"NumberParameterResolver\") &&\n\t\t\t\ts.contains(\"resolved a value of type [java.lang.Integer]\") &&\n\t\t\t\ts.contains(\"but a value assignment compatible with [java.lang.Double] is required\");\n\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(\n\t\t\t\ttest(\"doubleParameterInjection\"),\n\t\t\t\tfinishedWithFailure(instanceOf(ParameterResolutionException.class), message(expectations)\n\t\t\t)));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid executeTestsForMethodInjectionInBeforeAndAfterEachMethods() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(BeforeAndAfterMethodInjectionTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForMethodInjectionInBeforeAndAfterAllMethods() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(BeforeAndAfterAllMethodInjectionTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForMethodWithExtendWithAnnotation() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ExtendWithOnMethodTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t@Test\n\tvoid executeTestsForParameterizedTypesSelectingByClass() {\n\t\tassertEventsForParameterizedTypes(executeTestsForClass(ParameterizedTypeTestCase.class));\n\t}\n\n\t@Test\n\tvoid executeTestsForParameterizedTypesSelectingByFullyQualifiedMethodName() {\n\t\tString fqmn = ReflectionUtils.getFullyQualifiedMethodName(ParameterizedTypeTestCase.class, \"testMapOfStrings\",\n\t\t\tMap.class);\n\n\t\tassertEventsForParameterizedTypes(executeTests(selectMethod(fqmn)));\n\t}\n\n\t@Test\n\tvoid executeTestsForTypeBasedParameterResolverTestCaseSelectingByClass() {\n\t\tassertEventsForParameterizedTypes(executeTestsForClass(TypeBasedParameterResolverTestCase.class));\n\t}\n\n\t@Test\n\tvoid executeTestsForTypeBasedParameterResolverTestCaseSelectingByFullyQualifiedMethodName() {\n\t\tString fqmn = ReflectionUtils.getFullyQualifiedMethodName(TypeBasedParameterResolverTestCase.class,\n\t\t\t\"testMapOfLists\", Map.class);\n\n\t\tassertEventsForParameterizedTypes(executeTests(selectMethod(fqmn)));\n\t}\n\n\t@Disabled(\"Disabled until a decision has been made regarding #956\")\n\t@Test\n\tvoid executeTestsForParameterizedTypesSelectingByFullyQualifiedMethodNameContainingGenericInfo() throws Exception {\n\t\tMethod method = ParameterizedTypeTestCase.class.getDeclaredMethod(\"testMapOfStrings\", Map.class);\n\t\tString genericParameterTypeName = method.getGenericParameterTypes()[0].getTypeName();\n\t\tString fqmn = \"%s#%s(%s)\".formatted(ParameterizedTypeTestCase.class.getName(), \"testMapOfStrings\",\n\t\t\tgenericParameterTypeName);\n\n\t\tassertEventsForParameterizedTypes(executeTests(selectMethod(fqmn)));\n\t}\n\n\tprivate void assertEventsForParameterizedTypes(EngineExecutionResults executionResults) {\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\t\tassertEquals(0, executionResults.testEvents().skipped().count(), \"# tests skipped\");\n\t\tassertEquals(0, executionResults.testEvents().aborted().count(), \"# tests aborted\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests failed\");\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t@ExtendWith(CustomTypeParameterResolver.class)\n\tstatic class ConstructorInjectionTestCase {\n\n\t\tprivate final TestInfo outerTestInfo;\n\t\tprivate final CustomType outerCustomType;\n\n\t\tConstructorInjectionTestCase(TestInfo testInfo, CustomType customType) {\n\t\t\tthis.outerTestInfo = testInfo;\n\t\t\tthis.outerCustomType = customType;\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(this.outerTestInfo);\n\t\t\tassertNotNull(this.outerCustomType);\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\tprivate final TestInfo innerTestInfo;\n\t\t\tprivate final CustomType innerCustomType;\n\n\t\t\tNestedTestCase(TestInfo testInfo, CustomType customType) {\n\t\t\t\tthis.innerTestInfo = testInfo;\n\t\t\t\tthis.innerCustomType = customType;\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertNotNull(outerTestInfo);\n\t\t\t\tassertNotNull(outerCustomType);\n\t\t\t\tassertNotNull(this.innerTestInfo);\n\t\t\t\tassertNotNull(this.innerCustomType);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(CustomAnnotationParameterResolver.class)\n\tstatic class AnnotatedParameterConstructorInjectionTestCase {\n\n\t\tprivate final TestInfo outerTestInfo;\n\t\tprivate final CustomType outerCustomType;\n\n\t\tAnnotatedParameterConstructorInjectionTestCase(TestInfo testInfo, @CustomAnnotation CustomType customType) {\n\t\t\tthis.outerTestInfo = testInfo;\n\t\t\tthis.outerCustomType = customType;\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(this.outerTestInfo);\n\t\t\tassertNotNull(this.outerCustomType);\n\t\t}\n\n\t\t@Nested\n\t\t// See https://github.com/junit-team/junit-framework/issues/1345\n\t\tclass AnnotatedConstructorParameterNestedTestCase {\n\n\t\t\tprivate final TestInfo innerTestInfo;\n\t\t\tprivate final CustomType innerCustomType;\n\n\t\t\tAnnotatedConstructorParameterNestedTestCase(TestInfo testInfo, @CustomAnnotation CustomType customType) {\n\t\t\t\tthis.innerTestInfo = testInfo;\n\t\t\t\tthis.innerCustomType = customType;\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertNotNull(outerTestInfo);\n\t\t\t\tassertNotNull(outerCustomType);\n\t\t\t\tassertNotNull(this.innerTestInfo);\n\t\t\t\tassertNotNull(this.innerCustomType);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith({ CustomTypeParameterResolver.class, CustomAnnotationParameterResolver.class })\n\tstatic class MethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid parameterInjectionOfTestInfo(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t}\n\n\t\t@Test\n\t\tvoid parameterInjectionWithCompetingResolversFail(@CustomAnnotation CustomType customType) {\n\t\t\t// should fail\n\t\t}\n\n\t\t@Test\n\t\tvoid parameterInjectionByType(CustomType customType) {\n\t\t\tassertNotNull(customType);\n\t\t}\n\n\t\t@Test\n\t\tvoid parameterInjectionByAnnotation(@CustomAnnotation String value) {\n\t\t\tassertNotNull(value);\n\t\t}\n\n\t\t// some overloaded methods\n\n\t\t@Test\n\t\tvoid overloadedName() {\n\t\t\tassertTrue(true);\n\t\t}\n\n\t\t@Test\n\t\tvoid overloadedName(CustomType customType) {\n\t\t\tassertNotNull(customType);\n\t\t}\n\n\t\t@Test\n\t\tvoid overloadedName(CustomType customType, @CustomAnnotation String value) {\n\t\t\tassertNotNull(customType);\n\t\t\tassertNotNull(value);\n\t\t}\n\t}\n\n\t@ExtendWith(NullIntegerParameterResolver.class)\n\tstatic class NullMethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid injectWrapper(Integer number) {\n\t\t\tassertNull(number);\n\t\t}\n\n\t\t@Test\n\t\tvoid injectPrimitive(int number) {\n\t\t\t// should never be invoked since an int cannot be null\n\t\t}\n\t}\n\n\t@ExtendWith(PrimitiveIntegerParameterResolver.class)\n\tstatic class PrimitiveIntegerMethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid intPrimitive(int i) {\n\t\t\tassertEquals(42, i);\n\t\t}\n\t}\n\n\t@ExtendWith(PrimitiveArrayParameterResolver.class)\n\tstatic class PrimitiveArrayMethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid primitiveArray(int... ints) {\n\t\t\tassertArrayEquals(new int[] { 1, 2, 3 }, ints);\n\t\t}\n\t}\n\n\t@ExtendWith(NumberParameterResolver.class)\n\tstatic class PotentiallyIncompatibleTypeMethodInjectionTestCase {\n\n\t\t@Test\n\t\tvoid numberParameterInjection(Number number) {\n\t\t\tassertEquals(Integer.valueOf(42), number);\n\t\t}\n\n\t\t@Test\n\t\tvoid integerParameterInjection(Integer number) {\n\t\t\tassertEquals(Integer.valueOf(42), number);\n\t\t}\n\n\t\t/**\n\t\t * This test must fail, since {@link Double} is a {@link Number} but not an {@link Integer}.\n\t\t * @see NumberParameterResolver\n\t\t */\n\t\t@Test\n\t\tvoid doubleParameterInjection(Double number) {\n\t\t\t/* no-op */\n\t\t}\n\t}\n\n\tstatic class BeforeAndAfterMethodInjectionTestCase {\n\n\t\t@BeforeEach\n\t\tvoid before(TestInfo testInfo) {\n\t\t\tassertEquals(\"custom name\", testInfo.getDisplayName());\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"custom name\")\n\t\tvoid customNamedTest() {\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid after(TestInfo testInfo) {\n\t\t\tassertEquals(\"custom name\", testInfo.getDisplayName());\n\t\t}\n\t}\n\n\t@DisplayName(\"custom class name\")\n\tstatic class BeforeAndAfterAllMethodInjectionTestCase {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestInfo testInfo) {\n\t\t\tassertEquals(\"custom class name\", testInfo.getDisplayName());\n\t\t}\n\n\t\t@Test\n\t\tvoid aTest() {\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestInfo testInfo) {\n\t\t\tassertEquals(\"custom class name\", testInfo.getDisplayName());\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class ExtendWithOnMethodTestCase {\n\n\t\t/**\n\t\t * This set-up / tear-down method is here to verify that {@code @BeforeEach}\n\t\t * and {@code @AfterEach} methods are properly invoked using the same\n\t\t * {@code ExtensionRegistry} as the one used for the corresponding\n\t\t * {@code @Test} method.\n\t\t *\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/523\">#523</a>\n\t\t */\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@BeforeEach\n\t\t@AfterEach\n\t\tvoid setUpAndTearDown(CustomType customType, @CustomAnnotation String value) {\n\t\t\tassertNotNull(customType);\n\t\t\tassertNotNull(value);\n\t\t}\n\n\t\t@Test\n\t\t@ExtendWith(CustomTypeParameterResolver.class)\n\t\t@ExtendWith(CustomAnnotationParameterResolver.class)\n\t\tvoid testMethodWithExtensionAnnotation(CustomType customType, @CustomAnnotation String value) {\n\t\t\tassertNotNull(customType);\n\t\t\tassertNotNull(value);\n\t\t}\n\t}\n\n\tstatic class ParameterizedTypeTestCase {\n\n\t\t@Test\n\t\t@ExtendWith(MapOfStringsParameterResolver.class)\n\t\tvoid testMapOfStrings(Map<String, String> map) {\n\t\t\tassertNotNull(map);\n\t\t\tassertEquals(\"value\", map.get(\"key\"));\n\t\t}\n\t}\n\n\tstatic class TypeBasedParameterResolverTestCase {\n\t\t@Test\n\t\t@ExtendWith(MapOfListsTypeBasedParameterResolver.class)\n\t\tvoid testMapOfLists(Map<String, List<Integer>> map) {\n\t\t\tassertNotNull(map);\n\t\t\tassertEquals(asList(1, 42), map.get(\"ids\"));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/PreInterruptCallbackTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.jupiter.api.parallel.Resources.SYSTEM_OUT;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.nio.charset.StandardCharsets;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.UnaryOperator;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.PreInterruptCallback;\nimport org.junit.jupiter.api.extension.PreInterruptContext;\nimport org.junit.jupiter.api.parallel.Isolated;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * @since 5.12\n */\n@Isolated\nclass PreInterruptCallbackTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final String TC = \"test\";\n\tprivate static final String TIMEOUT_ERROR_MSG = TC + \"() timed out after 1 microsecond\";\n\tprivate static final AtomicBoolean interruptedTest = new AtomicBoolean();\n\tprivate static final CompletableFuture<?> testThreadExecutionDone = new CompletableFuture<>();\n\tprivate static final AtomicReference<@Nullable Thread> interruptedTestThread = new AtomicReference<>();\n\tprivate static final AtomicBoolean interruptCallbackShallThrowException = new AtomicBoolean();\n\tprivate static final AtomicReference<@Nullable PreInterruptContext> calledPreInterruptContext = new AtomicReference<>();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tinterruptedTest.set(false);\n\t\tinterruptCallbackShallThrowException.set(false);\n\t\tcalledPreInterruptContext.set(null);\n\t}\n\n\t@AfterEach\n\tvoid tearDown() {\n\t\tcalledPreInterruptContext.set(null);\n\t\tinterruptedTestThread.set(null);\n\t}\n\n\t@Test\n\t@ResourceLock(value = SYSTEM_OUT, mode = READ_WRITE)\n\tvoid testCaseWithDefaultInterruptCallbackEnabled() {\n\t\tPrintStream orgOutStream = System.out;\n\t\tEngineExecutionResults results;\n\t\tString output;\n\t\ttry {\n\t\t\tByteArrayOutputStream buffer = new ByteArrayOutputStream();\n\t\t\tPrintStream outStream = new PrintStream(buffer, false, StandardCharsets.UTF_8);\n\t\t\tSystem.setOut(outStream);\n\t\t\t// Use larger timeout to increase likelihood of the test being started when the timeout is reached\n\t\t\tvar timeout = WINDOWS.isCurrentOs() ? \"1 s\" : \"100 ms\";\n\t\t\tresults = executeDefaultPreInterruptCallbackTimeoutOnMethodTestCase(timeout, request -> request //\n\t\t\t\t\t.configurationParameter(Constants.EXTENSIONS_TIMEOUT_THREAD_DUMP_ENABLED_PROPERTY_NAME, \"true\"));\n\t\t\toutput = buffer.toString(StandardCharsets.UTF_8);\n\t\t}\n\t\tfinally {\n\t\t\tSystem.setOut(orgOutStream);\n\t\t}\n\n\t\tassertTestHasTimedOut(results.testEvents(), message(it -> it.startsWith(TC + \"() timed out after\")));\n\t\tassertTrue(interruptedTest.get());\n\t\tThread thread = Thread.currentThread();\n\n\t\tassertThat(output) //\n\t\t\t\t.containsSubsequence(\n\t\t\t\t\t\"Thread \\\"%s\\\" prio=%d Id=%d %s will be interrupted.\".formatted(thread.getName(),\n\t\t\t\t\t\tthread.getPriority(), thread.threadId(), Thread.State.TIMED_WAITING), //\n\t\t\t\t\t\"java.lang.Thread.sleep\", //\n\t\t\t\t\t\"%s.test(PreInterruptCallbackTests.java\".formatted(\n\t\t\t\t\t\tDefaultPreInterruptCallbackTimeoutOnMethodTestCase.class.getName()));\n\n\t\tassertThat(output) //\n\t\t\t\t.containsSubsequence( //\n\t\t\t\t\t\"junit-jupiter-timeout-watcher\", //\n\t\t\t\t\t\"%s.beforeThreadInterrupt\".formatted(PreInterruptThreadDumpPrinter.class.getName()));\n\t}\n\n\t@Test\n\tvoid testCaseWithNoInterruptCallbackEnabled() {\n\t\tEvents tests = executeDefaultPreInterruptCallbackTimeoutOnMethodTestCase(\"1 μs\", UnaryOperator.identity()) //\n\t\t\t\t.testEvents();\n\t\tassertTestHasTimedOut(tests);\n\t\tassertTrue(interruptedTest.get());\n\t}\n\n\tprivate EngineExecutionResults executeDefaultPreInterruptCallbackTimeoutOnMethodTestCase(String timeout,\n\t\t\tUnaryOperator<LauncherDiscoveryRequestBuilder> configurer) {\n\t\treturn executeTests(request -> configurer.apply(request //\n\t\t\t\t.selectors(selectClass(DefaultPreInterruptCallbackTimeoutOnMethodTestCase.class)) //\n\t\t\t\t.configurationParameter(Constants.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, timeout)));\n\t}\n\n\t@Test\n\tvoid testCaseWithDeclaredInterruptCallbackEnabled() {\n\t\tEvents tests = executeTestsForClass(DefaultPreInterruptCallbackWithExplicitCallbackTestCase.class).testEvents();\n\t\tassertTestHasTimedOut(tests);\n\t\tassertTrue(interruptedTest.get());\n\t\tPreInterruptContext preInterruptContext = calledPreInterruptContext.get();\n\t\tassertNotNull(preInterruptContext);\n\t\tassertNotNull(preInterruptContext.getThreadToInterrupt());\n\t\tassertEquals(preInterruptContext.getThreadToInterrupt(), interruptedTestThread.get());\n\t}\n\n\t@Test\n\tvoid testCaseWithDeclaredInterruptCallbackEnabledWithSeparateThread() throws Exception {\n\t\tEvents tests = executeTestsForClass(\n\t\t\tDefaultPreInterruptCallbackWithExplicitCallbackWithSeparateThreadTestCase.class).testEvents();\n\t\tassertOneFailedTest(tests);\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(TC), finishedWithFailure(instanceOf(TimeoutException.class))));\n\n\t\t//Wait until the real test thread was interrupted due to executor.shutdown(), otherwise the asserts below will be flaky.\n\t\ttestThreadExecutionDone.get(1, TimeUnit.SECONDS);\n\n\t\tassertTrue(interruptedTest.get());\n\t\tPreInterruptContext preInterruptContext = calledPreInterruptContext.get();\n\t\tassertNotNull(preInterruptContext);\n\t\tassertNotNull(preInterruptContext.getThreadToInterrupt());\n\t\tassertEquals(preInterruptContext.getThreadToInterrupt(), interruptedTestThread.get());\n\t}\n\n\t@Test\n\tvoid testCaseWithDeclaredInterruptCallbackThrowsException() {\n\t\tinterruptCallbackShallThrowException.set(true);\n\t\tEvents tests = executeTestsForClass(DefaultPreInterruptCallbackWithExplicitCallbackTestCase.class).testEvents();\n\t\ttests.failed().assertEventsMatchExactly(event(test(TC),\n\t\t\tfinishedWithFailure(instanceOf(TimeoutException.class), message(TIMEOUT_ERROR_MSG),\n\t\t\t\tsuppressed(0, instanceOf(InterruptedException.class)),\n\t\t\t\tsuppressed(1, instanceOf(IllegalStateException.class)))));\n\t\tassertTrue(interruptedTest.get());\n\t\tPreInterruptContext preInterruptContext = calledPreInterruptContext.get();\n\t\tassertNotNull(preInterruptContext);\n\t\tassertNotNull(preInterruptContext.getThreadToInterrupt());\n\t\tassertEquals(preInterruptContext.getThreadToInterrupt(), interruptedTestThread.get());\n\t}\n\n\tprivate static void assertTestHasTimedOut(Events tests) {\n\t\tassertTestHasTimedOut(tests, message(TIMEOUT_ERROR_MSG));\n\t}\n\n\tprivate static void assertTestHasTimedOut(Events tests, Condition<Throwable> messageCondition) {\n\t\tassertOneFailedTest(tests);\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(TC), finishedWithFailure(instanceOf(TimeoutException.class), messageCondition, //\n\t\t\t\tsuppressed(0, instanceOf(InterruptedException.class))//\n\t\t\t)));\n\t}\n\n\tprivate static void assertOneFailedTest(Events tests) {\n\t\ttests.assertStatistics(stats -> stats.started(1).succeeded(0).failed(1));\n\t}\n\n\tstatic class TestPreInterruptCallback implements PreInterruptCallback {\n\n\t\t@Override\n\t\tpublic void beforeThreadInterrupt(PreInterruptContext preInterruptContext, ExtensionContext extensionContext) {\n\t\t\tassertNotNull(extensionContext);\n\n\t\t\tcalledPreInterruptContext.set(preInterruptContext);\n\t\t\tif (interruptCallbackShallThrowException.get()) {\n\t\t\t\tthrow new IllegalStateException(\"Test-Ex\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class DefaultPreInterruptCallbackTimeoutOnMethodTestCase {\n\t\t@Test\n\t\tvoid test() throws InterruptedException {\n\t\t\ttry {\n\t\t\t\tThread.sleep(5_000);\n\t\t\t}\n\t\t\tcatch (InterruptedException ex) {\n\t\t\t\tinterruptedTest.set(true);\n\t\t\t\tinterruptedTestThread.set(Thread.currentThread());\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TestPreInterruptCallback.class)\n\tstatic class DefaultPreInterruptCallbackWithExplicitCallbackTestCase {\n\t\t@Test\n\t\t@Timeout(value = 1, unit = TimeUnit.MICROSECONDS)\n\t\tvoid test() throws InterruptedException {\n\t\t\ttry {\n\t\t\t\tThread.sleep(1000);\n\t\t\t}\n\t\t\tcatch (InterruptedException ex) {\n\t\t\t\tinterruptedTest.set(true);\n\t\t\t\tinterruptedTestThread.set(Thread.currentThread());\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TestPreInterruptCallback.class)\n\tstatic class DefaultPreInterruptCallbackWithExplicitCallbackWithSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 200, unit = TimeUnit.MILLISECONDS, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)\n\t\tvoid test() throws InterruptedException {\n\t\t\ttry {\n\t\t\t\tThread.sleep(2000);\n\t\t\t}\n\t\t\tcatch (InterruptedException ex) {\n\t\t\t\tinterruptedTest.set(true);\n\t\t\t\tinterruptedTestThread.set(Thread.currentThread());\n\t\t\t\tthrow ex;\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\ttestThreadExecutionDone.complete(null);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ProgrammaticExtensionRegistrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;\nimport static org.assertj.core.api.Assertions.allOf;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotatedFields;\nimport static org.junit.platform.commons.support.ReflectionSupport.makeAccessible;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.Extension;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.engine.execution.injection.sample.LongParameterResolver;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for programmatic extension registration\n * via {@link RegisterExtension @RegisterExtension} in the {@link JupiterTestEngine}.\n *\n * @since 5.1\n * @see OrderedProgrammaticExtensionRegistrationTests\n */\nclass ProgrammaticExtensionRegistrationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid clearCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid instanceLevel() {\n\t\tassertOneTestSucceeded(InstanceLevelExtensionRegistrationTestCase.class);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithInjectedExtension() {\n\t\tassertOneTestSucceeded(InstanceLevelExtensionRegistrationWithInjectedExtensionTestCase.class);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithTestInstancePerClassLifecycle() {\n\t\tassertOneTestSucceeded(InstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase.class);\n\t}\n\n\t@Test\n\tvoid classLevel() {\n\t\tassertOneTestSucceeded(ClassLevelExtensionRegistrationTestCase.class);\n\t}\n\n\t@Test\n\tvoid classLevelFromSuperclass() {\n\t\tassertOneTestSucceeded(SubClassLevelExtensionRegistrationTestCase.class);\n\t}\n\n\t@Test\n\tvoid classLevelFromInterface() {\n\t\tassertOneTestSucceeded(ExtensionRegistrationFromInterfaceTestCase.class);\n\t}\n\n\t@Test\n\tvoid instanceLevelWithInheritedExtensions() {\n\t\tClass<?> testClass = InstanceLevelExtensionRegistrationParentTestCase.class;\n\t\tString parent = testClass.getSimpleName();\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tparent + \" :: extension1 :: before test\", //\n\t\t\tparent + \" :: extension2 :: before test\" //\n\t\t);\n\n\t\tcallSequence.clear();\n\t\ttestClass = InstanceLevelExtensionRegistrationChildTestCase.class;\n\t\tString child = testClass.getSimpleName();\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tparent + \" :: extension1 :: before test\", //\n\t\t\tparent + \" :: extension2 :: before test\", //\n\t\t\tchild + \" :: extension2 :: before test\", //\n\t\t\tchild + \" :: extension3 :: before test\" //\n\t\t);\n\t}\n\n\t@Test\n\tvoid classLevelWithInheritedExtensions() {\n\t\tClass<?> testClass = ClassLevelExtensionRegistrationParentTestCase.class;\n\t\tString parent = testClass.getSimpleName();\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tparent + \" :: extension1 :: before test\", //\n\t\t\tparent + \" :: extension2 :: before test\" //\n\t\t);\n\n\t\tcallSequence.clear();\n\t\ttestClass = ClassLevelExtensionRegistrationChildTestCase.class;\n\t\tString child = testClass.getSimpleName();\n\t\tassertOneTestSucceeded(testClass);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tparent + \" :: extension1 :: before test\", //\n\t\t\tparent + \" :: extension2 :: before test\", //\n\t\t\tchild + \" :: extension2 :: before test\", //\n\t\t\tchild + \" :: extension3 :: before test\" //\n\t\t);\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid instanceLevelWithFieldThatDoesNotImplementAnExtensionApi() {\n\t\tassertOneTestSucceeded(InstanceLevelCustomExtensionApiTestCase.class);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tCustomExtensionImpl.class.getSimpleName() + \" :: before test\", //\n\t\t\tCustomExtensionImpl.class.getSimpleName() + \" :: doSomething()\" //\n\t\t);\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid classLevelWithFieldThatDoesNotImplementAnExtensionApi() {\n\t\tassertOneTestSucceeded(ClassLevelCustomExtensionApiTestCase.class);\n\t\tassertThat(callSequence).containsExactly( //\n\t\t\tCustomExtensionImpl.class.getSimpleName() + \" :: before test\", //\n\t\t\tCustomExtensionImpl.class.getSimpleName() + \" :: doSomething()\" //\n\t\t);\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid instanceLevelWithPrivateField() {\n\t\tClass<?> testClass = InstanceLevelExtensionRegistrationWithPrivateFieldTestCase.class;\n\t\texecuteTestsForClass(testClass).testEvents().assertStatistics(stats -> stats.succeeded(1));\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid classLevelWithPrivateField() {\n\t\tClass<?> testClass = ClassLevelExtensionRegistrationWithPrivateFieldTestCase.class;\n\t\texecuteTestsForClass(testClass).testEvents().assertStatistics(stats -> stats.succeeded(1));\n\t}\n\n\t@Test\n\tvoid instanceLevelWithNullField() {\n\t\tClass<?> testClass = InstanceLevelExtensionRegistrationWithNullFieldTestCase.class;\n\n\t\texecuteTestsForClass(testClass).testEvents().debug().assertThatEvents().haveExactly(1, finishedWithFailure(\n\t\t\tinstanceOf(PreconditionViolationException.class), message(expectedMessage(testClass, null))));\n\t}\n\n\t@Test\n\tvoid classLevelWithNullField() {\n\t\tClass<?> testClass = ClassLevelExtensionRegistrationWithNullFieldTestCase.class;\n\n\t\texecuteTestsForClass(testClass).containerEvents().assertThatEvents().haveExactly(1, finishedWithFailure(\n\t\t\tinstanceOf(PreconditionViolationException.class), message(expectedMessage(testClass, null))));\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid instanceLevelWithNonExtensionFieldValue() {\n\t\tClass<?> testClass = InstanceLevelExtensionRegistrationWithNonExtensionFieldValueTestCase.class;\n\n\t\texecuteTestsForClass(testClass).testEvents().assertThatEvents().haveExactly(1, finishedWithFailure(\n\t\t\tinstanceOf(PreconditionViolationException.class), message(expectedMessage(testClass, String.class))));\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid classLevelWithNonExtensionFieldValue() {\n\t\tClass<?> testClass = ClassLevelExtensionRegistrationWithNonExtensionFieldValueTestCase.class;\n\n\t\texecuteTestsForClass(testClass).containerEvents().assertThatEvents().haveExactly(1, finishedWithFailure(\n\t\t\tinstanceOf(PreconditionViolationException.class), message(expectedMessage(testClass, String.class))));\n\t}\n\n\tprivate String expectedMessage(Class<?> testClass, @Nullable Class<?> valueType) {\n\t\treturn \"Failed to register extension via @RegisterExtension field [\" + field(testClass)\n\t\t\t\t+ \"]: field value's type [\" + (valueType != null ? valueType.getName() : null) + \"] must implement an [\"\n\t\t\t\t+ Extension.class.getName() + \"] API.\";\n\t}\n\n\tprivate Field field(Class<?> testClass) {\n\t\ttry {\n\t\t\treturn testClass.getDeclaredField(\"extension\");\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new RuntimeException(ex);\n\t\t}\n\t}\n\n\t@Test\n\tvoid propagatesCheckedExceptionThrownDuringInitializationOfStaticField() {\n\t\tassertClassFails(ClassLevelExplosiveCheckedExceptionTestCase.class,\n\t\t\tallOf(instanceOf(ExceptionInInitializerError.class), cause(instanceOf(Exception.class), message(\"boom\"))));\n\t}\n\n\t@Test\n\tvoid propagatesUncheckedExceptionThrownDuringInitializationOfStaticField() {\n\t\tassertClassFails(ClassLevelExplosiveUncheckedExceptionTestCase.class, allOf(\n\t\t\tinstanceOf(ExceptionInInitializerError.class), cause(instanceOf(RuntimeException.class), message(\"boom\"))));\n\t}\n\n\t@Test\n\tvoid propagatesErrorThrownDuringInitializationOfStaticField() {\n\t\tassertClassFails(ClassLevelExplosiveErrorTestCase.class, allOf(instanceOf(Error.class), message(\"boom\")));\n\t}\n\n\t@Test\n\tvoid propagatesCheckedExceptionThrownDuringInitializationOfInstanceField() {\n\t\tassertTestFails(InstanceLevelExplosiveCheckedExceptionTestCase.class,\n\t\t\tallOf(instanceOf(Exception.class), message(\"boom\")));\n\t}\n\n\t@Test\n\tvoid propagatesUncheckedExceptionThrownDuringInitializationOfInstanceField() {\n\t\tassertTestFails(InstanceLevelExplosiveUncheckedExceptionTestCase.class,\n\t\t\tallOf(instanceOf(RuntimeException.class), message(\"boom\")));\n\t}\n\n\t@Test\n\tvoid propagatesErrorThrownDuringInitializationOfInstanceField() {\n\t\tassertTestFails(InstanceLevelExplosiveErrorTestCase.class, allOf(instanceOf(Error.class), message(\"boom\")));\n\t}\n\n\t@Test\n\tvoid storesExtensionInRegistryOfNestedTestMethods() {\n\t\tvar results = executeTestsForClass(TwoNestedClassesTestCase.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.succeeded(4));\n\t}\n\n\tprivate void assertClassFails(Class<?> testClass, Condition<Throwable> causeCondition) {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\t\texecutionResults.containerEvents().assertThatEvents().haveExactly(1, finishedWithFailure(causeCondition));\n\t}\n\n\tprivate void assertTestFails(Class<?> testClass, Condition<Throwable> causeCondition) {\n\t\texecuteTestsForClass(testClass).testEvents().assertThatEvents().haveExactly(1,\n\t\t\tfinishedWithFailure(causeCondition));\n\t}\n\n\tprivate void assertOneTestSucceeded(Class<?> testClass) {\n\t\texecuteTestsForClass(testClass).testEvents().assertStatistics(\n\t\t\tstats -> stats.started(1).succeeded(1).skipped(0).aborted(0).failed(0));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static void assertWisdom(CrystalBall crystalBall, String wisdom, String useCase) {\n\t\tassertNotNull(crystalBall, useCase);\n\t\tassertEquals(\"Outlook good\", wisdom, useCase);\n\t}\n\n\tstatic class InstanceLevelExtensionRegistrationTestCase {\n\n\t\t@RegisterExtension\n\t\tfinal CrystalBall crystalBall = new CrystalBall(\"Outlook good\");\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterEach\");\n\t\t}\n\n\t}\n\n\t@ExtendWith(ExtensionInjector.class)\n\t@NullUnmarked\n\tstatic class InstanceLevelExtensionRegistrationWithInjectedExtensionTestCase {\n\n\t\t@RegisterExtension\n\t\tprotected CrystalBall crystalBall; // Injected by ExtensionInjector.\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterEach\");\n\t\t}\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class InstanceLevelExtensionRegistrationWithTestInstancePerClassLifecycleTestCase {\n\n\t\t@RegisterExtension\n\t\tfinal CrystalBall crystalBall = new CrystalBall(\"Outlook good\");\n\n\t\t@BeforeAll\n\t\tvoid beforeAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterAll\");\n\t\t}\n\n\t}\n\n\tstatic class ClassLevelExtensionRegistrationTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic final CrystalBall crystalBall = new CrystalBall(\"Outlook good\");\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@Test\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterAll\");\n\t\t}\n\n\t}\n\n\tstatic class SubClassLevelExtensionRegistrationTestCase extends ClassLevelExtensionRegistrationTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@Test\n\t\t@Override\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"Overridden @Test\");\n\t\t}\n\n\t}\n\n\tinterface ClassLevelExtensionRegistrationInterface {\n\n\t\t@RegisterExtension\n\t\tCrystalBall crystalBall = new CrystalBall(\"Outlook good\");\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tdefault void beforeEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@BeforeEach\");\n\t\t}\n\n\t\t@AfterEach\n\t\tdefault void afterEach(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@AfterAll\");\n\t\t}\n\n\t}\n\n\tstatic class ExtensionRegistrationFromInterfaceTestCase implements ClassLevelExtensionRegistrationInterface {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@Test\n\t\tvoid test(String wisdom) {\n\t\t\tassertWisdom(crystalBall, wisdom, \"@Test\");\n\t\t}\n\n\t}\n\n\tprivate record CrystalBall(String wisdom) implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == String.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn this.wisdom;\n\t\t}\n\n\t}\n\n\tstatic class ClassLevelExtensionRegistrationParentTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension extension1 = new BeforeEachExtension(1);\n\n\t\t@RegisterExtension\n\t\tstatic Extension extension2 = new BeforeEachExtension(2);\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\tstatic class ClassLevelExtensionRegistrationChildTestCase extends ClassLevelExtensionRegistrationParentTestCase {\n\n\t\t// \"Hides\" ClassLevelExtensionRegistrationParentTestCase.extension2 in legacy mode\n\t\t@RegisterExtension\n\t\tstatic Extension extension2 = new BeforeEachExtension(2);\n\n\t\t@RegisterExtension\n\t\tstatic Extension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tstatic class InstanceLevelExtensionRegistrationParentTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension extension1 = new BeforeEachExtension(1);\n\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\tstatic class InstanceLevelExtensionRegistrationChildTestCase\n\t\t\textends InstanceLevelExtensionRegistrationParentTestCase {\n\n\t\t// \"Hides\" InstanceLevelExtensionRegistrationParentTestCase.extension2 in legacy mode\n\t\t@RegisterExtension\n\t\tExtension extension2 = new BeforeEachExtension(2);\n\n\t\t@RegisterExtension\n\t\tExtension extension3 = new BeforeEachExtension(3);\n\n\t}\n\n\tprivate static class BeforeEachExtension implements BeforeEachCallback {\n\n\t\tprivate final String prefix;\n\n\t\tBeforeEachExtension(int id) {\n\t\t\tClass<?> callerClass = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();\n\t\t\tthis.prefix = callerClass.getSimpleName() + \" :: extension\" + id + \" :: before \";\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) {\n\t\t\tcallSequence.add(this.prefix + context.getRequiredTestMethod().getName());\n\t\t}\n\n\t}\n\n\t/**\n\t * This interface intentionally does not implement a supported {@link Extension} API.\n\t */\n\tinterface CustomExtension {\n\n\t\tvoid doSomething();\n\n\t}\n\n\tstatic class CustomExtensionImpl implements CustomExtension, BeforeEachCallback {\n\n\t\t@Override\n\t\tpublic void doSomething() {\n\t\t\tcallSequence.add(getClass().getSimpleName() + \" :: doSomething()\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext context) throws Exception {\n\t\t\tcallSequence.add(getClass().getSimpleName() + \" :: before \" + context.getRequiredTestMethod().getName());\n\t\t}\n\t}\n\n\tstatic class InstanceLevelCustomExtensionApiTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tCustomExtension extension = new CustomExtensionImpl();\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tthis.extension.doSomething();\n\t\t}\n\n\t}\n\n\tstatic class ClassLevelCustomExtensionApiTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tstatic CustomExtension extension = new CustomExtensionImpl();\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\textension.doSomething();\n\t\t}\n\n\t}\n\n\tstatic class AbstractTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n\tstatic class InstanceLevelExtensionRegistrationWithPrivateFieldTestCase extends AbstractTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tprivate Extension extension = new Extension() {\n\t\t};\n\n\t}\n\n\tstatic class ClassLevelExtensionRegistrationWithPrivateFieldTestCase extends AbstractTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tprivate static Extension extension = new Extension() {\n\t\t};\n\n\t}\n\n\t@NullUnmarked\n\tstatic class InstanceLevelExtensionRegistrationWithNullFieldTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension extension;\n\n\t}\n\n\t@NullUnmarked\n\tstatic class ClassLevelExtensionRegistrationWithNullFieldTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension extension;\n\n\t}\n\n\tstatic class InstanceLevelExtensionRegistrationWithNonExtensionFieldValueTestCase extends AbstractTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tObject extension = \"not an extension type\";\n\n\t}\n\n\tstatic class ClassLevelExtensionRegistrationWithNonExtensionFieldValueTestCase extends AbstractTestCase {\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RegisterExtension\n\t\tstatic Object extension = \"not an extension type\";\n\n\t}\n\n\tstatic class ClassLevelExplosiveCheckedExceptionTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension field = new ExplosiveExtension(new Exception(\"boom\"));\n\n\t}\n\n\tstatic class ClassLevelExplosiveUncheckedExceptionTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension field = new ExplosiveExtension(new RuntimeException(\"boom\"));\n\n\t}\n\n\tstatic class ClassLevelExplosiveErrorTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic Extension field = new ExplosiveExtension(new Error(\"boom\"));\n\n\t}\n\n\tstatic class InstanceLevelExplosiveCheckedExceptionTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension field = new ExplosiveExtension(new Exception(\"boom\"));\n\n\t}\n\n\tstatic class InstanceLevelExplosiveUncheckedExceptionTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension field = new ExplosiveExtension(new RuntimeException(\"boom\"));\n\n\t}\n\n\tstatic class InstanceLevelExplosiveErrorTestCase extends AbstractTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension field = new ExplosiveExtension(new Error(\"boom\"));\n\n\t}\n\n\tstatic class ExplosiveExtension implements Extension {\n\n\t\tExplosiveExtension(Throwable t) {\n\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t}\n\n\t}\n\n\t/**\n\t * Mimics a dependency injection framework such as Spring, Guice, CDI, etc.,\n\t * where the instance of the extension registered via\n\t * {@link RegisterExtension @RegisterExtension} is managed by the DI\n\t * framework and injected into the test instance.\n\t */\n\tprivate static class ExtensionInjector implements TestInstancePostProcessor {\n\n\t\tprivate static final Predicate<Field> isCrystalBall = field -> CrystalBall.class.isAssignableFrom(\n\t\t\tfield.getType());\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\t// @formatter:off\n\t\t\tfindAnnotatedFields(testInstance.getClass(), RegisterExtension.class, isCrystalBall).stream()\n\t\t\t\t.findFirst()\n\t\t\t\t.ifPresent(field -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmakeAccessible(field).set(testInstance, new CrystalBall(\"Outlook good\"));\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Throwable t) {\n\t\t\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\tstatic class TwoNestedClassesTestCase {\n\n\t\t@RegisterExtension\n\t\tExtension extension = new LongParameterResolver();\n\n\t\t@Nested\n\t\tclass A {\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Test\n\t\t\tvoid first(Long n) {\n\t\t\t\tassertEquals(42L, n);\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Test\n\t\t\tvoid second(Long n) {\n\t\t\t\tassertEquals(42L, n);\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\tclass B {\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Test\n\t\t\tvoid first(Long n) {\n\t\t\t\tassertEquals(42L, n);\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t\t@Test\n\t\t\tvoid second(Long n) {\n\t\t\t\tassertEquals(42L, n);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/RepeatedTestTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.reflect.Method;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.RepetitionInfo;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for {@link RepeatedTest @RepeatedTest} and supporting\n * infrastructure.\n *\n * @since 5.0\n */\nclass RepeatedTestTests extends AbstractJupiterTestEngineTests {\n\n\t@RepeatedTest(1)\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayName(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"repetition 1 of 1\");\n\t}\n\n\t@Test\n\tvoid customDisplayNameWithBlankName() {\n\t\texecuteTests(request -> request //\n\t\t\t\t.selectors(selectClass(BlankDisplayNameTestCase.class)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.ERROR.name())) //\n\t\t\t\t\t\t.testEvents() //\n\t\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@RepeatedTest(value = 1, name = \"{displayName}\")\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayNameWithPatternIncludingDisplayName(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"Repeat!\");\n\t}\n\n\t@RepeatedTest(value = 1, name = \"#{currentRepetition}\")\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayNameWithPatternIncludingCurrentRepetition(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"#1\");\n\t}\n\n\t@RepeatedTest(value = 1, name = \"Repetition #{currentRepetition} for {displayName}\")\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayNameWithPatternIncludingDisplayNameAndCurrentRepetition(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"Repetition #1 for Repeat!\");\n\t}\n\n\t@RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME)\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayNameWithPredefinedLongPattern(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"Repeat! :: repetition 1 of 1\");\n\t}\n\n\t@RepeatedTest(value = 1, name = \"{displayName} {currentRepetition}/{totalRepetitions}\")\n\t@DisplayName(\"Repeat!\")\n\tvoid customDisplayNameWithPatternIncludingDisplayNameCurrentRepetitionAndTotalRepetitions(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"Repeat! 1/1\");\n\t}\n\n\t@RepeatedTest(value = 1, name = \"Repetition #{currentRepetition} for {displayName}\")\n\tvoid defaultDisplayNameWithPatternIncludingDisplayNameAndCurrentRepetition(TestInfo testInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\n\t\t\t\"Repetition #1 for defaultDisplayNameWithPatternIncludingDisplayNameAndCurrentRepetition(TestInfo)\");\n\t}\n\n\t@RepeatedTest(value = 5, name = \"{displayName}\")\n\tvoid injectRepetitionInfo(TestInfo testInfo, RepetitionInfo repetitionInfo) {\n\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"injectRepetitionInfo(TestInfo, RepetitionInfo)\");\n\t\tassertThat(repetitionInfo.getCurrentRepetition()).isBetween(1, 5);\n\t\tassertThat(repetitionInfo.getTotalRepetitions()).isEqualTo(5);\n\t}\n\n\t@Nested\n\tclass LifecycleMethodTests {\n\n\t\tprivate static int fortyTwo = 0;\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\tassertEquals(42, fortyTwo);\n\t\t}\n\n\t\t// Can be injected into test class constructors if the test class only has @RepeatedTest methods\n\t\tLifecycleMethodTests(RepetitionInfo repetitionInfo) {\n\t\t\tassertNotNull(repetitionInfo);\n\t\t}\n\n\t\t@BeforeEach\n\t\t@AfterEach\n\t\tvoid beforeAndAfterEach(TestInfo testInfo, RepetitionInfo repetitionInfo) {\n\t\t\tswitch (testInfo.getTestMethod().orElseThrow().getName()) {\n\t\t\t\tcase \"repeatedOnce\" -> {\n\t\t\t\t\tassertThat(repetitionInfo.getCurrentRepetition()).isEqualTo(1);\n\t\t\t\t\tassertThat(repetitionInfo.getTotalRepetitions()).isEqualTo(1);\n\t\t\t\t}\n\t\t\t\tcase \"repeatedFortyTwoTimes\" -> {\n\t\t\t\t\tassertThat(repetitionInfo.getCurrentRepetition()).isBetween(1, 42);\n\t\t\t\t\tassertThat(repetitionInfo.getTotalRepetitions()).isEqualTo(42);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t@RepeatedTest(1)\n\t\tvoid repeatedOnce(TestInfo testInfo) {\n\t\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"repetition 1 of 1\");\n\t\t}\n\n\t\t@RepeatedTest(42)\n\t\tvoid repeatedFortyTwoTimes(TestInfo testInfo) {\n\t\t\tassertThat(testInfo.getDisplayName()).matches(\"repetition \\\\d+ of 42\");\n\t\t\tfortyTwo++;\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass FailureTests {\n\n\t\tprivate static final Condition<Throwable> emptyPattern = message(\n\t\t\tvalue -> value.contains(\"must be declared with a non-empty name\"));\n\n\t\tprivate static final Condition<Throwable> invalidRepetitionCount = message(\n\t\t\tvalue -> value.contains(\"must be declared with a positive 'value'\"));\n\n\t\tprivate static final Condition<Throwable> invalidThreshold = message(value -> value.endsWith(\n\t\t\t\"must declare a 'failureThreshold' greater than zero and less than the total number of repetitions [10].\"));\n\n\t\t@BeforeEach\n\t\tvoid resetCounter() {\n\t\t\tTestCase.counter.set(0);\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForEmptyPattern() {\n\t\t\texecuteTest(\"testWithEmptyPattern\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"testWithEmptyPattern()\"), //\n\t\t\t\t\t\tfinishedWithFailure(emptyPattern)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForBlankPattern() {\n\t\t\texecuteTest(\"testWithBlankPattern\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"testWithBlankPattern()\"), //\n\t\t\t\t\t\tfinishedWithFailure(emptyPattern)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForNegativeRepeatCount() {\n\t\t\texecuteTest(\"negativeRepeatCount\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"negativeRepeatCount()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidRepetitionCount)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForZeroRepeatCount() {\n\t\t\texecuteTest(\"zeroRepeatCount\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"zeroRepeatCount()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidRepetitionCount)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForFailureThresholdSetToNegativeValue() {\n\t\t\texecuteTest(\"failureThresholdSetToNegativeValue\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"failureThresholdSetToNegativeValue()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidThreshold)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForFailureThresholdSetToZero() {\n\t\t\texecuteTest(\"failureThresholdSetToZero\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"failureThresholdSetToZero()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidThreshold)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForFailureThresholdGreaterThanRepetitionCount() {\n\t\t\texecuteTest(\"failureThresholdGreaterThanRepetitionCount\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"failureThresholdGreaterThanRepetitionCount()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidThreshold)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsContainerForFailureThresholdEqualToRepetitionCount() {\n\t\t\texecuteTest(\"failureThresholdEqualToRepetitionCount\").assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(), displayName(\"failureThresholdEqualToRepetitionCount()\"), //\n\t\t\t\t\t\tfinishedWithFailure(invalidThreshold)));\n\t\t}\n\n\t\t@Test\n\t\tvoid failureThresholdEqualToRepetitionCountMinusOne() {\n\t\t\tString methodName = \"failureThresholdEqualToRepetitionCountMinusOne\";\n\t\t\t// @formatter:off\n\t\t\texecuteTest(methodName).assertEventsMatchLooselyInOrder(\n\t\t\t\tevent(container(methodName), started()),\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#3\"), skippedWithReason(\"Failure threshold [2] exceeded\")),\n\t\t\t\tevent(container(methodName), finishedSuccessfully()));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid failureThreshold1() {\n\t\t\tString methodName = \"failureThreshold1\";\n\t\t\t// @formatter:off\n\t\t\texecuteTest(methodName).assertEventsMatchLooselyInOrder(\n\t\t\t\tevent(container(methodName), started()),\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#3\"), skippedWithReason(\"Failure threshold [1] exceeded\")),\n\t\t\t\tevent(container(methodName), finishedSuccessfully()));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid failureThreshold2() {\n\t\t\tString methodName = \"failureThreshold2\";\n\t\t\t// @formatter:off\n\t\t\texecuteTest(methodName).assertEventsMatchLooselyInOrder(\n\t\t\t\tevent(container(methodName), started()),\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#3\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#4\"), skippedWithReason(\"Failure threshold [2] exceeded\")),\n\t\t\t\tevent(container(methodName), finishedSuccessfully()));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid failureThreshold3() {\n\t\t\tString methodName = \"failureThreshold3\";\n\t\t\t// @formatter:off\n\t\t\texecuteTest(methodName).assertEventsMatchLooselyInOrder(\n\t\t\t\tevent(container(methodName), started()),\n\t\t\t\tevent(test(\"test-template-invocation:#1\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"test-template-invocation:#2\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#3\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"test-template-invocation:#4\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#5\"), finishedSuccessfully()),\n\t\t\t\tevent(test(\"test-template-invocation:#6\"), finishedWithFailure(message(\"Boom!\"))),\n\t\t\t\tevent(test(\"test-template-invocation:#7\"), skippedWithReason(\"Failure threshold [3] exceeded\")),\n\t\t\t\tevent(test(\"test-template-invocation:#8\"), skippedWithReason(\"Failure threshold [3] exceeded\")),\n\t\t\t\tevent(container(methodName), finishedSuccessfully()));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EnumSource(ParallelExecutorServiceType.class)\n\t\tvoid failureThresholdWithConcurrentExecution(ParallelExecutorServiceType executorServiceType) {\n\t\t\tClass<TestCase> testClass = TestCase.class;\n\t\t\tString methodName = \"failureThresholdWithConcurrentExecution\";\n\t\t\tMethod method = ReflectionSupport.findMethod(testClass, methodName).orElseThrow();\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(testClass, method))//\n\t\t\t\t\t.configurationParameter(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, \"true\")//\n\t\t\t\t\t.configurationParameter(PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME, executorServiceType.name()) //\n\t\t\t\t\t.configurationParameter(DEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\")//\n\t\t\t\t\t.configurationParameter(PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, \"fixed\")//\n\t\t\t\t\t.configurationParameter(PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, \"4\")//\n\t\t\t\t\t.build();\n\n\t\t\tEvents tests = executeTests(request).testEvents();\n\n\t\t\t// There are 20 repetitions/tests in total.\n\t\t\tassertThat(tests.dynamicallyRegistered().count()).as(\"registered\").isEqualTo(20);\n\t\t\tassertThat(tests.started().count() + tests.skipped().count()).as(\"started or skipped\").isEqualTo(20);\n\t\t\t// Would be 3 successful tests without parallel execution, but with race conditions\n\t\t\t// and multiple threads we may encounter more; and yet we still should not\n\t\t\t// encounter too many.\n\t\t\tassertThat(tests.succeeded().count()).as(\"succeeded\").isBetween(3L, 10L);\n\t\t\t// Would be 3 failed tests without parallel execution, but with race conditions\n\t\t\t// and multiple threads we may encounter more.\n\t\t\tassertThat(tests.failed().count()).as(\"failed\").isGreaterThanOrEqualTo(3);\n\t\t\t// Would be 14 skipped tests without parallel execution, but with race conditions\n\t\t\t// and multiple threads we may not encounter many.\n\t\t\tassertThat(tests.skipped().count()).as(\"skipped\").isGreaterThan(0);\n\t\t}\n\n\t\tprivate Events executeTest(String methodName) {\n\t\t\tClass<TestCase> testClass = TestCase.class;\n\t\t\tMethod method = ReflectionSupport.findMethod(testClass, methodName).orElseThrow();\n\t\t\treturn executeTests(selectMethod(testClass, method)).allEvents();\n\t\t}\n\n\t}\n\n\tstatic class TestCase {\n\n\t\tstatic final AtomicInteger counter = new AtomicInteger();\n\n\t\t@RepeatedTest(value = 1, name = \"\")\n\t\tvoid testWithEmptyPattern() {\n\t\t}\n\n\t\t@RepeatedTest(value = 1, name = \" \\t  \")\n\t\tvoid testWithBlankPattern() {\n\t\t}\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RepeatedTest(-99)\n\t\tvoid negativeRepeatCount() {\n\t\t}\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@RepeatedTest(0)\n\t\tvoid zeroRepeatCount() {\n\t\t}\n\n\t\t@RepeatedTest(value = 10, failureThreshold = -1)\n\t\tvoid failureThresholdSetToNegativeValue() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t\t@RepeatedTest(value = 10, failureThreshold = 0)\n\t\tvoid failureThresholdSetToZero() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t\t@RepeatedTest(value = 10, failureThreshold = 11)\n\t\tvoid failureThresholdGreaterThanRepetitionCount() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t\t@RepeatedTest(value = 10, failureThreshold = 10)\n\t\tvoid failureThresholdEqualToRepetitionCount() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t\t@RepeatedTest(value = 3, failureThreshold = 2)\n\t\tvoid failureThresholdEqualToRepetitionCountMinusOne() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t\t@RepeatedTest(value = 3, failureThreshold = 1)\n\t\tvoid failureThreshold1() {\n\t\t\tint count = counter.incrementAndGet();\n\t\t\tif (count > 1) {\n\t\t\t\tfail(\"Boom!\");\n\t\t\t}\n\t\t}\n\n\t\t@RepeatedTest(value = 4, failureThreshold = 2)\n\t\tvoid failureThreshold2() {\n\t\t\tint count = counter.incrementAndGet();\n\t\t\tif (count > 1) {\n\t\t\t\tfail(\"Boom!\");\n\t\t\t}\n\t\t}\n\n\t\t@RepeatedTest(value = 8, failureThreshold = 3)\n\t\tvoid failureThreshold3() {\n\t\t\tint count = counter.incrementAndGet();\n\t\t\tif ((count > 1) && (count % 2 == 0)) {\n\t\t\t\tfail(\"Boom!\");\n\t\t\t}\n\t\t}\n\n\t\t@RepeatedTest(value = 20, failureThreshold = 3)\n\t\tvoid failureThresholdWithConcurrentExecution() {\n\t\t\tint count = counter.incrementAndGet();\n\t\t\tif ((count > 1) && (count % 2 == 0)) {\n\t\t\t\tfail(\"Boom!\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class BlankDisplayNameTestCase {\n\n\t\t@RepeatedTest(1)\n\t\t@DisplayName(\"   \\t \")\n\t\tvoid test(TestInfo testInfo) {\n\t\t\tassertThat(testInfo.getDisplayName()).isEqualTo(\"repetition 1 of 1\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SameThreadTimeoutInvocationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.NANOSECONDS;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeoutException;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\n\n/**\n * @since 5.5\n */\nclass SameThreadTimeoutInvocationTests {\n\n\t@Test\n\tvoid resetsInterruptFlag() {\n\t\tvar exception = assertThrows(TimeoutException.class, () -> withExecutor(executor -> {\n\t\t\tvar delegate = new EventuallyInterruptibleInvocation();\n\t\t\tvar duration = new TimeoutDuration(1, NANOSECONDS);\n\t\t\tvar timeoutInvocation = new SameThreadTimeoutInvocation<>(delegate, duration, executor, () -> \"execution\",\n\t\t\t\tPreInterruptCallbackInvocation.NOOP);\n\t\t\ttimeoutInvocation.proceed();\n\t\t}));\n\t\tassertFalse(Thread.currentThread().isInterrupted());\n\t\tassertThat(exception).hasMessage(\"execution timed out after 1 nanosecond\");\n\t}\n\n\tprivate void withExecutor(ThrowingConsumer<ScheduledExecutorService> consumer) throws Throwable {\n\t\tScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();\n\t\ttry {\n\t\t\tconsumer.accept(executor);\n\t\t}\n\t\tfinally {\n\t\t\texecutor.shutdown();\n\t\t\tassertTrue(executor.awaitTermination(5, SECONDS));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/SeparateThreadTimeoutInvocationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\n\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout.ThreadMode;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.engine.execution.NamespaceAwareStore;\nimport org.junit.jupiter.engine.extension.TimeoutInvocationFactory.TimeoutInvocationParameters;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * @since 5.9\n */\n@DisplayName(\"SeparateThreadTimeoutInvocation\")\nclass SeparateThreadTimeoutInvocationTests {\n\n\tprivate static final long PREEMPTIVE_TIMEOUT_MILLIS = WINDOWS.isCurrentOs() ? 1000 : 100;\n\n\t@Test\n\t@DisplayName(\"throws timeout exception when timeout duration is exceeded\")\n\tvoid throwsTimeoutException() {\n\t\tAtomicReference<String> threadName = new AtomicReference<>();\n\t\tvar invocation = aSeparateThreadInvocation(() -> {\n\t\t\tthreadName.set(Thread.currentThread().getName());\n\t\t\tThread.sleep(PREEMPTIVE_TIMEOUT_MILLIS * 2);\n\t\t\treturn \"ignored\";\n\t\t});\n\n\t\tassertThatThrownBy(invocation::proceed) //\n\t\t\t\t.hasMessage(\"method() timed out after \" + PREEMPTIVE_TIMEOUT_MILLIS + \" milliseconds\") //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasRootCauseMessage(\"Execution timed out in thread \" + threadName.get());\n\t}\n\n\t@Test\n\t@DisplayName(\"executes invocation in a separate thread\")\n\tvoid runsInvocationUsingSeparateThread() throws Throwable {\n\t\tvar invocationThreadName = aSeparateThreadInvocation(() -> Thread.currentThread().getName()).proceed();\n\t\tassertThat(invocationThreadName).isNotEqualTo(Thread.currentThread().getName());\n\t}\n\n\t@Test\n\t@DisplayName(\"throws invocation exception\")\n\tvoid shouldThrowInvocationException() {\n\t\tvar invocation = aSeparateThreadInvocation(() -> {\n\t\t\tthrow new RuntimeException(\"hi!\");\n\t\t});\n\t\tassertThatThrownBy(invocation::proceed) //\n\t\t\t\t.isInstanceOf(RuntimeException.class) //\n\t\t\t\t.hasMessage(\"hi!\");\n\t}\n\n\tprivate static <T extends @Nullable Object> SeparateThreadTimeoutInvocation<T> aSeparateThreadInvocation(\n\t\t\tInvocation<T> invocation) {\n\t\tvar namespace = ExtensionContext.Namespace.create(SeparateThreadTimeoutInvocationTests.class);\n\t\tvar store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null),\n\t\t\tNamespace.create(namespace.getParts()));\n\t\tvar parameters = new TimeoutInvocationParameters<>(invocation,\n\t\t\tnew TimeoutDuration(PREEMPTIVE_TIMEOUT_MILLIS, MILLISECONDS), () -> \"method()\",\n\t\t\tPreInterruptCallbackInvocation.NOOP);\n\t\treturn (SeparateThreadTimeoutInvocation<T>) new TimeoutInvocationFactory(store) //\n\t\t\t\t.create(ThreadMode.SEPARATE_THREAD, parameters);\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/ServiceLoaderExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Demo extension for auto-detection of extensions loaded via Java's\n * {@link java.util.ServiceLoader} mechanism.\n *\n * @since 5.0\n */\npublic class ServiceLoaderExtension implements BeforeAllCallback {\n\n\t@Override\n\tpublic void beforeAll(ExtensionContext context) {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryCleanupTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.nio.file.Files.deleteIfExists;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.condition.OS.WINDOWS;\nimport static org.junit.jupiter.api.io.CleanupMode.ALWAYS;\nimport static org.junit.jupiter.api.io.CleanupMode.NEVER;\nimport static org.junit.jupiter.api.io.CleanupMode.ON_SUCCESS;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.condition.EnabledOnOs;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.CleanupMode;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * Test that {@linkplain TempDir temporary directories} are not deleted with\n * {@link CleanupMode#NEVER}, are deleted with {@link CleanupMode#ON_SUCCESS}\n * but only if the test passes, and are always deleted with {@link CleanupMode#ALWAYS}.\n *\n * @see CleanupMode\n * @see TempDir\n * @since 5.9\n */\nclass TempDirectoryCleanupTests extends AbstractJupiterTestEngineTests {\n\n\t@Nested\n\t@NullUnmarked\n\tclass TempDirFieldTests {\n\n\t\tprivate static Path defaultFieldDir;\n\t\tprivate static Path neverFieldDir;\n\t\tprivate static Path alwaysFieldDir;\n\t\tprivate static Path onSuccessFailingFieldDir;\n\t\tprivate static Path onSuccessPassingFieldDir;\n\t\tprivate static Path onSuccessPassingParameterDir;\n\n\t\t/**\n\t\t * Ensure the cleanup mode defaults to ALWAYS for fields.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeDefaultField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(DefaultFieldCase.class, \"testDefaultField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(defaultFieldDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that a custom, global cleanup mode is used for fields.\n\t\t * <p/>\n\t\t * Expect the TempDir NOT to be cleaned up if set to NEVER.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeCustomDefaultField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.configurationParameter(DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME, \"never\")//\n\t\t\t\t\t.selectors(selectMethod(DefaultFieldCase.class, \"testDefaultField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(defaultFieldDir).exists();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that NEVER cleanup modes are obeyed for fields.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeNeverField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(NeverFieldCase.class, \"testNeverField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(neverFieldDir).exists();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ALWAYS cleanup modes are obeyed for fields.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeAlwaysField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(AlwaysFieldCase.class, \"testAlwaysField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(alwaysFieldDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for passing field tests.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessPassingField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(OnSuccessPassingFieldCase.class, \"testOnSuccessPassingField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(onSuccessPassingFieldDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for failing field tests.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(OnSuccessFailingFieldCase.class, \"testOnSuccessFailingField\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(onSuccessFailingFieldDir).exists();\n\t\t}\n\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingThenPassingField() {\n\t\t\texecuteTests(selectClasses(OnSuccessFailingFieldCase.class, OnSuccessPassingFieldCase.class));\n\n\t\t\tassertThat(onSuccessFailingFieldDir).exists();\n\t\t\tassertThat(onSuccessPassingFieldDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for static fields when tests are failing.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingStaticField() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectClass(OnSuccessFailingStaticFieldCase.class))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(onSuccessFailingFieldDir).exists();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for static fields when nested tests are failing.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingStaticFieldWithNesting() {\n\t\t\texecuteTestsForClass(OnSuccessFailingStaticFieldWithNestingCase.class);\n\n\t\t\tassertThat(onSuccessFailingFieldDir).exists();\n\t\t\tassertThat(onSuccessPassingParameterDir).doesNotExist();\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid deleteTempDirs() throws IOException {\n\t\t\tdeleteIfNotNullAndExists(defaultFieldDir);\n\t\t\tdeleteIfNotNullAndExists(neverFieldDir);\n\t\t\tdeleteIfNotNullAndExists(alwaysFieldDir);\n\t\t\tdeleteIfNotNullAndExists(onSuccessFailingFieldDir);\n\t\t\tdeleteIfNotNullAndExists(onSuccessPassingFieldDir);\n\t\t\tdeleteIfNotNullAndExists(onSuccessPassingParameterDir);\n\t\t}\n\n\t\tstatic void deleteIfNotNullAndExists(Path dir) throws IOException {\n\t\t\tif (dir != null) {\n\t\t\t\tdeleteIfExists(dir);\n\t\t\t}\n\t\t}\n\n\t\t// -------------------------------------------------------------------\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class DefaultFieldCase {\n\n\t\t\t@TempDir\n\t\t\tPath defaultFieldDir;\n\n\t\t\t@Test\n\t\t\tvoid testDefaultField() {\n\t\t\t\tTempDirFieldTests.defaultFieldDir = defaultFieldDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class NeverFieldCase {\n\n\t\t\t@TempDir(cleanup = NEVER)\n\t\t\tPath neverFieldDir;\n\n\t\t\t@Test\n\t\t\tvoid testNeverField() {\n\t\t\t\tTempDirFieldTests.neverFieldDir = neverFieldDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class AlwaysFieldCase {\n\n\t\t\t@TempDir(cleanup = ALWAYS)\n\t\t\tPath alwaysFieldDir;\n\n\t\t\t@Test\n\t\t\tvoid testAlwaysField() {\n\t\t\t\tTempDirFieldTests.alwaysFieldDir = alwaysFieldDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OnSuccessPassingFieldCase {\n\n\t\t\t@TempDir(cleanup = ON_SUCCESS)\n\t\t\tPath onSuccessPassingFieldDir;\n\n\t\t\t@Test\n\t\t\tvoid testOnSuccessPassingField() {\n\t\t\t\tTempDirFieldTests.onSuccessPassingFieldDir = onSuccessPassingFieldDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OnSuccessFailingFieldCase {\n\n\t\t\t@TempDir(cleanup = ON_SUCCESS)\n\t\t\tPath onSuccessFailingFieldDir;\n\n\t\t\t@Test\n\t\t\tvoid testOnSuccessFailingField() {\n\t\t\t\tTempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir;\n\t\t\t\tfail();\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\t\tstatic class OnSuccessFailingStaticFieldCase {\n\n\t\t\t@TempDir(cleanup = ON_SUCCESS)\n\t\t\tstatic Path onSuccessFailingFieldDir;\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\tvoid failing() {\n\t\t\t\tTempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir;\n\t\t\t\tfail();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\tvoid passing() {\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OnSuccessFailingStaticFieldWithNestingCase {\n\n\t\t\t@TempDir(cleanup = ON_SUCCESS)\n\t\t\tstatic Path onSuccessFailingFieldDir;\n\n\t\t\t@Nested\n\t\t\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\t\t\tclass NestedTestCase {\n\n\t\t\t\t@Test\n\t\t\t\t@Order(1)\n\t\t\t\tvoid failingTest() {\n\t\t\t\t\tTempDirFieldTests.onSuccessFailingFieldDir = onSuccessFailingFieldDir;\n\t\t\t\t\tfail();\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\t@Order(2)\n\t\t\t\tvoid passingTest(@TempDir(cleanup = ON_SUCCESS) Path tempDir) {\n\t\t\t\t\tTempDirFieldTests.onSuccessPassingParameterDir = tempDir;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\t@NullUnmarked\n\tclass TempDirParameterTests {\n\n\t\tprivate static Path defaultParameterDir;\n\t\tprivate static Path neverParameterDir;\n\t\tprivate static Path alwaysParameterDir;\n\t\tprivate static Path onSuccessFailingParameterDir;\n\t\tprivate static Path onSuccessPassingParameterDir;\n\n\t\t/**\n\t\t * Ensure the cleanup mode defaults to ALWAYS for parameters.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeDefaultParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(DefaultParameterCase.class, \"testDefaultParameter\", \"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(defaultParameterDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that a custom, global cleanup mode is used for parameters.\n\t\t * <p/>\n\t\t * Expect the TempDir NOT to be cleaned up if set to NEVER.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeCustomDefaultParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.configurationParameter(DEFAULT_TEMP_DIR_CLEANUP_MODE_PROPERTY_NAME, \"never\")//\n\t\t\t\t\t.selectors(selectMethod(DefaultParameterCase.class, \"testDefaultParameter\", \"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(defaultParameterDir).exists();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that NEVER cleanup modes are obeyed for parameters.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeNeverParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(NeverParameterCase.class, \"testNeverParameter\", \"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(neverParameterDir).exists();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ALWAYS cleanup modes are obeyed for parameters.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeAlwaysParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(AlwaysParameterCase.class, \"testAlwaysParameter\", \"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(alwaysParameterDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for passing parameter tests.\n\t\t * <p/>\n\t\t * Expect the TempDir to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessPassingParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(OnSuccessPassingParameterCase.class, \"testOnSuccessPassingParameter\",\n\t\t\t\t\t\t\"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(onSuccessPassingParameterDir).doesNotExist();\n\t\t}\n\n\t\t/**\n\t\t * Ensure that ON_SUCCESS cleanup modes are obeyed for failing parameter tests.\n\t\t * <p/>\n\t\t * Expect the TempDir not to be cleaned up.\n\t\t */\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingParameter() {\n\t\t\tLauncherDiscoveryRequest request = request()//\n\t\t\t\t\t.selectors(selectMethod(OnSuccessFailingParameterCase.class, \"testOnSuccessFailingParameter\",\n\t\t\t\t\t\t\"java.nio.file.Path\"))//\n\t\t\t\t\t.build();\n\t\t\texecuteTests(request);\n\n\t\t\tassertThat(onSuccessFailingParameterDir).exists();\n\t\t}\n\n\t\t@Test\n\t\tvoid cleanupModeOnSuccessFailingThenPassingParameter() {\n\t\t\texecuteTestsForClass(OnSuccessFailingThenPassingParameterCase.class);\n\n\t\t\tassertThat(onSuccessFailingParameterDir).exists();\n\t\t\tassertThat(onSuccessPassingParameterDir).doesNotExist();\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid deleteTempDirs() throws IOException {\n\t\t\tTempDirFieldTests.deleteIfNotNullAndExists(defaultParameterDir);\n\t\t\tTempDirFieldTests.deleteIfNotNullAndExists(neverParameterDir);\n\t\t\tTempDirFieldTests.deleteIfNotNullAndExists(alwaysParameterDir);\n\t\t\tTempDirFieldTests.deleteIfNotNullAndExists(onSuccessFailingParameterDir);\n\t\t\tTempDirFieldTests.deleteIfNotNullAndExists(onSuccessPassingParameterDir);\n\t\t}\n\n\t\t// -------------------------------------------------------------------\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class DefaultParameterCase {\n\n\t\t\t@Test\n\t\t\tvoid testDefaultParameter(@TempDir Path defaultParameterDir) {\n\t\t\t\tTempDirParameterTests.defaultParameterDir = defaultParameterDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class NeverParameterCase {\n\n\t\t\t@Test\n\t\t\tvoid testNeverParameter(@TempDir(cleanup = NEVER) Path neverParameterDir) {\n\t\t\t\tTempDirParameterTests.neverParameterDir = neverParameterDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class AlwaysParameterCase {\n\n\t\t\t@Test\n\t\t\tvoid testAlwaysParameter(@TempDir(cleanup = ALWAYS) Path alwaysParameterDir) {\n\t\t\t\tTempDirParameterTests.alwaysParameterDir = alwaysParameterDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OnSuccessPassingParameterCase {\n\n\t\t\t@Test\n\t\t\tvoid testOnSuccessPassingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessPassingParameterDir) {\n\t\t\t\tTempDirParameterTests.onSuccessPassingParameterDir = onSuccessPassingParameterDir;\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\tstatic class OnSuccessFailingParameterCase {\n\n\t\t\t@Test\n\t\t\tvoid testOnSuccessFailingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessFailingParameterDir) {\n\t\t\t\tTempDirParameterTests.onSuccessFailingParameterDir = onSuccessFailingParameterDir;\n\t\t\t\tfail();\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\t\tstatic class OnSuccessFailingThenPassingParameterCase {\n\n\t\t\t@Test\n\t\t\t@Order(1)\n\t\t\tvoid testOnSuccessFailingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessFailingParameterDir) {\n\t\t\t\tTempDirParameterTests.onSuccessFailingParameterDir = onSuccessFailingParameterDir;\n\t\t\t\tfail();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Order(2)\n\t\t\tvoid testOnSuccessPassingParameter(@TempDir(cleanup = ON_SUCCESS) Path onSuccessPassingParameterDir) {\n\t\t\t\tTempDirParameterTests.onSuccessPassingParameterDir = onSuccessPassingParameterDir;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\t@EnabledOnOs(WINDOWS)\n\tclass WindowsTests {\n\n\t\t@Test\n\t\tvoid deletesBrokenJunctions(@TempDir Path dir) throws Exception {\n\t\t\tvar test = Files.createDirectory(dir.resolve(\"test\"));\n\t\t\tcreateWindowsJunction(dir.resolve(\"link\"), test);\n\t\t\t// The error might also occur without the source folder being deleted\n\t\t\t// but it depends on the order that the TempDir cleanup does its work,\n\t\t\t// so the following line forces the error to occur always\n\t\t\tFiles.delete(test);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotFollowJunctions(@TempDir Path tempDir, @TrackLogRecords LogRecordListener listener)\n\t\t\t\tthrows IOException {\n\t\t\tvar outsideDir = Files.createDirectory(tempDir.resolve(\"outside\"));\n\t\t\tvar testFile = Files.writeString(outsideDir.resolve(\"test.txt\"), \"test\");\n\n\t\t\tJunctionTestCase.target = outsideDir;\n\t\t\ttry {\n\t\t\t\texecuteTestsForClass(JunctionTestCase.class).testEvents() //\n\t\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tJunctionTestCase.target = null;\n\t\t\t}\n\n\t\t\tassertThat(outsideDir).exists();\n\t\t\tassertThat(testFile).exists();\n\t\t\tassertThat(listener.stream(Level.WARNING)) //\n\t\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t\t.anyMatch(m -> m.matches(\n\t\t\t\t\t\t\"Deleting link from location inside of temp dir \\\\(.+\\\\) to location outside of temp dir \\\\(.+\\\\) but not the target file/directory\"));\n\t\t}\n\n\t\t@NullUnmarked\n\t\tstatic class JunctionTestCase {\n\t\t\tpublic static Path target;\n\n\t\t\t@Test\n\t\t\tvoid createJunctionToTarget(@TempDir Path dir) throws Exception {\n\t\t\t\tvar link = createWindowsJunction(dir.resolve(\"link\"), target);\n\t\t\t\ttry (var files = Files.list(link)) {\n\t\t\t\t\tfiles.forEach(it -> System.out.println(\"- \" + it));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate static Path createWindowsJunction(Path link, Path target) throws Exception {\n\t\t\t// This creates a Windows \"junction\" which you can't do with Java code\n\t\t\tString[] command = { \"cmd.exe\", \"/c\", \"mklink\", \"/j\", link.toString(), target.toString() };\n\t\t\tRuntime.getRuntime().exec(command).waitFor();\n\t\t\treturn link;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryMetaAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\n/**\n * Integration tests for the use of {@link TempDir} as a meta-annotation.\n *\n * @since 5.10\n */\n@DisplayName(\"@TempDir as a meta-annotation\")\nclass TempDirectoryMetaAnnotationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid annotationOnField() {\n\t\texecuteTestsForClass(AnnotationOnFieldTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\tvoid annotationOnParameter() {\n\t\texecuteTestsForClass(AnnotationOnParameterTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@NullUnmarked\n\tstatic class AnnotationOnFieldTestCase {\n\n\t\t@CustomTempDir\n\t\tprivate Path tempDir;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t}\n\n\t}\n\n\tstatic class AnnotationOnParameterTestCase {\n\n\t\t@Test\n\t\tvoid test(@CustomTempDir Path tempDir) {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t}\n\n\t}\n\n\t@TempDir\n\t@Target({ ElementType.FIELD, ElementType.PARAMETER })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface CustomTempDir {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryPreconditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.io.File;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for preconditions and assertions in the {@link TempDirectory}\n * extension.\n *\n * @since 5.9\n */\nclass TempDirectoryPreconditionTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\t@DisplayName(\"Valid and invalid @TempDir parameter types\")\n\tvoid parameterTypes() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ParameterTypeTestCase.class);\n\t\tEvents tests = executionResults.testEvents();\n\t\ttests.assertStatistics(stats -> stats.started(2).failed(1).succeeded(1));\n\t\ttests.succeeded().assertEventsMatchExactly(event(test(\"validTempDirType\"), finishedSuccessfully()));\n\t\t// @formatter:off\n\t\ttests.failed().assertEventsMatchExactly(event(test(\"invalidTempDirType\"),\n\t\t\tfinishedWithFailure(instanceOf(ParameterResolutionException.class), message(\"\"\"\n\t\t\t\t\tFailed to resolve parameter [java.lang.String text] in method \\\n\t\t\t\t\t[void org.junit.jupiter.engine.extension.TempDirectoryPreconditionTests$ParameterTypeTestCase.invalidTempDirType(java.lang.String)]: \\\n\t\t\t\t\tCan only resolve @TempDir parameter of type java.nio.file.Path or java.io.File but was: java.lang.String\\\n\t\t\t\t\t\"\"\"))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@DisplayName(\"final static @TempDir fields are not supported\")\n\tvoid finalStaticFieldIsNotSupported() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(FinalStaticFieldTestCase.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tcontainers.assertStatistics(stats -> stats.started(2).failed(1).succeeded(1));\n\t\tcontainers.succeeded().assertEventsMatchExactly(event(container(\"junit-jupiter\"), finishedSuccessfully()));\n\t\tcontainers.failed().assertEventsMatchExactly(event(container(\"FinalStaticFieldTestCase\"),\n\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(\"\"\"\n\t\t\t\t\t@TempDir field [static final java.nio.file.Path \\\n\t\t\t\t\torg.junit.jupiter.engine.extension.TempDirectoryPreconditionTests$FinalStaticFieldTestCase.path] \\\n\t\t\t\t\tmust not be declared as final.\\\n\t\t\t\t\t\"\"\"))));\n\t}\n\n\t@Test\n\t@DisplayName(\"final instance @TempDir fields are not supported\")\n\tvoid finalInstanceFieldIsNotSupported() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(FinalInstanceFieldTestCase.class);\n\t\tEvents tests = executionResults.testEvents();\n\t\ttests.assertStatistics(stats -> stats.started(1).failed(1).succeeded(0));\n\t\ttests.failed().assertEventsMatchExactly(\n\t\t\tevent(test(\"test()\"), finishedWithFailure(instanceOf(ExtensionConfigurationException.class), message(\"\"\"\n\t\t\t\t\t@TempDir field [final java.nio.file.Path \\\n\t\t\t\t\torg.junit.jupiter.engine.extension.TempDirectoryPreconditionTests$FinalInstanceFieldTestCase.path] \\\n\t\t\t\t\tmust not be declared as final.\\\n\t\t\t\t\t\"\"\"))));\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tstatic class ParameterTypeTestCase {\n\n\t\t@Test\n\t\tvoid validTempDirType(@TempDir File file, @TempDir Path path) {\n\t\t}\n\n\t\t@Test\n\t\tvoid invalidTempDirType(@TempDir String text) {\n\t\t}\n\t}\n\n\tstatic class FinalStaticFieldTestCase {\n\n\t\tstatic final @TempDir Path path = Path.of(\".\");\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class FinalInstanceFieldTestCase {\n\n\t\tfinal @TempDir Path path = Path.of(\".\");\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TempDirectoryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.lang.annotation.ElementType.ANNOTATION_TYPE;\nimport static java.lang.annotation.ElementType.FIELD;\nimport static java.lang.annotation.ElementType.PARAMETER;\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.allOf;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNotSame;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.io.FailingTempDirDeletionStrategy.UNDELETABLE_PATH;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.cause;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Parameter;\nimport java.nio.file.DirectoryNotEmptyException;\nimport java.nio.file.FileSystem;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\nimport com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder;\nimport com.google.common.jimfs.Configuration;\nimport com.google.common.jimfs.Jimfs;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.condition.DisabledOnOs;\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.io.FailingTempDirDeletionStrategy;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.io.TempDirDeletionStrategy.DeletionException;\nimport org.junit.jupiter.api.io.TempDirFactory;\nimport org.junit.jupiter.api.io.TempDirFactory.Standard;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for the new behavior of the {@link TempDirectory} extension\n * to create separate temp directories for each {@link TempDir} declaration.\n *\n * @since 5.8\n */\n@DisplayName(\"TempDirectory extension\")\nclass TempDirectoryTests extends AbstractJupiterTestEngineTests {\n\n\tprivate EngineExecutionResults executeTestsForClassWithDefaultFactory(Class<?> testClass,\n\t\t\tClass<? extends TempDirFactory> factoryClass) {\n\t\treturn executeTests(request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.configurationParameter(DEFAULT_TEMP_DIR_FACTORY_PROPERTY_NAME, factoryClass.getName()) //\n\t\t\t\t.build());\n\t}\n\n\t@BeforeEach\n\t@AfterEach\n\tvoid resetStaticVariables() {\n\t\tAllPossibleDeclarationLocationsTestCase.tempDirs.clear();\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@ParameterizedTest(name = \"{0}\")\n\t@EnumSource(TestInstance.Lifecycle.class)\n\t@DisplayName(\"resolves separate temp dirs for each annotation declaration\")\n\tvoid resolvesSeparateTempDirsForEachAnnotationDeclaration(TestInstance.Lifecycle lifecycle) {\n\t\tvar results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(AllPossibleDeclarationLocationsTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, lifecycle.name()).build());\n\n\t\tresults.containerEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\tassertThat(AllPossibleDeclarationLocationsTestCase.tempDirs).hasSize(3);\n\n\t\tvar classTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get(\"class\");\n\t\tassertThat(classTempDirs) //\n\t\t\t\t.containsOnlyKeys(\"staticField1\", \"staticField2\", \"beforeAll1\", \"beforeAll2\", \"afterAll1\", \"afterAll2\") //\n\t\t\t\t.values().hasSize(6).doesNotHaveDuplicates().allMatch(Files::notExists);\n\n\t\tvar testATempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get(\"testA\");\n\t\tassertThat(testATempDirs) //\n\t\t\t\t.containsOnlyKeys(\"staticField1\", \"staticField2\", \"instanceFieldSetViaConstructorInjection\",\n\t\t\t\t\t\"instanceField1\", \"instanceField2\", \"beforeEach1\", \"beforeEach2\", \"test1\", \"test2\", \"afterEach1\",\n\t\t\t\t\t\"afterEach2\") //\n\t\t\t\t.values().hasSize(11).doesNotHaveDuplicates().allMatch(Files::notExists);\n\n\t\tvar testBTempDirs = AllPossibleDeclarationLocationsTestCase.tempDirs.get(\"testB\");\n\t\tassertThat(testBTempDirs) //\n\t\t\t\t.containsOnlyKeys(\"staticField1\", \"staticField2\", \"instanceFieldSetViaConstructorInjection\",\n\t\t\t\t\t\"instanceField1\", \"instanceField2\", \"beforeEach1\", \"beforeEach2\", \"test1\", \"test2\", \"afterEach1\",\n\t\t\t\t\t\"afterEach2\") //\n\t\t\t\t.values().hasSize(11).doesNotHaveDuplicates().allMatch(Files::notExists);\n\n\t\tassertThat(testATempDirs).containsEntry(\"staticField1\", classTempDirs.get(\"staticField1\"));\n\t\tassertThat(testBTempDirs).containsEntry(\"staticField1\", classTempDirs.get(\"staticField1\"));\n\t\tassertThat(testATempDirs).containsEntry(\"staticField2\", classTempDirs.get(\"staticField2\"));\n\t\tassertThat(testBTempDirs).containsEntry(\"staticField2\", classTempDirs.get(\"staticField2\"));\n\n\t\tswitch (lifecycle) {\n\t\t\tcase PER_CLASS -> assertThat(testATempDirs) //\n\t\t\t\t\t.containsEntry(\"instanceFieldSetViaConstructorInjection\",\n\t\t\t\t\t\ttestBTempDirs.get(\"instanceFieldSetViaConstructorInjection\"));\n\t\t\tcase PER_METHOD -> assertThat(testATempDirs) //\n\t\t\t\t\t.doesNotContainEntry(\"instanceFieldSetViaConstructorInjection\",\n\t\t\t\t\t\ttestBTempDirs.get(\"instanceFieldSetViaConstructorInjection\"));\n\t\t}\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"instanceField1\", testBTempDirs.get(\"instanceField1\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"instanceField2\", testBTempDirs.get(\"instanceField2\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"beforeEach1\", testBTempDirs.get(\"beforeEach1\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"beforeEach2\", testBTempDirs.get(\"beforeEach2\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"test1\", testBTempDirs.get(\"test1\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"test2\", testBTempDirs.get(\"test2\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"afterEach1\", testBTempDirs.get(\"afterEach1\"));\n\t\tassertThat(testATempDirs).doesNotContainEntry(\"afterEach2\", testBTempDirs.get(\"afterEach2\"));\n\t}\n\n\t@Test\n\tvoid supportsConstructorInjectionOnRecords() {\n\t\texecuteTestsForClass(TempDirRecordTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisplayName(\"does not prevent constructor parameter resolution\")\n\tvoid tempDirectoryDoesNotPreventConstructorParameterResolution() {\n\t\texecuteTestsForClass(TempDirectoryDoesNotPreventConstructorParameterResolutionTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisplayName(\"does not prevent user from deleting the temp dir within a test\")\n\tvoid tempDirectoryDoesNotPreventUserFromDeletingTempDir() {\n\t\texecuteTestsForClass(UserTempDirectoryDeletionDoesNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisplayName(\"is capable of removing a read-only file\")\n\tvoid nonWritableFileDoesNotCauseFailure() {\n\t\texecuteTestsForClass(NonWritableFileDoesNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisplayName(\"is capable of removing non-executable, non-writable, or non-readable directories and folders\")\n\tvoid nonMintPermissionsContentDoesNotCauseFailure() {\n\t\texecuteTestsForClass(NonMintPermissionContentInTempDirectoryDoesNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(13).succeeded(13));\n\t}\n\n\t@Test\n\t@DisplayName(\"is capable of removing a directory when its permissions have been changed\")\n\tvoid nonMintPermissionsDoNotCauseFailure() {\n\t\texecuteTestsForClass(NonMintTempDirectoryPermissionsDoNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(42).succeeded(42));\n\t}\n\n\t@Test\n\t@DisabledOnOs(OS.WINDOWS)\n\t@DisplayName(\"is capable of removing a read-only file in a read-only dir\")\n\tvoid readOnlyFileInReadOnlyDirDoesNotCauseFailure() {\n\t\texecuteTestsForClass(ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisabledOnOs(OS.WINDOWS)\n\t@DisplayName(\"is capable of removing a read-only file in a dir in a read-only dir\")\n\tvoid readOnlyFileInNestedReadOnlyDirDoesNotCauseFailure() {\n\t\texecuteTestsForClass(ReadOnlyFileInDirInReadOnlyDirDoesNotCauseFailureTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@DisplayName(\"can be used via instance field inside nested test classes\")\n\tvoid canBeUsedViaInstanceFieldInsideNestedTestClasses() {\n\t\texecuteTestsForClass(TempDirUsageInsideNestedClassesTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(3).succeeded(3));\n\t}\n\n\t@Test\n\t@DisplayName(\"can be used via static field inside nested test classes\")\n\tvoid canBeUsedViaStaticFieldInsideNestedTestClasses() {\n\t\texecuteTestsForClass(StaticTempDirUsageInsideNestedClassTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@ValueSource(classes = { UndeletableDirectoryTestCase.class, UndeletableFileTestCase.class })\n\t@DisplayName(\"only attempts to delete undeletable paths once\")\n\tvoid onlyAttemptsToDeleteUndeletablePathsOnce(Class<?> testClass) {\n\t\tvar results = executeTestsForClass(testClass);\n\n\t\tvar tempDir = determineTempDirFromReportEntries(results, UndeletableTestCase.TEMP_DIR);\n\n\t\tassertFailedDueToDeletionException(results, tempDir);\n\t}\n\n\t@Test\n\t@DisplayName(\"applies globally configured deletion strategy\")\n\tvoid appliesGloballyConfiguredDeletionStrategy() {\n\t\tvar results = executeTests(builder -> builder //\n\t\t\t\t.selectors(selectClass(UndeletableWithDefaultDeletionStrategyTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_TEMP_DIR_DELETION_STRATEGY_PROPERTY_NAME,\n\t\t\t\t\tFailingTempDirDeletionStrategy.class.getName()));\n\n\t\tvar tempDir = determineTempDirFromReportEntries(results,\n\t\t\tUndeletableWithDefaultDeletionStrategyTestCase.TEMP_DIR);\n\n\t\tassertFailedDueToDeletionException(results, tempDir);\n\t}\n\n\tprivate static void assertFailedDueToDeletionException(EngineExecutionResults results, Path tempDir) {\n\t\tassertSingleFailedTest(results, //\n\t\t\tcause( //\n\t\t\t\tinstanceOf(DeletionException.class), //\n\t\t\t\tmessage(\"Failed to delete temp directory \" + tempDir.toAbsolutePath() + \". \" + //\n\t\t\t\t\t\t\"The following paths could not be deleted (see suppressed exceptions for details): <root>, undeletable\"), //\n\t\t\t\tsuppressed(0, instanceOf(DirectoryNotEmptyException.class)), //\n\t\t\t\tsuppressed(1, instanceOf(IOException.class), message(\"Simulated failure\")) //\n\t\t\t) //\n\t\t);\n\t}\n\n\tprivate static Path determineTempDirFromReportEntries(EngineExecutionResults results, String key) {\n\t\treturn results.testEvents().reportingEntryPublished().stream() //\n\t\t\t\t.map(it -> it.getPayload(ReportEntry.class).orElseThrow()) //\n\t\t\t\t.map(it -> Path.of(it.getKeyValuePairs().get(key))) //\n\t\t\t\t.findAny() //\n\t\t\t\t.orElseThrow();\n\t}\n\n\t@Test\n\tvoid usingTheRemovedScopeConfigurationParameterProducesWarning() {\n\t\tvar results = discoverTests(request() //\n\t\t\t\t.selectors(selectClass(AllPossibleDeclarationLocationsTestCase.class)) //\n\t\t\t\t.configurationParameter(\"junit.jupiter.tempdir.scope\", \"per_context\"));\n\n\t\tassertThat(results.getDiscoveryIssues()) //\n\t\t\t\t.contains(DiscoveryIssue.create(Severity.WARNING, \"\"\"\n\t\t\t\t\t\tThe 'junit.jupiter.tempdir.scope' configuration parameter is no longer supported. \\\n\t\t\t\t\t\tPlease remove it from your configuration.\"\"\"));\n\t}\n\n\t@Nested\n\t@DisplayName(\"reports failure\")\n\t@TestMethodOrder(OrderAnnotation.class)\n\tclass Failures {\n\n\t\t@Test\n\t\t@DisplayName(\"when @TempDir is used on static field of an unsupported type\")\n\t\t@Order(20)\n\t\tvoid onlySupportsStaticFieldsOfTypePathAndFile() {\n\t\t\tvar results = executeTestsForClass(AnnotationOnStaticFieldWithUnsupportedTypeTestCase.class);\n\n\t\t\tassertSingleFailedContainer(results, ExtensionConfigurationException.class,\n\t\t\t\t\"Can only resolve @TempDir field of type java.nio.file.Path or java.io.File\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when @TempDir is used on instance field of an unsupported type\")\n\t\t@Order(21)\n\t\tvoid onlySupportsInstanceFieldsOfTypePathAndFile() {\n\t\t\tvar results = executeTestsForClass(AnnotationOnInstanceFieldWithUnsupportedTypeTestCase.class);\n\n\t\t\tassertSingleFailedTest(results, ExtensionConfigurationException.class,\n\t\t\t\t\"Can only resolve @TempDir field of type java.nio.file.Path or java.io.File\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when @TempDir is used on parameter of an unsupported type\")\n\t\t@Order(22)\n\t\tvoid onlySupportsParametersOfTypePathAndFile() {\n\t\t\tvar results = executeTestsForClass(InvalidTestCase.class);\n\n\t\t\t// @formatter:off\n\t\t\tassertSingleFailedTest(results, instanceOf(ParameterResolutionException.class),\n\t\t\t\tmessage(m -> m.matches(\"Failed to resolve parameter \\\\[java.lang.String .+] in method \\\\[.+]: .+\")),\n\t\t\t\tcause(\n\t\t\t\t\tinstanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(\"Can only resolve @TempDir parameter of type java.nio.file.Path or java.io.File but was: java.lang.String\")));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when @TempDir factory does not return directory\")\n\t\t@Order(32)\n\t\tvoid doesNotSupportTempDirFactoryNotReturningDirectory() {\n\t\t\tvar results = executeTestsForClass(FactoryNotReturningDirectoryTestCase.class);\n\n\t\t\t// @formatter:off\n\t\t\tassertSingleFailedTest(results, instanceOf(ParameterResolutionException.class),\n\t\t\t\t\tmessage(m -> m.matches(\"Failed to resolve parameter \\\\[.+] in method \\\\[.+]: .+\")),\n\t\t\t\t\tcause(\n\t\t\t\t\t\t\tinstanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\t\t\tmessage(\"Failed to create default temp directory\"),\n\t\t\t\t\t\t\tcause(\n\t\t\t\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class),\n\t\t\t\t\t\t\t\t\tmessage(\"temp directory must be a directory\")\n\t\t\t\t\t\t\t)\n\t\t\t\t\t));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when default @TempDir factory does not return directory\")\n\t\t@Order(33)\n\t\tvoid doesNotSupportCustomDefaultTempDirFactoryNotReturningDirectory() {\n\t\t\tvar results = executeTestsForClassWithDefaultFactory(\n\t\t\t\tCustomDefaultFactoryNotReturningDirectoryTestCase.class, FactoryNotReturningDirectory.class);\n\n\t\t\t// @formatter:off\n\t\t\tassertSingleFailedTest(results, instanceOf(ParameterResolutionException.class),\n\t\t\t\t\tmessage(m -> m.matches(\"Failed to resolve parameter \\\\[.+] in method \\\\[.+]: .+\")),\n\t\t\t\t\tcause(\n\t\t\t\t\t\t\tinstanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\t\t\tmessage(\"Failed to create default temp directory\"),\n\t\t\t\t\t\t\tcause(\n\t\t\t\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class),\n\t\t\t\t\t\t\t\t\tmessage(\"temp directory must be a directory\")\n\t\t\t\t\t\t\t)\n\t\t\t\t\t));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class FactoryNotReturningDirectory implements TempDirFactory {\n\n\t\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when @TempDir factory returns a non-default file system path for a File annotated element\")\n\t\t@Order(34)\n\t\tvoid doesNotSupportNonDefaultFileSystemTempDirFactoryOnFileAnnotatedElement() {\n\t\t\tvar results = executeTestsForClass(\n\t\t\t\tFactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase.class);\n\n\t\t\t// @formatter:off\n\t\t\tassertSingleFailedTest(results, instanceOf(ParameterResolutionException.class),\n\t\t\t\t\tmessage(m -> m.matches(\"Failed to resolve parameter \\\\[.+] in method \\\\[.+]: .+\")),\n\t\t\t\t\tcause(\n\t\t\t\t\t\t\tinstanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\t\t\tmessage(\"Failed to create default temp directory\"),\n\t\t\t\t\t\t\tcause(\n\t\t\t\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class),\n\t\t\t\t\t\t\t\t\tmessage(\"temp directory with non-default file system cannot be injected into \"\n\t\t\t\t\t\t\t\t\t\t\t+ File.class.getName() + \" target\")\n\t\t\t\t\t\t\t)\n\t\t\t\t\t));\n\t\t\t// @formatter:on\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"supports @TempDir\")\n\t@TestMethodOrder(OrderAnnotation.class)\n\tclass PrivateFields {\n\n\t\t@Test\n\t\t@DisplayName(\"on private static field\")\n\t\t@Order(10)\n\t\tvoid supportsPrivateInstanceFields() {\n\t\t\texecuteTestsForClass(AnnotationOnPrivateStaticFieldTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"on private instance field\")\n\t\t@Order(11)\n\t\tvoid supportsPrivateStaticFields() {\n\t\t\texecuteTestsForClass(AnnotationOnPrivateInstanceFieldTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"supports custom factory\")\n\tclass Factory {\n\n\t\t@Test\n\t\t@DisplayName(\"that uses test method name as temp dir name prefix\")\n\t\tvoid supportsFactoryWithTestMethodNameAsPrefix() {\n\t\t\texecuteTestsForClass(FactoryWithTestMethodNameAsPrefixTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"that uses custom temp dir parent directory\")\n\t\tvoid supportsFactoryWithCustomParentDirectory() {\n\t\t\texecuteTestsForClass(FactoryWithCustomParentDirectoryTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"that uses com.github.marschall:memoryfilesystem\")\n\t\tvoid supportsFactoryWithMemoryFileSystem() {\n\t\t\texecuteTestsForClass(FactoryWithMemoryFileSystemTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"that uses com.google.jimfs:jimfs\")\n\t\tvoid supportsFactoryWithJimfs() {\n\t\t\texecuteTestsForClass(FactoryWithJimfsTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"that uses annotated element name as temp dir name prefix\")\n\t\tvoid supportsFactoryWithAnnotatedElementNameAsPrefix() {\n\t\t\texecuteTestsForClass(FactoryWithAnnotatedElementNameAsPrefixTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"that uses custom meta-annotation\")\n\t\tvoid supportsFactoryWithCustomMetaAnnotation() {\n\t\t\texecuteTestsForClass(FactoryWithCustomMetaAnnotationTestCase.class).testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t}\n\n\t@Nested\n\t@DisplayName(\"supports default factory\")\n\t@TestMethodOrder(OrderAnnotation.class)\n\tclass DefaultFactory {\n\n\t\t@Test\n\t\t@DisplayName(\"set to Jupiter's default\")\n\t\tvoid supportsStandardDefaultFactory() {\n\t\t\texecuteTestsForClassWithDefaultFactory(StandardDefaultFactoryTestCase.class, Standard.class) //\n\t\t\t\t\t.testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"set to custom factory\")\n\t\tvoid supportsCustomDefaultFactory() {\n\t\t\texecuteTestsForClassWithDefaultFactory(CustomDefaultFactoryTestCase.class, Factory.class) //\n\t\t\t\t\t.testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"set to custom factory together with declaration of Jupiter's default\")\n\t\tvoid supportsCustomDefaultFactoryWithStandardFactoryOnDeclaration() {\n\t\t\texecuteTestsForClassWithDefaultFactory( //\n\t\t\t\tCustomDefaultFactoryWithStandardDeclarationTestCase.class, Factory.class) //\n\t\t\t\t\t\t.testEvents()//\n\t\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\tprivate boolean closed;\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn Files.createTempDirectory(\"custom\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tif (closed) {\n\t\t\t\t\tthrow new IllegalStateException(\"already closed\");\n\t\t\t\t}\n\t\t\t\tclosed = true;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tprivate static void assertSingleFailedContainer(EngineExecutionResults results, Class<? extends Throwable> clazz,\n\t\t\tString message) {\n\n\t\tassertSingleFailedContainer(results, instanceOf(clazz), message(actual -> actual.contains(message)));\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate static void assertSingleFailedContainer(EngineExecutionResults results,\n\t\t\tCondition<Throwable>... conditions) {\n\n\t\tresults.containerEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).failed(1).succeeded(1))//\n\t\t\t\t.assertThatEvents().haveExactly(1, finishedWithFailure(conditions));\n\t}\n\n\t@SuppressWarnings(\"SameParameterValue\")\n\tprivate static void assertSingleFailedTest(EngineExecutionResults results, Class<? extends Throwable> clazz,\n\t\t\tString message) {\n\n\t\tassertSingleFailedTest(results, instanceOf(clazz), message(actual -> actual.contains(message)));\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate static void assertSingleFailedTest(EngineExecutionResults results, Condition<Throwable>... conditions) {\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(1).failed(1).succeeded(0));\n\t\tvar failures = results.testEvents().stream().filter(finishedWithFailure()::matches) //\n\t\t\t\t.map(e -> e.getPayload(TestExecutionResult.class).flatMap(TestExecutionResult::getThrowable).orElse(\n\t\t\t\t\tnull)) //\n\t\t\t\t.filter(Objects::nonNull).toList();\n\t\tassertThat(failures).hasSize(1);\n\t\tassertThat(failures.getFirst()).has(allOf(conditions));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class AnnotationOnPrivateInstanceFieldTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@TempDir\n\t\tprivate Path tempDir;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t}\n\n\t}\n\n\tstatic class AnnotationOnPrivateStaticFieldTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@TempDir\n\t\tprivate static Path tempDir;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t}\n\n\t}\n\n\tstatic class AnnotationOnStaticFieldWithUnsupportedTypeTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@TempDir\n\t\tstatic String tempDir;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t}\n\n\tstatic class AnnotationOnInstanceFieldWithUnsupportedTypeTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@TempDir\n\t\tString tempDir;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t}\n\n\tstatic class InvalidTestCase {\n\n\t\t@Test\n\t\tvoid wrongParameterType(@SuppressWarnings(\"unused\") @TempDir String ignored) {\n\t\t\tfail(\"this should never be called\");\n\t\t}\n\t}\n\n\t@Nested\n\t@DisplayName(\"resolves java.io.File injection type\")\n\tclass FileAndPathInjection {\n\n\t\t@TempDir\n\t\tFile fileTempDir;\n\n\t\t@TempDir\n\t\tPath pathTempDir;\n\n\t\t@Test\n\t\t@DisplayName(\"and injected File and Path do not reference the same temp directory\")\n\t\tvoid checkFile(@TempDir File tempDir, @TempDir Path ref) {\n\t\t\tassertFileAndPathAreNotEqual(tempDir, ref);\n\t\t\tassertFileAndPathAreNotEqual(this.fileTempDir, this.pathTempDir);\n\t\t}\n\n\t\tprivate static void assertFileAndPathAreNotEqual(File tempDir, Path ref) {\n\t\t\tPath path = tempDir.toPath();\n\t\t\tassertNotEquals(ref.toAbsolutePath(), path.toAbsolutePath());\n\t\t\tassertTrue(Files.exists(path));\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/1748\n\tstatic class TempDirectoryDoesNotPreventConstructorParameterResolutionTestCase {\n\n\t\t@TempDir\n\t\tPath tempDir;\n\n\t\tTempDirectoryDoesNotPreventConstructorParameterResolutionTestCase(TestInfo testInfo) {\n\t\t\tassertNotNull(testInfo);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(tempDir);\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/1801\n\tstatic class UserTempDirectoryDeletionDoesNotCauseFailureTestCase {\n\n\t\t@Test\n\t\tvoid deleteTempDir(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.delete(tempDir);\n\t\t\tassertThat(tempDir).doesNotExist();\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2046\n\tstatic class NonWritableFileDoesNotCauseFailureTestCase {\n\n\t\t@Test\n\t\tvoid createReadonlyFile(@TempDir Path tempDir) throws IOException {\n\t\t\t// Removal of setWritable(false) files might fail (e.g. for Windows)\n\t\t\t// The test verifies that @TempDir is capable of removing of such files\n\t\t\tvar path = Files.write(tempDir.resolve(\"test.txt\"), new byte[0]);\n\t\t\tassumeTrue(path.toFile().setWritable(false),\n\t\t\t\t() -> \"Unable to set file \" + path + \" readonly via .toFile().setWritable(false)\");\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2171\n\tstatic class ReadOnlyFileInReadOnlyDirDoesNotCauseFailureTestCase {\n\n\t\t@Test\n\t\tvoid createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException {\n\t\t\tFile file = tempDir.toPath().resolve(\"file\").toFile();\n\t\t\tassumeTrue(file.createNewFile());\n\t\t\tassumeTrue(tempDir.setReadOnly());\n\t\t\tassumeTrue(file.setReadOnly());\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2171\n\tstatic class ReadOnlyFileInDirInReadOnlyDirDoesNotCauseFailureTestCase {\n\n\t\t@Test\n\t\tvoid createReadOnlyFileInReadOnlyDir(@TempDir File tempDir) throws IOException {\n\t\t\tFile file = tempDir.toPath().resolve(\"dir\").resolve(\"file\").toFile();\n\t\t\tassumeTrue(file.getParentFile().mkdirs());\n\t\t\tassumeTrue(file.createNewFile());\n\t\t\tassumeTrue(tempDir.setReadOnly());\n\t\t\tassumeTrue(file.getParentFile().setReadOnly());\n\t\t\tassumeTrue(file.setReadOnly());\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2609\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tstatic class NonMintPermissionContentInTempDirectoryDoesNotCauseFailureTestCase {\n\n\t\t@Test\n\t\tvoid createFile(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile();\n\t\t}\n\n\t\t@Test\n\t\tvoid createFolder(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile();\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonWritableFile(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setWritable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonReadableFile(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setReadable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonWritableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setWritable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonReadableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setReadable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonExecutableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setExecutable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyNonWritableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\tsubDir.toFile().setWritable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyNonReadableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\tsubDir.toFile().setReadable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyNonExecutableDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\tsubDir.toFile().setExecutable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyDirectory(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyDirectoryWithNonWritableFile(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setWritable(false);\n\t\t}\n\n\t\t@Test\n\t\tvoid createNonEmptyDirectoryWithNonReadableFile(@TempDir Path tempDir) throws IOException {\n\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setReadable(false);\n\t\t}\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2609\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tstatic class NonMintTempDirectoryPermissionsDoNotCauseFailureTestCase {\n\n\t\t@Nested\n\t\tclass NonWritable {\n\n\t\t\t@Test\n\t\t\tvoid makeEmptyTempDirectoryNonWritable(@TempDir Path tempDir) {\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithFileNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\"));\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithEmptyFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFileNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFileNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonExecutableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonReadableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonWritableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonExecutableFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderNonWritable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonWritableFileNonWritable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonReadableFileNonWritable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setWritable(false);\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\tclass NonReadable {\n\n\t\t\t@Test\n\t\t\tvoid makeEmptyTempDirectoryNonReadable(@TempDir Path tempDir) {\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithFileNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\"));\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithEmptyFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFileNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFileNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonExecutableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonWritableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonReadableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonExecutableFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderNonReadable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonWritableFileNonReadable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonReadableFileNonReadable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setReadable(false);\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\tclass NonExecutable {\n\n\t\t\t@Test\n\t\t\tvoid makeEmptyTempDirectoryNonExecutable(@TempDir Path tempDir) {\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithFileNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\"));\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithEmptyFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFileNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFileNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonWritableFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonReadableFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonExecutableFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\")).toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonWritableFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonReadableFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyNonExecutableFolderNonExecutable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tPath subDir = Files.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\tsubDir.toFile().setExecutable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderNonExecutable(@TempDir Path tempDir) throws IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\"));\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonWritableFileNonExecutable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setWritable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid makeTempDirectoryWithNonEmptyFolderContainingNonReadableFileNonExecutable(@TempDir Path tempDir)\n\t\t\t\t\tthrows IOException {\n\t\t\t\tFiles.createDirectory(tempDir.resolve(\"test-sub-dir\"));\n\t\t\t\tFiles.createFile(tempDir.resolve(\"test-sub-dir/test-file.txt\")).toFile().setReadable(false);\n\t\t\t\ttempDir.toFile().setExecutable(false);\n\t\t\t}\n\t\t}\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2079\n\tstatic class TempDirUsageInsideNestedClassesTestCase {\n\n\t\t@TempDir\n\t\tFile tempDir;\n\n\t\t@Test\n\t\tvoid topLevel() {\n\t\t\tassertNotNull(tempDir);\n\t\t\tassertTrue(tempDir.exists());\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestClass {\n\n\t\t\t@Test\n\t\t\tvoid nested() {\n\t\t\t\tassertNotNull(tempDir);\n\t\t\t\tassertTrue(tempDir.exists());\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass EvenDeeperNestedTestClass {\n\n\t\t\t\t@Test\n\t\t\t\tvoid deeplyNested() {\n\t\t\t\t\tassertNotNull(tempDir);\n\t\t\t\t\tassertTrue(tempDir.exists());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tstatic class StaticTempDirUsageInsideNestedClassTestCase {\n\n\t\t@TempDir\n\t\tstatic File tempDir;\n\n\t\tstatic File initialTempDir;\n\n\t\t@Test\n\t\tvoid topLevel() {\n\t\t\tassertNotNull(tempDir);\n\t\t\tassertTrue(tempDir.exists());\n\t\t\tinitialTempDir = tempDir;\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestClass {\n\n\t\t\t@Test\n\t\t\tvoid nested() {\n\t\t\t\tassertNotNull(tempDir);\n\t\t\t\tassertTrue(tempDir.exists());\n\t\t\t\tassertSame(initialTempDir, tempDir);\n\t\t\t}\n\t\t}\n\t}\n\n\t@DisplayName(\"class\")\n\tstatic class AllPossibleDeclarationLocationsTestCase {\n\n\t\tstatic final Map<String, Map<String, Path>> tempDirs = new HashMap<>();\n\n\t\t@TempDir\n\t\tstatic Path staticField1;\n\n\t\t@TempDir\n\t\tstatic Path staticField2;\n\n\t\tfinal Path instanceFieldSetViaConstructorInjection;\n\n\t\t@TempDir\n\t\tPath instanceField1;\n\n\t\t@TempDir\n\t\tPath instanceField2;\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"staticField1\", staticField1, //\n\t\t\t\t\"staticField2\", staticField2, //\n\t\t\t\t\"beforeAll1\", param1, //\n\t\t\t\t\"beforeAll2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\tAllPossibleDeclarationLocationsTestCase(@TempDir Path tempDir) {\n\t\t\tthis.instanceFieldSetViaConstructorInjection = tempDir;\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"staticField1\", staticField1, //\n\t\t\t\t\"staticField2\", staticField2, //\n\t\t\t\t\"instanceFieldSetViaConstructorInjection\", instanceFieldSetViaConstructorInjection, //\n\t\t\t\t\"instanceField1\", instanceField1, //\n\t\t\t\t\"instanceField2\", instanceField2, //\n\t\t\t\t\"beforeEach1\", param1, //\n\t\t\t\t\"beforeEach2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"testA\")\n\t\tvoid testA(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"test1\", param1, //\n\t\t\t\t\"test2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"testB\")\n\t\tvoid testB(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"test1\", param1, //\n\t\t\t\t\"test2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"afterEach1\", param1, //\n\t\t\t\t\"afterEach2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(@TempDir Path param1, @TempDir Path param2, TestInfo testInfo) {\n\t\t\tgetTempDirs(testInfo).putAll(Map.of( //\n\t\t\t\t\"afterAll1\", param1, //\n\t\t\t\t\"afterAll2\", param2 //\n\t\t\t));\n\t\t\tassertAllTempDirsExist(testInfo);\n\t\t}\n\n\t\tprivate static Map<String, Path> getTempDirs(TestInfo testInfo) {\n\t\t\treturn tempDirs.computeIfAbsent(testInfo.getDisplayName(), _ -> new LinkedHashMap<>());\n\t\t}\n\n\t\tprivate static void assertAllTempDirsExist(TestInfo testInfo) {\n\t\t\tassertAll(getTempDirs(testInfo).values().stream().map(tempDir -> () -> assertTrue(Files.exists(tempDir))));\n\t\t}\n\t}\n\n\tstatic class UndeletableTestCase {\n\n\t\tstatic final String TEMP_DIR = \"TEMP_DIR\";\n\n\t\t@TempDir(deletionStrategy = FailingTempDirDeletionStrategy.class)\n\t\tPath tempDir;\n\n\t\t@BeforeEach\n\t\tvoid reportTempDir(TestReporter reporter) {\n\t\t\treporter.publishEntry(TEMP_DIR, tempDir.toString());\n\t\t}\n\t}\n\n\tstatic class UndeletableDirectoryTestCase extends UndeletableTestCase {\n\t\t@Test\n\t\tvoid test() throws Exception {\n\t\t\tFiles.createDirectory(tempDir.resolve(UNDELETABLE_PATH));\n\t\t}\n\t}\n\n\tstatic class UndeletableFileTestCase extends UndeletableTestCase {\n\t\t@Test\n\t\tvoid test() throws Exception {\n\t\t\tFiles.createFile(tempDir.resolve(UNDELETABLE_PATH));\n\t\t}\n\t}\n\n\tstatic class UndeletableWithDefaultDeletionStrategyTestCase extends UndeletableTestCase {\n\n\t\tstatic final String TEMP_DIR = \"TEMP_DIR\";\n\n\t\t@TempDir\n\t\tPath tempDir;\n\n\t\t@BeforeEach\n\t\tvoid reportTempDir(TestReporter reporter) {\n\t\t\treporter.publishEntry(TEMP_DIR, tempDir.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid test() throws Exception {\n\t\t\tFiles.createFile(tempDir.resolve(UNDELETABLE_PATH));\n\t\t}\n\t}\n\n\tstatic class FactoryWithTestMethodNameAsPrefixTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = Factory.class) Path tempDir) {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t\tassertThat(tempDir.getFileName()).asString().startsWith(\"test\");\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn Files.createTempDirectory(extensionContext.getRequiredTestMethod().getName());\n\t\t\t}\n\t\t}\n\n\t}\n\n\t// https://github.com/junit-team/junit-framework/issues/2088\n\tstatic class FactoryWithCustomParentDirectoryTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = Factory.class) Path tempDir) {\n\t\t\tassertThat(tempDir).exists().hasParent(Factory.parent);\n\t\t\tassertThat(tempDir.getFileName()).asString().startsWith(\"prefix\");\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Nullable\n\t\t\tprivate static Path parent;\n\n\t\t\tprivate Factory() throws IOException {\n\t\t\t\tparent = Files.createTempDirectory(\"parent\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn Files.createTempDirectory(requireNonNull(parent), \"prefix\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class FactoryWithMemoryFileSystemTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = Factory.class) Path tempDir) {\n\t\t\tassertThat(tempDir).exists().hasFileSystem(Factory.fileSystem);\n\t\t\tassertThat(tempDir.getFileName()).asString().startsWith(\"prefix\");\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Nullable\n\t\t\tprivate static FileSystem fileSystem;\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\tfileSystem = MemoryFileSystemBuilder.newEmpty().build();\n\t\t\t\treturn Files.createTempDirectory(fileSystem.getPath(\"/\"), \"prefix\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\trequireNonNull(fileSystem).close();\n\t\t\t\tfileSystem = null;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class FactoryWithJimfsTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = Factory.class) Path tempDir) {\n\t\t\tassertThat(tempDir).exists().hasFileSystem(Factory.fileSystem);\n\t\t\tassertThat(tempDir.getFileName()).asString().startsWith(\"prefix\");\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Nullable\n\t\t\tprivate static FileSystem fileSystem;\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\tfileSystem = Jimfs.newFileSystem(Configuration.unix());\n\t\t\t\treturn Files.createTempDirectory(fileSystem.getPath(\"/\"), \"prefix\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\trequireNonNull(fileSystem).close();\n\t\t\t\tfileSystem = null;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class FactoryWithAnnotatedElementNameAsPrefixTestCase {\n\n\t\t@TempDir(factory = Factory.class)\n\t\tprivate Path tempDir1;\n\n\t\t@Test\n\t\tvoid test(@TempDir(factory = Factory.class) Path tempDir2) {\n\t\t\tassertThat(tempDir1.getFileName()).asString().startsWith(\"tempDir1\");\n\t\t\tassertThat(tempDir2.getFileName()).asString().startsWith(\"tempDir2\");\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn Files.createTempDirectory(getName(elementContext.getAnnotatedElement()));\n\t\t\t}\n\n\t\t\tprivate static String getName(AnnotatedElement element) {\n\t\t\t\treturn element instanceof Field field ? field.getName() : ((Parameter) element).getName();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@NullUnmarked\n\tstatic class FactoryWithCustomMetaAnnotationTestCase {\n\n\t\t@TempDirForField\n\t\tprivate Path tempDir1;\n\n\t\t@Test\n\t\tvoid test(@TempDirForParameter Path tempDir2) {\n\t\t\tassertThat(tempDir1.getFileName()).asString().startsWith(\"field\");\n\t\t\tassertThat(tempDir2.getFileName()).asString().startsWith(\"parameter\");\n\t\t}\n\n\t\t@Target(ANNOTATION_TYPE)\n\t\t@Retention(RUNTIME)\n\t\t@TempDir(factory = FactoryWithCustomMetaAnnotationTestCase.Factory.class)\n\t\tprivate @interface TempDirWithPrefix {\n\n\t\t\tString value();\n\n\t\t}\n\n\t\t@Target(FIELD)\n\t\t@Retention(RUNTIME)\n\t\t@TempDirWithPrefix(\"field\")\n\t\tprivate @interface TempDirForField {\n\t\t}\n\n\t\t@Target(PARAMETER)\n\t\t@Retention(RUNTIME)\n\t\t@TempDirWithPrefix(\"parameter\")\n\t\tprivate @interface TempDirForParameter {\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\tString prefix = elementContext.findAnnotation(TempDirWithPrefix.class) //\n\t\t\t\t\t\t.map(TempDirWithPrefix::value).orElseThrow();\n\t\t\t\treturn Files.createTempDirectory(prefix);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class FactoryNotReturningDirectoryTestCase {\n\n\t\t@Test\n\t\tvoid test(@SuppressWarnings(\"unused\") @TempDir(factory = Factory.class) Path tempDir) {\n\t\t\t// never called\n\t\t}\n\n\t\t@NullMarked\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class FactoryReturningNonDefaultFileSystemPathForFileAnnotatedElementTestCase {\n\n\t\t@Test\n\t\tvoid test(@SuppressWarnings(\"unused\") @TempDir(factory = Factory.class) File tempDir) {\n\t\t\t// never called\n\t\t}\n\n\t\t@NullMarked\n\t\tprivate static class Factory implements TempDirFactory {\n\n\t\t\tprivate final FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());\n\n\t\t\t@Override\n\t\t\tpublic Path createTempDirectory(AnnotatedElementContext elementContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows Exception {\n\t\t\t\treturn Files.createTempDirectory(fileSystem.getPath(\"/\"), \"prefix\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\tfileSystem.close();\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class StandardDefaultFactoryTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir Path tempDir1, @TempDir Path tempDir2) {\n\t\t\tassertNotSame(tempDir1, tempDir2);\n\t\t\tassertThat(tempDir1.getFileName()).asString().startsWith(\"junit\");\n\t\t\tassertThat(tempDir2.getFileName()).asString().startsWith(\"junit\");\n\t\t}\n\n\t}\n\n\tstatic class CustomDefaultFactoryTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir Path tempDir1, @TempDir Path tempDir2) {\n\t\t\tassertNotSame(tempDir1, tempDir2);\n\t\t\tassertThat(tempDir1.getFileName()).asString().startsWith(\"custom\");\n\t\t\tassertThat(tempDir2.getFileName()).asString().startsWith(\"custom\");\n\t\t}\n\n\t}\n\n\tstatic class CustomDefaultFactoryWithStandardDeclarationTestCase {\n\n\t\t@Test\n\t\tvoid test(@TempDir Path tempDir1, @TempDir(factory = Standard.class) Path tempDir2) {\n\t\t\tassertNotSame(tempDir1, tempDir2);\n\t\t\tassertThat(tempDir1.getFileName()).asString().startsWith(\"custom\");\n\t\t\tassertThat(tempDir2.getFileName()).asString().startsWith(\"junit\");\n\t\t}\n\n\t}\n\n\tstatic class CustomDefaultFactoryNotReturningDirectoryTestCase {\n\n\t\t@Test\n\t\tvoid test(@SuppressWarnings(\"unused\") @TempDir Path tempDir) {\n\t\t\t// never called\n\t\t}\n\n\t}\n\n\trecord TempDirRecordTestCase(@TempDir Path tempDir) {\n\t\t@Test\n\t\tvoid shouldExists() {\n\t\t\tassertTrue(Files.exists(tempDir));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestExecutionExceptionHandlerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestExecutionExceptionHandler;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for {@link TestExecutionExceptionHandler}.\n *\n * @since 5.0\n */\nclass TestExecutionExceptionHandlerTests extends AbstractJupiterTestEngineTests {\n\n\tstatic List<String> handlerCalls = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid resetStatics() {\n\t\thandlerCalls.clear();\n\t\tRethrowException.handleExceptionCalled = false;\n\t\tConvertException.handleExceptionCalled = false;\n\t\tSwallowException.handleExceptionCalled = false;\n\t\tShouldNotBeCalled.handleExceptionCalled = false;\n\t}\n\n\t@Test\n\tvoid exceptionHandlerRethrowsException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(ATestCase.class, \"testRethrow\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertTrue(RethrowException.handleExceptionCalled, \"TestExecutionExceptionHandler should have been called\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ATestCase.class), started()), //\n\t\t\tevent(test(\"testRethrow\"), started()), //\n\t\t\tevent(test(\"testRethrow\"), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))), //\n\t\t\tevent(container(ATestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionHandlerSwallowsException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(ATestCase.class, \"testSwallow\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertTrue(SwallowException.handleExceptionCalled, \"TestExecutionExceptionHandler should have been called\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ATestCase.class), started()), //\n\t\t\tevent(test(\"testSwallow\"), started()), //\n\t\t\tevent(test(\"testSwallow\"), finishedSuccessfully()), //\n\t\t\tevent(container(ATestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid exceptionHandlerConvertsException() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(ATestCase.class, \"testConvert\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertTrue(ConvertException.handleExceptionCalled, \"TestExecutionExceptionHandler should have been called\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ATestCase.class), started()), //\n\t\t\tevent(test(\"testConvert\"), started()), //\n\t\t\tevent(test(\"testConvert\"), finishedWithFailure(instanceOf(IOException.class), message(\"checked\"))), //\n\t\t\tevent(container(ATestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid severalHandlersAreCalledInOrder() {\n\t\tLauncherDiscoveryRequest request = request().selectors(selectMethod(ATestCase.class, \"testSeveral\")).build();\n\n\t\tEngineExecutionResults executionResults = executeTests(request);\n\n\t\tassertTrue(ConvertException.handleExceptionCalled, \"ConvertException should have been called\");\n\t\tassertTrue(RethrowException.handleExceptionCalled, \"RethrowException should have been called\");\n\t\tassertTrue(SwallowException.handleExceptionCalled, \"SwallowException should have been called\");\n\t\tassertFalse(ShouldNotBeCalled.handleExceptionCalled, \"ShouldNotBeCalled should not have been called\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(ATestCase.class), started()), //\n\t\t\tevent(test(\"testSeveral\"), started()), //\n\t\t\tevent(test(\"testSeveral\"), finishedSuccessfully()), //\n\t\t\tevent(container(ATestCase.class), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\n\t\tassertEquals(Arrays.asList(\"convert\", \"rethrow\", \"swallow\"), handlerCalls);\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tstatic class ATestCase {\n\n\t\t@Test\n\t\t@ExtendWith(RethrowException.class)\n\t\tvoid testRethrow() throws IOException {\n\t\t\tthrow new IOException(\"checked\");\n\t\t}\n\n\t\t@Test\n\t\t@ExtendWith(SwallowException.class)\n\t\tvoid testSwallow() throws IOException {\n\t\t\tthrow new IOException(\"checked\");\n\t\t}\n\n\t\t@Test\n\t\t@ExtendWith(ConvertException.class)\n\t\tvoid testConvert() {\n\t\t\tthrow new RuntimeException(\"unchecked\");\n\t\t}\n\n\t\t@Test\n\t\t@ExtendWith(ShouldNotBeCalled.class)\n\t\t@ExtendWith(SwallowException.class)\n\t\t@ExtendWith(RethrowException.class)\n\t\t@ExtendWith(ConvertException.class)\n\t\tvoid testSeveral() {\n\t\t\tthrow new RuntimeException(\"unchecked\");\n\t\t}\n\t}\n\n\tstatic class RethrowException implements TestExecutionExceptionHandler {\n\n\t\tstatic boolean handleExceptionCalled = false;\n\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\t\tassertInstanceOf(IOException.class, throwable);\n\t\t\thandleExceptionCalled = true;\n\t\t\thandlerCalls.add(\"rethrow\");\n\n\t\t\tthrow throwable;\n\t\t}\n\t}\n\n\tstatic class SwallowException implements TestExecutionExceptionHandler {\n\n\t\tstatic boolean handleExceptionCalled = false;\n\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\tassertInstanceOf(IOException.class, throwable);\n\t\t\thandleExceptionCalled = true;\n\t\t\thandlerCalls.add(\"swallow\");\n\t\t\t//swallow exception by not rethrowing it\n\t\t}\n\t}\n\n\tstatic class ConvertException implements TestExecutionExceptionHandler {\n\n\t\tstatic boolean handleExceptionCalled = false;\n\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) throws Throwable {\n\t\t\tassertInstanceOf(RuntimeException.class, throwable);\n\t\t\thandleExceptionCalled = true;\n\t\t\thandlerCalls.add(\"convert\");\n\t\t\tthrow new IOException(\"checked\");\n\t\t}\n\n\t}\n\n\tstatic class ShouldNotBeCalled implements TestExecutionExceptionHandler {\n\n\t\tstatic boolean handleExceptionCalled = false;\n\n\t\t@Override\n\t\tpublic void handleTestExecutionException(ExtensionContext context, Throwable throwable) {\n\t\t\thandleExceptionCalled = true;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInfoParameterResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\n\n/**\n * Integration tests for {@link TestInfoParameterResolver}.\n *\n * @since 5.0\n */\n@Tag(\"class-tag\")\nclass TestInfoParameterResolverTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> allDisplayNames = Arrays.asList(\"defaultDisplayName(TestInfo)\",\n\t\t\"custom display name\", \"getTags(TestInfo)\", \"customDisplayNameThatIsEmpty()\");\n\n\tTestInfoParameterResolverTests(TestInfo testInfo) {\n\t\tassertThat(testInfo.getTestClass()).contains(TestInfoParameterResolverTests.class);\n\t\tassertThat(testInfo.getTestMethod()).isPresent();\n\t}\n\n\t@Test\n\tvoid defaultDisplayName(TestInfo testInfo) {\n\t\tassertEquals(\"defaultDisplayName(TestInfo)\", testInfo.getDisplayName());\n\t}\n\n\t@Test\n\t@DisplayName(\"custom display name\")\n\tvoid providedDisplayName(TestInfo testInfo) {\n\t\tassertEquals(\"custom display name\", testInfo.getDisplayName());\n\t}\n\n\t@Test\n\tvoid customDisplayNameThatIsEmpty() {\n\t\texecuteTests(request -> request //\n\t\t\t\t.selectors(selectClass(BlankDisplayNameTestCase.class)) //\n\t\t\t\t.configurationParameter(CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, Severity.ERROR.name())) //\n\t\t\t\t\t\t.testEvents() //\n\t\t\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\t}\n\n\t@Test\n\t@Tag(\"method-tag\")\n\tvoid getTags(TestInfo testInfo) {\n\t\tassertEquals(2, testInfo.getTags().size());\n\t\tassertTrue(testInfo.getTags().contains(\"method-tag\"));\n\t\tassertTrue(testInfo.getTags().contains(\"class-tag\"));\n\t}\n\n\t@BeforeEach\n\t@AfterEach\n\tvoid beforeAndAfter(TestInfo testInfo) {\n\t\tassertThat(allDisplayNames).contains(testInfo.getDisplayName());\n\t}\n\n\t@BeforeAll\n\tstatic void beforeAll(TestInfo testInfo) {\n\t\tSet<String> tags = testInfo.getTags();\n\t\tassertEquals(1, tags.size());\n\t\tassertTrue(tags.contains(\"class-tag\"));\n\t}\n\n\t@BeforeAll\n\t@AfterAll\n\tstatic void beforeAndAfterAll(TestInfo testInfo) {\n\t\tassertEquals(TestInfoParameterResolverTests.class.getSimpleName(), testInfo.getDisplayName());\n\t}\n\n\tstatic class BlankDisplayNameTestCase {\n\n\t\t@Test\n\t\t@DisplayName(\"\")\n\t\tvoid test(TestInfo testInfo) {\n\t\t\tassertEquals(\"test(TestInfo)\", testInfo.getDisplayName());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstanceFactoryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;\nimport static org.junit.platform.commons.util.ClassUtils.nullSafeToString;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.nestedContainer;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TestInstanceFactory;\nimport org.junit.jupiter.api.extension.TestInstanceFactoryContext;\nimport org.junit.jupiter.api.extension.TestInstantiationException;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests that verify support for {@link TestInstanceFactory}.\n *\n * @since 5.3\n */\nclass TestInstanceFactoryTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid multipleFactoriesRegisteredOnSingleTestClass() {\n\t\tClass<?> testClass = MultipleFactoriesRegisteredOnSingleTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(\"The following TestInstanceFactory extensions were registered for test class [\"\n\t\t\t\t\t\t\t+ testClass.getName() + \"], but only one is permitted: \"\n\t\t\t\t\t\t\t+ nullSafeToString(FooInstanceFactory.class, BarInstanceFactory.class)))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid multipleFactoriesRegisteredWithinTestClassHierarchy() {\n\t\tClass<?> testClass = MultipleFactoriesRegisteredWithinClassHierarchyTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(\"The following TestInstanceFactory extensions were registered for test class [\"\n\t\t\t\t\t\t\t+ testClass.getName() + \"], but only one is permitted: \"\n\t\t\t\t\t\t\t+ nullSafeToString(FooInstanceFactory.class, BarInstanceFactory.class)))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid multipleFactoriesRegisteredWithinNestedClassStructure() {\n\t\tClass<?> outerClass = MultipleFactoriesRegisteredWithinNestedClassStructureTestCase.class;\n\t\tClass<?> nestedClass = MultipleFactoriesRegisteredWithinNestedClassStructureTestCase.InnerTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(outerClass);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(outerClass), started()), //\n\t\t\tevent(test(\"outerTest()\"), started()), //\n\t\t\tevent(test(\"outerTest()\"), finishedSuccessfully()), //\n\t\t\tevent(nestedContainer(nestedClass), started()), //\n\t\t\tevent(nestedContainer(nestedClass),\n\t\t\t\tfinishedWithFailure(instanceOf(ExtensionConfigurationException.class),\n\t\t\t\t\tmessage(\"The following TestInstanceFactory extensions were registered for test class [\"\n\t\t\t\t\t\t\t+ nestedClass.getName() + \"], but only one is permitted: \"\n\t\t\t\t\t\t\t+ nullSafeToString(FooInstanceFactory.class, BarInstanceFactory.class)))), //\n\t\t\tevent(container(outerClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nullTestInstanceFactoryWithPerMethodLifecycle() {\n\t\tClass<?> testClass = NullTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"),\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(m -> m.equals(\"TestInstanceFactory [\" + NullTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to return an instance of [\" + testClass.getName()\n\t\t\t\t\t\t\t+ \"] and instead returned an instance of [null].\")))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid nullTestInstanceFactoryWithPerClassLifecycle() {\n\t\tClass<?> testClass = PerClassLifecycleNullTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(m -> m.equals(\"TestInstanceFactory [\" + NullTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to return an instance of [\" + testClass.getName()\n\t\t\t\t\t\t\t+ \"] and instead returned an instance of [null].\")))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid bogusTestInstanceFactoryWithPerMethodLifecycle() {\n\t\tClass<?> testClass = BogusTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"),\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(m -> m.equals(\"TestInstanceFactory [\" + BogusTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to return an instance of [\" + testClass.getName()\n\t\t\t\t\t\t\t+ \"] and instead returned an instance of [java.lang.String].\")))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid bogusTestInstanceFactoryWithPerClassLifecycle() {\n\t\tClass<?> testClass = PerClassLifecycleBogusTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass),\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(m -> m.equals(\"TestInstanceFactory [\" + BogusTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to return an instance of [\" + testClass.getName()\n\t\t\t\t\t\t\t+ \"] and instead returned an instance of [java.lang.String].\")))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid explosiveTestInstanceFactoryWithPerMethodLifecycle() {\n\t\tClass<?> testClass = ExplosiveTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"), started()), //\n\t\t\tevent(test(\"testShouldNotBeCalled\"),\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(\"TestInstanceFactory [\" + ExplosiveTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to instantiate test class [\" + testClass.getName() + \"]: boom!\"))), //\n\t\t\tevent(container(testClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid explosiveTestInstanceFactoryWithPerClassLifecycle() {\n\t\tClass<?> testClass = PerClassLifecycleExplosiveTestInstanceFactoryTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), //\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(\"TestInstanceFactory [\" + ExplosiveTestInstanceFactory.class.getName()\n\t\t\t\t\t\t\t+ \"] failed to instantiate test class [\" + testClass.getName() + \"]: boom!\"))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid proxyTestInstanceFactoryFailsDueToUseOfDifferentClassLoader() {\n\t\tClass<?> testClass = ProxiedTestCase.class;\n\t\tEngineExecutionResults executionResults = executeTestsForClass(testClass);\n\n\t\tassertEquals(0, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(0, executionResults.testEvents().failed().count(), \"# tests aborted\");\n\n\t\texecutionResults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(testClass), started()), //\n\t\t\tevent(container(testClass), //\n\t\t\t\t// NOTE: the test class names are the same even though the objects are\n\t\t\t\t// instantiated using different ClassLoaders. Thus, we check for the\n\t\t\t\t// appended \"@\" but ignore the actual hash code for the test class\n\t\t\t\t// loaded by the different ClassLoader.\n\t\t\t\tfinishedWithFailure(instanceOf(TestInstantiationException.class),\n\t\t\t\t\tmessage(m -> m.startsWith(\"TestInstanceFactory [\" + ProxyTestInstanceFactory.class.getName() + \"]\")\n\t\t\t\t\t\t\t&& m.contains(\"failed to return an instance of [\" + testClass.getName() + \"@\"\n\t\t\t\t\t\t\t\t\t+ Integer.toHexString(System.identityHashCode(testClass)))\n\t\t\t\t\t\t\t&& m.contains(\"and instead returned an instance of [\" + testClass.getName() + \"@\")//\n\t\t\t\t\t))), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@Test\n\tvoid instanceFactoryOnTopLevelTestClass() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(ParentTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"FooInstanceFactory instantiated: ParentTestCase\",\n\t\t\t\t\"parentTest\",\n\t\t\t\"close ParentTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactorySupportedWhenTestClassDeclaresMultipleConstructors() {\n\t\texecuteTestsForClass(MultipleConstructorsTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"MultipleConstructorsTestInstanceFactory instantiated: MultipleConstructorsTestCase\",\n\t\t\t\t\"test: 42\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid inheritedFactoryInTestClassHierarchy() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(InheritedFactoryTestCase.class);\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"FooInstanceFactory instantiated: InheritedFactoryTestCase\",\n\t\t\t\t\"parentTest\",\n\t\t\t\"close InheritedFactoryTestCase\",\n\t\t\t\"FooInstanceFactory instantiated: InheritedFactoryTestCase\",\n\t\t\t\t\"childTest\",\n\t\t\t\"close InheritedFactoryTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoriesInNestedClassStructureAreInherited() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(OuterTestCase.class);\n\n\t\tassertEquals(3, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(3, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\n\t\t\t// OuterTestCase\n\t\t\t\"FooInstanceFactory instantiated: OuterTestCase\",\n\t\t\t\t\"outerTest\",\n\t\t\t\"close OuterTestCase\",\n\n\t\t\t// InnerTestCase\n\t\t\t\"FooInstanceFactory instantiated: OuterTestCase\",\n\t\t\t\t\"FooInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\t\"innerTest1\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\"close OuterTestCase\",\n\n\t\t\t// InnerInnerTestCase\n\t\t\t\"FooInstanceFactory instantiated: OuterTestCase\",\n\t\t\t\t\"FooInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\t\"FooInstanceFactory instantiated: InnerInnerTestCase\",\n\t\t\t\t\t\t\"innerTest2\",\n\t\t\t\t\t\"close InnerInnerTestCase\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\"close OuterTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoryRegisteredViaTestInterface() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(FactoryFromInterfaceTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"FooInstanceFactory instantiated: FactoryFromInterfaceTestCase\",\n\t\t\t\t\"test\",\n\t\t\t\"close FactoryFromInterfaceTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoryRegisteredAsLambdaExpression() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(LambdaFactoryTestCase.class);\n\n\t\tassertEquals(1, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(1, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"beforeEach: lambda\",\n\t\t\t\t\"test: lambda\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoryWithPerClassLifecycle() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(PerClassLifecycleTestCase.class);\n\n\t\tassertEquals(1, PerClassLifecycleTestCase.counter.get());\n\n\t\tassertEquals(2, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(2, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"FooInstanceFactory instantiated: PerClassLifecycleTestCase\",\n\t\t\t\t\"@BeforeAll\",\n\t\t\t\t\t\"@BeforeEach\",\n\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\"@BeforeEach\",\n\t\t\t\t\t\t\"test2\",\n\t\t\t\t\"@AfterAll\",\n\t\t\t\"close PerClassLifecycleTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoryWithLegacyContext() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(LegacyContextTestCase.class);\n\n\t\tassertEquals(3, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(3, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"outerTest\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\"innerTest1\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\"innerTest2\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\t\"close LegacyContextTestCase\",\n\t\t\t\t\"close LegacyContextTestCase\",\n\t\t\t\t\"close LegacyContextTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid instanceFactoryWithLegacyContextAndChangedDefaultScope() {\n\t\tvar executionResults = executeTests(request() //\n\t\t\t\t.selectors(selectClass(LegacyContextTestCase.class)) //\n\t\t\t\t.configurationParameter(\n\t\t\t\t\tConstants.DEFAULT_TEST_CLASS_INSTANCE_CONSTRUCTION_EXTENSION_CONTEXT_SCOPE_PROPERTY_NAME,\n\t\t\t\t\tTEST_METHOD.name()));\n\n\t\tassertEquals(3, executionResults.testEvents().started().count(), \"# tests started\");\n\t\tassertEquals(3, executionResults.testEvents().succeeded().count(), \"# tests succeeded\");\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"outerTest\",\n\t\t\t\t\"close LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\"innerTest1\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\t\"close LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: LegacyContextTestCase\",\n\t\t\t\t\"LegacyInstanceFactory instantiated: InnerTestCase\",\n\t\t\t\t\"innerTest2\",\n\t\t\t\t\"close InnerTestCase\",\n\t\t\t\t\"close LegacyContextTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith({ FooInstanceFactory.class, BarInstanceFactory.class })\n\tstatic class MultipleFactoriesRegisteredOnSingleTestCase {\n\n\t\t@Test\n\t\tvoid testShouldNotBeCalled() {\n\t\t\tcallSequence.add(\"testShouldNotBeCalled\");\n\t\t}\n\t}\n\n\t@ExtendWith(NullTestInstanceFactory.class)\n\tstatic class NullTestInstanceFactoryTestCase {\n\n\t\t@Test\n\t\tvoid testShouldNotBeCalled() {\n\t\t\tcallSequence.add(\"testShouldNotBeCalled\");\n\t\t}\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class PerClassLifecycleNullTestInstanceFactoryTestCase extends NullTestInstanceFactoryTestCase {\n\t}\n\n\t@ExtendWith(BogusTestInstanceFactory.class)\n\tstatic class BogusTestInstanceFactoryTestCase {\n\n\t\t@Test\n\t\tvoid testShouldNotBeCalled() {\n\t\t\tcallSequence.add(\"testShouldNotBeCalled\");\n\t\t}\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class PerClassLifecycleBogusTestInstanceFactoryTestCase extends BogusTestInstanceFactoryTestCase {\n\t}\n\n\t@ExtendWith(ExplosiveTestInstanceFactory.class)\n\tstatic class ExplosiveTestInstanceFactoryTestCase {\n\n\t\t@Test\n\t\tvoid testShouldNotBeCalled() {\n\t\t\tcallSequence.add(\"testShouldNotBeCalled\");\n\t\t}\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class PerClassLifecycleExplosiveTestInstanceFactoryTestCase extends ExplosiveTestInstanceFactoryTestCase {\n\t}\n\n\tprivate static class MultipleConstructorsTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\tinstantiated(getClass(), factoryContext.getTestClass());\n\t\t\treturn new MultipleConstructorsTestCase(42);\n\t\t}\n\t}\n\n\t@ExtendWith(MultipleConstructorsTestInstanceFactory.class)\n\tstatic class MultipleConstructorsTestCase {\n\n\t\tprivate final int number;\n\n\t\tMultipleConstructorsTestCase(String text) {\n\t\t\tthis.number = -1;\n\t\t}\n\n\t\tMultipleConstructorsTestCase(int number) {\n\t\t\tthis.number = number;\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test: \" + this.number);\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstanceFactory.class)\n\tstatic class ParentTestCase {\n\n\t\t@Test\n\t\tvoid parentTest() {\n\t\t\tcallSequence.add(\"parentTest\");\n\t\t}\n\t}\n\n\tstatic class InheritedFactoryTestCase extends ParentTestCase {\n\n\t\t@Test\n\t\tvoid childTest() {\n\t\t\tcallSequence.add(\"childTest\");\n\t\t}\n\t}\n\n\t@ExtendWith(BarInstanceFactory.class)\n\tstatic class MultipleFactoriesRegisteredWithinClassHierarchyTestCase extends ParentTestCase {\n\n\t\t@Test\n\t\tvoid childTest() {\n\t\t\tcallSequence.add(\"childTest\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstanceFactory.class)\n\tstatic class OuterTestCase {\n\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t\tcallSequence.add(\"outerTest\");\n\t\t}\n\n\t\t@Nested\n\t\tclass InnerTestCase {\n\n\t\t\t@Test\n\t\t\tvoid innerTest1() {\n\t\t\t\tcallSequence.add(\"innerTest1\");\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass InnerInnerTestCase {\n\n\t\t\t\t@Test\n\t\t\t\tvoid innerTest2() {\n\t\t\t\t\tcallSequence.add(\"innerTest2\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstanceFactory.class)\n\tstatic class MultipleFactoriesRegisteredWithinNestedClassStructureTestCase {\n\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(BarInstanceFactory.class)\n\t\tclass InnerTestCase {\n\n\t\t\t@Test\n\t\t\tvoid innerTest() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstanceFactory.class)\n\tinterface TestInterface {\n\t}\n\n\tstatic class FactoryFromInterfaceTestCase implements TestInterface {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\t}\n\n\tstatic class LambdaFactoryTestCase {\n\n\t\tprivate final String text;\n\n\t\t@RegisterExtension\n\t\tstatic final TestInstanceFactory factory = (__, ___) -> new LambdaFactoryTestCase(\"lambda\");\n\n\t\tLambdaFactoryTestCase(String text) {\n\t\t\tthis.text = text;\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"beforeEach: \" + this.text);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test: \" + this.text);\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstanceFactory.class)\n\t@TestInstance(PER_CLASS)\n\tstatic class PerClassLifecycleTestCase {\n\n\t\tstatic final AtomicInteger counter = new AtomicInteger();\n\n\t\tPerClassLifecycleTestCase() {\n\t\t\tcounter.incrementAndGet();\n\t\t}\n\n\t\t@BeforeAll\n\t\tvoid beforeAll() {\n\t\t\tcallSequence.add(\"@BeforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tcallSequence.add(\"@BeforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid afterAll() {\n\t\t\tcallSequence.add(\"@AfterAll\");\n\t\t}\n\t}\n\n\t@ExtendWith(LegacyInstanceFactory.class)\n\tstatic class LegacyContextTestCase {\n\n\t\t@Test\n\t\tvoid outerTest() {\n\t\t\tcallSequence.add(\"outerTest\");\n\t\t}\n\n\t\t@Nested\n\t\tclass InnerTestCase {\n\n\t\t\t@Test\n\t\t\tvoid innerTest1() {\n\t\t\t\tcallSequence.add(\"innerTest1\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid innerTest2() {\n\t\t\t\tcallSequence.add(\"innerTest2\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(ProxyTestInstanceFactory.class)\n\t@TestInstance(PER_CLASS)\n\tstatic class ProxiedTestCase {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static abstract class AbstractTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\tClass<?> testClass = factoryContext.getTestClass();\n\t\t\tinstantiated(getClass(), testClass);\n\n\t\t\textensionContext.getStore(ExtensionContext.Namespace.create(this)).put(new Object(),\n\t\t\t\t(AutoCloseable) () -> callSequence.add(\"close \" + testClass.getSimpleName()));\n\n\t\t\tif (factoryContext.getOuterInstance().isPresent()) {\n\t\t\t\treturn ReflectionSupport.newInstance(testClass, factoryContext.getOuterInstance().get());\n\t\t\t}\n\t\t\t// else\n\t\t\treturn ReflectionSupport.newInstance(testClass);\n\t\t}\n\t}\n\n\tprivate static class FooInstanceFactory extends AbstractTestInstanceFactory {\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tprivate static class BarInstanceFactory extends AbstractTestInstanceFactory {\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tprivate static class LegacyInstanceFactory extends AbstractTestInstanceFactory {\n\t}\n\n\t/**\n\t * {@link TestInstanceFactory} that returns null.\n\t */\n\tprivate static class NullTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * {@link TestInstanceFactory} that returns an object of a type that does\n\t * not match the supplied test class.\n\t */\n\tprivate static class BogusTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\treturn \"bogus\";\n\t\t}\n\t}\n\n\t/**\n\t * {@link TestInstanceFactory} that always throws an exception.\n\t */\n\tprivate static class ExplosiveTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\tthrow new RuntimeException(\"boom!\");\n\t\t}\n\t}\n\n\t/**\n\t * This does not actually create a proxy. Rather, it simulates what\n\t * a proxy-based implementation might do, by loading the class from a\n\t * different {@link ClassLoader}.\n\t */\n\tprivate static class ProxyTestInstanceFactory implements TestInstanceFactory {\n\n\t\t@Override\n\t\tpublic Object createTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext) {\n\t\t\tClass<?> testClass = factoryContext.getTestClass();\n\t\t\tString className = testClass.getName();\n\n\t\t\tinstantiated(getClass(), testClass);\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(testClass)) {\n\t\t\t\t// Load test class from different class loader\n\t\t\t\tClass<?> clazz = testClassLoader.loadClass(className);\n\t\t\t\treturn ReflectionSupport.newInstance(clazz);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tthrow new RuntimeException(\"Failed to load class [\" + className + \"]\", ex);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static void instantiated(Class<? extends TestInstanceFactory> factoryClass, Class<?> testClass) {\n\t\tcallSequence.add(factoryClass.getSimpleName() + \" instantiated: \" + testClass.getSimpleName());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorAndPreDestroyCallbackTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.Arrays.asList;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\n\n/**\n * Integration tests that verify support for {@link TestInstancePostProcessor}\n * and {@link TestInstancePreDestroyCallback} in the {@link JupiterTestEngine}.\n *\n * @since 5.6\n */\nclass TestInstancePostProcessorAndPreDestroyCallbackTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@Test\n\tvoid postProcessorAndPreDestroyCallbacks() {\n\t\t// @formatter:off\n\t\tassertPostProcessorAndPreDestroyCallbacks(TopLevelTestCase.class,\n\t\t\t\"fooPostProcessTestInstance\",\n\t\t\t\"barPostProcessTestInstance\",\n\t\t\t\t\"test-1\",\n\t\t\t\"barPreDestroyTestInstance\",\n\t\t\t\"fooPreDestroyTestInstance\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid postProcessorAndPreDestroyCallbacksInSubclass() {\n\t\t// @formatter:off\n\t\tassertPostProcessorAndPreDestroyCallbacks(SecondLevelTestCase.class,\n\t\t\t\"fooPostProcessTestInstance\",\n\t\t\t\"barPostProcessTestInstance\",\n\t\t\t\t\"bazPostProcessTestInstance\",\n\t\t\t\t\t\"test-2\",\n\t\t\t\t\"bazPreDestroyTestInstance\",\n\t\t\t\"barPreDestroyTestInstance\",\n\t\t\t\"fooPreDestroyTestInstance\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid postProcessorAndPreDestroyCallbacksInSubSubclass() {\n\t\t// @formatter:off\n\t\tassertPostProcessorAndPreDestroyCallbacks(ThirdLevelTestCase.class,\n\t\t\t\"fooPostProcessTestInstance\",\n\t\t\t\"barPostProcessTestInstance\",\n\t\t\t\t\"bazPostProcessTestInstance\",\n\t\t\t\t\t\"quuxPostProcessTestInstance\",\n\t\t\t\t\t\t\"test-3\",\n\t\t\t\t\t\"quuxPreDestroyTestInstance\",\n\t\t\t\t\"bazPreDestroyTestInstance\",\n\t\t\t\"barPreDestroyTestInstance\",\n\t\t\t\"fooPreDestroyTestInstance\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid preDestroyTestInstanceMethodThrowsAnException() {\n\t\t// @formatter:off\n\t\tassertPostProcessorAndPreDestroyCallbacks(ExceptionInTestInstancePreDestroyCallbackTestCase.class, 0,\n\t\t\t\"fooPostProcessTestInstance\",\n\t\t\t\t\"test\",\n\t\t\t\"exceptionThrowingTestInstancePreDestroyCallback\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid postProcessTestInstanceMethodThrowsAnException() {\n\t\tassertPostProcessorAndPreDestroyCallbacks(ExceptionInTestInstancePostProcessorTestCase.class, 0,\n\t\t\t\"exceptionThrowingTestInstancePostProcessor\", \"exceptionPreDestroyTestInstance\");\n\t}\n\n\t@Test\n\tvoid testClassConstructorThrowsAnException() {\n\t\tassertPostProcessorAndPreDestroyCallbacks(ExceptionInTestClassConstructorTestCase.class, 0,\n\t\t\t\"exceptionThrowingConstructor\");\n\t}\n\n\tprivate void assertPostProcessorAndPreDestroyCallbacks(Class<?> testClass, String... expectedCalls) {\n\t\tassertPostProcessorAndPreDestroyCallbacks(testClass, 1, expectedCalls);\n\t}\n\n\tprivate void assertPostProcessorAndPreDestroyCallbacks(Class<?> testClass, int testsSuccessful,\n\t\t\tString... expectedCalls) {\n\n\t\tcallSequence.clear();\n\n\t\texecuteTestsForClass(testClass).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(testsSuccessful));\n\n\t\tassertEquals(asList(expectedCalls), callSequence, () -> \"wrong call sequence for \" + testClass.getName());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t// Must NOT be private; otherwise, the @Test method gets discovered but never executed.\n\t@ExtendWith({ FooTestInstanceCallbacks.class, BarTestInstanceCallbacks.class })\n\tstatic class TopLevelTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-1\");\n\t\t}\n\t}\n\n\t// Must NOT be private; otherwise, the @Test method gets discovered but never executed.\n\t@ExtendWith(BazTestInstanceCallbacks.class)\n\tstatic class SecondLevelTestCase extends TopLevelTestCase {\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-2\");\n\t\t}\n\t}\n\n\t@ExtendWith(QuuxTestInstanceCallbacks.class)\n\tstatic class ThirdLevelTestCase extends SecondLevelTestCase {\n\n\t\t@Test\n\t\t@Override\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test-3\");\n\t\t}\n\t}\n\n\t@ExtendWith(ExceptionThrowingTestInstancePreDestroyCallback.class)\n\tstatic class ExceptionInTestInstancePreDestroyCallbackTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\t}\n\n\t@ExtendWith(ExceptionThrowingTestInstancePostProcessor.class)\n\tstatic class ExceptionInTestInstancePostProcessorTestCase {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\t}\n\n\t@ExtendWith(FooTestInstanceCallbacks.class)\n\tstatic class ExceptionInTestClassConstructorTestCase {\n\n\t\tExceptionInTestClassConstructorTestCase() {\n\t\t\tcallSequence.add(\"exceptionThrowingConstructor\");\n\t\t\tthrow new RuntimeException(\"in constructor\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class FooTestInstanceCallbacks extends AbstractTestInstanceCallbacks {\n\n\t\tprotected FooTestInstanceCallbacks() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\t}\n\n\tstatic class BarTestInstanceCallbacks extends AbstractTestInstanceCallbacks {\n\n\t\tprotected BarTestInstanceCallbacks() {\n\t\t\tsuper(\"bar\");\n\t\t}\n\t}\n\n\tstatic class BazTestInstanceCallbacks extends AbstractTestInstanceCallbacks {\n\n\t\tprotected BazTestInstanceCallbacks() {\n\t\t\tsuper(\"baz\");\n\t\t}\n\t}\n\n\tstatic class QuuxTestInstanceCallbacks extends AbstractTestInstanceCallbacks {\n\n\t\tprotected QuuxTestInstanceCallbacks() {\n\t\t\tsuper(\"quux\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingTestInstancePreDestroyCallback extends AbstractTestInstanceCallbacks {\n\n\t\tprotected ExceptionThrowingTestInstancePreDestroyCallback() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingTestInstancePreDestroyCallback\");\n\t\t\tthrow new EnigmaException(\"preDestroyTestInstance\");\n\t\t}\n\t}\n\n\tstatic class ExceptionThrowingTestInstancePostProcessor extends AbstractTestInstanceCallbacks {\n\n\t\tprotected ExceptionThrowingTestInstancePostProcessor() {\n\t\t\tsuper(\"exception\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\tcallSequence.add(\"exceptionThrowingTestInstancePostProcessor\");\n\t\t\tthrow new EnigmaException(\"postProcessTestInstance\");\n\t\t}\n\t}\n\n\tprivate static abstract class AbstractTestInstanceCallbacks\n\t\t\timplements TestInstancePostProcessor, TestInstancePreDestroyCallback {\n\n\t\tprivate final String name;\n\n\t\tAbstractTestInstanceCallbacks(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\tcallSequence.add(name + \"PostProcessTestInstance\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\t\tcallSequence.add(name + \"PreDestroyTestInstance\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePostProcessorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\n/**\n * Integration tests that verify support for {@link TestInstancePostProcessor}.\n *\n * @since 5.0\n */\nclass TestInstancePostProcessorTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid instancePostProcessorsInNestedClasses() {\n\t\texecuteTestsForClass(OuterTestCase.class).testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\n\t\t\t// OuterTestCase\n\t\t\t\"foo:OuterTestCase\",\n\t\t\t\"legacy:OuterTestCase\",\n\t\t\t\t\"beforeOuterMethod\",\n\t\t\t\t\t\"testOuter\",\n\t\t\t\"close:foo:OuterTestCase\",\n\n\t\t\t// InnerTestCase\n\n\t\t\t\"foo:OuterTestCase\",\n\t\t\t\"legacy:OuterTestCase\",\n\t\t\t\"foo:InnerTestCase\",\n\t\t\t\"legacy:InnerTestCase\",\n\t\t\t\t\"bar:InnerTestCase\",\n\t\t\t\t\t\"beforeOuterMethod\",\n\t\t\t\t\t\t\"beforeInnerMethod\",\n\t\t\t\t\t\t\t\"testInner\",\n\t\t\t\t\"close:bar:InnerTestCase\",\n\t\t\t\"close:foo:InnerTestCase\",\n\t\t\t\"close:foo:OuterTestCase\",\n\t\t\t\"close:legacy:InnerTestCase\",\n\t\t\t\"close:legacy:OuterTestCase\",\n\t\t\t\"close:legacy:OuterTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid testSpecificTestInstancePostProcessorIsCalled() {\n\t\texecuteTestsForClass(TestCaseWithTestSpecificTestInstancePostProcessor.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"foo:TestCaseWithTestSpecificTestInstancePostProcessor\",\n\t\t\t\"legacy:TestCaseWithTestSpecificTestInstancePostProcessor\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"test1\",\n\t\t\t\"close:foo:TestCaseWithTestSpecificTestInstancePostProcessor\",\n\t\t\t\t\"beforeEachMethod\",\n\t\t\t\t\t\"test2\",\n\t\t\t\"close:legacy:TestCaseWithTestSpecificTestInstancePostProcessor\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------\n\n\t@ExtendWith(FooInstancePostProcessor.class)\n\t@ExtendWith(LegacyInstancePostProcessor.class)\n\tstatic class OuterTestCase implements Named {\n\n\t\tprivate final Map<String, String> outerNames = new HashMap<>();\n\n\t\t@Override\n\t\tpublic void setName(String source, String name) {\n\t\t\touterNames.put(source, name);\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeOuterMethod() {\n\t\t\tcallSequence.add(\"beforeOuterMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testOuter() {\n\t\t\tassertEquals(\n\t\t\t\tMap.of(\"foo\", OuterTestCase.class.getSimpleName(), \"legacy\", OuterTestCase.class.getSimpleName()),\n\t\t\t\touterNames);\n\t\t\tcallSequence.add(\"testOuter\");\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(BarInstancePostProcessor.class)\n\t\tclass InnerTestCase implements Named {\n\n\t\t\tprivate final Map<String, String> innerNames = new HashMap<>();\n\n\t\t\t@Override\n\t\t\tpublic void setName(String source, String name) {\n\t\t\t\tinnerNames.put(source, name);\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeInnerMethod() {\n\t\t\t\tcallSequence.add(\"beforeInnerMethod\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid testInner() {\n\t\t\t\tassertEquals(\n\t\t\t\t\tMap.of(\"foo\", InnerTestCase.class.getSimpleName(), \"legacy\", OuterTestCase.class.getSimpleName()),\n\t\t\t\t\touterNames);\n\t\t\t\tassertEquals(Map.of(\"foo\", InnerTestCase.class.getSimpleName(), \"bar\",\n\t\t\t\t\tInnerTestCase.class.getSimpleName(), \"legacy\", InnerTestCase.class.getSimpleName()), innerNames);\n\t\t\t\tcallSequence.add(\"testInner\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\tstatic class TestCaseWithTestSpecificTestInstancePostProcessor implements Named {\n\n\t\tprivate final Map<String, String> names = new HashMap<>();\n\n\t\t@Override\n\t\tpublic void setName(String source, String name) {\n\t\t\tnames.put(source, name);\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEachMethod() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@ExtendWith(FooInstancePostProcessor.class)\n\t\t@ExtendWith(LegacyInstancePostProcessor.class)\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t\tassertEquals(Map.of(\"foo\", getClass().getSimpleName(), \"legacy\", getClass().getSimpleName()), names);\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t\tassertEquals(Map.of(), names);\n\t\t}\n\t}\n\n\tstatic abstract class AbstractInstancePostProcessor implements TestInstancePostProcessor {\n\t\tprivate final String name;\n\n\t\tAbstractInstancePostProcessor(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\tif (testInstance instanceof Named named) {\n\t\t\t\tnamed.setName(name, context.getRequiredTestClass().getSimpleName());\n\t\t\t}\n\t\t\tString instanceType = testInstance.getClass().getSimpleName();\n\t\t\tcallSequence.add(name + \":\" + instanceType);\n\t\t\tcontext.getStore(ExtensionContext.Namespace.create(this)).put(new Object(),\n\t\t\t\t(AutoCloseable) () -> callSequence.add(\"close:\" + name + \":\" + instanceType));\n\t\t}\n\t}\n\n\tstatic class FooInstancePostProcessor extends AbstractInstancePostProcessor {\n\t\tFooInstancePostProcessor() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tstatic class BarInstancePostProcessor extends AbstractInstancePostProcessor {\n\t\tBarInstancePostProcessor() {\n\t\t\tsuper(\"bar\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tstatic class LegacyInstancePostProcessor extends AbstractInstancePostProcessor {\n\t\tLegacyInstancePostProcessor() {\n\t\t\tsuper(\"legacy\");\n\t\t}\n\t}\n\n\tprivate interface Named {\n\n\t\tvoid setName(String source, String name);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreConstructCallbackTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope.TEST_METHOD;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TestInstanceFactory;\nimport org.junit.jupiter.api.extension.TestInstanceFactoryContext;\nimport org.junit.jupiter.api.extension.TestInstancePreConstructCallback;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\n/**\n * Integration tests that verify support for {@link TestInstancePreConstructCallback}.\n *\n * @since 5.9\n */\nclass TestInstancePreConstructCallbackTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid instancePreConstruct() {\n\t\texecuteTestsForClass(InstancePreConstructTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"beforeAll\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=InstancePreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=InstancePreConstructTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=InstancePreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=InstancePreConstructTestCase\",\n\n\t\t\t\t\"afterAll\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid factoryPreConstruct() {\n\t\texecuteTestsForClass(FactoryPreConstructTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"beforeAll\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=FactoryPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"testInstanceFactory\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=FactoryPreConstructTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=FactoryPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"testInstanceFactory\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=FactoryPreConstructTestCase\",\n\n\t\t\t\t\"afterAll\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid preConstructInNested() {\n\t\texecuteTestsForClass(PreConstructInNestedTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(3).succeeded(3));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"beforeAll\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=PreConstructInNestedTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"outerTest1\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=PreConstructInNestedTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=PreConstructInNestedTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"outerTest2\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=PreConstructInNestedTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=PreConstructInNestedTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=InnerTestCase, outerInstance: #3\",\n\t\t\t\t\"PreConstructCallback: name=bar, testClass=InnerTestCase, outerInstance: #3\",\n\t\t\t\t\"PreConstructCallback: name=baz, testClass=InnerTestCase, outerInstance: #3\",\n\t\t\t\t\"constructorInner\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"beforeEachInner\",\n\t\t\t\t\"innerTest1\",\n\t\t\t\t\"afterEachInner\",\n\t\t\t\t\"afterEach\",\n\n\t\t\t\t\"close: name=baz, testClass=InnerTestCase\",\n\t\t\t\t\"close: name=bar, testClass=InnerTestCase\",\n\t\t\t\t\"close: name=foo, testClass=InnerTestCase\",\n\t\t\t\t\"close: name=foo, testClass=PreConstructInNestedTestCase\",\n\n\t\t\t\t\"afterAll\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid preConstructOnMethod() {\n\t\texecuteTestsForClass(PreConstructOnMethod.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=PreConstructOnMethod, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=PreConstructOnMethod\",\n\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"afterEach\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid preConstructWithClassLifecycle() {\n\t\texecuteTestsForClass(PreConstructWithClassLifecycle.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=PreConstructWithClassLifecycle, outerInstance: null\",\n\t\t\t\t\"PreConstructCallback: name=bar, testClass=PreConstructWithClassLifecycle, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"close: name=bar, testClass=PreConstructWithClassLifecycle\",\n\t\t\t\t\"close: name=foo, testClass=PreConstructWithClassLifecycle\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid legacyPreConstruct() {\n\t\texecuteTestsForClass(LegacyPreConstructTestCase.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(3).succeeded(3));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\t\"beforeAll\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"PreConstructCallback: name=legacy, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"outerTest1\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=LegacyPreConstructTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"PreConstructCallback: name=legacy, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"outerTest2\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=LegacyPreConstructTestCase\",\n\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"PreConstructCallback: name=legacy, testClass=LegacyPreConstructTestCase, outerInstance: null\",\n\t\t\t\t\"constructor\",\n\t\t\t\t\"PreConstructCallback: name=foo, testClass=InnerTestCase, outerInstance: LegacyPreConstructTestCase\",\n\t\t\t\t\"PreConstructCallback: name=legacy, testClass=InnerTestCase, outerInstance: LegacyPreConstructTestCase\",\n\t\t\t\t\"constructorInner\",\n\t\t\t\t\"beforeEach\",\n\t\t\t\t\"beforeEachInner\",\n\t\t\t\t\"innerTest1\",\n\t\t\t\t\"afterEachInner\",\n\t\t\t\t\"afterEach\",\n\t\t\t\t\"close: name=foo, testClass=InnerTestCase\",\n\t\t\t\t\"close: name=foo, testClass=LegacyPreConstructTestCase\",\n\n\t\t\t\t\"close: name=legacy, testClass=InnerTestCase\",\n\t\t\t\t\"afterAll\",\n\t\t\t\t\"close: name=legacy, testClass=LegacyPreConstructTestCase\",\n\t\t\t\t\"close: name=legacy, testClass=LegacyPreConstructTestCase\",\n\t\t\t\t\"close: name=legacy, testClass=LegacyPreConstructTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate abstract static class CallSequenceRecordingTestCase {\n\t\tprotected static void record(String event) {\n\t\t\tcallSequence.add(event);\n\t\t}\n\t}\n\n\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\tstatic class InstancePreConstructTestCase extends CallSequenceRecordingTestCase {\n\n\t\tInstancePreConstructTestCase() {\n\t\t\trecord(\"constructor\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\trecord(\"beforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\trecord(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\trecord(\"test2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\trecord(\"afterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\trecord(\"afterAll\");\n\t\t}\n\t}\n\n\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\tstatic class FactoryPreConstructTestCase extends CallSequenceRecordingTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic final TestInstanceFactory factory = (factoryContext, extensionContext) -> {\n\t\t\trecord(\"testInstanceFactory\");\n\t\t\treturn new FactoryPreConstructTestCase();\n\t\t};\n\n\t\tFactoryPreConstructTestCase() {\n\t\t\trecord(\"constructor\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\trecord(\"beforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\trecord(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\trecord(\"test2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\trecord(\"afterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\trecord(\"afterAll\");\n\t\t}\n\t}\n\n\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\tstatic class PreConstructInNestedTestCase extends CallSequenceRecordingTestCase {\n\n\t\tstatic AtomicInteger instanceCounter = new AtomicInteger();\n\n\t\tprivate final String instanceId;\n\n\t\tPreConstructInNestedTestCase() {\n\t\t\trecord(\"constructor\");\n\t\t\tinstanceId = \"#\" + instanceCounter.incrementAndGet();\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tinstanceCounter.set(0);\n\t\t\trecord(\"beforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid outerTest1() {\n\t\t\trecord(\"outerTest1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid outerTest2() {\n\t\t\trecord(\"outerTest2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\trecord(\"afterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\trecord(\"afterAll\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn instanceId;\n\t\t}\n\n\t\t@ExtendWith(InstancePreConstructCallbackRecordingBar.class)\n\t\tabstract class InnerParent extends CallSequenceRecordingTestCase {\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(InstancePreConstructCallbackRecordingBaz.class)\n\t\tclass InnerTestCase extends InnerParent {\n\n\t\t\tInnerTestCase() {\n\t\t\t\trecord(\"constructorInner\");\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEachInner() {\n\t\t\t\trecord(\"beforeEachInner\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid innerTest1() {\n\t\t\t\trecord(\"innerTest1\");\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEachInner() {\n\t\t\t\trecord(\"afterEachInner\");\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class PreConstructOnMethod extends CallSequenceRecordingTestCase {\n\t\tPreConstructOnMethod() {\n\t\t\trecord(\"constructor\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\t\t@Test\n\t\tvoid test1() {\n\t\t\trecord(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\trecord(\"test2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\trecord(\"afterEach\");\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\t@ExtendWith(InstancePreConstructCallbackRecordingBar.class)\n\tstatic class PreConstructWithClassLifecycle extends CallSequenceRecordingTestCase {\n\t\tPreConstructWithClassLifecycle() {\n\t\t\trecord(\"constructor\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t}\n\t}\n\n\t@ExtendWith(InstancePreConstructCallbackRecordingFoo.class)\n\t@ExtendWith(InstancePreConstructCallbackRecordingLegacy.class)\n\tstatic class LegacyPreConstructTestCase extends CallSequenceRecordingTestCase {\n\n\t\tLegacyPreConstructTestCase() {\n\t\t\trecord(\"constructor\");\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\trecord(\"beforeAll\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\trecord(\"beforeEach\");\n\t\t}\n\n\t\t@Test\n\t\tvoid outerTest1() {\n\t\t\trecord(\"outerTest1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid outerTest2() {\n\t\t\trecord(\"outerTest2\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\trecord(\"afterEach\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() {\n\t\t\trecord(\"afterAll\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"LegacyPreConstructTestCase\";\n\t\t}\n\n\t\t@Nested\n\t\tclass InnerTestCase extends CallSequenceRecordingTestCase {\n\n\t\t\tInnerTestCase() {\n\t\t\t\trecord(\"constructorInner\");\n\t\t\t}\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeEachInner() {\n\t\t\t\trecord(\"beforeEachInner\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid innerTest1() {\n\t\t\t\trecord(\"innerTest1\");\n\t\t\t}\n\n\t\t\t@AfterEach\n\t\t\tvoid afterEachInner() {\n\t\t\t\trecord(\"afterEachInner\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic abstract class AbstractTestInstancePreConstructCallback implements TestInstancePreConstructCallback {\n\t\tprivate final String name;\n\n\t\tAbstractTestInstancePreConstructCallback(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void preConstructTestInstance(TestInstanceFactoryContext factoryContext, ExtensionContext context) {\n\t\t\tassertThat(context.getTestInstance()).isNotPresent();\n\t\t\tassertThat(context.getTestClass()).isPresent();\n\t\t\tif (name.equals(\"legacy\")) {\n\t\t\t\tassertThat(factoryContext.getTestClass()).isSameAs(context.getTestClass().get());\n\t\t\t}\n\t\t\telse if (context.getTestInstanceLifecycle().orElse(null) != TestInstance.Lifecycle.PER_CLASS) {\n\t\t\t\tassertThat(context.getTestMethod()).isPresent();\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertThat(context.getTestMethod()).isEmpty();\n\t\t\t}\n\t\t\tString testClass = factoryContext.getTestClass().getSimpleName();\n\t\t\tcallSequence.add(\"PreConstructCallback: name=\" + name + \", testClass=\" + testClass + \", outerInstance: \"\n\t\t\t\t\t+ factoryContext.getOuterInstance().orElse(null));\n\t\t\tcontext.getStore(ExtensionContext.Namespace.create(this)).put(new Object(),\n\t\t\t\t(AutoCloseable) () -> callSequence.add(\"close: name=\" + name + \", testClass=\" + testClass));\n\t\t}\n\t}\n\n\tstatic class InstancePreConstructCallbackRecordingFoo extends AbstractTestInstancePreConstructCallback {\n\t\tInstancePreConstructCallbackRecordingFoo() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tstatic class InstancePreConstructCallbackRecordingBar extends AbstractTestInstancePreConstructCallback {\n\t\tInstancePreConstructCallbackRecordingBar() {\n\t\t\tsuper(\"bar\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tstatic class InstancePreConstructCallbackRecordingBaz extends AbstractTestInstancePreConstructCallback {\n\t\tInstancePreConstructCallbackRecordingBaz() {\n\t\t\tsuper(\"baz\");\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn TEST_METHOD;\n\t\t}\n\t}\n\n\tstatic class InstancePreConstructCallbackRecordingLegacy extends AbstractTestInstancePreConstructCallback {\n\t\tInstancePreConstructCallbackRecordingLegacy() {\n\t\t\tsuper(\"legacy\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\n\n/**\n * Integration tests that verify support for {@link TestInstancePreDestroyCallback}.\n *\n * @since 5.6\n */\nclass TestInstancePreDestroyCallbackTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> callSequence = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid resetCallSequence() {\n\t\tcallSequence.clear();\n\t}\n\n\t@Test\n\tvoid instancePreDestroyCallbacksInNestedClasses() {\n\t\texecuteTestsForClass(OuterTestCase.class).testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t// OuterTestCase\n\t\t\t\"beforeOuterMethod\",\n\t\t\t\t\"testOuter\",\n\t\t\t\"fooPreDestroyCallbackTestInstance:OuterTestCase\",\n\n\t\t\t// InnerTestCase\n\t\t\t\"beforeOuterMethod\",\n\t\t\t\t\"beforeInnerMethod\",\n\t\t\t\t\t\"testInner\",\n\t\t\t\t\"bazPreDestroyCallbackTestInstance:InnerTestCase\",\n\t\t\t\t\"barPreDestroyCallbackTestInstance:InnerTestCase\",\n\t\t\t\"fooPreDestroyCallbackTestInstance:InnerTestCase\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid testSpecificTestInstancePreDestroyCallbackIsCalled() {\n\t\texecuteTestsForClass(TestCaseWithTestSpecificTestInstancePreDestroyCallback.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(1).succeeded(1));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"test\",\n\t\t\t\"fooPreDestroyCallbackTestInstance:TestCaseWithTestSpecificTestInstancePreDestroyCallback\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid classLifecyclePreDestroyCallbacks() {\n\t\texecuteTestsForClass(PerClassLifecyclePreDestroyCallbacksWithTwoTestMethods.class).testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.started(2).succeeded(2));\n\n\t\t// @formatter:off\n\t\tassertThat(callSequence).containsExactly(\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"test1\",\n\t\t\t\"beforeEachMethod\",\n\t\t\t\t\"test2\",\n\t\t\t\"barPreDestroyCallbackTestInstance:PerClassLifecyclePreDestroyCallbacksWithTwoTestMethods\",\n\t\t\t\"fooPreDestroyCallbackTestInstance:PerClassLifecyclePreDestroyCallbacksWithTwoTestMethods\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate abstract static class Destroyable {\n\n\t\tboolean destroyed;\n\n\t\tvoid setDestroyed() {\n\t\t\tthis.destroyed = true;\n\t\t}\n\t}\n\n\t@ExtendWith(FooInstancePreDestroyCallback.class)\n\tstatic class OuterTestCase extends Destroyable {\n\n\t\t@BeforeEach\n\t\tvoid beforeOuterMethod() {\n\t\t\tcallSequence.add(\"beforeOuterMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid testOuter() {\n\t\t\tassertFalse(destroyed);\n\t\t\tcallSequence.add(\"testOuter\");\n\t\t}\n\n\t\t@Nested\n\t\t@ExtendWith(BarInstancePreDestroyCallback.class)\n\t\t@ExtendWith(BazInstancePreDestroyCallback.class)\n\t\tclass InnerTestCase extends Destroyable {\n\n\t\t\t@BeforeEach\n\t\t\tvoid beforeInnerMethod() {\n\t\t\t\tassertFalse(destroyed);\n\t\t\t\tcallSequence.add(\"beforeInnerMethod\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid testInner() {\n\t\t\t\tcallSequence.add(\"testInner\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TestCaseWithTestSpecificTestInstancePreDestroyCallback extends Destroyable {\n\n\t\t@BeforeEach\n\t\tvoid beforeEachMethod() {\n\t\t\tassertFalse(destroyed);\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@ExtendWith(FooInstancePreDestroyCallback.class)\n\t\t@Test\n\t\tvoid test() {\n\t\t\tcallSequence.add(\"test\");\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@TestInstance(PER_CLASS)\n\t@ExtendWith(FooInstancePreDestroyCallback.class)\n\t@ExtendWith(BarInstancePreDestroyCallback.class)\n\tstatic class PerClassLifecyclePreDestroyCallbacksWithTwoTestMethods extends Destroyable {\n\n\t\t@BeforeEach\n\t\tvoid beforeEachMethod() {\n\t\t\tcallSequence.add(\"beforeEachMethod\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t}\n\t}\n\n\tstatic abstract class AbstractTestInstancePreDestroyCallback implements TestInstancePreDestroyCallback {\n\n\t\tprivate final String name;\n\n\t\tAbstractTestInstancePreDestroyCallback(String name) {\n\t\t\tthis.name = name;\n\t\t}\n\n\t\t@Override\n\t\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\t\tassertThat(context.getTestInstance()).isPresent();\n\t\t\tObject testInstance = context.getTestInstance().get();\n\t\t\tif (testInstance instanceof Destroyable destroyable) {\n\t\t\t\tdestroyable.setDestroyed();\n\t\t\t}\n\t\t\tcallSequence.add(name + \"PreDestroyCallbackTestInstance:\" + testInstance.getClass().getSimpleName());\n\t\t}\n\t}\n\n\tstatic class FooInstancePreDestroyCallback extends AbstractTestInstancePreDestroyCallback {\n\n\t\tFooInstancePreDestroyCallback() {\n\t\t\tsuper(\"foo\");\n\t\t}\n\t}\n\n\tstatic class BarInstancePreDestroyCallback extends AbstractTestInstancePreDestroyCallback {\n\n\t\tBarInstancePreDestroyCallback() {\n\t\t\tsuper(\"bar\");\n\t\t}\n\t}\n\n\tstatic class BazInstancePreDestroyCallback extends AbstractTestInstancePreDestroyCallback {\n\n\t\tBazInstancePreDestroyCallback() {\n\t\t\tsuper(\"baz\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestInstancePreDestroyCallbackUtilityMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.reportEntry;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.extension.TestInstancePreDestroyCallback;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\npublic class TestInstancePreDestroyCallbackUtilityMethodTests extends AbstractJupiterTestEngineTests {\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@ValueSource(classes = { PerMethodLifecycleOnAllLevels.class, PerMethodWithinPerClassLifecycle.class,\n\t\t\tPerClassWithinPerMethodLifecycle.class, PerClassLifecycleOnAllLevels.class })\n\tvoid destroysWhatWasPostProcessed(Class<?> testClass) {\n\t\texecuteTestsForClass(testClass).allEvents() //\n\t\t\t\t.assertStatistics(stats -> stats.reportingEntryPublished(4)) //\n\t\t\t\t.assertEventsMatchLooselyInOrder( //\n\t\t\t\t\treportEntry(Map.of(\"post-process\", testClass.getSimpleName())),\n\t\t\t\t\treportEntry(Map.of(\"post-process\", \"Inner\")), //\n\t\t\t\t\tevent(test(), started()), //\n\t\t\t\t\treportEntry(Map.of(\"pre-destroy\", \"Inner\")), //\n\t\t\t\t\treportEntry(Map.of(\"pre-destroy\", testClass.getSimpleName())) //\n\t\t\t\t);\n\t}\n\n\t@ExtendWith(TestInstanceLifecycleExtension.class)\n\tstatic class PerMethodLifecycleOnAllLevels {\n\t\t@Nested\n\t\tclass Inner {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TestInstanceLifecycleExtension.class)\n\t@TestInstance(PER_CLASS)\n\tstatic class PerMethodWithinPerClassLifecycle {\n\t\t@Nested\n\t\tclass Inner {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TestInstanceLifecycleExtension.class)\n\tstatic class PerClassWithinPerMethodLifecycle {\n\t\t@Nested\n\t\t@TestInstance(PER_CLASS)\n\t\tclass Inner {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(TestInstanceLifecycleExtension.class)\n\t@TestInstance(PER_CLASS)\n\tstatic class PerClassLifecycleOnAllLevels {\n\t\t@Nested\n\t\t@TestInstance(PER_CLASS)\n\t\tclass Inner {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static class TestInstanceLifecycleExtension\n\t\t\timplements TestInstancePostProcessor, TestInstancePreDestroyCallback {\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) {\n\t\t\tcontext.publishReportEntry(\"post-process\", testInstance.getClass().getSimpleName());\n\t\t}\n\n\t\t@Override\n\t\tpublic void preDestroyTestInstance(ExtensionContext context) {\n\t\t\tTestInstancePreDestroyCallback.preDestroyTestInstances(context,\n\t\t\t\ttestInstance -> context.publishReportEntry(\"pre-destroy\", testInstance.getClass().getSimpleName()));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestReporterParameterResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * @since 5.0\n */\nclass TestReporterParameterResolverTests {\n\n\tTestReporterParameterResolver resolver = new TestReporterParameterResolver();\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid supports() {\n\t\tParameter parameter1 = findParameterOfMethod(\"methodWithTestReporterParameter\", TestReporter.class);\n\t\tassertTrue(this.resolver.supportsParameter(parameterContext(parameter1), null));\n\n\t\tParameter parameter2 = findParameterOfMethod(\"methodWithoutTestReporterParameter\", String.class);\n\t\tassertFalse(this.resolver.supportsParameter(parameterContext(parameter2), null));\n\t}\n\n\t@Test\n\tvoid resolve() {\n\t\tParameter parameter = findParameterOfMethod(\"methodWithTestReporterParameter\", TestReporter.class);\n\n\t\tTestReporter testReporter = this.resolver.resolveParameter(parameterContext(parameter), mock());\n\t\tassertNotNull(testReporter);\n\t}\n\n\tprivate Parameter findParameterOfMethod(String methodName, Class<?>... parameterTypes) {\n\t\tMethod method = ReflectionSupport.findMethod(Sample.class, methodName, parameterTypes).orElseThrow();\n\t\treturn method.getParameters()[0];\n\t}\n\n\tprivate static ParameterContext parameterContext(Parameter parameter) {\n\t\tParameterContext parameterContext = mock();\n\t\twhen(parameterContext.getParameter()).thenReturn(parameter);\n\t\treturn parameterContext;\n\t}\n\n\tstatic class Sample {\n\n\t\tvoid methodWithTestReporterParameter(TestReporter reporter) {\n\t\t}\n\n\t\tvoid methodWithoutTestReporterParameter(String nothing) {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TestWatcherTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.function.Predicate.not;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TestWatcher;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\n\n/**\n * Integration tests for the {@link TestWatcher} extension API.\n *\n * @since 5.4\n */\nclass TestWatcherTests extends AbstractJupiterTestEngineTests {\n\n\tprivate static final List<String> testWatcherMethodNames = Arrays.stream(TestWatcher.class.getDeclaredMethods()) //\n\t\t\t.filter(not(Method::isSynthetic)) //\n\t\t\t.map(Method::getName) //\n\t\t\t.toList();\n\n\t@BeforeEach\n\tvoid clearResults() {\n\t\tTrackingTestWatcher.results.clear();\n\t\tDataRetrievingTestWatcher.results.clear();\n\t}\n\n\t@Test\n\tvoid testWatcherIsInvokedForTestMethodsInTopLevelAndNestedTestClasses() {\n\t\tassertCommonStatistics(executeTestsForClass(TrackingTestWatcherTestMethodsTestCase.class));\n\t\tassertThat(TrackingTestWatcher.results.keySet()).containsAll(testWatcherMethodNames);\n\t\tTrackingTestWatcher.results.values().forEach(testMethodNames -> assertEquals(2, testMethodNames.size()));\n\t}\n\n\t@Test\n\tvoid testWatcherIsInvokedForRepeatedTestMethods() {\n\t\tEngineExecutionResults results = executeTestsForClass(TrackingTestWatcherRepeatedTestMethodsTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(\n\t\t\tstats -> stats.skipped(1).started(5).succeeded(5).aborted(0).failed(0));\n\t\tresults.testEvents().assertStatistics(\n\t\t\tstats -> stats.dynamicallyRegistered(6).skipped(0).started(6).succeeded(2).aborted(2).failed(2));\n\n\t\t// Since the @RepeatedTest container is disabled, the individual invocations never occur.\n\t\tassertThat(TrackingTestWatcher.results.keySet()).containsAll(testWatcherMethodNames);\n\t\t// 2 => number of iterations declared in @RepeatedTest(2).\n\t\tTrackingTestWatcher.results.forEach((testWatcherMethod, testMethodNames) -> assertEquals(\n\t\t\t\"testDisabled\".equals(testWatcherMethod) ? 1 : 2, testMethodNames.size()));\n\t}\n\n\t@Test\n\tvoid testWatcherIsNotInvokedForTestFactoryMethods() {\n\t\tEngineExecutionResults results = executeTestsForClass(TrackingTestWatcherTestFactoryMethodsTestCase.class);\n\n\t\tresults.containerEvents().assertStatistics(\n\t\t\tstats -> stats.skipped(1).started(5).succeeded(5).aborted(0).failed(0));\n\t\tresults.testEvents().assertStatistics(\n\t\t\tstats -> stats.dynamicallyRegistered(6).skipped(0).started(6).succeeded(2).aborted(2).failed(2));\n\n\t\t// There should be zero results, since the TestWatcher API is not supported for @TestFactory containers.\n\t\tassertThat(TrackingTestWatcher.results).isEmpty();\n\t}\n\n\t@Test\n\tvoid testWatcherExceptionsAreLoggedAndSwallowed(@TrackLogRecords LogRecordListener logRecordListener) {\n\t\tassertCommonStatistics(executeTestsForClass(ExceptionThrowingTestWatcherTestCase.class));\n\n\t\t// @formatter:off\n\t\tlong exceptionCount = logRecordListener.stream(MethodBasedTestDescriptor.class, Level.WARNING)\n\t\t\t\t.map(LogRecord::getThrown)\n\t\t\t\t.filter(JUnitException.class::isInstance)\n\t\t\t\t.map(throwable -> throwable.getStackTrace()[0].getMethodName())\n\t\t\t\t.filter(testWatcherMethodNames::contains)\n\t\t\t\t.count();\n\t\t// @formatter:on\n\n\t\tassertEquals(8, exceptionCount, \"Thrown exceptions were not logged properly.\");\n\t}\n\n\t@Test\n\tvoid testWatcherIsInvokedForTestMethodsInTestCaseWithProblematicConstructor() {\n\t\tEngineExecutionResults results = executeTestsForClass(ProblematicConstructorTestCase.class);\n\t\tresults.testEvents().assertStatistics(stats -> stats.skipped(0).started(8).succeeded(0).aborted(0).failed(8));\n\t\tassertThat(TrackingTestWatcher.results.keySet()).containsExactly(\"testFailed\");\n\t\tassertThat(TrackingTestWatcher.results.get(\"testFailed\")).hasSize(8);\n\t}\n\n\t@Test\n\tvoid testWatcherSemanticsWhenRegisteredAtClassLevel() {\n\t\tClass<?> testClass = ClassLevelTestWatcherTestCase.class;\n\t\tassertStatsForAbstractDisabledMethodsTestCase(testClass);\n\n\t\t// We get \"testDisabled\" events for the @Test method and the @RepeatedTest container.\n\t\tassertThat(TrackingTestWatcher.results.get(\"testDisabled\")).containsExactly(\"test\", \"repeatedTest\");\n\t}\n\n\t@Test\n\tvoid testWatcherSemanticsWhenRegisteredAtInstanceLevelWithTestInstanceLifecyclePerClass() {\n\t\tClass<?> testClass = TestInstancePerClassInstanceLevelTestWatcherTestCase.class;\n\t\tassertStatsForAbstractDisabledMethodsTestCase(testClass);\n\n\t\t// We get \"testDisabled\" events for the @Test method and the @RepeatedTest container.\n\t\tassertThat(TrackingTestWatcher.results.get(\"testDisabled\")).containsExactly(\"test\", \"repeatedTest\");\n\t}\n\n\t@Test\n\tvoid testWatcherSemanticsWhenRegisteredAtInstanceLevelWithTestInstanceLifecyclePerMethod() {\n\t\tClass<?> testClass = TestInstancePerMethodInstanceLevelTestWatcherTestCase.class;\n\t\tassertStatsForAbstractDisabledMethodsTestCase(testClass);\n\n\t\t// Since the TestWatcher is registered at the instance level with test instance\n\t\t// lifecycle per-method semantics, we get a \"testDisabled\" event only for the @Test\n\t\t// method and NOT for the @RepeatedTest container.\n\t\tassertThat(TrackingTestWatcher.results.get(\"testDisabled\")).containsExactly(\"test\");\n\t}\n\n\t@Test\n\tvoid testWatcherSemanticsWhenRegisteredAtMethodLevel() {\n\t\tClass<?> testClass = MethodLevelTestWatcherTestCase.class;\n\t\tassertStatsForAbstractDisabledMethodsTestCase(testClass);\n\n\t\t// We get \"testDisabled\" events for the @Test method and the @RepeatedTest container.\n\t\tassertThat(TrackingTestWatcher.results.get(\"testDisabled\")).containsExactly(\"test\", \"repeatedTest\");\n\t}\n\n\t@Test\n\tvoid testWatcherCanRetrieveDataFromTheExtensionContextStore() {\n\t\tClass<?> testClass = DataRetrievingTestWatcherTestCase.class;\n\t\tEngineExecutionResults results = executeTestsForClass(testClass);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1).failed(0));\n\n\t\tassertThat(DataRetrievingTestWatcher.results).containsExactly(entry(\"key\", \"enigma\"));\n\t}\n\n\tprivate void assertCommonStatistics(EngineExecutionResults results) {\n\t\tresults.containerEvents().assertStatistics(stats -> stats.started(3).succeeded(3).failed(0));\n\t\tresults.testEvents().assertStatistics(stats -> stats.skipped(2).started(6).succeeded(2).aborted(2).failed(2));\n\t}\n\n\tprivate void assertStatsForAbstractDisabledMethodsTestCase(Class<?> testClass) {\n\t\tEngineExecutionResults results = executeTestsForClass(testClass);\n\n\t\tresults.containerEvents().assertStatistics(//\n\t\t\tstats -> stats.skipped(1).started(2).succeeded(2).aborted(0).failed(0));\n\t\tresults.testEvents().assertStatistics(//\n\t\t\tstats -> stats.skipped(1).started(0).succeeded(0).aborted(0).failed(0));\n\n\t\tassertThat(TrackingTestWatcher.results.keySet()).containsExactly(\"testDisabled\");\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static abstract class AbstractTestCase {\n\n\t\t@Test\n\t\tpublic void successfulTest() {\n\t\t\t//no-op\n\t\t}\n\n\t\t@Test\n\t\tpublic void failedTest() {\n\t\t\tfail(\"Must fail\");\n\t\t}\n\n\t\t@Test\n\t\tpublic void abortedTest() {\n\t\t\tabort();\n\t\t}\n\n\t\t@Test\n\t\t@Disabled\n\t\tpublic void skippedTest() {\n\t\t\t//no-op\n\t\t}\n\n\t\t@Nested\n\t\tclass SecondLevel {\n\n\t\t\t@Test\n\t\t\tpublic void successfulTest() {\n\t\t\t\t//no-op\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tpublic void failedTest() {\n\t\t\t\tfail(\"Must fail\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tpublic void abortedTest() {\n\t\t\t\tabort();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@Disabled\n\t\t\tpublic void skippedTest() {\n\t\t\t\t//no-op\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t@ExtendWith(TrackingTestWatcher.class)\n\tstatic class TrackingTestWatcherTestMethodsTestCase extends AbstractTestCase {\n\t}\n\n\t@ExtendWith(TrackingTestWatcher.class)\n\tstatic class TrackingTestWatcherRepeatedTestMethodsTestCase {\n\n\t\t@RepeatedTest(2)\n\t\tvoid successfulTest() {\n\t\t\t//no-op\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\tvoid failedTest() {\n\t\t\tfail(\"Must fail\");\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\tvoid abortedTest() {\n\t\t\tabort();\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\t@Disabled\n\t\tvoid skippedTest() {\n\t\t\t//no-op\n\t\t}\n\n\t}\n\n\t@NullMarked\n\t@ExtendWith(TrackingTestWatcher.class)\n\tstatic class TrackingTestWatcherTestFactoryMethodsTestCase {\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> successfulTest() {\n\t\t\treturn Stream.of(\"A\", \"B\").map(text -> dynamicTest(text, () -> assertTrue(true)));\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> failedTest() {\n\t\t\treturn Stream.of(\"A\", \"B\").map(text -> dynamicTest(text, () -> fail(\"Must fail\")));\n\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> abortedTest() {\n\t\t\treturn Stream.of(\"A\", \"B\").map(text -> dynamicTest(text, Assumptions::abort));\n\n\t\t}\n\n\t\t@TestFactory\n\t\t@Disabled\n\t\tStream<DynamicTest> skippedTest() {\n\t\t\treturn Stream.of(\"A\", \"B\").map(text -> dynamicTest(text, Assertions::fail));\n\t\t}\n\n\t}\n\n\t@ExtendWith(ExceptionThrowingTestWatcher.class)\n\tstatic class ExceptionThrowingTestWatcherTestCase extends AbstractTestCase {\n\t}\n\n\t@ExtendWith(TrackingTestWatcher.class)\n\tstatic class ProblematicConstructorTestCase extends AbstractTestCase {\n\t\tProblematicConstructorTestCase(Object ignore) {\n\t\t}\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tprivate static abstract class AbstractDisabledMethodsTestCase {\n\n\t\t@Disabled\n\t\t@Test\n\t\t@Order(1)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Disabled\n\t\t@RepeatedTest(2)\n\t\t@Order(2)\n\t\tvoid repeatedTest() {\n\t\t}\n\t}\n\n\tstatic class ClassLevelTestWatcherTestCase extends AbstractDisabledMethodsTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic TestWatcher watcher = new TrackingTestWatcher();\n\t}\n\n\t@TestInstance(Lifecycle.PER_CLASS)\n\tstatic class TestInstancePerClassInstanceLevelTestWatcherTestCase extends AbstractDisabledMethodsTestCase {\n\n\t\t@RegisterExtension\n\t\tTestWatcher watcher = new TrackingTestWatcher();\n\t}\n\n\t@TestInstance(Lifecycle.PER_METHOD)\n\tstatic class TestInstancePerMethodInstanceLevelTestWatcherTestCase extends AbstractDisabledMethodsTestCase {\n\n\t\t@RegisterExtension\n\t\tTestWatcher watcher = new TrackingTestWatcher();\n\t}\n\n\tstatic class MethodLevelTestWatcherTestCase extends AbstractDisabledMethodsTestCase {\n\n\t\t@Override\n\t\t@Disabled\n\t\t@Test\n\t\t@Order(1)\n\t\t@ExtendWith(TrackingTestWatcher.class)\n\t\tvoid test() {\n\t\t}\n\n\t\t@Override\n\t\t@Disabled\n\t\t@RepeatedTest(1)\n\t\t@Order(2)\n\t\t@ExtendWith(TrackingTestWatcher.class)\n\t\tvoid repeatedTest() {\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class TrackingTestWatcher implements TestWatcher {\n\n\t\tprivate static final Map<String, List<String>> results = new HashMap<>();\n\n\t\t@Override\n\t\tpublic void testSuccessful(ExtensionContext context) {\n\t\t\ttrackResult(\"testSuccessful\", context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void testAborted(ExtensionContext context, @Nullable Throwable cause) {\n\t\t\ttrackResult(\"testAborted\", context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void testFailed(ExtensionContext context, @Nullable Throwable cause) {\n\t\t\ttrackResult(\"testFailed\", context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void testDisabled(ExtensionContext context, Optional<String> reason) {\n\t\t\ttrackResult(\"testDisabled\", context);\n\t\t}\n\n\t\tprotected void trackResult(String testWatcherMethod, ExtensionContext context) {\n\t\t\tString testMethod = context.getRequiredTestMethod().getName();\n\t\t\tresults.computeIfAbsent(testWatcherMethod, k -> new ArrayList<>()).add(testMethod);\n\t\t}\n\n\t}\n\n\t@NullMarked\n\tprivate static class ExceptionThrowingTestWatcher implements TestWatcher {\n\n\t\t@Override\n\t\tpublic void testSuccessful(ExtensionContext context) {\n\t\t\tthrow new JUnitException(\"Exception in testSuccessful()\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void testDisabled(ExtensionContext context, Optional<String> reason) {\n\t\t\tthrow new JUnitException(\"Exception in testDisabled()\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void testAborted(ExtensionContext context, @Nullable Throwable cause) {\n\t\t\tthrow new JUnitException(\"Exception in testAborted()\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void testFailed(ExtensionContext context, @Nullable Throwable cause) {\n\t\t\tthrow new JUnitException(\"Exception in testFailed()\");\n\t\t}\n\n\t}\n\n\t/**\n\t * {@link TestWatcher} that retrieves data from the {@link ExtensionContext.Store}.\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3944\">#3944</a>\n\t */\n\t@NullMarked\n\tstatic class DataRetrievingTestWatcher implements BeforeTestExecutionCallback, TestWatcher {\n\n\t\tprivate static final Namespace NAMESPACE = Namespace.create(DataRetrievingTestWatcher.class);\n\n\t\tprivate static final String KEY = \"key\";\n\n\t\tprivate static final Map<String, @Nullable String> results = new HashMap<>();\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) throws Exception {\n\t\t\tgetStore(context).put(KEY, \"enigma\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void testSuccessful(ExtensionContext context) {\n\t\t\tresults.put(KEY, getStore(context).get(KEY, String.class));\n\t\t}\n\n\t\tprivate static Store getStore(ExtensionContext context) {\n\t\t\treturn context.getStore(NAMESPACE);\n\t\t}\n\t}\n\n\t@ExtendWith(DataRetrievingTestWatcher.class)\n\tstatic class DataRetrievingTestWatcherTestCase {\n\n\t\t@Test\n\t\tpublic void test() {\n\t\t\t//no-op\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutConfigurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.DAYS;\nimport static java.util.concurrent.TimeUnit.HOURS;\nimport static java.util.concurrent.TimeUnit.MICROSECONDS;\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static java.util.concurrent.TimeUnit.MINUTES;\nimport static java.util.concurrent.TimeUnit.NANOSECONDS;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.api.Constants.DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.TIMEOUT_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Timeout.ThreadMode.SEPARATE_THREAD;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\nimport java.util.function.Function;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.util.RuntimeUtils;\nimport org.mockito.stubbing.Answer;\n\n/**\n * @since 5.5\n */\nclass TimeoutConfigurationTests {\n\n\tExtensionContext extensionContext = mock();\n\tTimeoutConfiguration config = new TimeoutConfiguration(extensionContext);\n\n\t@Test\n\tvoid notDisabledByDefault() {\n\t\tassertThat(config.isTimeoutDisabled()).isFalse();\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource(textBlock = \"\"\"\n\t\t\tEnabled,           false\n\t\t\tDisabled,          true\n\t\t\tDisabled_ON_debug, false\n\t\t\t\"\"\")\n\tvoid disabledBasedOnTimeoutMode(String timeoutMode, boolean disabled) {\n\t\twhen(extensionContext.getConfigurationParameter(eq(TIMEOUT_MODE_PROPERTY_NAME), any())) //\n\t\t\t\t.thenAnswer(callConverter(timeoutMode));\n\n\t\tconfig = new TimeoutConfiguration(extensionContext);\n\n\t\tassertThat(config.isTimeoutDisabled()) //\n\t\t\t\t.isEqualTo(\"disabled_on_debug\".equalsIgnoreCase(timeoutMode) ? RuntimeUtils.isDebugMode() : disabled);\n\t}\n\n\t@Test\n\tvoid noTimeoutIfNoPropertiesAreSet() {\n\t\tassertThat(config.getDefaultBeforeAllMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultBeforeEachMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultTestMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultTestTemplateMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultTestFactoryMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultAfterEachMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultAfterAllMethodTimeout()).isEmpty();\n\t\tassertThat(config.getDefaultTimeoutThreadMode()).isEmpty();\n\t}\n\n\t@Test\n\tvoid defaultTimeoutIsUsedUnlessAMoreSpecificOneIsSet() {\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TIMEOUT_PROPERTY_NAME)).thenReturn(Optional.of(\"42\"));\n\n\t\tassertThat(config.getDefaultBeforeAllMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultBeforeEachMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultTestMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultTestTemplateMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultTestFactoryMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultAfterEachMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t\tassertThat(config.getDefaultAfterAllMethodTimeout()).contains(new TimeoutDuration(42, SECONDS));\n\t}\n\n\t@Test\n\tvoid defaultCategoryTimeoutIsUsedUnlessAMoreSpecificOneIsSet() {\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TIMEOUT_PROPERTY_NAME)).thenReturn(Optional.of(\"2\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"3\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"5\"));\n\n\t\tassertThat(config.getDefaultBeforeAllMethodTimeout()).contains(new TimeoutDuration(3, SECONDS));\n\t\tassertThat(config.getDefaultBeforeEachMethodTimeout()).contains(new TimeoutDuration(3, SECONDS));\n\t\tassertThat(config.getDefaultTestMethodTimeout()).contains(new TimeoutDuration(5, SECONDS));\n\t\tassertThat(config.getDefaultTestTemplateMethodTimeout()).contains(new TimeoutDuration(5, SECONDS));\n\t\tassertThat(config.getDefaultTestFactoryMethodTimeout()).contains(new TimeoutDuration(5, SECONDS));\n\t\tassertThat(config.getDefaultAfterEachMethodTimeout()).contains(new TimeoutDuration(3, SECONDS));\n\t\tassertThat(config.getDefaultAfterAllMethodTimeout()).contains(new TimeoutDuration(3, SECONDS));\n\t}\n\n\t@Test\n\tvoid specificTimeoutsAreUsedIfSet() {\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TIMEOUT_PROPERTY_NAME)).thenReturn(Optional.of(\"2\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_LIFECYCLE_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"3\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TESTABLE_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"5\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"7ns\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"11μs\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"13ms\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"17s\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"19m\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"23h\"));\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"29d\"));\n\n\t\tassertThat(config.getDefaultBeforeAllMethodTimeout()).contains(new TimeoutDuration(7, NANOSECONDS));\n\t\tassertThat(config.getDefaultBeforeEachMethodTimeout()).contains(new TimeoutDuration(11, MICROSECONDS));\n\t\tassertThat(config.getDefaultTestMethodTimeout()).contains(new TimeoutDuration(13, MILLISECONDS));\n\t\tassertThat(config.getDefaultTestTemplateMethodTimeout()).contains(new TimeoutDuration(17, SECONDS));\n\t\tassertThat(config.getDefaultTestFactoryMethodTimeout()).contains(new TimeoutDuration(19, MINUTES));\n\t\tassertThat(config.getDefaultAfterEachMethodTimeout()).contains(new TimeoutDuration(23, HOURS));\n\t\tassertThat(config.getDefaultAfterAllMethodTimeout()).contains(new TimeoutDuration(29, DAYS));\n\t}\n\n\t@Test\n\tvoid logsInvalidValues(@TrackLogRecords LogRecordListener logRecordListener) {\n\t\twhen(extensionContext.getConfigurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME)).thenReturn(\n\t\t\tOptional.of(\"invalid\"));\n\n\t\tassertThat(config.getDefaultTestMethodTimeout()).isEmpty();\n\t\tassertThat(logRecordListener.stream(Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"Ignored invalid timeout 'invalid' set via the 'junit.jupiter.execution.timeout.test.method.default' configuration parameter.\");\n\t}\n\n\t@Test\n\tvoid specificThreadModeIsUsed() {\n\t\twhen(extensionContext.getConfigurationParameter(eq(DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME), any())) //\n\t\t\t\t.thenAnswer(callConverter(\"SEPARATE_THREAD\"));\n\n\t\tassertThat(config.getDefaultTimeoutThreadMode()).contains(SEPARATE_THREAD);\n\t}\n\n\t@Test\n\tvoid logsInvalidThreadModeValueAndReturnEmpty() {\n\t\twhen(extensionContext.getConfigurationParameter(eq(DEFAULT_TIMEOUT_THREAD_MODE_PROPERTY_NAME), any())) //\n\t\t\t\t.thenAnswer(callConverter(\"invalid\"));\n\n\t\tassertThatThrownBy(() -> config.getDefaultTimeoutThreadMode()) //\n\t\t\t\t.hasMessage(\n\t\t\t\t\t\"Invalid timeout thread mode 'INVALID' set via the 'junit.jupiter.execution.timeout.thread.mode.default' configuration parameter.\");\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static Answer<?> callConverter(String value) {\n\t\treturn invocation -> Optional.ofNullable(invocation.getArgument(1, Function.class).apply(value));\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationParserTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.DAYS;\nimport static java.util.concurrent.TimeUnit.HOURS;\nimport static java.util.concurrent.TimeUnit.MICROSECONDS;\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static java.util.concurrent.TimeUnit.MINUTES;\nimport static java.util.concurrent.TimeUnit.NANOSECONDS;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.time.format.DateTimeParseException;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\n\n/**\n * @since 5.5\n */\nclass TimeoutDurationParserTests {\n\n\tprivate final TimeoutDurationParser parser = new TimeoutDurationParser();\n\n\t@Test\n\tvoid parsesNumberWithoutUnitIntoSecondsDurations() {\n\t\tassertEquals(new TimeoutDuration(42, SECONDS), parser.parse(\"42\"));\n\t}\n\n\t@TestFactory\n\tStream<DynamicNode> parsesNumbersWithUnits() {\n\t\tvar unitsWithRepresentations = Map.of( //\n\t\t\tNANOSECONDS, \"ns\", //\n\t\t\tMICROSECONDS, \"μs\", //\n\t\t\tMILLISECONDS, \"ms\", //\n\t\t\tSECONDS, \"s\", //\n\t\t\tMINUTES, \"m\", //\n\t\t\tHOURS, \"h\", //\n\t\t\tDAYS, \"d\");\n\t\treturn unitsWithRepresentations.entrySet().stream() //\n\t\t\t\t.map(entry -> {\n\t\t\t\t\tvar unit = entry.getKey();\n\t\t\t\t\tvar plainRepresentation = entry.getValue();\n\t\t\t\t\tvar representations = Stream.of( //\n\t\t\t\t\t\tplainRepresentation, //\n\t\t\t\t\t\t\" \" + plainRepresentation, //\n\t\t\t\t\t\tplainRepresentation.toUpperCase(), //\n\t\t\t\t\t\t\" \" + plainRepresentation.toUpperCase());\n\t\t\t\t\treturn dynamicContainer(unit.name().toLowerCase(),\n\t\t\t\t\t\trepresentations.map(representation -> dynamicTest(\"\\\"\" + representation + \"\\\"\", () -> {\n\t\t\t\t\t\t\tvar expected = new TimeoutDuration(42, unit);\n\t\t\t\t\t\t\tvar actual = parser.parse(\"42\" + representation);\n\t\t\t\t\t\t\tassertEquals(expected, actual);\n\t\t\t\t\t\t})));\n\t\t\t\t});\n\t}\n\n\t@Test\n\tvoid rejectsNumbersStartingWithZero() {\n\t\tassertThrows(DateTimeParseException.class, () -> parser.parse(\"01\"));\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutDurationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.MINUTES;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.5\n */\nclass TimeoutDurationTests {\n\n\t@Test\n\tvoid formatsDurationNicely() {\n\t\tassertThat(new TimeoutDuration(1, SECONDS)).hasToString(\"1 second\");\n\t\tassertThat(new TimeoutDuration(2, SECONDS)).hasToString(\"2 seconds\");\n\t}\n\n\t@Test\n\tvoid fulfillsEqualsAndHashCodeContract() {\n\t\tvar oneSecond = new TimeoutDuration(1, SECONDS);\n\n\t\tassertThat(oneSecond) //\n\t\t\t\t.isEqualTo(oneSecond) //\n\t\t\t\t.isEqualTo(new TimeoutDuration(1, SECONDS)) //\n\t\t\t\t.hasSameHashCodeAs(new TimeoutDuration(1, SECONDS)) //\n\t\t\t\t.isNotEqualTo(\"foo\") //\n\t\t\t\t.isNotEqualTo(new TimeoutDuration(2, SECONDS)) //\n\t\t\t\t.isNotEqualTo(new TimeoutDuration(1, MINUTES));\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExceptionFactoryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.engine.extension.TimeoutExceptionFactory.create;\n\nimport java.util.concurrent.TimeoutException;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.9\n */\n@DisplayName(\"TimeoutExceptionFactory\")\nclass TimeoutExceptionFactoryTests {\n\n\tprivate static final TimeoutDuration tenMillisDuration = new TimeoutDuration(10, MILLISECONDS);\n\tprivate static final Exception suppressedException = new Exception(\"Winke!\");\n\tprivate static final String methodSignature = \"test()\";\n\n\t@Test\n\t@DisplayName(\"creates exception with method signature and timeout\")\n\tvoid createExceptionWithMethodSignatureTimeout() {\n\t\tTimeoutException exception = create(methodSignature, tenMillisDuration);\n\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessage(\"test() timed out after 10 milliseconds\") //\n\t\t\t\t.hasNoSuppressedExceptions();\n\t}\n\n\t@Test\n\t@DisplayName(\"creates exception with method signature, timeout and throwable\")\n\tvoid createExceptionWithMethodSignatureTimeoutAndThrowable() {\n\t\tTimeoutException exception = create(methodSignature, tenMillisDuration, suppressedException);\n\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessage(\"test() timed out after 10 milliseconds\") //\n\t\t\t\t.hasSuppressedException(suppressedException);\n\t}\n\n\t@SuppressWarnings({ \"DataFlowIssue\", \"ThrowableNotThrown\" })\n\t@Nested\n\t@DisplayName(\"throws exception when\")\n\tclass ThrowException {\n\n\t\t@Test\n\t\t@DisplayName(\"method signature is null\")\n\t\tvoid methodSignatureIsNull() {\n\t\t\tassertThatThrownBy(() -> create(null, tenMillisDuration, suppressedException)) //\n\t\t\t\t\t.hasMessage(\"method signature must not be null\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"method timeout duration is null\")\n\t\tvoid timeoutDurationIsnull() {\n\t\t\tassertThatThrownBy(() -> create(methodSignature, null, suppressedException)) //\n\t\t\t\t\t.hasMessage(\"timeout duration must not be null\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutExtensionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static java.util.concurrent.TimeUnit.NANOSECONDS;\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Constants.DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.TIMEOUT_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.Timeout.ThreadMode.SAME_THREAD;\nimport static org.junit.jupiter.api.Timeout.ThreadMode.SEPARATE_THREAD;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.time.Duration;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeoutException;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.RuntimeUtils;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Events;\nimport org.junit.platform.testkit.engine.Execution;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * @since 5.5\n */\n@DisplayName(\"@Timeout\")\nclass TimeoutExtensionTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @Test methods\")\n\tvoid appliesTimeoutOnAnnotatedTestMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectMethod(TimeoutAnnotatedTestMethodTestCase.class, \"testMethod\")) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"testMethod() timed out after 10 milliseconds\");\n\t}\n\n\t@Test\n\t@DisplayName(\"is not applied on annotated @Test methods using timeout mode: disabled\")\n\tvoid doesNotApplyTimeoutOnAnnotatedTestMethodsUsingDisabledTimeoutMode() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectMethod(TimeoutAnnotatedTestMethodTestCase.class, \"testMethod\")) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.configurationParameter(TIMEOUT_MODE_PROPERTY_NAME, \"disabled\").build());\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable()) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t@Test\n\t@DisplayName(\"is not applied on annotated @Test methods using timeout mode: disabled\")\n\tvoid applyTimeoutOnAnnotatedTestMethodsUsingDisabledOnDebugTimeoutMode() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectMethod(TimeoutAnnotatedTestMethodTestCase.class, \"testMethod\")) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.configurationParameter(TIMEOUT_MODE_PROPERTY_NAME, \"disabled_on_debug\").build());\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t// The check to see if debugging is pushing the timer just above 1 second\n\t\t\t\t.isLessThan(Duration.ofSeconds(2));\n\n\t\t// Should we test if we're debugging? This test will fail if we are debugging.\n\t\tif (RuntimeUtils.isDebugMode()) {\n\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable()) //\n\t\t\t\t\t.isEmpty();\n\t\t}\n\t\telse {\n\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testMethod() timed out after 10 milliseconds\");\n\t\t}\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @TestTemplate methods\")\n\tvoid appliesTimeoutOnAnnotatedTestTemplateMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectMethod(TimeoutAnnotatedTestMethodTestCase.class, \"testTemplateMethod\")) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tStream.of(\"repetition 1\", \"repetition 2\").forEach(displayName -> {\n\t\t\tExecution execution = findExecution(results.testEvents(), displayName);\n\t\t\tassertThat(execution.getDuration()) //\n\t\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testTemplateMethod() timed out after 10 milliseconds\");\n\t\t});\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @TestFactory methods\")\n\tvoid appliesTimeoutOnAnnotatedTestFactoryMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectMethod(TimeoutAnnotatedTestMethodTestCase.class, \"testFactoryMethod\")) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.containerEvents(), \"testFactoryMethod()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"testFactoryMethod() timed out after 10 milliseconds\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@ValueSource(classes = { TimeoutAnnotatedClassTestCase.class, InheritedTimeoutAnnotatedClassTestCase.class })\n\t@DisplayName(\"is applied on testable methods in annotated classes\")\n\tvoid appliesTimeoutOnTestableMethodsInAnnotatedClasses(Class<?> testClass) {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(testClass)) //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.configurationParameter(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tassertAll(Stream.of(\"testMethod()\", \"repetition 1\", \"repetition 2\", \"testFactoryMethod()\") //\n\t\t\t\t.map(displayName -> () -> {\n\t\t\t\t\tExecution execution = findExecution(results.allEvents(), displayName);\n\t\t\t\t\tassertThat(execution.getDuration()) //\n\t\t\t\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\t\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t\t\t.hasMessageEndingWith(\"timed out after 10000000 nanoseconds\");\n\t\t\t\t}));\n\t}\n\n\t@Test\n\t@DisplayName(\"fails methods that do not throw InterruptedException\")\n\tvoid failsMethodsWithoutInterruptedException() {\n\t\tEngineExecutionResults results = executeTestsForClass(MethodWithoutInterruptedExceptionTestCase.class);\n\n\t\tExecution execution = findExecution(results.testEvents(), \"methodThatDoesNotThrowInterruptedException()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(1)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"methodThatDoesNotThrowInterruptedException() timed out after 1 millisecond\");\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @BeforeAll methods\")\n\tvoid appliesTimeoutOnAnnotatedBeforeAllMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(TimeoutAnnotatedBeforeAllMethodTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.containerEvents(),\n\t\t\tTimeoutAnnotatedBeforeAllMethodTestCase.class.getSimpleName());\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"setUp() timed out after 10 milliseconds\");\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @BeforeEach methods\")\n\tvoid appliesTimeoutOnAnnotatedBeforeEachMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(TimeoutAnnotatedBeforeEachMethodTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"setUp() timed out after 10 milliseconds\");\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @AfterEach methods\")\n\tvoid appliesTimeoutOnAnnotatedAfterEachMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(TimeoutAnnotatedAfterEachMethodTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"tearDown() timed out after 10 milliseconds\");\n\t}\n\n\t@Test\n\t@DisplayName(\"is applied on annotated @AfterAll methods\")\n\tvoid appliesTimeoutOnAnnotatedAfterAllMethods() {\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(TimeoutAnnotatedAfterAllMethodTestCase.class)) //\n\t\t\t\t.configurationParameter(DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME, \"42ns\") //\n\t\t\t\t.build());\n\n\t\tExecution execution = findExecution(results.containerEvents(),\n\t\t\tTimeoutAnnotatedAfterAllMethodTestCase.class.getSimpleName());\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessage(\"tearDown() timed out after 10 milliseconds\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource\n\t@DisplayName(\"is applied from configuration parameters by default\")\n\tvoid appliesDefaultTimeoutsFromConfigurationParameters(String propertyName, String slowMethod) {\n\t\tPlainTestCase.slowMethod = slowMethod;\n\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t.selectors(selectClass(PlainTestCase.class)) //\n\t\t\t\t.configurationParameter(propertyName, \"1ns\") //\n\t\t\t\t.build());\n\t\tvar failure = results.allEvents().executions().failed() //\n\t\t\t\t.map(execution -> execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.findFirst();\n\t\tassertThat(failure).containsInstanceOf(TimeoutException.class);\n\t\tassertThat(failure.orElseThrow()).hasMessage(slowMethod + \" timed out after 1 nanosecond\");\n\t}\n\n\tstatic Stream<Arguments> appliesDefaultTimeoutsFromConfigurationParameters() {\n\t\treturn Stream.of( //\n\t\t\tArguments.of(DEFAULT_BEFORE_ALL_METHOD_TIMEOUT_PROPERTY_NAME, \"beforeAll()\"), //\n\t\t\tArguments.of(DEFAULT_BEFORE_EACH_METHOD_TIMEOUT_PROPERTY_NAME, \"beforeEach()\"), //\n\t\t\tArguments.of(DEFAULT_TEST_METHOD_TIMEOUT_PROPERTY_NAME, \"test()\"), //\n\t\t\tArguments.of(DEFAULT_TEST_TEMPLATE_METHOD_TIMEOUT_PROPERTY_NAME, \"testTemplate()\"), //\n\t\t\tArguments.of(DEFAULT_TEST_FACTORY_METHOD_TIMEOUT_PROPERTY_NAME, \"testFactory()\"), //\n\t\t\tArguments.of(DEFAULT_AFTER_EACH_METHOD_TIMEOUT_PROPERTY_NAME, \"afterEach()\"), //\n\t\t\tArguments.of(DEFAULT_AFTER_ALL_METHOD_TIMEOUT_PROPERTY_NAME, \"afterAll()\") //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"does not swallow unrecoverable exceptions\")\n\tvoid doesNotSwallowUnrecoverableExceptions() {\n\t\tassertThrows(OutOfMemoryError.class, () -> executeTestsForClass(UnrecoverableExceptionTestCase.class));\n\t}\n\n\t@Test\n\t@DisplayName(\"does not affect tests that don't exceed the timeout\")\n\tvoid doesNotAffectTestsThatDoNotExceedTimeoutDuration() {\n\t\texecuteTestsForClass(NonTimeoutExceedingTestCase.class).allEvents().assertStatistics(stats -> stats.failed(0));\n\t}\n\n\t@Test\n\t@DisplayName(\"includes fully qualified class name if method is not in the test class\")\n\tvoid includesClassNameIfMethodIsNotInTestClass() {\n\t\tEngineExecutionResults results = executeTestsForClass(NestedClassWithOuterSetupMethodTestCase.class);\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getDuration()) //\n\t\t\t\t.isGreaterThanOrEqualTo(Duration.ofMillis(10)) //\n\t\t\t\t.isLessThan(Duration.ofSeconds(1));\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t.hasMessageEndingWith(\n\t\t\t\t\t\"$NestedClassWithOuterSetupMethodTestCase#setUp() timed out after 10 milliseconds\");\n\t}\n\n\t@Test\n\t@DisplayName(\"reports illegal timeout durations\")\n\tvoid reportsIllegalTimeoutDurations() {\n\t\tEngineExecutionResults results = executeTestsForClass(IllegalTimeoutDurationTestCase.class);\n\n\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(PreconditionViolationException.class) //\n\t\t\t\t.hasMessage(\"timeout duration must be a positive number: 0\");\n\t}\n\n\tprivate static Execution findExecution(Events events, String displayName) {\n\t\treturn events.executions()//\n\t\t\t\t.filter(execution -> execution.getTestDescriptor().getDisplayName().contains(displayName))//\n\t\t\t\t.findFirst() //\n\t\t\t\t.orElseThrow();\n\t}\n\n\t@Nested\n\t@DisplayName(\"separate thread\")\n\tclass SeparateThread {\n\t\t@Test\n\t\t@DisplayName(\"timeout exceeded\")\n\t\tvoid timeoutExceededInSeparateThread() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(TimeoutExceedingSeparateThreadTestCase.class);\n\n\t\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\t\tThrowable failure = execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow();\n\t\t\tassertThat(failure) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testMethod() timed out after 100 milliseconds\");\n\t\t\tassertThat(failure.getCause()) //\n\t\t\t\t\t.hasMessageStartingWith(\"Execution timed out in \") //\n\t\t\t\t\t.hasStackTraceContaining(TimeoutExceedingSeparateThreadTestCase.class.getName() + \".testMethod\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"non timeout exceeded\")\n\t\tvoid nonTimeoutExceededInSeparateThread() {\n\t\t\texecuteTestsForClass(NonTimeoutExceedingSeparateThreadTestCase.class).allEvents() //\n\t\t\t\t\t.assertStatistics(stats -> stats.failed(0));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"does not swallow unrecoverable exceptions\")\n\t\tvoid separateThreadDoesNotSwallowUnrecoverableExceptions() {\n\t\t\tassertThrows(OutOfMemoryError.class,\n\t\t\t\t() -> executeTestsForClass(UnrecoverableExceptionInSeparateThreadTestCase.class));\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"handles invocation exceptions\")\n\t\tvoid separateThreadHandlesInvocationExceptions() {\n\t\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t\t.selectors(selectMethod(ExceptionInSeparateThreadTestCase.class, \"test\")) //\n\t\t\t\t\t.build());\n\n\t\t\tExecution execution = findExecution(results.testEvents(), \"test()\");\n\t\t\tassertThat(execution.getDuration()) //\n\t\t\t\t\t.isLessThan(Duration.ofSeconds(5));\n\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(RuntimeException.class) //\n\t\t\t\t\t.hasMessage(\"Oppps!\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"propagates assertion exceptions\")\n\t\tvoid separateThreadHandlesOpenTestFailedAssertion() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(FailedAssertionInSeparateThreadTestCase.class);\n\n\t\t\tExecution openTestFailure = findExecution(results.testEvents(), \"testOpenTestAssertion()\");\n\t\t\tassertThat(openTestFailure.getDuration()) //\n\t\t\t\t\t.isLessThan(Duration.ofSeconds(5));\n\t\t\tassertThat(openTestFailure.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(AssertionFailedError.class);\n\n\t\t\tExecution javaLangFailure = findExecution(results.testEvents(), \"testJavaLangAssertion()\");\n\t\t\tassertThat(javaLangFailure.getDuration()) //\n\t\t\t\t\t.isLessThan(Duration.ofSeconds(5));\n\t\t\tassertThat(javaLangFailure.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(AssertionError.class);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"when one test is stuck \\\"forever\\\" the next tests should not get stuck\")\n\t\tvoid oneThreadStuckForever() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(OneTestStuckForeverAndTheOthersNotTestCase.class);\n\n\t\t\tExecution stuckExecution = findExecution(results.testEvents(), \"stuck()\");\n\t\t\tassertThat(stuckExecution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"stuck() timed out after 10 milliseconds\");\n\n\t\t\tExecution testZeroExecution = findExecution(results.testEvents(), \"testZero()\");\n\t\t\tassertThat(testZeroExecution.getTerminationInfo().getExecutionResult().getStatus()) //\n\t\t\t\t\t.isEqualTo(Status.SUCCESSFUL);\n\n\t\t\tExecution testOneExecution = findExecution(results.testEvents(), \"testOne()\");\n\t\t\tassertThat(testOneExecution.getTerminationInfo().getExecutionResult().getStatus()) //\n\t\t\t\t\t.isEqualTo(Status.SUCCESSFUL);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"mixed same thread and separate thread tests\")\n\t\tvoid mixedSameThreadAndSeparateThreadTests() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(MixedSameThreadAndSeparateThreadTestCase.class);\n\n\t\t\tExecution stuck = findExecution(results.testEvents(), \"testZero()\");\n\t\t\tassertThat(stuck.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testZero() timed out after 10 milliseconds\");\n\n\t\t\tExecution testZeroExecution = findExecution(results.testEvents(), \"testOne()\");\n\t\t\tassertThat(testZeroExecution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testOne() timed out after 10 milliseconds\");\n\n\t\t\tExecution testOneExecution = findExecution(results.testEvents(), \"testTwo()\");\n\t\t\tassertThat(testOneExecution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"testTwo() timed out after 10 milliseconds\");\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"one test is stuck \\\"forever\\\" in separate thread and other tests in same thread not\")\n\t\tvoid oneThreadStuckForeverAndOtherTestsInSameThread() {\n\t\t\tEngineExecutionResults results = executeTestsForClass(\n\t\t\t\tOneTestStuckForeverAndTheOthersInSameThreadNotTestCase.class);\n\n\t\t\tExecution stuckExecution = findExecution(results.testEvents(), \"stuck()\");\n\t\t\tassertThat(stuckExecution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t.hasMessage(\"stuck() timed out after 10 milliseconds\");\n\n\t\t\tExecution testZeroExecution = findExecution(results.testEvents(), \"testZero()\");\n\t\t\tassertThat(testZeroExecution.getTerminationInfo().getExecutionResult().getStatus()) //\n\t\t\t\t\t.isEqualTo(Status.SUCCESSFUL);\n\n\t\t\tExecution testOneExecution = findExecution(results.testEvents(), \"testOne()\");\n\t\t\tassertThat(testOneExecution.getTerminationInfo().getExecutionResult().getStatus()) //\n\t\t\t\t\t.isEqualTo(Status.SUCCESSFUL);\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\"is not applied on annotated @Test methods using timeout mode: disabled\")\n\t\tvoid doesNotApplyTimeoutOnAnnotatedTestMethodsUsingDisabledTimeoutMode() {\n\t\t\tEngineExecutionResults results = executeTests(request() //\n\t\t\t\t\t.selectors(selectMethod(TimeoutExceedingSeparateThreadTestCase.class, \"testMethod\")) //\n\t\t\t\t\t.configurationParameter(TIMEOUT_MODE_PROPERTY_NAME, \"disabled\").build());\n\n\t\t\tExecution execution = findExecution(results.testEvents(), \"testMethod()\");\n\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable()) //\n\t\t\t\t\t.isEmpty();\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"on class level\")\n\t\tclass OnClassLevel {\n\t\t\t@Test\n\t\t\t@DisplayName(\"timeout exceeded\")\n\t\t\tvoid timeoutExceededInSeparateThreadOnClassLevel() {\n\t\t\t\tEngineExecutionResults results = executeTestsForClass(TimeoutExceededOnClassLevelTestCase.class);\n\n\t\t\t\tExecution execution = findExecution(results.testEvents(), \"exceptionThrown()\");\n\t\t\t\tassertThat(execution.getTerminationInfo().getExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t\t\t.isInstanceOf(TimeoutException.class) //\n\t\t\t\t\t\t.hasMessage(\"exceptionThrown() timed out after 100 milliseconds\");\n\t\t\t}\n\n\t\t\t@Test\n\t\t\t@DisplayName(\"non timeout exceeded\")\n\t\t\tvoid nonTimeoutExceededInSeparateThreadOnClassLevel() {\n\t\t\t\texecuteTestsForClass(NonTimeoutExceededOnClassLevelTestCase.class).allEvents() //\n\t\t\t\t\t\t.assertStatistics(stats -> stats.failed(0));\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class TimeoutAnnotatedTestMethodTestCase {\n\t\t@Test\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tvoid testMethod() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tvoid testTemplateMethod() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@TestFactory\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tStream<DynamicTest> testFactoryMethod() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tstatic class TimeoutAnnotatedBeforeAllMethodTestCase {\n\t\t@BeforeAll\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tstatic void setUp() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@Test\n\t\tvoid testMethod() {\n\t\t\t// never called\n\t\t}\n\t}\n\n\tstatic class TimeoutAnnotatedBeforeEachMethodTestCase {\n\t\t@BeforeEach\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tvoid setUp() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@Test\n\t\tvoid testMethod() {\n\t\t\t// never called\n\t\t}\n\t}\n\n\tstatic class TimeoutAnnotatedAfterEachMethodTestCase {\n\t\t@Test\n\t\tvoid testMethod() {\n\t\t\t// do nothing\n\t\t}\n\n\t\t@AfterEach\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tvoid tearDown() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\tstatic class TimeoutAnnotatedAfterAllMethodTestCase {\n\t\t@Test\n\t\tvoid testMethod() {\n\t\t\t// do nothing\n\t\t}\n\n\t\t@AfterAll\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tstatic void tearDown() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\t@Timeout(value = 10_000_000, unit = NANOSECONDS)\n\tstatic class TimeoutAnnotatedClassTestCase {\n\t\t@Nested\n\t\tclass NestedClass {\n\t\t\t@Test\n\t\t\tvoid testMethod() throws Exception {\n\t\t\t\tThread.sleep(1000);\n\t\t\t}\n\n\t\t\t@RepeatedTest(2)\n\t\t\tvoid testTemplateMethod() throws Exception {\n\t\t\t\tThread.sleep(1000);\n\t\t\t}\n\n\t\t\t@TestFactory\n\t\t\tStream<DynamicTest> testFactoryMethod() throws Exception {\n\t\t\t\tThread.sleep(1000);\n\t\t\t\treturn Stream.empty();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class InheritedTimeoutAnnotatedClassTestCase extends TimeoutAnnotatedClassTestCase {\n\t}\n\n\tstatic class MethodWithoutInterruptedExceptionTestCase {\n\t\t@Test\n\t\t@Timeout(value = 1, unit = MILLISECONDS)\n\t\tvoid methodThatDoesNotThrowInterruptedException() {\n\t\t\tnew EventuallyInterruptibleInvocation().proceed();\n\t\t}\n\t}\n\n\tstatic class PlainTestCase {\n\n\t\t@Nullable\n\t\tpublic static String slowMethod;\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() throws Exception {\n\t\t\twaitForInterrupt(\"beforeAll()\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() throws Exception {\n\t\t\twaitForInterrupt(\"beforeEach()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() throws Exception {\n\t\t\twaitForInterrupt(\"test()\");\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\tvoid testTemplate() throws Exception {\n\t\t\twaitForInterrupt(\"testTemplate()\");\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> testFactory() throws Exception {\n\t\t\twaitForInterrupt(\"testFactory()\");\n\t\t\treturn Stream.empty();\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() throws Exception {\n\t\t\twaitForInterrupt(\"afterEach()\");\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll() throws Exception {\n\t\t\twaitForInterrupt(\"afterAll()\");\n\t\t}\n\n\t\tprivate static void waitForInterrupt(String methodName) throws InterruptedException {\n\t\t\tif (methodName.equals(slowMethod)) {\n\t\t\t\tblockUntilInterrupted();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class UnrecoverableExceptionTestCase {\n\t\t@Test\n\t\t@Timeout(value = 1, unit = NANOSECONDS)\n\t\tvoid test() {\n\t\t\tnew EventuallyInterruptibleInvocation().proceed();\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\t}\n\n\t@Timeout(10)\n\tstatic class NonTimeoutExceedingTestCase {\n\t\t@Test\n\t\tvoid testMethod() {\n\t\t}\n\n\t\t@RepeatedTest(1)\n\t\tvoid testTemplateMethod() {\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> testFactoryMethod() {\n\t\t\treturn Stream.of(dynamicTest(\"dynamicTest\", () -> {\n\t\t\t}));\n\t\t}\n\t}\n\n\tstatic class NestedClassWithOuterSetupMethodTestCase {\n\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\t@BeforeEach\n\t\tvoid setUp() throws Exception {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedClass {\n\n\t\t\t@BeforeEach\n\t\t\tvoid setUp() {\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid testMethod() {\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class IllegalTimeoutDurationTestCase {\n\n\t\t@Test\n\t\t@Timeout(0)\n\t\tvoid testMethod() {\n\t\t}\n\n\t}\n\n\tstatic class TimeoutExceedingWithInferredThreadModeTestCase {\n\t\t@Test\n\t\t@Timeout(value = 10, unit = MILLISECONDS)\n\t\tvoid testMethod() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\tstatic class TimeoutExceedingSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testMethod() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\tstatic class NonTimeoutExceedingSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testMethod() {\n\t\t}\n\t}\n\n\tstatic class UnrecoverableExceptionInSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 100, unit = SECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid test() {\n\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\t}\n\n\tstatic class ExceptionInSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 5, unit = SECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid test() {\n\t\t\tthrow new RuntimeException(\"Oppps!\");\n\t\t}\n\t}\n\n\tstatic class FailedAssertionInSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 5, unit = SECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testOpenTestAssertion() {\n\t\t\tthrow new AssertionFailedError();\n\t\t}\n\n\t\t@Test\n\t\t@Timeout(value = 5, unit = SECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testJavaLangAssertion() {\n\t\t\tthrow new AssertionError();\n\t\t}\n\t}\n\n\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\tstatic class TimeoutExceededOnClassLevelTestCase {\n\t\t@Test\n\t\tvoid exceptionThrown() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\tstatic class NonTimeoutExceededOnClassLevelTestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class OneTestStuckForeverAndTheOthersNotTestCase {\n\n\t\t@Test\n\t\t@Order(0)\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid stuck() throws InterruptedException {\n\t\t\tblockUntilInterrupted();\n\t\t}\n\n\t\t@Test\n\t\t@Order(1)\n\t\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testZero() {\n\t\t}\n\n\t\t@Test\n\t\t@Order(2)\n\t\t@Timeout(value = 100, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testOne() {\n\t\t}\n\t}\n\n\tstatic class MixedSameThreadAndSeparateThreadTestCase {\n\t\t@Test\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testZero() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@Test\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SAME_THREAD)\n\t\tvoid testOne() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\n\t\t@Test\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid testTwo() throws InterruptedException {\n\t\t\tThread.sleep(1000);\n\t\t}\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class OneTestStuckForeverAndTheOthersInSameThreadNotTestCase {\n\n\t\t@Test\n\t\t@Order(0)\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SEPARATE_THREAD)\n\t\tvoid stuck() throws InterruptedException {\n\t\t\tblockUntilInterrupted();\n\t\t}\n\n\t\t@Test\n\t\t@Order(1)\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SAME_THREAD)\n\t\tvoid testZero() {\n\t\t}\n\n\t\t@Test\n\t\t@Order(2)\n\t\t@Timeout(value = 10, unit = MILLISECONDS, threadMode = SAME_THREAD)\n\t\tvoid testOne() {\n\t\t}\n\t}\n\n\tprivate static void blockUntilInterrupted() throws InterruptedException {\n\t\tnew CountDownLatch(1).await();\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/TimeoutInvocationFactoryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.Mockito.verify;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout.ThreadMode;\nimport org.junit.jupiter.api.extension.DisabledInEclipse;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext.Store;\nimport org.junit.jupiter.api.extension.InvocationInterceptor.Invocation;\nimport org.junit.jupiter.engine.execution.NamespaceAwareStore;\nimport org.junit.jupiter.engine.extension.TimeoutInvocationFactory.SingleThreadExecutorResource;\nimport org.junit.jupiter.engine.extension.TimeoutInvocationFactory.TimeoutInvocationParameters;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n// org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'store'.\n// Mockito cannot mock this class: class org.junit.jupiter.engine.execution.NamespaceAwareStore.\n// You are seeing this disclaimer because Mockito is configured to create inlined mocks.\n// Byte Buddy could not instrument all classes within the mock's type hierarchy.\n@DisabledInEclipse(\"Mockito cannot create a spy for NamespaceAwareStore using the inline MockMaker in Eclipse IDE\")\n@DisplayName(\"TimeoutInvocationFactory\")\n@ExtendWith(MockitoExtension.class)\nclass TimeoutInvocationFactoryTests {\n\n\t@Spy\n\tprivate final Store store = new NamespaceAwareStore(new NamespacedHierarchicalStore<>(null),\n\t\tNamespace.create(TimeoutInvocationFactoryTests.class));\n\n\t@Mock\n\tprivate Invocation<String> invocation;\n\n\t@Mock\n\tprivate TimeoutDuration timeoutDuration;\n\n\tprivate TimeoutInvocationFactory timeoutInvocationFactory;\n\n\tprivate TimeoutInvocationParameters<String> parameters;\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tparameters = new TimeoutInvocationParameters<>(invocation, timeoutDuration, () -> \"description\",\n\t\t\tPreInterruptCallbackInvocation.NOOP);\n\t\ttimeoutInvocationFactory = new TimeoutInvocationFactory(store);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\t@DisplayName(\"throws exception when null store is provided on create\")\n\tvoid shouldThrowExceptionWhenInstantiatingWithNullStore() {\n\t\tassertThatThrownBy(() -> new TimeoutInvocationFactory(null)) //\n\t\t\t\t.hasMessage(\"store must not be null\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\t@DisplayName(\"throws exception when null timeout thread mode is provided on create\")\n\tvoid shouldThrowExceptionWhenNullTimeoutThreadModeIsProvidedWhenCreate() {\n\t\tassertThatThrownBy(() -> timeoutInvocationFactory.create(null, parameters)) //\n\t\t\t\t.hasMessage(\"thread mode must not be null\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\t@DisplayName(\"throws exception when null timeout invocation parameters is provided on create\")\n\tvoid shouldThrowExceptionWhenNullTimeoutInvocationParametersIsProvidedWhenCreate() {\n\t\tassertThatThrownBy(() -> timeoutInvocationFactory.create(ThreadMode.SAME_THREAD, null)) //\n\t\t\t\t.hasMessage(\"timeout invocation parameters must not be null\");\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\t@Test\n\t@DisplayName(\"creates timeout invocation for SAME_THREAD thread mode\")\n\tvoid shouldCreateTimeoutInvocationForSameThreadTimeoutThreadMode() {\n\t\tvar invocation = timeoutInvocationFactory.create(ThreadMode.SAME_THREAD, parameters);\n\t\tassertThat(invocation).isInstanceOf(SameThreadTimeoutInvocation.class);\n\t\tverify(store).computeIfAbsent(SingleThreadExecutorResource.class);\n\t}\n\n\t@Test\n\t@DisplayName(\"creates timeout invocation for SEPARATE_THREAD thread mode\")\n\tvoid shouldCreateTimeoutInvocationForSeparateThreadTimeoutThreadMode() {\n\t\tvar invocation = timeoutInvocationFactory.create(ThreadMode.SEPARATE_THREAD, parameters);\n\t\tassertThat(invocation).isInstanceOf(SeparateThreadTimeoutInvocation.class);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AlwaysDisabledCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension.sub;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Intentionally in a subpackage in order to properly test deactivation\n * of conditions based on patterns. In other words, we do not want this\n * condition declared in the same package as the\n * {@link org.junit.jupiter.engine.extension.DisabledCondition}\n *\n * ExecutionCondition always returns disabled, since we want to test the\n * deactivation of the condition itself.\n *\n * @since 5.7\n */\npublic class AlwaysDisabledCondition implements ExecutionCondition {\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\treturn ConditionEvaluationResult.disabled(\"Always Disabled\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/AnotherAlwaysDisabledCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension.sub;\n\n/**\n * Intentionally in a subpackage in order to properly test deactivation\n * of conditions based on patterns. In other words, we do not want this\n * condition declared in the same package as the\n * {@link org.junit.jupiter.engine.extension.DisabledCondition}\n *\n * ExecutionCondition always returns disabled, since we want to test the\n * deactivation of the condition itself.\n *\n * @since 5.7\n */\npublic class AnotherAlwaysDisabledCondition extends AlwaysDisabledCondition {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/sub/SystemPropertyCondition.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.extension.sub;\n\nimport static org.junit.platform.commons.support.AnnotationSupport.findAnnotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.Objects;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * Intentionally in a subpackage in order to properly test deactivation\n * of conditions based on patterns. In other words, we do not want this\n * condition declared in the same package as the\n * {@link org.junit.jupiter.engine.extension.DisabledCondition}\n *\n * @since 5.0\n */\npublic class SystemPropertyCondition implements ExecutionCondition {\n\n\t@Target({ ElementType.METHOD, ElementType.TYPE })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ExtendWith(SystemPropertyCondition.class)\n\tpublic @interface SystemProperty {\n\n\t\tString key();\n\n\t\tString value();\n\t}\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\tOptional<SystemProperty> optional = findAnnotation(context.getElement(), SystemProperty.class);\n\n\t\tif (optional.isPresent()) {\n\t\t\tSystemProperty systemProperty = optional.get();\n\t\t\tString key = systemProperty.key();\n\t\t\tString expected = systemProperty.value();\n\t\t\tString actual = System.getProperty(key);\n\n\t\t\tif (!Objects.equals(expected, actual)) {\n\t\t\t\treturn ConditionEvaluationResult.disabled(\n\t\t\t\t\t\"System property [%s] has a value of [%s] instead of [%s]\".formatted(key, actual, expected));\n\t\t\t}\n\t\t}\n\n\t\treturn ConditionEvaluationResult.enabled(\"@SystemProperty is not present\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/subpackage/SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.subpackage;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestReporter;\n\n/**\n * @since 5.9\n */\npublic class SuperClassWithPackagePrivateLifecycleMethodInDifferentPackageTestCase {\n\n\tprotected boolean beforeEachInvoked = false;\n\n\t@BeforeEach\n\tvoid beforeEach() {\n\t\tthis.beforeEachInvoked = true;\n\t}\n\n\t@Test\n\tvoid test(TestInfo testInfo, TestReporter reporter) {\n\t\treporter.publishEntry(\"invokedSuper\", testInfo.getTestMethod().orElseThrow().toGenericString());\n\t\tassertThat(this.beforeEachInvoked).isTrue();\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/engine/support/OpenTest4JAndJUnit4AwareThrowableCollectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.internal.AssumptionViolatedException;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Unit tests for {@link OpenTest4JAndJUnit4AwareThrowableCollector}.\n *\n * @since 5.5.2\n */\n@TrackLogRecords\nclass OpenTest4JAndJUnit4AwareThrowableCollectorTests {\n\n\t@Test\n\tvoid simulateJUnit4NotInTheClasspath(LogRecordListener listener) throws Throwable {\n\t\tTestClassLoader classLoader = new TestClassLoader(true, false);\n\n\t\tdoWithCustomClassLoader(classLoader, () -> {\n\t\t\t// Ensure that our custom ClassLoader actually throws a ClassNotFoundException\n\t\t\t// when attempting to load the AssumptionViolatedException class.\n\t\t\tassertThrows(ClassNotFoundException.class,\n\t\t\t\t() -> ReflectionSupport.tryToLoadClass(AssumptionViolatedException.class.getName()).get());\n\n\t\t\tClass<?> clazz = classLoader.loadClass(OpenTest4JAndJUnit4AwareThrowableCollector.class.getName());\n\t\t\tassertNotNull(ReflectionSupport.newInstance(clazz));\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(listener.stream(Level.FINE).map(LogRecord::getMessage).findFirst().orElse(\"<not found>\"))\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Failed to load class org.junit.internal.AssumptionViolatedException: \" +\n\t\t\t\t\t\"only supporting org.opentest4j.TestAbortedException for aborted execution.\");\n\t\t\t// @formatter:on\n\t\t});\n\t}\n\n\t@Test\n\tvoid simulateHamcrestNotInTheClasspath(LogRecordListener listener) throws Throwable {\n\t\tTestClassLoader classLoader = new TestClassLoader(false, true);\n\n\t\tdoWithCustomClassLoader(classLoader, () -> {\n\t\t\t// Ensure that our custom ClassLoader actually throws a NoClassDefFoundError\n\t\t\t// when attempting to load the AssumptionViolatedException class.\n\t\t\tassertThrows(NoClassDefFoundError.class,\n\t\t\t\t() -> ReflectionUtils.tryToLoadClass(AssumptionViolatedException.class.getName()).get());\n\n\t\t\tClass<?> clazz = classLoader.loadClass(OpenTest4JAndJUnit4AwareThrowableCollector.class.getName());\n\t\t\tassertNotNull(ReflectionUtils.newInstance(clazz));\n\n\t\t\t// @formatter:off\n\t\t\tassertThat(listener.stream(Level.FINE).map(LogRecord::getMessage).findFirst().orElse(\"<not found>\"))\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"Failed to load class org.junit.internal.AssumptionViolatedException: \" +\n\t\t\t\t\t\"only supporting org.opentest4j.TestAbortedException for aborted execution. \" +\n\t\t\t\t\t\"Note that org.junit.internal.AssumptionViolatedException requires that Hamcrest is on the classpath.\");\n\t\t\t// @formatter:on\n\t\t});\n\t}\n\n\tprivate void doWithCustomClassLoader(ClassLoader classLoader, Executable executable) throws Throwable {\n\t\tClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\ttry {\n\t\t\t// We have to set our custom ClassLoader as the TCCL so that\n\t\t\t// ReflectionUtils uses it (indirectly via ClassLoaderUtils).\n\t\t\tThread.currentThread().setContextClassLoader(classLoader);\n\n\t\t\texecutable.execute();\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\tprivate static class TestClassLoader extends URLClassLoader {\n\n\t\tprivate static final URL[] CLASSPATH_URLS = new URL[] {\n\t\t\t\tOpenTest4JAndJUnit4AwareThrowableCollector.class.getProtectionDomain().getCodeSource().getLocation() };\n\n\t\tprivate final boolean simulateJUnit4Missing;\n\t\tprivate final boolean simulateHamcrestMissing;\n\n\t\tTestClassLoader(boolean simulateJUnit4Missing, boolean simulateHamcrestMissing) {\n\t\t\tsuper(CLASSPATH_URLS, getSystemClassLoader());\n\t\t\tthis.simulateJUnit4Missing = simulateJUnit4Missing;\n\t\t\tthis.simulateHamcrestMissing = simulateHamcrestMissing;\n\t\t}\n\n\t\t@Override\n\t\tpublic Class<?> loadClass(String name) throws ClassNotFoundException {\n\n\t\t\t// Load a new instance of the OpenTest4JAndJUnit4AwareThrowableCollector class\n\t\t\tif (name.equals(OpenTest4JAndJUnit4AwareThrowableCollector.class.getName())) {\n\t\t\t\treturn findClass(name);\n\t\t\t}\n\n\t\t\t// Simulate that JUnit 4 is not in the classpath when loading AssumptionViolatedException\n\t\t\tif (this.simulateJUnit4Missing && name.equals(AssumptionViolatedException.class.getName())) {\n\t\t\t\tthrow new ClassNotFoundException(AssumptionViolatedException.class.getName());\n\t\t\t}\n\n\t\t\t// Simulate that Hamcrest is not in the classpath when loading AssumptionViolatedException\n\t\t\tif (this.simulateHamcrestMissing && name.equals(AssumptionViolatedException.class.getName())) {\n\t\t\t\tthrow new NoClassDefFoundError(\"org/hamcrest/SelfDescribing\");\n\t\t\t}\n\n\t\t\t// Else\n\t\t\treturn super.loadClass(name);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/JupiterMigrationSupportTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * Test suite for JUnit Jupiter migration support.\n *\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 5.0\n */\n@Suite\n@SelectPackages(\"org.junit.jupiter.migrationsupport\")\n@IncludeClassNamePatterns(\".*Tests?\")\n@IncludeEngines(\"junit-jupiter\")\nclass JupiterMigrationSupportTestSuite {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreAnnotationIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.conditions;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.migrationsupport.EnableJUnit4MigrationSupport;\n\n/**\n * Empirical integration tests for JUnit 4's {@link Ignore @Ignore} support in\n * JUnit Jupiter, covering the {@link IgnoreCondition} and\n * {@link EnableJUnit4MigrationSupport @EnableJUnit4MigrationSupport}.\n *\n * @since 5.4\n * @see IgnoreConditionTests\n */\n@SuppressWarnings(\"removal\")\nclass IgnoreAnnotationIntegrationTests {\n\n\t@Nested\n\t@ExtendWith(IgnoreCondition.class)\n\tclass ExplicitIgnoreConditionRegistration extends BaseNestedTestCase {\n\t}\n\n\t@Nested\n\t@EnableJUnit4MigrationSupport\n\tclass ImplicitIgnoreConditionRegistration extends BaseNestedTestCase {\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tprivate static abstract class BaseNestedTestCase {\n\n\t\tprivate static List<String> tests = new ArrayList<>();\n\n\t\t@BeforeAll\n\t\tvoid clearTracking() {\n\t\t\ttests.clear();\n\t\t}\n\n\t\t@AfterAll\n\t\tvoid verifyTracking() {\n\t\t\tassertThat(tests).containsExactly(\"notIgnored\");\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid track(TestInfo testInfo) {\n\t\t\ttests.add(testInfo.getTestMethod().get().getName());\n\t\t}\n\n\t\t@Test\n\t\t@Ignore\n\t\tvoid ignored() {\n\t\t\tfail(\"This method should have been disabled via @Ignore\");\n\t\t}\n\n\t\t@Test\n\t\t// @Ignore\n\t\tvoid notIgnored() {\n\t\t\t/* no-op */\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/conditions/IgnoreConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.conditions;\n\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\n\nimport org.junit.Ignore;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * Integration tests for JUnit 4's {@link Ignore @Ignore} support in JUnit\n * Jupiter provided by the {@link IgnoreCondition}.\n *\n * @since 5.4\n * @see IgnoreAnnotationIntegrationTests\n */\n@SuppressWarnings(\"removal\")\nclass IgnoreConditionTests {\n\n\t@Test\n\tvoid ignoredTestClassWithDefaultMessage() {\n\t\tClass<?> testClass = IgnoredClassWithDefaultMessageTestCase.class;\n\n\t\t// @formatter:off\n\t\texecuteTestsForClass(testClass).allEvents().assertEventsMatchExactly(\n\t\t\tevent(engine(), started()),\n\t\t\tevent(container(testClass), skippedWithReason(testClass + \" is disabled via @org.junit.Ignore\")),\n\t\t\tevent(engine(), finishedSuccessfully())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid ignoredTestClassWithCustomMessage() {\n\t\tClass<?> testClass = IgnoredClassWithCustomMessageTestCase.class;\n\n\t\t// @formatter:off\n\t\texecuteTestsForClass(testClass).allEvents().assertEventsMatchExactly(\n\t\t\tevent(engine(), started()),\n\t\t\tevent(container(testClass), skippedWithReason(\"Ignored Class\")),\n\t\t\tevent(engine(), finishedSuccessfully())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid ignoredAndNotIgnoredTestMethods() {\n\t\tEngineExecutionResults executionResults = executeTestsForClass(IgnoredMethodsTestCase.class);\n\t\tEvents containers = executionResults.containerEvents();\n\t\tEvents tests = executionResults.testEvents();\n\n\t\t// executionResults.allEvents().debug();\n\t\t// executionResults.allEvents().debug(System.err);\n\n\t\t// containers.debug();\n\n\t\t// tests.debug(System.err);\n\t\t// tests.debug();\n\t\t// tests.skipped().debug();\n\t\t// tests.started().debug();\n\t\t// tests.succeeded().debug();\n\n\t\t// executionResults.allEvents().executions().debug();\n\t\t// containers.executions().debug();\n\t\t// tests.executions().debug();\n\n\t\texecutionResults.allEvents().executions().assertThatExecutions().hasSize(5);\n\t\tcontainers.executions().assertThatExecutions().hasSize(2);\n\t\ttests.executions().assertThatExecutions().hasSize(3);\n\n\t\t// @formatter:off\n\t\t// tests.debug().assertEventsMatchExactly(\n\t\ttests.assertEventsMatchExactly(\n\t\t\tevent(test(\"ignoredWithCustomMessage\"), skippedWithReason(\"Ignored Method\")),\n\t\t\tevent(test(\"notIgnored\"), started()),\n\t\t\tevent(test(\"notIgnored\"), finishedSuccessfully()),\n\t\t\tevent(test(\"ignoredWithDefaultMessage\"), skippedWithReason(\n\t\t\t\treason -> reason.endsWith(\"ignoredWithDefaultMessage() is disabled via @org.junit.Ignore\")))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate EngineExecutionResults executeTestsForClass(Class<?> testClass) {\n\t\treturn EngineTestKit.execute(\"junit-jupiter\", request().selectors(selectClass(testClass)).build());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith(IgnoreCondition.class)\n\t@Ignore\n\tstatic class IgnoredClassWithDefaultMessageTestCase {\n\n\t\t@Test\n\t\tvoid ignoredBecauseClassIsIgnored() {\n\t\t\t/* no-op */\n\t\t}\n\t}\n\n\t@ExtendWith(IgnoreCondition.class)\n\t@Ignore(\"Ignored Class\")\n\tstatic class IgnoredClassWithCustomMessageTestCase {\n\n\t\t@Test\n\t\tvoid ignoredBecauseClassIsIgnored() {\n\t\t\t/* no-op */\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMixedFramework\")\n\t@ExtendWith(IgnoreCondition.class)\n\tstatic class IgnoredMethodsTestCase {\n\n\t\t@Test\n\t\tvoid notIgnored() {\n\t\t\t/* no-op */\n\t\t}\n\n\t\t@Test\n\t\t@Ignore\n\t\tvoid ignoredWithDefaultMessage() {\n\t\t\tfail(\"This method should have been disabled via @Ignore\");\n\t\t}\n\n\t\t@Test\n\t\t@Ignore(\"Ignored Method\")\n\t\tvoid ignoredWithCustomMessage() {\n\t\t\tfail(\"This method should have been disabled via @Ignore\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/AbstractTestRuleAdapterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.migrationsupport.rules.adapter.AbstractTestRuleAdapter;\nimport org.junit.jupiter.migrationsupport.rules.member.TestRuleAnnotatedMember;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.rules.ErrorCollector;\nimport org.junit.rules.TemporaryFolder;\nimport org.junit.rules.TestRule;\nimport org.junit.rules.Verifier;\n\n/**\n * @since 5.0\n */\n@SuppressWarnings(\"removal\")\npublic class AbstractTestRuleAdapterTests {\n\n\t@Test\n\tvoid constructionWithAssignableArgumentsIsSuccessful() {\n\t\tnew TestableTestRuleAdapter(new SimpleRuleAnnotatedMember(new ErrorCollector()), Verifier.class);\n\t}\n\n\t@Test\n\tvoid constructionWithUnassignableArgumentsFails() {\n\t\tassertPreconditionViolationFor(() -> new TestableTestRuleAdapter(\n\t\t\tnew SimpleRuleAnnotatedMember(new TemporaryFolder()), Verifier.class)).withMessage(\n\t\t\t\t\"class org.junit.rules.Verifier is not assignable from class org.junit.rules.TemporaryFolder\");\n\t}\n\n\t@Test\n\tvoid exceptionsDuringMethodLookupAreWrappedAndThrown() {\n\t\tAbstractTestRuleAdapter adapter = new AbstractTestRuleAdapter(\n\t\t\tnew SimpleRuleAnnotatedMember(new ErrorCollector()), Verifier.class) {\n\n\t\t\t@Override\n\t\t\tpublic void before() {\n\t\t\t\tsuper.executeMethod(\"foo\");\n\t\t\t}\n\t\t};\n\n\t\tJUnitException exception = assertThrows(JUnitException.class, adapter::before);\n\n\t\tassertEquals(exception.getMessage(), \"Failed to find method foo() in class org.junit.rules.ErrorCollector\");\n\t}\n\n\tprivate static class TestableTestRuleAdapter extends AbstractTestRuleAdapter {\n\n\t\tTestableTestRuleAdapter(TestRuleAnnotatedMember annotatedMember, Class<? extends TestRule> adapteeClass) {\n\t\t\tsuper(annotatedMember, adapteeClass);\n\t\t}\n\t}\n\n\tprivate record SimpleRuleAnnotatedMember(TestRule testRule) implements TestRuleAnnotatedMember {\n\n\t\t@Override\n\t\tpublic TestRule getTestRule() {\n\t\t\treturn this.testRule;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/EnableRuleMigrationSupportWithBothRuleTypesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.rules.ExternalResource;\nimport org.junit.rules.Verifier;\n\n@SuppressWarnings(\"removal\")\n@EnableRuleMigrationSupport\npublic class EnableRuleMigrationSupportWithBothRuleTypesTests {\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\n\tprivate static boolean beforeOfRule2WasExecuted = false;\n\tprivate static boolean afterOfRule2WasExecuted = false;\n\n\tprivate static int numberOfRule1InstancesCreated = 0;\n\tprivate static int numberOfRule2InstancesCreated = 0;\n\n\t@Rule\n\tpublic Verifier verifier1 = new Verifier() {\n\t\t{\n\t\t\tnumberOfRule1InstancesCreated++;\n\t\t}\n\n\t\t@Override\n\t\tprotected void verify() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t};\n\n\t@Rule\n\tpublic ExternalResource getResource2() {\n\t\treturn new ExternalResource() {\n\t\t\t{\n\t\t\t\tnumberOfRule2InstancesCreated++;\n\t\t\t}\n\n\t\t\t@Nullable\n\t\t\tprivate Object instance;\n\n\t\t\t@Override\n\t\t\tprotected void before() {\n\t\t\t\tinstance = this;\n\t\t\t\tbeforeOfRule2WasExecuted = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void after() {\n\t\t\t\tassertNotNull(instance);\n\t\t\t\tassertSame(this, instance);\n\t\t\t\tafterOfRule2WasExecuted = true;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tvoid beforeMethodOfBothRule2WasExecuted() {\n\t\tassertTrue(beforeOfRule2WasExecuted);\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tassertEquals(1, numberOfRule1InstancesCreated);\n\t\tassertEquals(1, numberOfRule2InstancesCreated);\n\t\tif (!afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t\tif (!afterOfRule2WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExpectedExceptionSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.io.IOException;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\nimport org.junit.rules.ExpectedException;\n\n/**\n * Integration tests for {@link ExpectedExceptionSupport}.\n *\n * @since 5.0\n */\n@SuppressWarnings(\"removal\")\nclass ExpectedExceptionSupportTests {\n\n\t@Test\n\tvoid expectedExceptionIsProcessedCorrectly() {\n\t\tEvents tests = executeTestsForClass(ExpectedExceptionTestCase.class);\n\n\t\ttests.assertStatistics(stats -> stats.started(4).succeeded(1).aborted(0).failed(3));\n\n\t\ttests.succeeded().assertThatEvents().have(\n\t\t\tevent(test(\"correctExceptionExpectedThrown\"), finishedSuccessfully()));\n\n\t\ttests.failed().assertThatEvents()//\n\t\t\t\t.haveExactly(1, //\n\t\t\t\t\tevent(test(\"noExceptionExpectedButThrown\"), //\n\t\t\t\t\t\tfinishedWithFailure(message(\"no exception expected\")))) //\n\t\t\t\t.haveExactly(1, //\n\t\t\t\t\tevent(test(\"exceptionExpectedButNotThrown\"), //\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), //\n\t\t\t\t\t\t\tmessage(\"Expected test to throw an instance of java.lang.RuntimeException\")))) //\n\t\t\t\t.haveExactly(1, //\n\t\t\t\t\tevent(test(\"wrongExceptionExpected\"), //\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(AssertionError.class), //\n\t\t\t\t\t\t\tmessage(value -> value.contains(\"Expected: an instance of java.io.IOException\")))));\n\t}\n\n\t@Test\n\tvoid expectedExceptionSupportWithoutExpectedExceptionRule() {\n\t\tClass<?> testClass = ExpectedExceptionSupportWithoutExpectedExceptionRuleTestCase.class;\n\t\tEvents tests = executeTestsForClass(testClass);\n\n\t\ttests.assertStatistics(stats -> stats.started(2).succeeded(1).aborted(0).failed(1));\n\n\t\ttests.succeeded().assertThatEvents().have(event(test(\"success\"), finishedSuccessfully()));\n\n\t\ttests.failed().assertThatEvents()//\n\t\t\t\t.haveExactly(1, event(test(\"failure\"), finishedWithFailure(message(\"must fail\"))));\n\t}\n\n\tprivate Events executeTestsForClass(Class<?> testClass) {\n\t\treturn EngineTestKit.execute(\"junit-jupiter\", request().selectors(selectClass(testClass)).build()).testEvents();\n\t}\n\n\t@ExtendWith(ExpectedExceptionSupport.class)\n\tstatic class ExpectedExceptionTestCase {\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Rule\n\t\tpublic ExpectedException thrown = ExpectedException.none();\n\n\t\t@Test\n\t\tvoid noExceptionExpectedButThrown() {\n\t\t\tthrow new RuntimeException(\"no exception expected\");\n\t\t}\n\n\t\t@Test\n\t\tvoid exceptionExpectedButNotThrown() {\n\t\t\tthrown.expect(RuntimeException.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid wrongExceptionExpected() {\n\t\t\tthrown.expect(IOException.class);\n\t\t\tthrow new RuntimeException(\"wrong exception\");\n\t\t}\n\n\t\t@Test\n\t\tvoid correctExceptionExpectedThrown() {\n\t\t\tthrown.expect(RuntimeException.class);\n\t\t\tthrow new RuntimeException(\"right exception\");\n\t\t}\n\n\t}\n\n\t@ExtendWith(ExpectedExceptionSupport.class)\n\tstatic class ExpectedExceptionSupportWithoutExpectedExceptionRuleTestCase {\n\n\t\t@Test\n\t\tvoid success() {\n\t\t\t/* no-op */\n\t\t}\n\n\t\t@Test\n\t\tvoid failure() {\n\t\t\tfail(\"must fail\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.ExternalResource;\nimport org.junit.rules.TestRule;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\nclass ExternalResourceSupportForDifferentDeclaredReturnTypesRulesTests {\n\n\tprivate static boolean beforeOfRule1WasExecuted = false;\n\tprivate static boolean beforeOfRule2WasExecuted = false;\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\tprivate static boolean afterOfRule2WasExecuted = false;\n\n\t@Rule\n\tpublic MyExternalResource1 getResource1() {\n\t\treturn new MyExternalResource1();\n\t}\n\n\t@Rule\n\tpublic TestRule getResource2() {\n\t\treturn new ExternalResource() {\n\t\t\t@Override\n\t\t\tprotected void before() {\n\t\t\t\tbeforeOfRule2WasExecuted = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void after() {\n\t\t\t\tafterOfRule2WasExecuted = true;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tvoid beforeMethodsOfBothRulesWereExecuted() {\n\t\tassertTrue(beforeOfRule1WasExecuted);\n\t\tassertTrue(beforeOfRule2WasExecuted);\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tif (!afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t\tif (!afterOfRule2WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n\tprivate static class MyExternalResource1 extends ExternalResource {\n\t\t@Override\n\t\tprotected void before() {\n\t\t\tbeforeOfRule1WasExecuted = true;\n\t\t}\n\n\t\t@Override\n\t\tprotected void after() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMixedMethodAndFieldRulesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.ExternalResource;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class ExternalResourceSupportForMixedMethodAndFieldRulesTests {\n\n\tprivate static List<String> initEvents = new ArrayList<>();\n\tprivate static List<String> beforeEvents = new ArrayList<>();\n\tprivate static List<String> afterEvents = new ArrayList<>();\n\n\t@BeforeAll\n\tstatic void clear() {\n\t\tinitEvents.clear();\n\t\tbeforeEvents.clear();\n\t\tafterEvents.clear();\n\t}\n\n\t@Rule\n\tpublic ExternalResource fieldRule1 = new MyExternalResource(\"fieldRule1\");\n\n\t@Rule\n\tpublic ExternalResource fieldRule2 = new MyExternalResource(\"fieldRule2\");\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@Rule\n\tExternalResource methodRule1() {\n\t\treturn new MyExternalResource(\"methodRule1\");\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@Rule\n\tExternalResource methodRule2() {\n\t\treturn new MyExternalResource(\"methodRule2\");\n\t}\n\n\t@Test\n\tvoid constructorsAndBeforeEachMethodsOfAllRulesWereExecuted() {\n\t\tassertThat(initEvents).hasSize(4);\n\t\t// the order of fields and methods is not stable, but fields are initialized before methods are called\n\t\tassertThat(initEvents.subList(0, 2)).allMatch(item -> item.startsWith(\"fieldRule\"));\n\t\tassertThat(initEvents.subList(2, 4)).allMatch(item -> item.startsWith(\"methodRule\"));\n\t\t// beforeEach methods of rules from fields are run before those from methods but in reverse order of instantiation\n\t\tassertEquals(asList(initEvents.get(1), initEvents.get(0), initEvents.get(3), initEvents.get(2)), beforeEvents);\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfAllRulesWereExecuted() {\n\t\t// beforeEach methods of rules from methods are run before those from fields but in reverse order\n\t\tif (!asList(initEvents.get(2), initEvents.get(3), initEvents.get(0), initEvents.get(1)).equals(afterEvents)) {\n\t\t\tfail();\n\t\t}\n\t}\n\n\tstatic class MyExternalResource extends ExternalResource {\n\n\t\tprivate final String name;\n\n\t\tMyExternalResource(String name) {\n\t\t\tthis.name = name;\n\t\t\tinitEvents.add(name);\n\t\t}\n\n\t\t@Override\n\t\tprotected void before() {\n\t\t\tbeforeEvents.add(name);\n\t\t}\n\n\t\t@Override\n\t\tprotected void after() {\n\t\t\tafterEvents.add(name);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleFieldRulesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.ExternalResource;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class ExternalResourceSupportForMultipleFieldRulesTests {\n\n\tprivate static boolean beforeOfRule1WasExecuted = false;\n\tprivate static boolean beforeOfRule2WasExecuted = false;\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\tprivate static boolean afterOfRule2WasExecuted = false;\n\n\t@Rule\n\tpublic ExternalResource resource1 = new ExternalResource() {\n\t\t@Override\n\t\tprotected void before() {\n\t\t\tbeforeOfRule1WasExecuted = true;\n\t\t}\n\n\t\t@Override\n\t\tprotected void after() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t};\n\n\t@Rule\n\tpublic ExternalResource resource2 = new ExternalResource() {\n\t\t@Override\n\t\tprotected void before() {\n\t\t\tbeforeOfRule2WasExecuted = true;\n\t\t}\n\n\t\t@Override\n\t\tprotected void after() {\n\t\t\tafterOfRule2WasExecuted = true;\n\t\t}\n\t};\n\n\t@Test\n\tvoid beforeMethodsOfBothRulesWereExecuted() {\n\t\tassertTrue(beforeOfRule1WasExecuted);\n\t\tassertTrue(beforeOfRule2WasExecuted);\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tif (!afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t\tif (!afterOfRule2WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForMultipleMethodRulesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.ExternalResource;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class ExternalResourceSupportForMultipleMethodRulesTests {\n\n\tprivate static boolean beforeOfRule1WasExecuted = false;\n\tprivate static boolean beforeOfRule2WasExecuted = false;\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\tprivate static boolean afterOfRule2WasExecuted = false;\n\n\t@Rule\n\tpublic ExternalResource getResource1() {\n\t\treturn new ExternalResource() {\n\t\t\t@Override\n\t\t\tprotected void before() {\n\t\t\t\tbeforeOfRule1WasExecuted = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void after() {\n\t\t\t\tafterOfRule1WasExecuted = true;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Rule\n\tpublic ExternalResource getResource2() {\n\t\treturn new ExternalResource() {\n\t\t\t@Override\n\t\t\tprotected void before() {\n\t\t\t\tbeforeOfRule2WasExecuted = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void after() {\n\t\t\t\tafterOfRule2WasExecuted = true;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Test\n\tvoid beforeMethodsOfBothRulesWereExecuted() {\n\t\tassertTrue(beforeOfRule1WasExecuted);\n\t\tassertTrue(beforeOfRule2WasExecuted);\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tif (!afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t\tif (!afterOfRule2WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportForTemporaryFolderFieldTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.File;\nimport java.io.IOException;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.TemporaryFolder;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class ExternalResourceSupportForTemporaryFolderFieldTests {\n\n\tprivate File file;\n\n\t@Rule\n\tpublic TemporaryFolder folder = new TemporaryFolder();\n\n\t@BeforeEach\n\tvoid setup() throws IOException {\n\t\tthis.file = folder.newFile(\"temp.txt\");\n\t}\n\n\t@Test\n\tvoid checkTemporaryFolder() {\n\t\tassertTrue(file.canRead());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceSupportWithInheritanceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\npublic class ExternalResourceSupportWithInheritanceTests\n\t\textends ExternalResourceSupportForMixedMethodAndFieldRulesTests {\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/ExternalResourceWithoutAdapterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.rules.TemporaryFolder;\n\npublic class ExternalResourceWithoutAdapterTests {\n\n\t@Rule\n\tpublic TemporaryFolder folder = new TemporaryFolder();\n\n\t@BeforeEach\n\tvoid setup() {\n\t\ttry {\n\t\t\tfolder.newFile(\"temp.txt\");\n\t\t}\n\t\tcatch (Exception exception) {\n\t\t\tassertEquals(\"the temporary folder has not yet been created\", exception.getMessage());\n\t\t}\n\t}\n\n\t@Test\n\tvoid checkTemporaryFolder() {\n\t\t// only needed to invoke testing at all\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/FailAfterAllHelper.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\n/**\n * @since 5.0\n */\nclass FailAfterAllHelper {\n\n\tstatic void fail() {\n\t\t// hack: use this unrecoverable exception to fail the build, since all others would be swallowed...\n\t\tthrow new OutOfMemoryError(\"a postcondition was violated\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/LauncherBasedEnableRuleMigrationSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Events;\nimport org.junit.rules.ErrorCollector;\nimport org.junit.rules.ExternalResource;\nimport org.junit.rules.Verifier;\n\n@SuppressWarnings(\"removal\")\nclass LauncherBasedEnableRuleMigrationSupportTests {\n\n\t@Test\n\tvoid enableRuleMigrationSupportAnnotationWorksForBothRuleTypes() {\n\t\tEvents tests = executeTestsForClass(EnableRuleMigrationSupportWithBothRuleTypesTestCase.class);\n\n\t\ttests.assertStatistics(stats -> stats.started(1).succeeded(1).aborted(0).failed(0));\n\n\t\tassertTrue(EnableRuleMigrationSupportWithBothRuleTypesTestCase.afterOfRule1WasExecuted,\n\t\t\t\"after of rule 1 executed?\");\n\t\tassertTrue(EnableRuleMigrationSupportWithBothRuleTypesTestCase.beforeOfRule2WasExecuted,\n\t\t\t\"before of rule 2 executed?\");\n\t\tassertTrue(EnableRuleMigrationSupportWithBothRuleTypesTestCase.afterOfRule2WasExecuted,\n\t\t\t\"before of rule 2 executed?\");\n\t}\n\n\t@Test\n\tvoid verifierSupportForErrorCollectorFieldFailsTheTest() {\n\t\tEvents tests = executeTestsForClass(VerifierSupportForErrorCollectorTestCase.class);\n\n\t\ttests.assertStatistics(stats -> stats.started(1).succeeded(0).aborted(0).failed(1));\n\n\t\tassertTrue(VerifierSupportForErrorCollectorTestCase.survivedBothErrors, \"after of rule 1 executed?\");\n\t}\n\n\tprivate Events executeTestsForClass(Class<?> testClass) {\n\t\treturn EngineTestKit.execute(\"junit-jupiter\", request().selectors(selectClass(testClass)).build()).testEvents();\n\t}\n\n\t@EnableRuleMigrationSupport\n\tstatic class EnableRuleMigrationSupportWithBothRuleTypesTestCase {\n\n\t\tstatic boolean afterOfRule1WasExecuted = false;\n\n\t\tstatic boolean beforeOfRule2WasExecuted = false;\n\t\tstatic boolean afterOfRule2WasExecuted = false;\n\n\t\t@Rule\n\t\tpublic Verifier verifier1 = new Verifier() {\n\n\t\t\t@Override\n\t\t\tprotected void verify() {\n\t\t\t\tafterOfRule1WasExecuted = true;\n\t\t\t}\n\t\t};\n\n\t\tprivate final ExternalResource resource2 = new ExternalResource() {\n\t\t\t@Override\n\t\t\tprotected void before() {\n\t\t\t\tbeforeOfRule2WasExecuted = true;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void after() {\n\t\t\t\tafterOfRule2WasExecuted = true;\n\t\t\t}\n\t\t};\n\n\t\t@Rule\n\t\tpublic ExternalResource getResource2() {\n\t\t\treturn resource2;\n\t\t}\n\n\t\t@Test\n\t\tvoid beforeMethodOfBothRule2WasExecuted() {\n\t\t\tassertTrue(beforeOfRule2WasExecuted);\n\t\t}\n\n\t}\n\n\t@ExtendWith(VerifierSupport.class)\n\tstatic class VerifierSupportForErrorCollectorTestCase {\n\n\t\tstatic boolean survivedBothErrors = false;\n\n\t\t@Rule\n\t\tpublic ErrorCollector collector = new ErrorCollector();\n\n\t\t@Test\n\t\tvoid addingTwoThrowablesToErrorCollectorFailsLate() {\n\t\t\tcollector.addError(new Throwable(\"first thing went wrong\"));\n\t\t\tcollector.addError(new Throwable(\"second thing went wrong\"));\n\n\t\t\tsurvivedBothErrors = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/VerifierSupportForMixedMethodAndFieldRulesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.Verifier;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(VerifierSupport.class)\npublic class VerifierSupportForMixedMethodAndFieldRulesTests {\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\tprivate static boolean afterOfRule2WasExecuted = false;\n\n\t@Rule\n\tpublic Verifier verifier1 = new Verifier() {\n\n\t\t@Override\n\t\tprotected void verify() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t};\n\n\tprivate Verifier verifier2 = new Verifier() {\n\n\t\t@Override\n\t\tprotected void verify() {\n\t\t\tafterOfRule2WasExecuted = true;\n\t\t}\n\t};\n\n\t@Rule\n\tpublic Verifier getVerifier2() {\n\t\treturn verifier2;\n\t}\n\n\t@Test\n\tvoid testNothing() {\n\t\t//needed to start the test process at all\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tif (!afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t\tif (!afterOfRule2WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierFieldTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.Verifier;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class WrongExtendWithForVerifierFieldTests {\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\n\t@Rule\n\tpublic Verifier verifier1 = new Verifier() {\n\n\t\t@Override\n\t\tprotected void verify() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t};\n\n\t@Test\n\tvoid testNothing() {\n\t\t//needed to start the test process at all\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodOfRuleWasNotExecuted() {\n\t\tif (afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/migrationsupport/rules/WrongExtendWithForVerifierMethodTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.migrationsupport.rules;\n\nimport static org.junit.jupiter.migrationsupport.rules.FailAfterAllHelper.fail;\n\nimport org.junit.Rule;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.rules.Verifier;\n\n@SuppressWarnings(\"removal\")\n@ExtendWith(ExternalResourceSupport.class)\npublic class WrongExtendWithForVerifierMethodTests {\n\n\tprivate static boolean afterOfRule1WasExecuted = false;\n\n\tprivate Verifier verifier1 = new Verifier() {\n\n\t\t@Override\n\t\tprotected void verify() {\n\t\t\tafterOfRule1WasExecuted = true;\n\t\t}\n\t};\n\n\t@Rule\n\tpublic Verifier getVerifier1() {\n\t\treturn verifier1;\n\t}\n\n\t@Test\n\tvoid testNothing() {\n\t\t//needed to start the test process at all\n\t}\n\n\t@AfterAll\n\tstatic void afterMethodsOfBothRulesWereExecuted() {\n\t\tif (afterOfRule1WasExecuted) {\n\t\t\tfail();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterInfoIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * @since 1.14\n */\nclass ParameterInfoIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid storesParameterInfoInExtensionContextStoreOnDifferentLevels() {\n\t\tvar results = executeTestsForClass(TestCase.class);\n\n\t\tresults.allEvents().debug().assertStatistics(stats -> stats.started(7).succeeded(7));\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\t@ExtendWith(ParameterInfoConsumingExtension.class)\n\trecord TestCase(int i) {\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(ints = 2)\n\t\tclass Inner {\n\n\t\t\t@Parameter\n\t\t\tint j;\n\n\t\t\t@ParameterizedTest\n\t\t\t@ValueSource(ints = 3)\n\t\t\tvoid test(int k) {\n\t\t\t\tassertEquals(1, i);\n\t\t\t\tassertEquals(2, j);\n\t\t\t\tassertEquals(3, k);\n\t\t\t}\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class ParameterInfoConsumingExtension\n\t\t\timplements BeforeClassTemplateInvocationCallback, BeforeEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeClassTemplateInvocation(ExtensionContext parameterizedClassInvocationContext) {\n\t\t\tif (TestCase.Inner.class.equals(parameterizedClassInvocationContext.getRequiredTestClass())) {\n\t\t\t\tassertParameterInfo(parameterizedClassInvocationContext, \"j\", 2);\n\n\t\t\t\tvar nestedParameterizedClassContext = parameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\t\tassertParameterInfo(nestedParameterizedClassContext, \"i\", 1);\n\n\t\t\t\tparameterizedClassInvocationContext = nestedParameterizedClassContext.getParent().orElseThrow();\n\t\t\t}\n\n\t\t\tassertParameterInfo(parameterizedClassInvocationContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassContext = parameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertNull(ParameterInfo.get(outerParameterizedClassContext));\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext parameterizedTestInvocationContext) {\n\t\t\tassertParameterInfo(parameterizedTestInvocationContext, \"k\", 3);\n\n\t\t\tvar parameterizedTestContext = parameterizedTestInvocationContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(parameterizedTestContext, \"j\", 2);\n\n\t\t\tvar nestedParameterizedClassInvocationContext = parameterizedTestContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(nestedParameterizedClassInvocationContext, \"j\", 2);\n\n\t\t\tvar nestedParameterizedClassContext = nestedParameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(nestedParameterizedClassContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassInvocationContext = nestedParameterizedClassContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(outerParameterizedClassInvocationContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassContext = outerParameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertNull(ParameterInfo.get(outerParameterizedClassContext));\n\t\t}\n\n\t\tprivate static void assertParameterInfo(ExtensionContext context, String parameterName, int argumentValue) {\n\t\t\tvar parameterInfo = ParameterInfo.get(context);\n\t\t\tassertNotNull(parameterInfo);\n\t\t\tvar declaration = parameterInfo.getDeclarations().get(0).orElseThrow();\n\t\t\tassertEquals(parameterName, declaration.getParameterName().orElseThrow());\n\t\t\tassertEquals(int.class, declaration.getParameterType());\n\t\t\tassertEquals(argumentValue, parameterInfo.getArguments().getInteger(0));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedClassIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.util.Comparator.comparing;\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.tuple;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.params.ArgumentCountValidationMode.NONE;\nimport static org.junit.jupiter.params.ArgumentCountValidationMode.STRICT;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DISPLAY_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.INDEX_PLACEHOLDER;\nimport static org.junit.jupiter.params.provider.Arguments.argumentSet;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.dynamicTestRegistered;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.legacyReportingName;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.EventConditions.uniqueId;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.TemplateInvocationValidationException;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.descriptor.ClassTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.ArgumentsAggregationException;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.converter.ArgumentConversionException;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.converter.SimpleArgumentConverter;\nimport org.junit.jupiter.params.converter.TypedArgumentConverter;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.ArgumentsProvider;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\nimport org.junit.jupiter.params.provider.CsvFileSource;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.EmptySource;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.FieldSource;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.jupiter.params.support.FieldContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.util.StringUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.EventType;\nimport org.junit.platform.testkit.engine.Events;\n\npublic class ParameterizedClassIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { ConstructorInjectionTestCase.class, RecordTestCase.class,\n\t\t\tRecordWithParameterAnnotationOnComponentTestCase.class, FieldInjectionTestCase.class,\n\t\t\tRecordWithBuiltInConverterTestCase.class, RecordWithRegisteredConversionTestCase.class,\n\t\t\tFieldInjectionWithRegisteredConversionTestCase.class, RecordWithBuiltInAggregatorTestCase.class,\n\t\t\tFieldInjectionWithBuiltInAggregatorTestCase.class, RecordWithCustomAggregatorTestCase.class,\n\t\t\tFieldInjectionWithCustomAggregatorTestCase.class })\n\tvoid injectsParametersIntoClass(Class<?> classTemplateClass) {\n\n\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\tString parameterNamePrefix = classTemplateClass.getSimpleName().contains(\"Aggregator\") ? \"\" : \"value = \";\n\n\t\tresults.allEvents().assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(container(classTemplateClass), started()), //\n\n\t\t\tevent(dynamicTestRegistered(\"#1\"), displayName(\"[1] %s-1\".formatted(parameterNamePrefix))), //\n\t\t\tevent(container(\"#1\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"test1\")), //\n\t\t\tevent(dynamicTestRegistered(\"test2\")), //\n\t\t\tevent(test(\"test1\"), legacyReportingName(\"test1()[1]\"), started()), //\n\t\t\tevent(test(\"test1\"), finishedSuccessfully()), //\n\t\t\tevent(test(\"test2\"), legacyReportingName(\"test2()[1]\"), started()), //\n\t\t\tevent(test(\"test2\"), finishedSuccessfully()), //\n\t\t\tevent(container(\"#1\"), finishedSuccessfully()), //\n\n\t\t\tevent(dynamicTestRegistered(\"#2\"), displayName(\"[2] %s1\".formatted(parameterNamePrefix))), //\n\t\t\tevent(container(\"#2\"), started()), //\n\t\t\tevent(dynamicTestRegistered(\"test1\")), //\n\t\t\tevent(dynamicTestRegistered(\"test2\")), //\n\t\t\tevent(test(\"test1\"), legacyReportingName(\"test1()[2]\"), started()), //\n\t\t\tevent(test(\"test1\"), finishedWithFailure(message(it -> it.contains(\"negative\")))), //\n\t\t\tevent(test(\"test2\"), legacyReportingName(\"test2()[2]\"), started()), //\n\t\t\tevent(test(\"test2\"), finishedWithFailure(message(it -> it.contains(\"negative\")))), //\n\t\t\tevent(container(\"#2\"), finishedSuccessfully()), //\n\n\t\t\tevent(container(classTemplateClass), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { ArgumentConversionPerInvocationConstructorInjectionTestCase.class,\n\t\t\tArgumentConversionPerInvocationFieldInjectionTestCase.class })\n\tvoid argumentConverterIsOnlyCalledOncePerInvocation(Class<?> classTemplateClass) {\n\n\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(5).succeeded(5));\n\t}\n\n\t@Nested\n\tclass Sources {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { NullAndEmptySourceConstructorInjectionTestCase.class,\n\t\t\t\tNullAndEmptySourceConstructorFieldInjectionTestCase.class })\n\t\tvoid supportsNullAndEmptySource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = null\", \"[2] value = \\\"\\\"\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { CsvFileSourceConstructorInjectionTestCase.class,\n\t\t\t\tCsvFileSourceFieldInjectionTestCase.class })\n\t\tvoid supportsCsvFileSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(10).succeeded(10));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] name = \\\"foo\\\", value = \\\"1\\\"\", \"[2] name = \\\"bar\\\", value = \\\"2\\\"\",\n\t\t\t\t\t\t\"[3] name = \\\"baz\\\", value = \\\"3\\\"\", \"[4] name = \\\"qux\\\", value = \\\"4\\\"\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { SingleEnumSourceConstructorInjectionTestCase.class,\n\t\t\t\tSingleEnumSourceFieldInjectionTestCase.class })\n\t\tvoid supportsSingleEnumSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = FOO\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { RepeatedEnumSourceConstructorInjectionTestCase.class,\n\t\t\t\tRepeatedEnumSourceFieldInjectionTestCase.class })\n\t\tvoid supportsRepeatedEnumSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = FOO\", \"[2] value = BAR\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { MethodSourceConstructorInjectionTestCase.class,\n\t\t\t\tMethodSourceFieldInjectionTestCase.class })\n\t\tvoid supportsMethodSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = \\\"foo\\\"\", \"[2] value = \\\"bar\\\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotSupportDerivingMethodName() {\n\n\t\t\tvar results = executeTestsForClass(MethodSourceWithoutMethodNameTestCase.class);\n\n\t\t\tresults.allEvents().failed() //\n\t\t\t\t\t.assertEventsMatchExactly(finishedWithFailure(\n\t\t\t\t\t\tmessage(\"You must specify a method name when using @MethodSource with @ParameterizedClass\")));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { FieldSourceConstructorInjectionTestCase.class,\n\t\t\t\tFieldSourceFieldInjectionTestCase.class })\n\t\tvoid supportsFieldSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = \\\"foo\\\"\", \"[2] value = \\\"bar\\\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotSupportDerivingFieldName() {\n\n\t\t\tvar results = executeTestsForClass(FieldSourceWithoutFieldNameTestCase.class);\n\n\t\t\tresults.allEvents().failed() //\n\t\t\t\t\t.assertEventsMatchExactly(finishedWithFailure(\n\t\t\t\t\t\tmessage(\"You must specify a field name when using @FieldSource with @ParameterizedClass\")));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { ArgumentsSourceConstructorInjectionTestCase.class,\n\t\t\t\tArgumentsSourceFieldInjectionTestCase.class })\n\t\tvoid supportsArgumentsSource(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"[1] value = \\\"foo\\\"\", \"[2] value = \\\"bar\\\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWhenNoArgumentsSourceIsDeclared() {\n\t\t\tvar results = executeTestsForClass(NoArgumentSourceTestCase.class);\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: You must configure at least one arguments source for this @ParameterizedClass\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid annotationsAreInherited() {\n\t\t\tvar results = executeTestsForClass(ConcreteInheritanceTestCase.class);\n\n\t\t\tint numArgumentSets = 13;\n\t\t\tvar numContainers = numArgumentSets * 3; // once for outer invocation, once for nested class, once for inner invocation\n\t\t\tvar numTests = numArgumentSets * 2; // once for outer test, once for inner test\n\t\t\tresults.containerEvents() //\n\t\t\t\t\t.assertStatistics(stats -> stats.started(numContainers + 2).succeeded(numContainers + 2));\n\t\t\tresults.testEvents() //\n\t\t\t\t\t.assertStatistics(stats -> stats.started(numTests).succeeded(numTests));\n\t\t}\n\t}\n\n\t@Nested\n\tclass AnnotationAttributes {\n\n\t\t@Test\n\t\tvoid supportsCustomNamePatterns() {\n\n\t\t\tvar results = executeTestsForClass(CustomNamePatternTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly(\"1 | TesT | 1, \\\"foo\\\" | set\",\n\t\t\t\t\t\t\"2 | TesT | 2, \\\"bar\\\" | number = 2, name = \\\"bar\\\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid closesAutoCloseableArguments() {\n\t\t\tAutoCloseableArgument.closeCounter = 0;\n\n\t\t\tvar results = executeTestsForClass(AutoCloseableArgumentTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t\tassertThat(AutoCloseableArgument.closeCounter).isEqualTo(2);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotCloseAutoCloseableArgumentsWhenDisabled() {\n\t\t\tAutoCloseableArgument.closeCounter = 0;\n\n\t\t\tvar results = executeTestsForClass(AutoCloseableArgumentWithDisabledCleanupTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t\tassertThat(AutoCloseableArgument.closeCounter).isEqualTo(0);\n\t\t}\n\n\t\t@Test\n\t\tvoid failsOnStrictArgumentCountValidationMode() {\n\t\t\tvar results = executeTestsForClass(StrictArgumentCountValidationModeTestCase.class);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedClass consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused]\".formatted()))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { NoneArgumentCountValidationModeTestCase.class,\n\t\t\t\tDefaultArgumentCountValidationModeTestCase.class })\n\t\tvoid doesNotFailOnNoneOrDefaultArgumentCountValidationMode(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsOnStrictArgumentCountValidationModeSetViaConfigurationParameter() {\n\t\t\tvar results = executeTests(request -> request //\n\t\t\t\t\t.selectors(selectClass(DefaultArgumentCountValidationModeTestCase.class)).configurationParameter(\n\t\t\t\t\t\tArgumentCountValidator.ARGUMENT_COUNT_VALIDATION_KEY, STRICT.name()));\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedClass consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused]\".formatted()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsForSkippedParameters() {\n\t\t\tvar results = executeTestsForClass(InvalidUnusedParameterIndexesTestCase.class);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"2 configuration errors:%n- no field annotated with @Parameter(0) declared%n- no field annotated with @Parameter(2) declared\".formatted()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWhenInvocationIsRequiredButNoArgumentSetsAreProvided() {\n\t\t\tvar results = executeTestsForClass(ForbiddenZeroInvocationsTestCase.class);\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(finishedWithFailure(instanceOf(TemplateInvocationValidationException.class), message(\n\t\t\t\t\t\t\t\"Configuration error: You must configure at least one set of arguments for this @ParameterizedClass\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotFailWhenInvocationIsNotRequiredAndNoArgumentSetsAreProvided() {\n\t\t\tvar results = executeTestsForClass(AllowedZeroInvocationsTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\t}\n\t}\n\n\t@Nested\n\tclass Nesting {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { NestedFieldInjectionTestCase.class, NestedConstructorInjectionTestCase.class })\n\t\tvoid supportsNestedParameterizedClass(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.containerEvents().assertStatistics(stats -> stats.started(14).succeeded(14));\n\t\t\tresults.testEvents().assertStatistics(stats -> stats.started(8).succeeded(8));\n\t\t\tassertThat(invocationDisplayNames(results)) //\n\t\t\t\t\t.containsExactly( //\n\t\t\t\t\t\t\"[1] number = 1\", \"[1] text = \\\"foo\\\"\", \"[2] text = \\\"bar\\\"\", //\n\t\t\t\t\t\t\"[2] number = 2\", \"[1] text = \\\"foo\\\"\", \"[2] text = \\\"bar\\\"\" //\n\t\t\t\t\t);\n\t\t\tassertThat(allReportEntries(results)).map(it -> it.get(\"value\")).containsExactly(\n\t\t\t// @formatter:off\n\t\t\t\t\t\"beforeAll: %s\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeParameterizedClassInvocation: %s\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeAll: InnerTestCase\",\n\t\t\t\t\t\"beforeParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"test(1, foo, true)\",\n\t\t\t\t\t\"afterEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"test(1, foo, false)\",\n\t\t\t\t\t\"afterEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"afterParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"test(1, bar, true)\",\n\t\t\t\t\t\"afterEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"test(1, bar, false)\",\n\t\t\t\t\t\"afterEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"afterParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"afterAll: InnerTestCase\",\n\t\t\t\t\t\"afterParameterizedClassInvocation: %s\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeParameterizedClassInvocation: %s\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeAll: InnerTestCase\",\n\t\t\t\t\t\"beforeParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"test(2, foo, true)\",\n\t\t\t\t\t\"afterEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"test(2, foo, false)\",\n\t\t\t\t\t\"afterEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"afterParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"beforeEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"test(2, bar, true)\",\n\t\t\t\t\t\"afterEach: [1] flag = true [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [1] flag = true [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"beforeEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"test(2, bar, false)\",\n\t\t\t\t\t\"afterEach: [2] flag = false [InnerTestCase]\",\n\t\t\t\t\t\"afterEach: [2] flag = false [%s]\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"afterParameterizedClassInvocation: InnerTestCase\",\n\t\t\t\t\t\"afterAll: InnerTestCase\",\n\t\t\t\t\t\"afterParameterizedClassInvocation: %s\".formatted(classTemplateClass.getSimpleName()),\n\t\t\t\t\t\"afterAll: %s\".formatted(classTemplateClass.getSimpleName())\n\t\t\t\t\t// @formatter:on\n\t\t\t);\n\t\t\tvar legacyReportingNames = results.testEvents() //\n\t\t\t\t\t.filter(it -> it.getType() == EventType.STARTED) //\n\t\t\t\t\t.map(e -> e.getTestDescriptor().getLegacyReportingName());\n\t\t\tassertThat(legacyReportingNames).containsExactly( //\n\t\t\t\t\"test(boolean, TestReporter)[1][1][1]\", //\n\t\t\t\t\"test(boolean, TestReporter)[1][1][2]\", //\n\t\t\t\t\"test(boolean, TestReporter)[1][2][1]\", //\n\t\t\t\t\"test(boolean, TestReporter)[1][2][2]\", //\n\t\t\t\t\"test(boolean, TestReporter)[2][1][1]\", //\n\t\t\t\t\"test(boolean, TestReporter)[2][1][2]\", //\n\t\t\t\t\"test(boolean, TestReporter)[2][2][1]\", //\n\t\t\t\t\"test(boolean, TestReporter)[2][2][2]\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { ConstructorInjectionWithRegularNestedTestCase.class,\n\t\t\t\tFieldInjectionWithRegularNestedTestCase.class })\n\t\tvoid supportsRegularNestedTestClassesInsideParameterizedClass(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.containerEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t\tresults.testEvents().assertStatistics(stats -> stats.started(2).succeeded(2));\n\t\t}\n\t}\n\n\t@Nested\n\tclass FieldInjection {\n\n\t\t@Test\n\t\tvoid supportsMultipleAggregatorFields() {\n\n\t\t\tvar results = executeTestsForClass(MultiAggregatorFieldInjectionTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\t\t}\n\n\t\t@Test\n\t\tvoid supportsInjectionOfInheritedFields() {\n\n\t\t\tvar results = executeTestsForClass(InheritedHiddenParameterFieldTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(6).succeeded(6));\n\n\t\t\tassertThat(allReportEntries(results)) //\n\t\t\t\t\t.extracting(it -> tuple(it.get(\"super.value\"), it.get(\"this.value\"))) //\n\t\t\t\t\t.containsExactly(tuple(\"foo\", \"1\"), tuple(\"bar\", \"2\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotSupportInjectionForFinalFields() {\n\n\t\t\tvar classTemplateClass = InvalidFinalFieldTestCase.class;\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @Parameter field [final int %s.i] must not be declared as final.\".formatted(\n\t\t\t\t\t\t\tclassTemplateClass.getName()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid aggregatorFieldsMustNotDeclareIndex() {\n\n\t\t\tvar classTemplateClass = InvalidAggregatorFieldWithIndexTestCase.class;\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: no index may be declared in @Parameter(0) annotation on aggregator field [%s %s.accessor].\".formatted(\n\t\t\t\t\t\t\tArgumentsAccessor.class.getName(), classTemplateClass.getName()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid declaredIndexMustNotBeNegative() {\n\n\t\t\tvar classTemplateClass = InvalidParameterIndexTestCase.class;\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: index must be greater than or equal to zero in @Parameter(-42) annotation on field [int %s.i].\".formatted(\n\t\t\t\t\t\t\tclassTemplateClass.getName()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid declaredIndexMustBeUnique() {\n\n\t\t\tvar classTemplateClass = InvalidDuplicateParameterDeclarationTestCase.class;\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: duplicate index declared in @Parameter(0) annotation on fields [int %s.i, long %s.l].\".formatted(\n\t\t\t\t\t\t\tclassTemplateClass.getName(), classTemplateClass.getName()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithMeaningfulErrorWhenTooFewArgumentsProvidedForFieldInjection() {\n\t\t\tvar results = executeTestsForClass(NotEnoughArgumentsForFieldsTestCase.class);\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(withPlatformSpecificLineSeparator(\n\t\t\t\t\t\t\"\"\"\n\t\t\t\t\t\t\t\tConfiguration error: @ParameterizedClass has 2 required parameters (due to field injection) but there was 1 argument provided.\n\t\t\t\t\t\t\t\tNote: the provided arguments were [1]\"\"\"))));\n\t\t}\n\t}\n\n\t@Nested\n\tclass PerClassLifecycle {\n\n\t\t@Test\n\t\tvoid supportsFieldInjectionForTestInstanceLifecyclePerClass() {\n\n\t\t\tvar results = executeTestsForClass(FieldInjectionWithPerClassTestInstanceLifecycleTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(8).succeeded(8));\n\n\t\t\tSupplier<Stream<Map<String, String>>> valueTrackingReportEntries = () -> allReportEntries(results) //\n\t\t\t\t\t.filter(it -> it.containsKey(\"instanceHashCode\"));\n\t\t\tSupplier<Stream<Map<String, String>>> lifecycleReportEntries = () -> allReportEntries(results) //\n\t\t\t\t\t.filter(it -> !it.containsKey(\"instanceHashCode\"));\n\n\t\t\tassertThat(valueTrackingReportEntries.get().map(it -> it.get(\"value\"))) //\n\t\t\t\t\t.containsExactly(\"foo\", \"foo\", \"bar\", \"bar\");\n\t\t\tassertThat(valueTrackingReportEntries.get().map(it -> it.get(\"instanceHashCode\")).distinct()) //\n\t\t\t\t\t.hasSize(1);\n\t\t\tassertThat(lifecycleReportEntries.get().map(it -> it.get(\"value\"))) //\n\t\t\t\t\t.containsExactly(\n\t\t\t\t\t//@formatter:off\n\t\t\t\t\t\t\t\"beforeParameterizedClassInvocation1\",\n\t\t\t\t\t\t\t\"beforeParameterizedClassInvocation2\",\n\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\"afterParameterizedClassInvocation1\",\n\t\t\t\t\t\t\t\"afterParameterizedClassInvocation2\",\n\t\t\t\t\t\t\t\"beforeParameterizedClassInvocation1\",\n\t\t\t\t\t\t\t\"beforeParameterizedClassInvocation2\",\n\t\t\t\t\t\t\t\"test1\",\n\t\t\t\t\t\t\t\"test2\",\n\t\t\t\t\t\t\t\"afterParameterizedClassInvocation1\",\n\t\t\t\t\t\t\t\"afterParameterizedClassInvocation2\"\n\t\t\t\t\t\t\t//@formatter:on\n\t\t\t\t\t);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotSupportConstructorInjectionForTestInstanceLifecyclePerClass() {\n\n\t\t\tvar results = executeTests(request -> request //\n\t\t\t\t\t.selectors(selectClass(ConstructorInjectionTestCase.class)) //\n\t\t\t\t\t.configurationParameter(Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, PER_CLASS.name()));\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(it -> it.contains(\n\t\t\t\t\t\t\"Constructor injection is not supported for @ParameterizedClass classes with @TestInstance(Lifecycle.PER_CLASS)\"))));\n\t\t}\n\t}\n\n\t@Nested\n\tclass LifecycleMethods {\n\n\t\t@ParameterizedTest\n\t\t@CsvSource(textBlock = \"\"\"\n\t\t\t\tNonStaticBeforeLifecycleMethodTestCase, @BeforeParameterizedClassInvocation, beforeParameterizedClassInvocation\n\t\t\t\tNonStaticAfterLifecycleMethodTestCase,  @AfterParameterizedClassInvocation,  afterParameterizedClassInvocation\n\t\t\t\t\"\"\")\n\t\tvoid lifecycleMethodsNeedToBeStaticByDefault(String simpleClassName, String annotationName,\n\t\t\t\tString lifecycleMethodName) throws Exception {\n\n\t\t\tvar className = ParameterizedClassIntegrationTests.class.getName() + \"$\" + simpleClassName;\n\n\t\t\tvar results = discoverTestsForClass(Class.forName(className));\n\n\t\t\tvar issue = getOnlyElement(results.getDiscoveryIssues());\n\t\t\tassertThat(issue.severity()) //\n\t\t\t\t\t.isEqualTo(Severity.ERROR);\n\t\t\tassertThat(issue.message()) //\n\t\t\t\t\t.isEqualTo(\n\t\t\t\t\t\t\"%s method 'void %s.%s()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).\",\n\t\t\t\t\t\tannotationName, className, lifecycleMethodName);\n\t\t\tassertThat(issue.source()) //\n\t\t\t\t\t.containsInstanceOf(org.junit.platform.engine.support.descriptor.MethodSource.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid lifecycleMethodsMustNotBePrivate() {\n\n\t\t\tvar results = discoverTestsForClass(PrivateLifecycleMethodTestCase.class);\n\n\t\t\tvar issue = getOnlyElement(results.getDiscoveryIssues());\n\t\t\tassertThat(issue.severity()) //\n\t\t\t\t\t.isEqualTo(Severity.ERROR);\n\t\t\tassertThat(issue.message()) //\n\t\t\t\t\t.isEqualTo(\n\t\t\t\t\t\t\"@BeforeParameterizedClassInvocation method 'private static void %s.beforeParameterizedClassInvocation()' must not be private.\",\n\t\t\t\t\t\tPrivateLifecycleMethodTestCase.class.getName());\n\t\t\tassertThat(issue.source()) //\n\t\t\t\t\t.containsInstanceOf(org.junit.platform.engine.support.descriptor.MethodSource.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid lifecycleMethodsMustNotDeclareReturnType() {\n\n\t\t\tvar results = discoverTestsForClass(NonVoidLifecycleMethodTestCase.class);\n\n\t\t\tvar issue = getOnlyElement(results.getDiscoveryIssues());\n\t\t\tassertThat(issue.severity()) //\n\t\t\t\t\t.isEqualTo(Severity.ERROR);\n\t\t\tassertThat(issue.message()) //\n\t\t\t\t\t.isEqualTo(\n\t\t\t\t\t\t\"@BeforeParameterizedClassInvocation method 'static int %s.beforeParameterizedClassInvocation()' must not return a value.\",\n\t\t\t\t\t\tNonVoidLifecycleMethodTestCase.class.getName());\n\t\t\tassertThat(issue.source()) //\n\t\t\t\t\t.containsInstanceOf(org.junit.platform.engine.support.descriptor.MethodSource.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid lifecycleMethodsFromSuperclassAreWrappedAroundLifecycleMethodsFromTestClass() {\n\n\t\t\tvar results = executeTestsForClass(LifecycleMethodsFromSuperclassTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\n\t\t\tassertThat(allReportEntries(results).map(it -> it.get(\"value\"))) //\n\t\t\t\t\t.containsExactly(\"zzz_before\", \"aaa_before\", \"test\", \"aaa_after\", \"zzz_after\");\n\t\t}\n\n\t\t@Test\n\t\tvoid exceptionsInLifecycleMethodsArePropagated() {\n\n\t\t\tvar results = executeTestsForClass(LifecycleMethodsErrorHandlingTestCase.class);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(3).failed(1).succeeded(2));\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure( //\n\t\t\t\t\t\tmessage(\"zzz_before\"), //\n\t\t\t\t\t\tsuppressed(0, message(\"aaa_after\")), //\n\t\t\t\t\t\tsuppressed(1, message(\"zzz_after\"))));\n\n\t\t\tassertThat(allReportEntries(results).map(it -> it.get(\"value\"))) //\n\t\t\t\t\t.containsExactly(\"zzz_before\", \"aaa_after\", \"zzz_after\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { LifecycleMethodArgumentInjectionWithConstructorInjectionTestCase.class,\n\t\t\t\tLifecycleMethodArgumentInjectionWithFieldInjectionTestCase.class })\n\t\tvoid supportsInjectingArgumentsIntoLifecycleMethods(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(5).succeeded(5));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { CustomConverterAnnotationsWithLifecycleMethodsAndConstructorInjectionTestCase.class,\n\t\t\t\tCustomConverterAnnotationsWithLifecycleMethodsAndFieldInjectionTestCase.class })\n\t\tvoid convertersHaveAccessToTheirAnnotations(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { ValidLifecycleMethodInjectionWithConstructorInjectionTestCase.class,\n\t\t\t\tValidLifecycleMethodInjectionWithFieldInjectionTestCase.class })\n\t\tvoid supportsMixedInjectionsForLifecycleMethods(Class<?> classTemplateClass) {\n\n\t\t\tvar results = executeTestsForClass(classTemplateClass);\n\n\t\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsForLifecycleMethodWithInvalidParameters() {\n\n\t\t\tvar results = executeTestsForClass(LifecycleMethodWithInvalidParametersTestCase.class);\n\n\t\t\tvar expectedMessage = withPlatformSpecificLineSeparator(\n\t\t\t\t\"\"\"\n\t\t\t\t\t\t2 configuration errors:\n\t\t\t\t\t\t- parameter 'value' with index 0 is incompatible with the parameter declared on the parameterized class: \\\n\t\t\t\t\t\texpected type 'int' but found 'long'\n\t\t\t\t\t\t- parameter 'anotherValue' with index 1 must not be annotated with @ConvertWith\"\"\");\n\n\t\t\tvar failedResult = getFirstTestExecutionResult(results.containerEvents().failed());\n\t\t\tassertThat(failedResult.getThrowable().orElseThrow()) //\n\t\t\t\t\t.hasMessage(\n\t\t\t\t\t\t\"Invalid @BeforeParameterizedClassInvocation lifecycle method declaration: static void %s.before(long,int)\".formatted(\n\t\t\t\t\t\t\tLifecycleMethodWithInvalidParametersTestCase.class.getName())) //\n\t\t\t\t\t.cause().hasMessage(expectedMessage);\n\t\t}\n\n\t\t@Test\n\t\tvoid failsForLifecycleMethodWithInvalidParameterOrder() {\n\n\t\t\tvar results = executeTestsForClass(LifecycleMethodWithInvalidParameterOrderTestCase.class);\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(message(\n\t\t\t\t\t\t(\"@BeforeParameterizedClassInvocation method [static void %s.before(%s,int,%s)] declares formal parameters in an invalid order: \"\n\t\t\t\t\t\t\t\t+ \"argument aggregators must be declared after any indexed arguments and before any arguments resolved by another ParameterResolver.\").formatted(\n\t\t\t\t\t\t\t\t\tLifecycleMethodWithInvalidParameterOrderTestCase.class.getName(),\n\t\t\t\t\t\t\t\t\tArgumentsAccessor.class.getName(), ArgumentsAccessor.class.getName()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsForLifecycleMethodWithParameterAfterAggregator() {\n\n\t\t\tvar results = executeTestsForClass(LifecycleMethodWithParameterAfterAggregatorTestCase.class);\n\n\t\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, finishedWithFailure(\n\t\t\t\t\t\tmessage(it -> it.contains(\"No ParameterResolver registered for parameter [int value]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid lifecycleMethodsMustNotBeDeclaredInRegularTestClasses() {\n\t\t\tvar testClassName = RegularClassWithLifecycleMethodsTestCase.class.getName();\n\n\t\t\tvar results = discoverTestsForClass(RegularClassWithLifecycleMethodsTestCase.class);\n\n\t\t\tassertThat(results.getDiscoveryIssues()).hasSize(2);\n\n\t\t\tvar issues = results.getDiscoveryIssues().stream() //\n\t\t\t\t\t.sorted(comparing(DiscoveryIssue::message)) //\n\t\t\t\t\t.toList();\n\n\t\t\tassertThat(issues) //\n\t\t\t\t\t.extracting(DiscoveryIssue::severity) //\n\t\t\t\t\t.containsOnly(Severity.ERROR);\n\t\t\tassertThat(issues) //\n\t\t\t\t\t.extracting(DiscoveryIssue::source) //\n\t\t\t\t\t.extracting(Optional::orElseThrow) //\n\t\t\t\t\t.allMatch(org.junit.platform.engine.support.descriptor.MethodSource.class::isInstance);\n\t\t\tassertThat(issues.getFirst().message()) //\n\t\t\t\t\t.isEqualTo(\n\t\t\t\t\t\t\"@AfterParameterizedClassInvocation method 'static void %s.after()' must not be declared in test class '%s' because it is not annotated with @ParameterizedClass.\",\n\t\t\t\t\t\ttestClassName, testClassName);\n\t\t\tassertThat(issues.getLast().message()) //\n\t\t\t\t\t.isEqualTo(\n\t\t\t\t\t\t\"@BeforeParameterizedClassInvocation method 'static void %s.before()' must not be declared in test class '%s' because it is not annotated with @ParameterizedClass.\",\n\t\t\t\t\t\ttestClassName, testClassName);\n\t\t}\n\t}\n\n\tprivate static String withPlatformSpecificLineSeparator(String textBlock) {\n\t\treturn textBlock.replace(\"\\n\", System.lineSeparator());\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static Stream<String> invocationDisplayNames(EngineExecutionResults results) {\n\t\treturn results.containerEvents() //\n\t\t\t\t.started() //\n\t\t\t\t.filter(uniqueId(lastSegmentType(ClassTemplateInvocationTestDescriptor.SEGMENT_TYPE))::matches) //\n\t\t\t\t.map(Event::getTestDescriptor) //\n\t\t\t\t.map(TestDescriptor::getDisplayName);\n\t}\n\n\tprivate static Stream<Map<String, String>> allReportEntries(EngineExecutionResults results) {\n\t\treturn results.allEvents().reportingEntryPublished() //\n\t\t\t\t.map(e -> e.getRequiredPayload(ReportEntry.class)) //\n\t\t\t\t.map(ReportEntry::getKeyValuePairs);\n\t}\n\n\tprivate static Condition<UniqueId> lastSegmentType(@SuppressWarnings(\"SameParameterValue\") String segmentType) {\n\t\treturn new Condition<>(it -> segmentType.equals(it.getLastSegment().getType()), \"last segment type is '%s'\",\n\t\t\tsegmentType);\n\t}\n\n\tprivate static TestExecutionResult getFirstTestExecutionResult(Events events) {\n\t\treturn events.stream() //\n\t\t\t\t.findFirst() //\n\t\t\t\t.flatMap(Event::getPayload) //\n\t\t\t\t.map(TestExecutionResult.class::cast) //\n\t\t\t\t.orElseThrow();\n\t}\n\n\t@ParameterizedClassWithNegativeAndPositiveValue\n\tstatic class ConstructorInjectionTestCase {\n\n\t\tprivate int value;\n\t\tprivate final TestInfo testInfo;\n\n\t\tConstructorInjectionTestCase(int value, TestInfo testInfo) {\n\t\t\tthis.value = value;\n\t\t\tthis.testInfo = testInfo;\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertEquals(\"test1()\", testInfo.getDisplayName());\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t\tvalue *= -1;\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertEquals(\"test2()\", testInfo.getDisplayName());\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t\tvalue *= -1;\n\t\t}\n\t}\n\n\t@ParameterizedClassWithNegativeAndPositiveValue\n\trecord RecordTestCase(int value, TestInfo testInfo) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertEquals(\"test1()\", testInfo.getDisplayName());\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertEquals(\"test2()\", testInfo.getDisplayName());\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\trecord RecordWithParameterAnnotationOnComponentTestCase(@Parameter int value) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\tstatic class FieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tprivate int value;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t\tvalue *= -1;\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t\tvalue *= -1;\n\t\t}\n\t}\n\n\t@ParameterizedClass(quoteTextArguments = false)\n\t@CsvSource({ \"-1\", \"1\" })\n\trecord RecordWithBuiltInConverterTestCase(int value) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value < 0, \"negative\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\trecord RecordWithRegisteredConversionTestCase(@ConvertWith(CustomIntegerToStringConverter.class) String value) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value.startsWith(\"minus\"), \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value.startsWith(\"minus\"), \"negative\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\tstatic class FieldInjectionWithRegisteredConversionTestCase {\n\n\t\t@Parameter\n\t\t@ConvertWith(CustomIntegerToStringConverter.class)\n\t\tprivate String value;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertNotNull(value);\n\t\t\tassertTrue(value.startsWith(\"minus\"), \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertNotNull(value);\n\t\t\tassertTrue(value.startsWith(\"minus\"), \"negative\");\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class CustomIntegerToStringConverter extends TypedArgumentConverter<Integer, @Nullable String> {\n\n\t\tCustomIntegerToStringConverter() {\n\t\t\tsuper(Integer.class, String.class);\n\t\t}\n\n\t\t@Override\n\t\tprotected String convert(@Nullable Integer source) throws ArgumentConversionException {\n\t\t\treturn switch (requireNonNull(source)) {\n\t\t\t\tcase -1 -> \"minus one\";\n\t\t\t\tcase +1 -> \"plus one\";\n\t\t\t\tdefault -> throw new IllegalArgumentException(\"Unsupported value: \" + source);\n\t\t\t};\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\trecord RecordWithBuiltInAggregatorTestCase(ArgumentsAccessor accessor) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(requireNonNull(accessor.getInteger(0)) < 0, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(requireNonNull(accessor.getInteger(0)) < 0, \"negative\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\tstatic class FieldInjectionWithBuiltInAggregatorTestCase {\n\n\t\t@Parameter\n\t\tprivate ArgumentsAccessor accessor;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(requireNonNull(accessor.getInteger(0)) < 0, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(requireNonNull(accessor.getInteger(0)) < 0, \"negative\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\trecord RecordWithCustomAggregatorTestCase(@AggregateWith(TimesTwoAggregator.class) int value) {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value <= -2, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value <= -2, \"negative\");\n\t\t}\n\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { -1, 1 })\n\tstatic class FieldInjectionWithCustomAggregatorTestCase {\n\n\t\t@TimesTwo\n\t\tprivate int value;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tassertTrue(value <= -2, \"negative\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tassertTrue(value <= -2, \"negative\");\n\t\t}\n\n\t}\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ParameterizedClass(quoteTextArguments = false)\n\t@ValueSource(ints = { -1, 1 })\n\t@interface ParameterizedClassWithNegativeAndPositiveValue {\n\t}\n\n\t@NullMarked\n\tprivate static class TimesTwoAggregator extends SimpleArgumentsAggregator {\n\n\t\t@Override\n\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\n\t\t\tassertThat(targetType).isEqualTo(int.class);\n\t\t\treturn requireNonNull(accessor.getInteger(0)) * 2;\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@NullAndEmptySource\n\trecord NullAndEmptySourceConstructorInjectionTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(StringUtils.isBlank(value));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@NullAndEmptySource\n\tstatic class NullAndEmptySourceConstructorFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(StringUtils.isBlank(value));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvFileSource(resources = \"two-column.csv\")\n\trecord CsvFileSourceConstructorInjectionTestCase(String name, int value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(name);\n\t\t\tassertTrue(value > 0 && value < 5);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvFileSource(resources = \"two-column.csv\")\n\tstatic class CsvFileSourceFieldInjectionTestCase {\n\n\t\t@Parameter(0)\n\t\tString name;\n\n\t\t@Parameter(1)\n\t\tint value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(name);\n\t\t\tassertTrue(value > 0 && value < 5);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@EnumSource\n\trecord SingleEnumSourceConstructorInjectionTestCase(EnumOne value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(EnumOne.FOO, value);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@EnumSource\n\tstatic class SingleEnumSourceFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tEnumOne value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(EnumOne.FOO, value);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@EnumSource(EnumOne.class)\n\t@EnumSource(EnumTwo.class)\n\trecord RepeatedEnumSourceConstructorInjectionTestCase(Object value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value == EnumOne.FOO || value == EnumTwo.BAR);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@EnumSource(EnumOne.class)\n\t@EnumSource(EnumTwo.class)\n\tstatic class RepeatedEnumSourceFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tObject value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value == EnumOne.FOO || value == EnumTwo.BAR);\n\t\t}\n\t}\n\n\tprivate enum EnumOne {\n\t\tFOO\n\t}\n\n\tprivate enum EnumTwo {\n\t\tBAR\n\t}\n\n\t@ParameterizedClass\n\t@MethodSource(\"parameters\")\n\trecord MethodSourceConstructorInjectionTestCase(String value) {\n\n\t\tstatic Stream<String> parameters() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@MethodSource(\"parameters\")\n\tstatic class MethodSourceFieldInjectionTestCase {\n\n\t\tstatic Stream<String> parameters() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Parameter\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ParameterizedClass\n\t@MethodSource\n\trecord MethodSourceWithoutMethodNameTestCase(String value) {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be executed\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@FieldSource(\"parameters\")\n\trecord FieldSourceConstructorInjectionTestCase(String value) {\n\n\t\tstatic final List<String> parameters = List.of(\"foo\", \"bar\");\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@FieldSource(\"parameters\")\n\tstatic class FieldSourceFieldInjectionTestCase {\n\n\t\tstatic final List<String> parameters = List.of(\"foo\", \"bar\");\n\n\t\t@Parameter\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@FieldSource\n\trecord FieldSourceWithoutFieldNameTestCase(String value) {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be executed\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ArgumentsSource(CustomArgumentsProvider.class)\n\trecord ArgumentsSourceConstructorInjectionTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ArgumentsSource(CustomArgumentsProvider.class)\n\tstatic class ArgumentsSourceFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(value.equals(\"foo\") || value.equals(\"bar\"));\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class CustomArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters, ExtensionContext context)\n\t\t\t\tthrows Exception {\n\t\t\treturn Stream.of(\"foo\", \"bar\").map(Arguments::of);\n\t\t}\n\t}\n\n\t@ParameterizedClass(name = INDEX_PLACEHOLDER + \" | \" //\n\t\t\t+ DISPLAY_NAME_PLACEHOLDER + \" | \" //\n\t\t\t+ ARGUMENTS_PLACEHOLDER + \" | \" //\n\t\t\t+ ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER)\n\t@MethodSource(\"arguments\")\n\t@DisplayName(\"TesT\")\n\trecord CustomNamePatternTestCase(int number, String name) {\n\n\t\tstatic Stream<Arguments> arguments() {\n\t\t\treturn Stream.of(argumentSet(\"set\", 1, \"foo\"), Arguments.of(2, \"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertTrue(number > 0);\n\t\t\tassertFalse(name.isBlank());\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ArgumentsSource(AutoCloseableArgumentProvider.class)\n\trecord AutoCloseableArgumentTestCase(AutoCloseableArgument argument) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(argument);\n\t\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t\t}\n\t}\n\n\t@ParameterizedClass(autoCloseArguments = false)\n\t@ArgumentsSource(AutoCloseableArgumentProvider.class)\n\trecord AutoCloseableArgumentWithDisabledCleanupTestCase(AutoCloseableArgument argument) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertNotNull(argument);\n\t\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class AutoCloseableArgumentProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(arguments(new AutoCloseableArgument(), Named.of(\"unused\", new AutoCloseableArgument())));\n\t\t}\n\t}\n\n\tstatic class AutoCloseableArgument implements AutoCloseable {\n\n\t\tstatic int closeCounter = 0;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tcloseCounter++;\n\t\t}\n\t}\n\n\t@ParameterizedClass(argumentCountValidation = STRICT)\n\t@CsvSource(\"foo, unused\")\n\trecord StrictArgumentCountValidationModeTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass(argumentCountValidation = NONE)\n\t@CsvSource(\"foo, unused\")\n\trecord NoneArgumentCountValidationModeTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(\"foo\", value);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvSource(\"foo, unused\")\n\trecord DefaultArgumentCountValidationModeTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(\"foo\", value);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@MethodSource(\"org.junit.jupiter.params.ParameterizedClassIntegrationTests#zeroArguments\")\n\trecord ForbiddenZeroInvocationsTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass(allowZeroInvocations = true)\n\t@MethodSource(\"org.junit.jupiter.params.ParameterizedClassIntegrationTests#zeroArguments\")\n\trecord AllowedZeroInvocationsTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\tstatic Stream<Arguments> zeroArguments() {\n\t\treturn Stream.empty();\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ParameterizedClass\n\trecord NoArgumentSourceTestCase(String value) {\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\tstatic class NestedFieldInjectionTestCase extends LifecycleCallbacks {\n\n\t\t@Parameter\n\t\tint number;\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(strings = { \"foo\", \"bar\" })\n\t\tclass InnerTestCase extends LifecycleCallbacks {\n\n\t\t\t@Parameter\n\t\t\tString text;\n\n\t\t\t@ParameterizedTest\n\t\t\t@ValueSource(booleans = { true, false })\n\t\t\tvoid test(boolean flag, TestReporter reporter) {\n\t\t\t\treporter.publishEntry(\"test(\" + number + \", \" + text + \", \" + flag + \")\");\n\t\t\t\tassertTrue(number > 0);\n\t\t\t\tassertTrue(List.of(\"foo\", \"bar\").contains(text));\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\tstatic class NestedConstructorInjectionTestCase extends LifecycleCallbacks {\n\n\t\tfinal int number;\n\n\t\tNestedConstructorInjectionTestCase(int number) {\n\t\t\tthis.number = number;\n\t\t}\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(strings = { \"foo\", \"bar\" })\n\t\tclass InnerTestCase extends LifecycleCallbacks {\n\n\t\t\tfinal String text;\n\n\t\t\tInnerTestCase(String text) {\n\t\t\t\tthis.text = text;\n\t\t\t}\n\n\t\t\t@ParameterizedTest\n\t\t\t@ValueSource(booleans = { true, false })\n\t\t\tvoid test(boolean flag, TestReporter reporter) {\n\t\t\t\treporter.publishEntry(\"test(\" + number + \", \" + text + \", \" + flag + \")\");\n\t\t\t\tassertTrue(number > 0);\n\t\t\t\tassertTrue(List.of(\"foo\", \"bar\").contains(text));\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class LifecycleCallbacks {\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\"beforeAll: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation(injectArguments = false)\n\t\tstatic void beforeParameterizedClassInvocation(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\n\t\t\t\t\"beforeParameterizedClassInvocation: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\n\t\t\t\t\"beforeEach: \" + testInfo.getDisplayName() + \" [\" + this.getClass().getSimpleName() + \"]\");\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\n\t\t\t\t\"afterEach: \" + testInfo.getDisplayName() + \" [\" + this.getClass().getSimpleName() + \"]\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation(injectArguments = false)\n\t\tstatic void afterParameterizedClassInvocation(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\n\t\t\t\t\"afterParameterizedClassInvocation: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestReporter reporter, TestInfo testInfo) {\n\t\t\treporter.publishEntry(\"afterAll: \" + testInfo.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\trecord ConstructorInjectionWithRegularNestedTestCase(int number) {\n\n\t\t@Nested\n\t\t@TestInstance(PER_CLASS)\n\t\tclass InnerTestCase {\n\n\t\t\tInnerTestCase(TestInfo testInfo) {\n\t\t\t\tassertThat(testInfo.getTestClass()).contains(InnerTestCase.class);\n\t\t\t\tassertThat(testInfo.getTestMethod()).isEmpty();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertTrue(number >= 0);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\tstatic class FieldInjectionWithRegularNestedTestCase {\n\n\t\t@Parameter\n\t\tint number;\n\n\t\t@Nested\n\t\t@TestInstance(PER_CLASS)\n\t\tclass InnerTestCase {\n\n\t\t\tInnerTestCase(TestInfo testInfo) {\n\t\t\t\tassertThat(testInfo.getTestClass()).contains(InnerTestCase.class);\n\t\t\t\tassertThat(testInfo.getTestMethod()).isEmpty();\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t\tassertTrue(number >= 0);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvSource({ \"1, foo\", \"2, bar\" })\n\tstatic class MultiAggregatorFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\tArgumentsAccessor accessor;\n\n\t\t@TimesTwo\n\t\tint numberTimesTwo;\n\n\t\t@Parameter(0)\n\t\tint number;\n\n\t\t@Parameter(1)\n\t\tString text;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(2, accessor.size());\n\t\t\tassertEquals(number, accessor.getInteger(0));\n\t\t\tassertEquals(number * 2, numberTimesTwo);\n\t\t\tassertEquals(text, accessor.getString(1));\n\t\t}\n\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.FIELD, ElementType.PARAMETER })\n\t@Parameter\n\t@AggregateWith(TimesTwoAggregator.class)\n\t@interface TimesTwo {\n\t}\n\n\t@ParameterizedClass\n\t@MethodSource(\"methodSource\")\n\t@FieldSource(\"fieldSource\")\n\t@TestInstance(PER_CLASS)\n\tstatic class FieldInjectionWithPerClassTestInstanceLifecycleTestCase {\n\n\t\tList<String> methodSource() {\n\t\t\treturn List.of(\"foo\");\n\t\t}\n\n\t\tfinal List<String> fieldSource = List.of(\"bar\");\n\n\t\t@BeforeParameterizedClassInvocation(injectArguments = false)\n\t\tvoid beforeParameterizedClassInvocation1(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"beforeParameterizedClassInvocation1\");\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation(injectArguments = false)\n\t\tvoid beforeParameterizedClassInvocation2(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"beforeParameterizedClassInvocation2\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation(injectArguments = false)\n\t\tvoid afterParameterizedClassInvocation1(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"afterParameterizedClassInvocation1\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation(injectArguments = false)\n\t\tvoid afterParameterizedClassInvocation2(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"afterParameterizedClassInvocation2\");\n\t\t}\n\n\t\t@Parameter\n\t\tprivate String value;\n\n\t\t@Test\n\t\tvoid test1(TestReporter reporter, TestInfo testInfo) {\n\t\t\tpublishReportEntry(reporter, testInfo);\n\t\t}\n\n\t\t@Test\n\t\tvoid test2(TestReporter reporter, TestInfo testInfo) {\n\t\t\tpublishReportEntry(reporter, testInfo);\n\t\t}\n\n\t\tprivate void publishReportEntry(TestReporter reporter, TestInfo testInfo) {\n\t\t\tassertNotNull(value);\n\t\t\treporter.publishEntry(testInfo.getTestMethod().orElseThrow().getName());\n\t\t\treporter.publishEntry(Map.of( //\n\t\t\t\t\"instanceHashCode\", Integer.toHexString(hashCode()), //\n\t\t\t\t\"value\", value //\n\t\t\t));\n\t\t}\n\t}\n\n\tabstract static class BaseTestCase {\n\t\t@Parameter(0)\n\t\tString value;\n\t}\n\n\t@ParameterizedClass\n\t@CsvSource({ \"foo, 1\", \"bar, 2\" })\n\tstatic class InheritedHiddenParameterFieldTestCase extends BaseTestCase {\n\t\t@Parameter(1)\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\treporter.publishEntry(Map.of( //\n\t\t\t\t\"super.value\", super.value, //\n\t\t\t\t\"this.value\", this.value //\n\t\t\t));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class InvalidFinalFieldTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter\n\t\tfinal int i = -1;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class InvalidAggregatorFieldWithIndexTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(0)\n\t\tArgumentsAccessor accessor;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class InvalidParameterIndexTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(-42)\n\t\tint i;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class InvalidDuplicateParameterDeclarationTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(0)\n\t\tint i;\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(0)\n\t\tlong l;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class NotEnoughArgumentsForFieldsTestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(0)\n\t\tint i;\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter(1)\n\t\tString s;\n\n\t\t@org.junit.jupiter.api.Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvSource({ \"unused1, foo, unused2, bar\", \"unused4, baz, unused5, qux\" })\n\tstatic class InvalidUnusedParameterIndexesTestCase {\n\n\t\t@Parameter(1)\n\t\tString second;\n\n\t\t@Parameter(3)\n\t\tString fourth;\n\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\treporter.publishEntry(Map.of( //\n\t\t\t\t\"second\", second, //\n\t\t\t\t\"fourth\", fourth //\n\t\t\t));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord ArgumentConversionPerInvocationConstructorInjectionTestCase(\n\t\t\t@ConvertWith(Wrapper.Converter.class) Wrapper wrapper) {\n\n\t\t@Nullable\n\t\tstatic Wrapper instance;\n\n\t\t@BeforeAll\n\t\t@AfterAll\n\t\tstatic void clearWrapper() {\n\t\t\tinstance = null;\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tsetOrCheckWrapper();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tsetOrCheckWrapper();\n\t\t}\n\n\t\tprivate void setOrCheckWrapper() {\n\t\t\tif (instance == null) {\n\t\t\t\tinstance = wrapper;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertSame(instance, wrapper);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class ArgumentConversionPerInvocationFieldInjectionTestCase {\n\n\t\t@Nullable\n\t\tstatic Wrapper instance;\n\n\t\t@BeforeAll\n\t\t@AfterAll\n\t\tstatic void clearWrapper() {\n\t\t\tinstance = null;\n\t\t}\n\n\t\t@Parameter\n\t\t@ConvertWith(Wrapper.Converter.class)\n\t\tWrapper wrapper;\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tsetOrCheckWrapper();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tsetOrCheckWrapper();\n\t\t}\n\n\t\tprivate void setOrCheckWrapper() {\n\t\t\tif (instance == null) {\n\t\t\t\tinstance = wrapper;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tassertSame(instance, wrapper);\n\t\t\t}\n\t\t}\n\t}\n\n\trecord Wrapper(int value) {\n\t\t@NullMarked\n\t\tstatic class Converter extends SimpleArgumentConverter {\n\t\t\t@Override\n\t\t\tprotected Object convert(@Nullable Object source, Class<?> targetType) {\n\t\t\t\treturn new Wrapper((Integer) requireNonNull(source));\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord NonStaticBeforeLifecycleMethodTestCase() {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tvoid beforeParameterizedClassInvocation() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord NonStaticAfterLifecycleMethodTestCase() {\n\n\t\t@AfterParameterizedClassInvocation\n\t\tvoid afterParameterizedClassInvocation() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord PrivateLifecycleMethodTestCase() {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tprivate static void beforeParameterizedClassInvocation() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord NonVoidLifecycleMethodTestCase() {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic int beforeParameterizedClassInvocation() {\n\t\t\treturn fail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\tstatic abstract class AbstractBaseLifecycleTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void zzz_before(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"zzz_before\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void zzz_after(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"zzz_after\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class LifecycleMethodsFromSuperclassTestCase extends AbstractBaseLifecycleTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void aaa_before(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"aaa_before\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void aaa_after(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"aaa_after\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"test\");\n\t\t}\n\t}\n\n\tstatic abstract class AbstractBaseLifecycleWithErrorsTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void zzz_before(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"zzz_before\");\n\t\t\tfail(\"zzz_before\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void zzz_after(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"zzz_after\");\n\t\t\tfail(\"zzz_after\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class LifecycleMethodsErrorHandlingTestCase extends AbstractBaseLifecycleWithErrorsTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void aaa_before(TestReporter reporter) {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void aaa_after(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"aaa_after\");\n\t\t\tfail(\"aaa_after\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"test\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord LifecycleMethodArgumentInjectionWithConstructorInjectionTestCase(\n\t\t\t@ConvertWith(AtomicIntegerConverter.class) AtomicInteger counter) {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(AtomicInteger counter) {\n\t\t\tassertEquals(2, counter.incrementAndGet());\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void after(AtomicInteger counter) {\n\t\t\tassertEquals(4, counter.get());\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tthis.counter.incrementAndGet();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tthis.counter.incrementAndGet();\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class LifecycleMethodArgumentInjectionWithFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\t@ConvertWith(AtomicIntegerConverter.class)\n\t\tAtomicInteger counter;\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(AtomicInteger counter) {\n\t\t\tassertEquals(2, counter.incrementAndGet());\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void after(AtomicInteger counter) {\n\t\t\tassertEquals(4, counter.get());\n\t\t}\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tthis.counter.incrementAndGet();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t\tthis.counter.incrementAndGet();\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class AtomicIntegerConverter extends SimpleArgumentConverter {\n\t\t@Override\n\t\tprotected Object convert(@Nullable Object source, Class<?> targetType) {\n\t\t\treturn new AtomicInteger((Integer) requireNonNull(source));\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(strings = \"foo\")\n\trecord CustomConverterAnnotationsWithLifecycleMethodsAndConstructorInjectionTestCase(\n\t\t\t@CustomConversion String value) {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(String value) {\n\t\t\tassertEquals(\"foo\", value);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(\"foo\", this.value);\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(strings = \"foo\")\n\tstatic class CustomConverterAnnotationsWithLifecycleMethodsAndFieldInjectionTestCase {\n\n\t\t@Parameter\n\t\t@CustomConversion\n\t\tString value;\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(String value) {\n\t\t\tassertEquals(\"foo\", value);\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(\"foo\", this.value);\n\t\t}\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target({ ElementType.PARAMETER, ElementType.FIELD })\n\t@ConvertWith(CustomConversion.Converter.class)\n\t@interface CustomConversion {\n\n\t\tclass Converter implements ArgumentConverter {\n\t\t\t@Override\n\t\t\tpublic @Nullable Object convert(@Nullable Object source, ParameterContext context)\n\t\t\t\t\tthrows ArgumentConversionException {\n\t\t\t\tassertNotNull(context.getParameter().getAnnotation(CustomConversion.class));\n\t\t\t\treturn source;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic @Nullable Object convert(@Nullable Object source, FieldContext context)\n\t\t\t\t\tthrows ArgumentConversionException {\n\t\t\t\tassertNotNull(context.getField().getAnnotation(CustomConversion.class));\n\t\t\t\treturn source;\n\t\t\t}\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class ValidLifecycleMethodInjectionWithConstructorInjectionTestCase\n\t\t\textends AbstractValidLifecycleMethodInjectionTestCase {\n\n\t\tprivate final AtomicInteger value;\n\n\t\tValidLifecycleMethodInjectionWithConstructorInjectionTestCase(\n\t\t\t\t@ConvertWith(AtomicIntegerConverter.class) AtomicInteger value) {\n\t\t\tthis.value = value;\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(5, this.value.getAndIncrement());\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\tstatic class ValidLifecycleMethodInjectionWithFieldInjectionTestCase\n\t\t\textends AbstractValidLifecycleMethodInjectionTestCase {\n\n\t\t@Parameter\n\t\t@ConvertWith(AtomicIntegerConverter.class)\n\t\tAtomicInteger value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tassertEquals(5, this.value.getAndIncrement());\n\t\t}\n\t}\n\n\tabstract static class AbstractValidLifecycleMethodInjectionTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before0() {\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before1(AtomicInteger value) {\n\t\t\tvalue.incrementAndGet();\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before2(ArgumentsAccessor accessor) {\n\t\t\tassertEquals(1, accessor.getInteger(0));\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before3(AtomicInteger value, TestInfo testInfo) {\n\t\t\tassertEquals(\"[1] value = 1\", testInfo.getDisplayName());\n\t\t\tvalue.incrementAndGet();\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before4(ArgumentsAccessor accessor, TestInfo testInfo) {\n\t\t\tassertEquals(1, accessor.getInteger(0));\n\t\t\tassertEquals(\"[1] value = 1\", testInfo.getDisplayName());\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before4(AtomicInteger value, ArgumentsAccessor accessor) {\n\t\t\tassertEquals(1, accessor.getInteger(0));\n\t\t\tvalue.incrementAndGet();\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before5(AtomicInteger value, ArgumentsAccessor accessor, TestInfo testInfo) {\n\t\t\tassertEquals(1, accessor.getInteger(0));\n\t\t\tassertEquals(\"[1] value = 1\", testInfo.getDisplayName());\n\t\t\tvalue.incrementAndGet();\n\t\t}\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before6(@TimesTwo int valueTimesTwo) {\n\t\t\tassertEquals(2, valueTimesTwo);\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void after(AtomicInteger value, ArgumentsAccessor accessor, TestInfo testInfo) {\n\t\t\tassertEquals(6, value.get());\n\t\t\tassertEquals(1, accessor.getInteger(0));\n\t\t\tassertEquals(\"[1] value = 1\", testInfo.getDisplayName());\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@CsvSource(\"1, 2\")\n\trecord LifecycleMethodWithInvalidParametersTestCase(int value, int anotherValue) {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(long value, @ConvertWith(CustomIntegerToStringConverter.class) int anotherValue) {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord LifecycleMethodWithInvalidParameterOrderTestCase(int value) {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(ArgumentsAccessor accessor1, int value, ArgumentsAccessor accessor2) {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\trecord LifecycleMethodWithParameterAfterAggregatorTestCase(int value) {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before(@TimesTwo int valueTimesTwo, int value) {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t\tfail(\"should not be called\");\n\t\t}\n\t}\n\n\t@ParameterizedClass // argument sets: 13 = 2 + 4 + 1 + 1 + 1 + 1 + 1 + 1 + 1\n\t@ArgumentsSource(CustomArgumentsProvider.class) // 2\n\t@CsvFileSource(resources = \"two-column.csv\") // 4\n\t@CsvSource(\"csv\") // 1\n\t@EmptySource // 1\n\t@EnumSource(EnumOne.class) // 1\n\t@FieldSource(\"field\") // 1\n\t@MethodSource(\"method\") // 1\n\t@NullSource // 1\n\t@ValueSource(strings = \"value\") // 1\n\tabstract static class BaseInheritanceTestCase {\n\n\t\tstatic final List<String> field = List.of(\"field\");\n\n\t\tstatic List<String> method() {\n\t\t\treturn List.of(\"method\");\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@Parameter\n\t\t@ConvertWith(ToStringConverter.class) // For @EnumSource\n\t\tString value;\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(ints = 1)\n\t\tclass Inner {\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\n\t\t@NullMarked\n\t\tstatic class ToStringConverter extends SimpleArgumentConverter {\n\t\t\t@Override\n\t\t\tprotected @Nullable Object convert(@Nullable Object source, Class<?> targetType)\n\t\t\t\t\tthrows ArgumentConversionException {\n\t\t\t\treturn source == null ? null : String.valueOf(source);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class ConcreteInheritanceTestCase extends BaseInheritanceTestCase {\n\t}\n\n\tstatic class RegularClassWithLifecycleMethodsTestCase {\n\n\t\t@BeforeParameterizedClassInvocation\n\t\tstatic void before() {\n\t\t}\n\n\t\t@AfterParameterizedClassInvocation\n\t\tstatic void after() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedInvocationNameFormatterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_WITH_NAMES_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENT_SET_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DEFAULT_DISPLAY_NAME;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.DISPLAY_NAME_PLACEHOLDER;\nimport static org.junit.jupiter.params.ParameterizedInvocationConstants.INDEX_PLACEHOLDER;\nimport static org.junit.jupiter.params.provider.Arguments.argumentSet;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.sql.Date;\nimport java.time.LocalDate;\nimport java.time.LocalTime;\nimport java.time.ZoneId;\nimport java.util.Arrays;\nimport java.util.Locale;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionConfigurationException;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.support.ParameterNameAndArgument;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * Tests for {@link ParameterizedInvocationNameFormatter}.\n *\n * @since 5.0\n */\n@SuppressWarnings(\"ALL\")\nclass ParameterizedInvocationNameFormatterTests {\n\n\tprivate final Locale originalLocale = Locale.getDefault();\n\n\t@AfterEach\n\tvoid restoreLocale() {\n\t\tLocale.setDefault(originalLocale);\n\t}\n\n\t@Test\n\tvoid formatsDisplayName() {\n\t\tvar formatter = formatter(DISPLAY_NAME_PLACEHOLDER, \"enigma\");\n\n\t\tassertEquals(\"enigma\", format(formatter, 1, arguments()));\n\t\tassertEquals(\"enigma\", format(formatter, 2, arguments()));\n\t}\n\n\t@Test\n\tvoid formatsDisplayNameContainingApostrophe() {\n\t\tString displayName = \"display'Zero\";\n\t\tvar formatter = formatter(DISPLAY_NAME_PLACEHOLDER, \"display'Zero\");\n\n\t\tassertEquals(displayName, format(formatter, 1, arguments()));\n\t\tassertEquals(displayName, format(formatter, 2, arguments()));\n\t}\n\n\t@Test\n\tvoid formatsDisplayNameContainingFormatElements() {\n\t\tString displayName = \"{enigma} {0} '{1}'\";\n\t\tvar formatter = formatter(DISPLAY_NAME_PLACEHOLDER, displayName);\n\n\t\tassertEquals(displayName, format(formatter, 1, arguments()));\n\t\tassertEquals(displayName, format(formatter, 2, arguments()));\n\t}\n\n\t@Test\n\tvoid formatsInvocationIndex() {\n\t\tvar formatter = formatter(INDEX_PLACEHOLDER, \"enigma\");\n\n\t\tassertEquals(\"1\", format(formatter, 1, arguments()));\n\t\tassertEquals(\"2\", format(formatter, 2, arguments()));\n\t}\n\n\t@Test\n\tvoid defaultDisplayName() {\n\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(\"apple\", \"banana\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"[1] \\\"apple\\\", \\\"banana\\\"\");\n\t}\n\n\t@Test\n\tvoid formatsIndividualArguments() {\n\t\tvar formatter = formatter(\"{0} -> {1}\", \"enigma\");\n\n\t\tassertEquals(\"\\\"foo\\\" -> 42\", format(formatter, 1, arguments(\"foo\", 42)));\n\t}\n\n\t@Test\n\tvoid formatsCompleteArgumentsList() {\n\t\tvar formatter = formatter(ARGUMENTS_PLACEHOLDER, \"enigma\");\n\n\t\t// @formatter:off\n\t\tArguments args = arguments(\n\t\t\t42,\n\t\t\t'$',\n\t\t\t\"enigma\",\n\t\t\tnull,\n\t\t\tnew int[] { 1, 2, 3 },\n\t\t\tnew String[] { \"foo\", \"bar\" },\n\t\t\tnew Integer[][] { { 2, 4 }, { 3, 9 } }\n\t\t);\n\t\t// @formatter:on\n\n\t\tassertEquals(\"42, '$', \\\"enigma\\\", null, [1, 2, 3], [foo, bar], [[2, 4], [3, 9]]\", format(formatter, 1, args));\n\t}\n\n\t@Test\n\tvoid formatsCompleteArgumentsListWithNames() {\n\t\tvar testMethod = ParameterizedTestCases.getMethod(\"parameterizedTest\", int.class, String.class, Object[].class);\n\t\tvar formatter = formatter(ARGUMENTS_WITH_NAMES_PLACEHOLDER, \"enigma\", testMethod);\n\n\t\tvar formattedName = format(formatter, 1, arguments(42, \"enigma\", new Object[] { \"foo\", 1 }));\n\t\tassertEquals(\"someNumber = 42, someString = \\\"enigma\\\", someArray = [foo, 1]\", formattedName);\n\t}\n\n\t@Test\n\tvoid formatsCompleteArgumentsListWithoutNamesForAggregators() {\n\t\tvar testMethod = ParameterizedTestCases.getMethod(\"parameterizedTestWithAggregator\", int.class, String.class);\n\t\tvar formatter = formatter(ARGUMENTS_WITH_NAMES_PLACEHOLDER, \"enigma\", testMethod);\n\n\t\tvar formattedName = format(formatter, 1, arguments(42, \"foo\", \"bar\"));\n\t\tassertEquals(\"someNumber = 42, \\\"foo\\\", \\\"bar\\\"\", formattedName);\n\t}\n\n\t@Test\n\tvoid formatsCompleteArgumentsListWithArrays() {\n\t\tvar formatter = formatter(ARGUMENTS_PLACEHOLDER, \"enigma\");\n\n\t\t// Explicit test for https://github.com/junit-team/junit-framework/issues/814\n\t\tassertEquals(\"[foo, bar]\", format(formatter, 1, arguments((Object) new String[] { \"foo\", \"bar\" })));\n\n\t\tassertEquals(\"[foo, bar], 42, true\", format(formatter, 1, arguments(new String[] { \"foo\", \"bar\" }, 42, true)));\n\t}\n\n\t@Test\n\tvoid formatsEverythingUsingCustomPattern() {\n\t\tvar pattern = DISPLAY_NAME_PLACEHOLDER + \" \" + INDEX_PLACEHOLDER + \" :: \" + ARGUMENTS_PLACEHOLDER + \" :: {1}\";\n\t\tvar formatter = formatter(pattern, \"enigma\");\n\n\t\tassertEquals(\"enigma 1 :: \\\"foo\\\", \\\"bar\\\" :: \\\"bar\\\"\", format(formatter, 1, arguments(\"foo\", \"bar\")));\n\t\tassertEquals(\"enigma 2 :: \\\"foo\\\", 42 :: 42\", format(formatter, 2, arguments(\"foo\", 42)));\n\t}\n\n\t@Test\n\tvoid formatDoesNotAlterArgumentsArray() {\n\t\tObject[] actual = { 1, \"two\", Byte.valueOf(\"-128\"), new Integer[][] { { 2, 4 }, { 3, 9 } } };\n\t\tvar formatter = formatter(ARGUMENTS_PLACEHOLDER, \"enigma\");\n\t\tvar expected = Arrays.copyOf(actual, actual.length);\n\t\tassertEquals(\"1, \\\"two\\\", -128, [[2, 4], [3, 9]]\", format(formatter, 1, arguments(actual)));\n\t\tassertArrayEquals(expected, actual);\n\t}\n\n\t@Test\n\tvoid formatDoesNotRaiseAnArrayStoreException() {\n\t\tvar formatter = formatter(\"{0} -> {1}\", \"enigma\");\n\n\t\tObject[] arguments = new Number[] { 1, 2 };\n\t\tassertEquals(\"1 -> 2\", format(formatter, 1, arguments(arguments)));\n\t}\n\n\t@Test\n\tvoid throwsReadableExceptionForInvalidPattern() {\n\t\tvar exception = assertThrows(JUnitException.class, () -> formatter(\"{index\", \"enigma\"));\n\t\tassertNotNull(exception.getCause());\n\t\tassertEquals(IllegalArgumentException.class, exception.getCause().getClass());\n\t}\n\n\t@Test\n\tvoid formattingDoesNotFailIfArgumentToStringImplementationReturnsNull() {\n\t\tvar formatter = formatter(ARGUMENTS_PLACEHOLDER, \"enigma\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(new ToStringReturnsNull(), \"foo\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"null, \\\"foo\\\"\");\n\t}\n\n\t@Test\n\tvoid formattingDoesNotFailIfArgumentToStringImplementationThrowsAnException() {\n\t\tvar formatter = formatter(ARGUMENTS_PLACEHOLDER, \"enigma\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(new ToStringThrowsException(), \"foo\"));\n\n\t\tassertThat(formattedName).startsWith(ToStringThrowsException.class.getName() + \"@\");\n\t\tassertThat(formattedName).endsWith(\"\\\"foo\\\"\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@CsvSource(delimiter = '|', textBlock = \"\"\"\n\t\t\tUS | 42.23 is positive on 2019 Jan 13 at 12:34:56\n\t\t\tDE | 42,23 is positive on 13.01.2019 at 12:34:56\n\t\t\t\"\"\")\n\tvoid customFormattingExpressionsAreSupported(Locale locale, String expectedValue) {\n\t\tvar pattern = \"[{index}] {1,number,#.##} is {1,choice,0<positive} on {0,date} at {0,time} even though {2}\";\n\t\tLocale.setDefault(Locale.US);\n\n\t\tvar date = Date.from(\n\t\t\tLocalDate.of(2019, 1, 13).atTime(LocalTime.of(12, 34, 56)).atZone(ZoneId.systemDefault()).toInstant());\n\t\tLocale.setDefault(locale);\n\n\t\tvar formatter = formatter(pattern, \"enigma\");\n\t\tvar formattedName = format(formatter, 1,\n\t\t\targuments(date, new BigDecimal(\"42.23\"), new ToStringThrowsException()));\n\n\t\tassertThat(formattedName).startsWith(\n\t\t\t\"[1] \" + expectedValue + \" even though \" + ToStringThrowsException.class.getName() + \"@\");\n\t}\n\n\t@Test\n\tvoid ignoresExcessPlaceholders() {\n\t\tvar formatter = formatter(\"{0}, {1}\", \"enigma\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(\"foo\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"\\\"foo\\\", {1}\");\n\t}\n\n\t@Test\n\tvoid placeholdersCanBeOmitted() {\n\t\tvar formatter = formatter(\"{0}\", \"enigma\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(\"foo\", \"bar\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"\\\"foo\\\"\");\n\t}\n\n\t@Test\n\tvoid placeholdersCanBeSkipped() {\n\t\tvar formatter = formatter(\"{0}, {2}\", \"enigma\");\n\n\t\tvar formattedName = format(formatter, 1, arguments(\"foo\", \"bar\", \"baz\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"\\\"foo\\\", \\\"baz\\\"\");\n\t}\n\n\t@Test\n\tvoid truncatesArgumentsThatExceedMaxLength() {\n\t\tvar formatter = formatter(\"{arguments}\", \"display name\", 3);\n\n\t\tvar formattedName = format(formatter, 1, arguments(\"fo\", \"foo\", \"food\"));\n\n\t\tassertThat(formattedName).isEqualTo(\"\\\"fo\\\", \\\"foo\\\", \\\"fo…\\\"\");\n\t}\n\n\t@Nested\n\tclass ArgumentSetTests {\n\n\t\t@Test\n\t\tvoid throwsExceptionForArgumentSetNamePlaceholderWithoutArgumentSet() {\n\t\t\tvar formatter = formatter(ARGUMENT_SET_NAME_PLACEHOLDER, \"IGNORED\");\n\n\t\t\t// @formatter:off\n\t\t\tassertThatExceptionOfType(JUnitException.class)\n\t\t\t\t.isThrownBy(() -> format(formatter, 1, arguments()))\n\t\t\t\t.havingCause()\n\t\t\t\t\t.isExactlyInstanceOf(ExtensionConfigurationException.class)\n\t\t\t\t\t.withMessage(\"When the display name pattern for a @ParameterizedTest contains %s, \"\n\t\t\t\t\t\t+ \"the arguments must be supplied as an ArgumentSet.\", ARGUMENT_SET_NAME_PLACEHOLDER);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid defaultDisplayName() {\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\");\n\n\t\t\tvar formattedName = format(formatter, 42, argumentSet(\"Fruits\", \"apple\", \"banana\"));\n\n\t\t\tassertThat(formattedName).isEqualTo(\"[42] Fruits\");\n\t\t}\n\n\t\t@Test\n\t\tvoid argumentSetNameAndArgumentsPlaceholders() {\n\t\t\tvar pattern = ARGUMENT_SET_NAME_PLACEHOLDER + \" :: \" + ARGUMENTS_PLACEHOLDER;\n\t\t\tvar formatter = formatter(pattern, \"IGNORED\");\n\n\t\t\tvar formattedName = format(formatter, -1, argumentSet(\"Fruits\", \"apple\", \"banana\"));\n\n\t\t\tassertThat(formattedName).isEqualTo(\"Fruits :: \\\"apple\\\", \\\"banana\\\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid mixedTypesOfArgumentsImplementationsAndCustomDisplayNamePattern() {\n\t\t\tvar pattern = \"[%s] %s :: %s\".formatted(INDEX_PLACEHOLDER, DISPLAY_NAME_PLACEHOLDER,\n\t\t\t\tARGUMENT_SET_NAME_OR_ARGUMENTS_WITH_NAMES_PLACEHOLDER);\n\t\t\tvar testMethod = ParameterizedTestCases.getMethod(\"processFruits\", String.class, String.class);\n\t\t\tvar formatter = formatter(pattern, \"Mixed Arguments Types\", testMethod);\n\n\t\t\tvar name1 = format(formatter, 1, argumentSet(\"Fruits\", \"apple\", \"banana\"));\n\t\t\tvar name2 = format(formatter, 2, arguments(\"apple\", \"banana\"));\n\n\t\t\tassertThat(name1).isEqualTo(\"[1] Mixed Arguments Types :: Fruits\");\n\t\t\tassertThat(name2).isEqualTo(\"[2] Mixed Arguments Types :: fruit1 = \\\"apple\\\", fruit2 = \\\"banana\\\"\");\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass QuotedTextTests {\n\n\t\t@ParameterizedTest\n\t\t@CsvSource(delimiterString = \"->\", textBlock = \"\"\"\n\t\t\t\t'Jane Smith' -> 'Jane Smith'\n\t\t\t\t\\\\           -> \\\\\\\\\n\t\t\t\t\"            -> \\\\\"\n\t\t\t\t# The following represents a single ' enclosed in ''.\n\t\t\t\t''''         -> ''''\n\t\t\t\t'\\n'         -> \\\\n\n\t\t\t\t'\\r\\n'       -> \\\\r\\\\n\n\t\t\t\t'   \\t   '   -> '   \\\\t   '\n\t\t\t\t'\\b'         -> \\\\b\n\t\t\t\t'\\f'         -> \\\\f\n\t\t\t\t'\\u0007'     -> '\\u0007'\n\t\t\t\t\"\"\")\n\t\tvoid quotedStrings(String argument, String expected) {\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\");\n\n\t\t\tvar formattedName = format(formatter, 1, arguments(argument));\n\t\t\tassertThat(formattedName).isEqualTo(\"[1] \" + '\"' + expected + '\"');\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@CsvSource(quoteCharacter = '\"', delimiterString = \"->\", textBlock = \"\"\"\n\t\t\t\tX        -> X\n\t\t\t\t\\\\       -> \\\\\\\\\n\t\t\t\t'        -> \\\\'\n\t\t\t\t# The following represents a single \" enclosed in \"\". The escaping is\n\t\t\t\t# necessary, because three \" characters in a row close the text block.\n\t\t\t\t\\\"\"\"\\\"   -> \\\"\"\"\\\"\n\t\t\t\t\"\\n\"     -> \\\\n\n\t\t\t\t\"\\r\"     -> \\\\r\n\t\t\t\t\"\\t\"     -> \\\\t\n\t\t\t\t\"\\b\"     -> \\\\b\n\t\t\t\t\"\\f\"     -> \\\\f\n\t\t\t\t\"\\u0007\" -> \"\\u0007\"\n\t\t\t\t\"\"\")\n\t\tvoid quotedCharacters(char argument, String expected) {\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\");\n\n\t\t\tvar formattedName = format(formatter, 1, arguments(argument));\n\t\t\tassertThat(formattedName).isEqualTo(\"[1] \" + \"'\" + expected + \"'\");\n\t\t}\n\n\t\t@Test\n\t\tvoid quotedStringsForArgumentsWithNames() {\n\t\t\tvar testMethod = ParameterizedTestCases.getMethod(\"processFruit\", String.class, int.class);\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\", testMethod);\n\n\t\t\tvar name1 = format(formatter, 1, arguments(\"apple\", 42));\n\t\t\tvar name2 = format(formatter, 2, arguments(\"banana\", 99));\n\n\t\t\tassertThat(name1).isEqualTo(\"[1] fruit = \\\"apple\\\", ranking = 42\");\n\t\t\tassertThat(name2).isEqualTo(\"[2] fruit = \\\"banana\\\", ranking = 99\");\n\t\t}\n\n\t\t@Test\n\t\tvoid quotedStringsForArgumentsWithNamesAndNamedArguments() {\n\t\t\tvar testMethod = ParameterizedTestCases.getMethod(\"processFruit\", String.class, int.class);\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\", testMethod);\n\n\t\t\tvar name1 = format(formatter, 1, arguments(named(\"Apple\", \"apple\"), 42));\n\t\t\tvar name2 = format(formatter, 2, arguments(named(\"Banana\", \"banana\"), 99));\n\n\t\t\tassertThat(name1).isEqualTo(\"[1] fruit = Apple, ranking = 42\");\n\t\t\tassertThat(name2).isEqualTo(\"[2] fruit = Banana, ranking = 99\");\n\t\t}\n\n\t\t@Test\n\t\tvoid quotedStringsForArgumentsWithNamesAndParameterNameAndArgument() {\n\t\t\tvar testMethod = ParameterizedTestCases.getMethod(\"processFruit\", String.class, int.class);\n\t\t\tvar formatter = formatter(DEFAULT_DISPLAY_NAME, \"IGNORED\", testMethod);\n\n\t\t\tvar name1 = format(formatter, 1, arguments(new ParameterNameAndArgument(\"FRUIT\", \"apple\"), 42));\n\t\t\tvar name2 = format(formatter, 2, arguments(new ParameterNameAndArgument(\"FRUCHT\", \"Banane\"), 99));\n\n\t\t\tassertThat(name1).isEqualTo(\"[1] FRUIT = \\\"apple\\\", ranking = 42\");\n\t\t\tassertThat(name2).isEqualTo(\"[2] FRUCHT = \\\"Banane\\\", ranking = 99\");\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static ParameterizedInvocationNameFormatter formatter(String pattern, String displayName) {\n\t\treturn formatter(pattern, displayName, 512);\n\t}\n\n\tprivate static ParameterizedInvocationNameFormatter formatter(String pattern, String displayName,\n\t\t\tint argumentMaxLength) {\n\t\tParameterizedDeclarationContext<?> context = mock();\n\t\twhen(context.getResolverFacade()).thenReturn(mock());\n\t\twhen(context.getAnnotationName()).thenReturn(ParameterizedTest.class.getSimpleName());\n\t\treturn new ParameterizedInvocationNameFormatter(pattern, displayName, context, argumentMaxLength);\n\t}\n\n\tprivate static ParameterizedInvocationNameFormatter formatter(String pattern, String displayName, Method method) {\n\t\tvar context = new ParameterizedTestContext(method.getDeclaringClass(), method,\n\t\t\tmethod.getAnnotation(ParameterizedTest.class));\n\t\treturn new ParameterizedInvocationNameFormatter(pattern, displayName, context, 512);\n\t}\n\n\tprivate static String format(ParameterizedInvocationNameFormatter formatter, int invocationIndex,\n\t\t\tArguments arguments) {\n\t\treturn formatter.format(invocationIndex, EvaluatedArgumentSet.allOf(arguments), true);\n\t}\n\n\t@NullUnmarked\n\tprivate static class ToStringReturnsNull {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class ToStringThrowsException {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tthrow new RuntimeException(\"Boom!\");\n\t\t}\n\t}\n\n\tprivate static class ParameterizedTestCases {\n\n\t\tstatic Method getMethod(String methodName, Class<?>... parameterTypes) {\n\t\t\treturn ReflectionSupport.findMethod(ParameterizedTestCases.class, methodName, parameterTypes).orElseThrow();\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ParameterizedTest\n\t\tvoid parameterizedTest(int someNumber, String someString, Object[] someArray) {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ParameterizedTest\n\t\tvoid parameterizedTestWithAggregator(int someNumber,\n\t\t\t\t@AggregateWith(CustomAggregator.class) String someAggregatedString) {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ParameterizedTest\n\t\tvoid processFruit(String fruit, int ranking) {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\t@ParameterizedTest\n\t\tvoid processFruits(String fruit1, String fruit2) {\n\t\t}\n\n\t\tprivate static class CustomAggregator extends SimpleArgumentsAggregator {\n\t\t\t@Override\n\t\t\tprotected @Nullable Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\t\treturn accessor.get(0);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestContextTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.params.aggregator.AggregatorIntegrationTests.CsvToPerson;\nimport org.junit.jupiter.params.aggregator.AggregatorIntegrationTests.Person;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Unit tests for {@link ParameterizedTestContext}.\n *\n * @since 5.2\n */\nclass ParameterizedTestContextTests {\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"onePrimitive\", \"twoPrimitives\", \"twoAggregators\", \"twoAggregatorsWithTestInfoAtTheEnd\",\n\t\t\t\"mixedMode\" })\n\tvoid validSignatures(String methodName) {\n\t\tassertDoesNotThrow(() -> createMethodContext(ValidTestCase.class, methodName));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"twoAggregatorsWithPrimitiveInTheMiddle\", \"twoAggregatorsWithTestInfoInTheMiddle\" })\n\tvoid invalidSignatures(String methodName) {\n\t\tassertPreconditionViolationFor(() -> createMethodContext(InvalidTestCase.class, methodName));\n\t}\n\n\tprivate ParameterizedTestContext createMethodContext(Class<?> testClass, String methodName) {\n\t\tvar method = ReflectionUtils.findMethods(testClass, m -> m.getName().equals(methodName)).getFirst();\n\t\treturn new ParameterizedTestContext(testClass, method, method.getAnnotation(ParameterizedTest.class));\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class ValidTestCase {\n\n\t\t@ParameterizedTest\n\t\tvoid onePrimitive(int num) {\n\t\t}\n\n\t\t@ParameterizedTest\n\t\tvoid twoPrimitives(int num1, int num2) {\n\t\t}\n\n\t\t@ParameterizedTest\n\t\tvoid twoAggregators(@CsvToPerson Person person, ArgumentsAccessor arguments) {\n\t\t}\n\n\t\t@ParameterizedTest\n\t\tvoid twoAggregatorsWithTestInfoAtTheEnd(@CsvToPerson Person person1, @CsvToPerson Person person2,\n\t\t\t\tTestInfo testInfo) {\n\t\t}\n\n\t\t@ParameterizedTest\n\t\tvoid mixedMode(int num1, int num2, ArgumentsAccessor arguments1, ArgumentsAccessor arguments2,\n\t\t\t\t@CsvToPerson Person person1, @CsvToPerson Person person2, TestInfo testInfo1, TestInfo testInfo2) {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class InvalidTestCase {\n\n\t\t@ParameterizedTest\n\t\tvoid twoAggregatorsWithPrimitiveInTheMiddle(@CsvToPerson Person person1, int num, @CsvToPerson Person person2) {\n\t\t}\n\n\t\t@ParameterizedTest\n\t\tvoid twoAggregatorsWithTestInfoInTheMiddle(@CsvToPerson Person person1, TestInfo testInfo,\n\t\t\t\t@CsvToPerson Person person2) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestExtensionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.params.ParameterizedInvocationContextProvider.arguments;\nimport static org.junit.jupiter.params.ParameterizedTestExtension.DECLARATION_CONTEXT_KEY;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.FileNotFoundException;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExecutableInvoker;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TemplateInvocationValidationException;\nimport org.junit.jupiter.api.extension.TestInstances;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.api.parallel.ExecutionMode;\nimport org.junit.jupiter.engine.execution.NamespaceAwareStore;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.ArgumentsProvider;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\n\n/**\n * Unit tests for {@link ParameterizedTestExtension}.\n *\n * @since 5.0\n */\nclass ParameterizedTestExtensionTests {\n\n\tprivate final ParameterizedTestExtension parameterizedTestExtension = new ParameterizedTestExtension();\n\n\tstatic boolean streamWasClosed = false;\n\n\t@Test\n\tvoid supportsReturnsFalseForMissingTestMethod() {\n\t\tvar extensionContextWithoutTestMethod = getExtensionContextReturningSingleMethod(new TestCaseWithoutMethod());\n\t\tassertFalse(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithoutTestMethod));\n\t}\n\n\t@Test\n\tvoid supportsReturnsFalseForTestMethodWithoutParameterizedTestAnnotation() {\n\t\tvar extensionContextWithUnAnnotatedTestMethod = getExtensionContextReturningSingleMethod(\n\t\t\tnew TestCaseWithMethod());\n\t\tassertFalse(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithUnAnnotatedTestMethod));\n\t}\n\n\t@Test\n\tvoid supportsReturnsTrueForTestMethodWithParameterizedTestAnnotation() {\n\t\tvar extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(\n\t\t\tnew TestCaseWithAnnotatedMethod());\n\t\tassertTrue(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithAnnotatedTestMethod));\n\t}\n\n\t@Test\n\tvoid streamsReturnedByProvidersAreClosedWhenCallingProvide() {\n\t\tvar extensionContext = getExtensionContextReturningSingleMethod(\n\t\t\tnew ArgumentsProviderWithCloseHandlerTestCase());\n\t\t// we need to call supportsTestTemplate() first, because it creates and\n\t\t// puts the ParameterizedTestMethodContext into the Store\n\t\tthis.parameterizedTestExtension.supportsTestTemplate(extensionContext);\n\n\t\tvar stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext);\n\n\t\tassertFalse(streamWasClosed);\n\t\t// cause the stream to be evaluated\n\t\tstream.count();\n\t\tassertTrue(streamWasClosed);\n\t}\n\n\t@Test\n\tvoid emptyDisplayNameIsIllegal() {\n\t\tvar extensionContext = getExtensionContextReturningSingleMethod(new EmptyDisplayNameProviderTestCase());\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext));\n\t}\n\n\t@Test\n\tvoid defaultDisplayNameWithEmptyStringInConfigurationIsIllegal() {\n\t\tAtomicInteger invocations = new AtomicInteger();\n\t\tFunction<String, Optional<String>> configurationSupplier = key -> {\n\t\t\tif (key.equals(ParameterizedInvocationNameFormatter.DISPLAY_NAME_PATTERN_KEY)) {\n\t\t\t\tinvocations.incrementAndGet();\n\t\t\t\treturn Optional.of(\"\");\n\t\t\t}\n\t\t\telse {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\t\t};\n\t\tvar extensionContext = getExtensionContextReturningSingleMethod(new DefaultDisplayNameProviderTestCase(),\n\t\t\tconfigurationSupplier);\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext));\n\t\tassertEquals(1, invocations.get());\n\t}\n\n\t@Test\n\tvoid argumentsRethrowsOriginalExceptionFromProviderAsUncheckedException() {\n\t\tArgumentsProvider failingProvider = new ArgumentsProvider() {\n\t\t\t@Override\n\t\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\t\tExtensionContext context) throws Exception {\n\t\t\t\tthrow new FileNotFoundException(\"a message\");\n\t\t\t}\n\t\t};\n\n\t\tvar exception = assertThrows(FileNotFoundException.class, () -> arguments(failingProvider, mock(), mock()));\n\t\tassertEquals(\"a message\", exception.getMessage());\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenParameterizedTestIsNotInvokedAtLeastOnce() {\n\t\tvar extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(\n\t\t\tnew TestCaseWithAnnotatedMethod());\n\n\t\tvar stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(\n\t\t\textensionContextWithAnnotatedTestMethod);\n\t\t// cause the stream to be evaluated\n\t\tstream.toArray();\n\t\tvar exception = assertThrows(TemplateInvocationValidationException.class, stream::close);\n\n\t\tassertThat(exception).hasMessage(\n\t\t\t\"Configuration error: You must configure at least one set of arguments for this @ParameterizedTest\");\n\t}\n\n\t@Test\n\tvoid doesNotThrowExceptionWhenParametrizedTestDoesNotRequireArguments() {\n\t\tvar extensionContext = getExtensionContextReturningSingleMethod(new TestCaseAllowNoArgumentsMethod());\n\n\t\tvar stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext);\n\t\t// cause the stream to be evaluated\n\t\tstream.toArray();\n\t\tstream.close();\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenParameterizedTestHasNoArgumentsSource() {\n\t\tvar extensionContext = getExtensionContextReturningSingleMethod(new TestCaseWithNoArgumentsSource());\n\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext))//\n\t\t\t\t\t.withMessage(\n\t\t\t\t\t\t\"Configuration error: You must configure at least one arguments source for this @ParameterizedTest\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenArgumentsProviderIsNotStatic() {\n\t\tvar extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(\n\t\t\tnew NonStaticArgumentsProviderTestCase());\n\n\t\tvar stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(\n\t\t\textensionContextWithAnnotatedTestMethod);\n\n\t\tvar exception = assertThrows(JUnitException.class, stream::toArray);\n\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessage(\n\t\t\t\t\t\"The ArgumentsProvider [%s] must be either a top-level class or a static nested class\".formatted(\n\t\t\t\t\t\tNonStaticArgumentsProvider.class.getName()));\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenArgumentsProviderDoesNotContainUnambiguousConstructor() {\n\t\tvar extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(\n\t\t\tnew MissingNoArgumentsConstructorArgumentsProviderTestCase());\n\n\t\tvar stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(\n\t\t\textensionContextWithAnnotatedTestMethod);\n\n\t\tvar exception = assertThrows(JUnitException.class, stream::toArray);\n\n\t\tString className = AmbiguousConstructorArgumentsProvider.class.getName();\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessage(\"\"\"\n\t\t\t\t\t\tFailed to find constructor for ArgumentsProvider [%s]. \\\n\t\t\t\t\t\tPlease ensure that a no-argument or a single constructor exists.\"\"\", className);\n\t}\n\n\tprivate ExtensionContext getExtensionContextReturningSingleMethod(Object testCase) {\n\t\treturn getExtensionContextReturningSingleMethod(testCase, ignored -> Optional.empty());\n\t}\n\n\t@NullMarked\n\tprivate ExtensionContext getExtensionContextReturningSingleMethod(Object testCase,\n\t\t\tFunction<String, Optional<String>> configurationSupplier) {\n\n\t\tClass<?> testClass = testCase.getClass();\n\t\tvar method = ReflectionUtils.findMethods(testClass, it -> \"method\".equals(it.getName())).stream().findFirst();\n\n\t\treturn new ExtensionContext() {\n\n\t\t\tprivate final NamespacedHierarchicalStore<org.junit.platform.engine.support.store.Namespace> store = new NamespacedHierarchicalStore<>(\n\t\t\t\tnull);\n\n\t\t\t@Override\n\t\t\tpublic Optional<Method> getTestMethod() {\n\t\t\t\treturn method;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<ExtensionContext> getParent() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ExtensionContext getRoot() {\n\t\t\t\treturn this;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getUniqueId() {\n\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getDisplayName() {\n\t\t\t\treturn \"display-name\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<String> getTags() {\n\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<AnnotatedElement> getElement() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<Class<?>> getTestClass() {\n\t\t\t\treturn Optional.of(testClass);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic List<Class<?>> getEnclosingTestClasses() {\n\t\t\t\treturn List.of();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<Lifecycle> getTestInstanceLifecycle() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<Object> getTestInstance() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<TestInstances> getTestInstances() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<Throwable> getExecutionException() {\n\t\t\t\treturn Optional.empty();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Optional<String> getConfigurationParameter(String key) {\n\t\t\t\treturn configurationSupplier.apply(key);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic <T> Optional<T> getConfigurationParameter(String key,\n\t\t\t\t\tFunction<? super String, ? extends @Nullable T> transformer) {\n\t\t\t\treturn configurationSupplier.apply(key).map(transformer);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void publishReportEntry(Map<String, String> map) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void publishFile(String fileName, MediaType mediaType, ThrowingConsumer<Path> action) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void publishDirectory(String name, ThrowingConsumer<Path> action) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Store getStore(Namespace namespace) {\n\t\t\t\tvar store = new NamespaceAwareStore(this.store,\n\t\t\t\t\torg.junit.platform.engine.support.store.Namespace.create(namespace.getParts()));\n\t\t\t\tmethod //\n\t\t\t\t\t\t.map(it -> new ParameterizedTestContext(testClass, it,\n\t\t\t\t\t\t\tAnnotationSupport.findAnnotation(it, ParameterizedTest.class).orElseThrow())) //\n\t\t\t\t\t\t.ifPresent(ctx -> store.put(DECLARATION_CONTEXT_KEY, ctx));\n\t\t\t\treturn store;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Store getStore(StoreScope scope, Namespace namespace) {\n\t\t\t\treturn getStore(namespace);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ExecutionMode getExecutionMode() {\n\t\t\t\treturn ExecutionMode.SAME_THREAD;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic ExecutableInvoker getExecutableInvoker() {\n\t\t\t\treturn new ExecutableInvoker() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Object invoke(Method method, @Nullable Object target) {\n\t\t\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic <T> T invoke(Constructor<T> constructor, @Nullable Object outerInstance) {\n\t\t\t\t\t\treturn ReflectionUtils.newInstance(constructor);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t};\n\t}\n\n\tstatic class TestCaseWithoutMethod {\n\t}\n\n\tstatic class TestCaseWithMethod {\n\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class TestCaseWithAnnotatedMethod {\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(ZeroArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class TestCaseAllowNoArgumentsMethod {\n\n\t\t@ParameterizedTest(allowZeroInvocations = true)\n\t\t@ArgumentsSource(ZeroArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class TestCaseWithNoArgumentsSource {\n\n\t\t@ParameterizedTest(allowZeroInvocations = true)\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\tvoid method() {\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class ZeroArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tstatic class ArgumentsProviderWithCloseHandlerTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(ArgumentsProviderWithCloseHandler.class)\n\t\tvoid method(String parameter) {\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class ArgumentsProviderWithCloseHandler implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\tvar argumentsStream = Stream.of(\"foo\", \"bar\").map(Arguments::of);\n\t\t\treturn argumentsStream.onClose(() -> streamWasClosed = true);\n\t\t}\n\t}\n\n\tstatic class NonStaticArgumentsProviderTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(NonStaticArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t@NullMarked\n\tclass NonStaticArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\t}\n\n\tstatic class MissingNoArgumentsConstructorArgumentsProviderTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(AmbiguousConstructorArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class EmptyDisplayNameProviderTestCase {\n\n\t\t@ParameterizedTest(name = \"\")\n\t\t@ArgumentsSource(AmbiguousConstructorArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class DefaultDisplayNameProviderTestCase {\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(AmbiguousConstructorArgumentsProvider.class)\n\t\tvoid method() {\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class AmbiguousConstructorArgumentsProvider implements ArgumentsProvider {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tAmbiguousConstructorArgumentsProvider(String parameter) {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tAmbiguousConstructorArgumentsProvider(int parameter) {\n\t\t}\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.within;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.appendTestTemplateInvocationSegment;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForTestTemplateMethod;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.testkit.engine.EventConditions.abortedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Constructor;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.NavigableMap;\nimport java.util.NavigableSet;\nimport java.util.Set;\nimport java.util.SortedMap;\nimport java.util.SortedSet;\nimport java.util.TreeMap;\nimport java.util.TreeSet;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer.OrderAnnotation;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.extension.TemplateInvocationValidationException;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.jupiter.params.ParameterizedTestIntegrationTests.RepeatableSourcesTestCase.Action;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.ArgumentsAggregationException;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.converter.ArgumentConversionException;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.converter.TypedArgumentConverter;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.ArgumentsProvider;\nimport org.junit.jupiter.params.provider.ArgumentsSource;\nimport org.junit.jupiter.params.provider.CsvFileSource;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.EmptySource;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.FieldSource;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.NullAndEmptySource;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.PreconditionViolationException;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.EventConditions;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * @since 5.0\n */\nclass ParameterizedTestIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\tprivate final Locale originalLocale = Locale.getDefault(Locale.Category.FORMAT);\n\n\t@AfterEach\n\tvoid reset() {\n\t\tLocale.setDefault(Locale.Category.FORMAT, originalLocale);\n\t\tAutoCloseableArgument.closeCounter = 0;\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource(textBlock = \"\"\"\n\t\t\tapple,   True\n\t\t\tbanana,  true\n\t\t\tlemon,   false\n\t\t\tkumquat, FALSE\n\t\t\t\"\"\")\n\tvoid sweetFruit(String fruit, Boolean sweet) {\n\t\tswitch (fruit) {\n\t\t\tcase \"apple\" -> assertThat(sweet).isTrue();\n\t\t\tcase \"banana\" -> assertThat(sweet).isTrue();\n\t\t\tcase \"lemon\" -> assertThat(sweet).isFalse();\n\t\t\tcase \"kumquat\" -> assertThat(sweet).isFalse();\n\t\t\tdefault -> fail(\"Unexpected fruit : \" + fruit);\n\t\t}\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource(nullValues = \"null\", textBlock = \"\"\"\n\t\t\tapple,   True\n\t\t\tbanana,  true\n\t\t\tlemon,   false\n\t\t\tkumquat, null\n\t\t\t\"\"\")\n\tvoid sweetFruitWithNullableBoolean(String fruit, Boolean sweet) {\n\t\tswitch (fruit) {\n\t\t\tcase \"apple\" -> assertThat(sweet).isTrue();\n\t\t\tcase \"banana\" -> assertThat(sweet).isTrue();\n\t\t\tcase \"lemon\" -> assertThat(sweet).isFalse();\n\t\t\tcase \"kumquat\" -> assertThat(sweet).isNull(); // null --> null\n\t\t\tdefault -> fail(\"Unexpected fruit : \" + fruit);\n\t\t}\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource(quoteCharacter = '\"', textBlock = \"\"\"\n\n\n\t\t\t# This is a comment preceded by multiple opening blank lines.\n\t\t\tapple,         1\n\t\t\tbanana,        2\n\t\t\t# This is a comment pointing out that the next line contains multiple explicit newlines in quoted text.\n\t\t\t\"lemon  \\s\n\n\n\t\t\t\\s  lime\",         0xF1\n\t\t\t# The next line is a blank line in the middle of the CSV rows.\n\n\t\t\tstrawberry,    700_000\n\t\t\t# This is a comment followed by 2 closing blank line.\n\n\t\t\t\"\"\")\n\tvoid executesLinesFromTextBlock(String fruit, int rank) {\n\t\tswitch (fruit) {\n\t\t\tcase \"apple\" -> assertThat(rank).isEqualTo(1);\n\t\t\tcase \"banana\" -> assertThat(rank).isEqualTo(2);\n\t\t\tcase \"lemon   \\n\\n\\n   lime\" -> assertThat(rank).isEqualTo(241);\n\t\t\tcase \"strawberry\" -> assertThat(rank).isEqualTo(700_000);\n\t\t\tdefault -> fail(\"Unexpected fruit : \" + fruit);\n\t\t}\n\t}\n\n\t@ParameterizedTest(name = \"[{index}] {arguments}\")\n\t@CsvSource(delimiter = '|', useHeadersInDisplayName = true, nullValues = \"NIL\", textBlock = \"\"\"\n\t\t\t#---------------------------------\n\t\t\t  FRUIT  | RANK\n\t\t\t#---------------------------------\n\t\t\t  apple  | 1\n\t\t\t#---------------------------------\n\t\t\t  banana | 2\n\t\t\t#---------------------------------\n\t\t\t  cherry | 3.14159265358979323846\n\t\t\t#---------------------------------\n\t\t\t         | 0\n\t\t\t#---------------------------------\n\t\t\t  NIL    | 0\n\t\t\t#---------------------------------\n\t\t\t\"\"\")\n\tvoid executesLinesFromTextBlockUsingTableFormatAndHeadersAndNullValues(String fruit, double rank,\n\t\t\tTestInfo testInfo) {\n\t\tassertFruitTable(fruit, rank, testInfo);\n\t}\n\n\t@ParameterizedTest(name = \"[{index}] {arguments}\")\n\t@CsvFileSource(resources = \"two-column-with-headers.csv\", delimiter = '|', useHeadersInDisplayName = true, nullValues = \"NIL\")\n\tvoid executesLinesFromClasspathResourceUsingTableFormatAndHeadersAndNullValues(String fruit, double rank,\n\t\t\tTestInfo testInfo) {\n\t\tassertFruitTable(fruit, rank, testInfo);\n\t}\n\n\tprivate void assertFruitTable(@Nullable String fruit, double rank, TestInfo testInfo) {\n\t\tString displayName = testInfo.getDisplayName();\n\n\t\tif (fruit == null) {\n\t\t\tassertThat(rank).isEqualTo(0);\n\t\t\tassertThat(displayName).matches(\"\\\\[(4|5)\\\\] FRUIT = null, RANK = \\\"0\\\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (fruit) {\n\t\t\tcase \"apple\" -> {\n\t\t\t\tassertThat(rank).isEqualTo(1);\n\t\t\t\tassertThat(displayName).isEqualTo(\"[1] FRUIT = \\\"apple\\\", RANK = \\\"1\\\"\");\n\t\t\t}\n\t\t\tcase \"banana\" -> {\n\t\t\t\tassertThat(rank).isEqualTo(2);\n\t\t\t\tassertThat(displayName).isEqualTo(\"[2] FRUIT = \\\"banana\\\", RANK = \\\"2\\\"\");\n\t\t\t}\n\t\t\tcase \"cherry\" -> {\n\t\t\t\tassertThat(rank).isCloseTo(Math.PI, within(0.0));\n\t\t\t\tassertThat(displayName).isEqualTo(\"[3] FRUIT = \\\"cherry\\\", RANK = \\\"3.14159265358979323846\\\"\");\n\t\t\t}\n\t\t\tdefault -> fail(\"Unexpected fruit : \" + fruit);\n\t\t}\n\t}\n\n\t@Test\n\tvoid executesWithSingleArgumentsProviderWithMultipleInvocations() {\n\t\tvar results = execute(\"testWithTwoSingleStringArgumentsProvider\", String.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t}\n\n\t@Test\n\tvoid executesWithCsvSource() {\n\t\tvar results = execute(\"testWithCsvSource\", String.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t}\n\n\t/**\n\t * @since 6.0\n\t */\n\t@Test\n\tvoid executesWithCsvSourceAndSpecialCharacters() {\n\t\t// @formatter:off\n\t\texecute(\"testWithCsvSourceAndSpecialCharacters\", String.class)\n\t\t\t\t.testEvents()\n\t\t\t\t.started()\n\t\t\t\t.assertEventsMatchExactly(\n\t\t\t\t\tdisplayName(quoted(\"üñåé\")),\n\t\t\t\t\tdisplayName(quoted(\"\\\\n\")),\n\t\t\t\t\tdisplayName(quoted(\"\\\\r\")),\n\t\t\t\t\tdisplayName(quoted(\"\\uFFFD\")),\n\t\t\t\t\tdisplayName(quoted(\"😱\")),\n\t\t\t\t\tdisplayName(quoted(\"Zero\\u200BWidth\\u200BSpaces\"))\n\t\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate static String quoted(String text) {\n\t\treturn '\"' + text + '\"';\n\t}\n\n\t@Test\n\tvoid executesWithCustomName() {\n\t\tvar results = execute(\"testWithCustomName\", String.class, int.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"foo and 23\"), finishedWithFailure(message(\"foo, 23\")))) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"bar and 42\"), finishedWithFailure(message(\"bar, 42\"))));\n\t}\n\n\t@Test\n\tvoid executesWithMessageFormat() {\n\t\tLocale.setDefault(Locale.Category.FORMAT, Locale.ROOT);\n\n\t\tvar results = execute(\"testWithMessageFormat\", double.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(), displayName(\"3.1416\"), finishedWithFailure(message(String.valueOf(Math.PI)))));\n\t}\n\n\t/**\n\t * @since 5.2\n\t */\n\t@Test\n\tvoid executesWithPrimitiveWideningConversion() {\n\t\tvar results = execute(\"testWithPrimitiveWideningConversion\", double.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] num = 1\"), finishedWithFailure(message(\"num: 1.0\")))) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] num = 2\"), finishedWithFailure(message(\"num: 2.0\"))));\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid executesWithImplicitGenericConverter() {\n\t\tvar results = execute(\"testWithImplicitGenericConverter\", Book.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] book = book 1\"), finishedWithFailure(message(\"book 1\")))) //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(), displayName(\"[2] book = book 2\"), finishedWithFailure(message(\"book 2\"))));\n\t}\n\n\t/**\n\t * @since 6.0\n\t */\n\t@Test\n\tvoid executesWithImplicitGenericConverterWithCharSequenceConstructor() {\n\t\tvar results = execute(\"testWithImplicitGenericConverterWithCharSequenceConstructor\", Record.class);\n\t\tresults.testEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(displayName(\"\\\"record 1\\\"\"), finishedWithFailure(message(\"record 1\")))) //\n\t\t\t\t.haveExactly(1, event(displayName(\"\\\"record 2\\\"\"), finishedWithFailure(message(\"record 2\"))));\n\t}\n\n\t@Test\n\tvoid legacyReportingNames() {\n\t\tvar results = execute(\"testWithCustomName\", String.class, int.class);\n\n\t\t// @formatter:off\n\t\tvar legacyReportingNames = results.testEvents().dynamicallyRegistered()\n\t\t\t\t.map(Event::getTestDescriptor)\n\t\t\t\t.map(TestDescriptor::getLegacyReportingName);\n\t\t// @formatter:on\n\t\tassertThat(legacyReportingNames).containsExactly(\"testWithCustomName(String, int)[1]\",\n\t\t\t\"testWithCustomName(String, int)[2]\");\n\t}\n\n\t@Test\n\tvoid executesWithExplicitConverter() {\n\t\tvar results = execute(\"testWithExplicitConverter\", int.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] length = O\"), finishedWithFailure(message(\"length: 1\")))) //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(), displayName(\"[2] length = XXX\"), finishedWithFailure(message(\"length: 3\"))));\n\t}\n\n\t@Test\n\tvoid executesWithAggregator() {\n\t\tvar results = execute(\"testWithAggregator\", String.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(), displayName(\"[1] ab, cd\"), finishedWithFailure(message(\"concatenation: abcd\")))) //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(), displayName(\"[2] ef, gh\"), finishedWithFailure(message(\"concatenation: efgh\"))));\n\t}\n\n\t@Test\n\tvoid executesWithIgnoreLeadingAndTrailingSetToFalseForCsvSource() {\n\t\tvar results = execute(\"testWithIgnoreLeadingAndTrailingWhitespaceSetToFalseForCsvSource\", String.class,\n\t\t\tString.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: ' ab ', ' cd'\")))) //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ef ', 'gh'\"))));\n\t}\n\n\t@Test\n\tvoid executesWithIgnoreLeadingAndTrailingSetToTrueForCsvSource() {\n\t\tvar results = execute(\"testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvSource\", String.class,\n\t\t\tString.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ab', 'cd'\")))) //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ef', 'gh'\"))));\n\t}\n\n\t@Test\n\tvoid executesWithIgnoreLeadingAndTrailingSetToFalseForCsvFileSource() {\n\t\tvar results = execute(\"testWithIgnoreLeadingAndTrailingWhitespaceSetToFalseForCsvFileSource\", String.class,\n\t\t\tString.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: ' ab ', ' cd'\")))) //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ef ', 'gh'\"))));\n\t}\n\n\t@Test\n\tvoid executesWithIgnoreLeadingAndTrailingSetToTrueForCsvFileSource() {\n\t\tvar results = execute(\"testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvFileSource\", String.class,\n\t\t\tString.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ab', 'cd'\")))) //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"arguments: 'ef', 'gh'\"))));\n\t}\n\n\t@Test\n\tvoid failsContainerOnEmptyName() {\n\t\tvar results = execute(\"testWithEmptyName\", String.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(container(), displayName(\"testWithEmptyName(String)\"), //\n\t\t\t\t\tfinishedWithFailure(message(msg -> msg.contains(\"must be declared with a non-empty name\")))));\n\t}\n\n\t@Test\n\tvoid reportsExceptionForErroneousConverter() {\n\t\tvar results = execute(\"testWithErroneousConverter\", Object.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(instanceOf(ParameterResolutionException.class), //\n\t\t\t\t\tmessage(\"Error converting parameter at index 0: something went horribly wrong\"))));\n\t}\n\n\t@Test\n\tvoid executesLifecycleMethods() {\n\t\t// reset static collections\n\t\tLifecycleTestCase.lifecycleEvents.clear();\n\t\tLifecycleTestCase.testMethods.clear();\n\n\t\tvar results = executeTestsForClass(LifecycleTestCase.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(\"test1\"), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(test(\"test1\"), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\n\t\tList<String> testMethods = new ArrayList<>(LifecycleTestCase.testMethods);\n\n\t\t// @formatter:off\n\t\tassertThat(LifecycleTestCase.lifecycleEvents).containsExactly(\n\t\t\t\"beforeAll:ParameterizedTestIntegrationTests$LifecycleTestCase\",\n\t\t\t\t\"providerMethod\",\n\t\t\t\t\"constructor:[1] argument = foo\",\n\t\t\t\t\t\"beforeEach:[1] argument = foo\",\n\t\t\t\t\t\ttestMethods.get(0) + \":[1] argument = foo\",\n\t\t\t\t\t\"afterEach:[1] argument = foo\",\n\t\t\t\t\t\"constructor:[2] argument = bar\",\n\t\t\t\t\t\"beforeEach:[2] argument = bar\",\n\t\t\t\t\t\ttestMethods.get(0) + \":[2] argument = bar\",\n\t\t\t\t\t\"afterEach:[2] argument = bar\",\n\t\t\t\t\"providerMethod\",\n\t\t\t\t\t\"constructor:[1] argument = foo\",\n\t\t\t\t\t\"beforeEach:[1] argument = foo\",\n\t\t\t\t\t\ttestMethods.get(1) + \":[1] argument = foo\",\n\t\t\t\t\t\"afterEach:[1] argument = foo\",\n\t\t\t\t\t\"constructor:[2] argument = bar\",\n\t\t\t\t\t\"beforeEach:[2] argument = bar\",\n\t\t\t\t\t\ttestMethods.get(1) + \":[2] argument = bar\",\n\t\t\t\t\t\"afterEach:[2] argument = bar\",\n\t\t\t\"afterAll:ParameterizedTestIntegrationTests$LifecycleTestCase\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid truncatesArgumentsThatExceedMaxLength() {\n\t\tvar results = EngineTestKit.engine(new JupiterTestEngine()) //\n\t\t\t\t.configurationParameter(ParameterizedInvocationNameFormatter.ARGUMENT_MAX_LENGTH_KEY, \"2\") //\n\t\t\t\t.selectors(selectMethod(TestCase.class, \"testWithCsvSource\", String.class.getName())) //\n\t\t\t\t.execute();\n\t\tresults.testEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(displayName(\"[1] argument = f…\"), started())) //\n\t\t\t\t.haveExactly(1, event(displayName(\"[2] argument = b…\"), started()));\n\t}\n\n\t@Test\n\tvoid displayNamePatternFromConfiguration() {\n\t\tvar results = EngineTestKit.engine(new JupiterTestEngine()) //\n\t\t\t\t.configurationParameter(ParameterizedInvocationNameFormatter.DISPLAY_NAME_PATTERN_KEY, \"{index}\") //\n\t\t\t\t.selectors(selectMethod(TestCase.class, \"testWithCsvSource\", String.class.getName())) //\n\t\t\t\t.execute();\n\t\tresults.testEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(displayName(\"1\"), started())) //\n\t\t\t\t.haveExactly(1, event(displayName(\"2\"), started()));\n\t}\n\n\t@Test\n\tvoid failsWhenInvocationIsRequiredButNoArgumentSetsAreProvided() {\n\t\tvar results = execute(ZeroInvocationsTestCase.class, \"testThatRequiresInvocations\", String.class);\n\n\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1,\n\t\t\t\t\tevent(finishedWithFailure(instanceOf(TemplateInvocationValidationException.class), message(\n\t\t\t\t\t\t\"Configuration error: You must configure at least one set of arguments for this @ParameterizedTest\"))));\n\t}\n\n\t@Test\n\tvoid doesNotFailWhenInvocationIsNotRequiredAndNoArgumentSetsAreProvided() {\n\t\tvar results = execute(ZeroInvocationsTestCase.class, \"testThatDoesNotRequireInvocations\", String.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(3).succeeded(3));\n\t}\n\n\t@Test\n\tvoid failsWhenNoArgumentsSourceIsDeclared() {\n\t\tvar results = execute(ZeroInvocationsTestCase.class, \"testThatHasNoArgumentsSource\", String.class);\n\n\t\tresults.containerEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, //\n\t\t\t\t\tevent(displayName(\"testThatHasNoArgumentsSource(String)\"), finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: You must configure at least one arguments source for this @ParameterizedTest\"))));\n\t}\n\n\t@Test\n\tvoid executesWithDefaultLocaleConversionFormat() {\n\t\tvar results = execute(LocaleConversionTestCase.class, \"testWithBcp47\", Locale.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t}\n\n\t@Test\n\tvoid emitsWarningForNoLongerSupportedConfigurationParameter() {\n\t\tvar results = discoverTests(request -> request //\n\t\t\t\t.configurationParameter(\"junit.jupiter.params.arguments.conversion.locale.format\", \"iso_639\") //\n\t\t\t\t.selectors(selectMethod(LocaleConversionTestCase.class, \"testWithBcp47\", Locale.class)));\n\n\t\tassertThat(results.getDiscoveryIssues()) //\n\t\t\t\t.contains(DiscoveryIssue.create(Severity.WARNING, \"\"\"\n\t\t\t\t\t\tThe 'junit.jupiter.params.arguments.conversion.locale.format' configuration parameter \\\n\t\t\t\t\t\tis no longer supported. Please remove it from your configuration.\"\"\"));\n\t}\n\n\t@Test\n\tvoid executesWithCustomLocalConverterUsingIso639Format() {\n\t\tvar results = execute(LocaleConversionTestCase.class, \"testWithIso639\", Locale.class);\n\n\t\tresults.allEvents().assertStatistics(stats -> stats.started(4).succeeded(4));\n\t}\n\n\t@Test\n\tvoid reportsExceptionInStaticInitializersWithoutInvocationCountValidation() {\n\t\tvar results = executeTestsForClass(ExceptionInStaticInitializerTestCase.class);\n\n\t\tvar failure = results.containerEvents().stream() //\n\t\t\t\t.filter(finishedWithFailure()::matches) //\n\t\t\t\t.findAny() //\n\t\t\t\t.orElseThrow();\n\n\t\tvar throwable = failure.getRequiredPayload(TestExecutionResult.class).getThrowable().orElseThrow();\n\n\t\tassertThat(throwable) //\n\t\t\t\t.isInstanceOf(ExceptionInInitializerError.class) //\n\t\t\t\t.hasNoSuppressedExceptions();\n\t}\n\n\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\treturn execute(TestCase.class, methodName, methodParameterTypes);\n\t}\n\n\tprivate EngineExecutionResults execute(Class<?> testClass, String methodName, Class<?>... methodParameterTypes) {\n\t\treturn executeTests(selectMethod(testClass, methodName, ClassUtils.nullSafeToString(methodParameterTypes)));\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\t@Nested\n\tclass NullSourceIntegrationTests {\n\n\t\t@Test\n\t\tvoid executesWithNullSourceForString() {\n\t\t\tvar results = execute(\"testWithNullSourceForString\", String.class);\n\t\t\tresults.testEvents().failed().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = null\"), finishedWithFailure(message(\"null\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullSourceForStringAndTestInfo() {\n\t\t\tvar results = execute(\"testWithNullSourceForStringAndTestInfo\", String.class, TestInfo.class);\n\t\t\tresults.testEvents().failed().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = null\"), finishedWithFailure(message(\"null\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullSourceForNumber() {\n\t\t\tvar results = execute(\"testWithNullSourceForNumber\", Number.class);\n\t\t\tresults.testEvents().failed().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = null\"), finishedWithFailure(message(\"null\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithNullSourceWithZeroFormalParameters() {\n\t\t\tvar methodName = \"testWithNullSourceWithZeroFormalParameters\";\n\t\t\texecute(methodName).containerEvents().failed().assertEventsMatchExactly(//\n\t\t\t\tevent(container(methodName), //\n\t\t\t\t\tfinishedWithFailure(//\n\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class), //\n\t\t\t\t\t\tmessage(msg -> msg.matches(\n\t\t\t\t\t\t\t\"@NullSource cannot provide a null argument to method .+: no formal parameters declared.\")))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithNullSourceForPrimitive() {\n\t\t\tvar results = execute(\"testWithNullSourceForPrimitive\", int.class);\n\t\t\tresults.testEvents().failed().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = null\"),\n\t\t\t\tfinishedWithFailure(instanceOf(ParameterResolutionException.class), message(\n\t\t\t\t\t\"Error converting parameter at index 0: Cannot convert null to primitive value of type int\"))));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(NullSourceTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\t@Nested\n\tclass EmptySourceIntegrationTests {\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForString() {\n\t\t\tvar results = execute(\"testWithEmptySourceForString\", String.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = \\\"\\\"\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid testWithEmptySourceForStringFromAnnotationAttribute() {\n\t\t\tvar results = execute(\"testWithEmptySourceForStringFromAnnotationAttribute\", Object.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = \\\"\\\"\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForStringAndTestInfo() {\n\t\t\tvar results = execute(\"testWithEmptySourceForStringAndTestInfo\", String.class, TestInfo.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(\n\t\t\t\tevent(test(), displayName(\"[1] argument = \\\"\\\"\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 6.1\n\t\t */\n\t\t@Test\n\t\tvoid executesWithEmptySourceForIterable() {\n\t\t\tvar results = execute(\"testWithEmptySourceForIterable\", Iterable.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 6.1\n\t\t */\n\t\t@Test\n\t\tvoid executesWithEmptySourceForIterator() {\n\t\t\tvar results = execute(\"testWithEmptySourceForIterator\", Iterator.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 6.1\n\t\t */\n\t\t@Test\n\t\tvoid executesWithEmptySourceForListIterator() {\n\t\t\tvar results = execute(\"testWithEmptySourceForListIterator\", ListIterator.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.10\n\t\t */\n\t\t@Test\n\t\tvoid executesWithEmptySourceForCollection() {\n\t\t\tvar results = execute(\"testWithEmptySourceForCollection\", Collection.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForList() {\n\t\t\tvar results = execute(\"testWithEmptySourceForList\", List.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.10\n\t\t */\n\t\t@ParameterizedTest(name = \"{1}\")\n\t\t@CsvSource(textBlock = \"\"\"\n\t\t\t\ttestWithEmptySourceForArrayList,  java.util.ArrayList\n\t\t\t\ttestWithEmptySourceForLinkedList, java.util.LinkedList\n\t\t\t\t\"\"\")\n\t\tvoid executesWithEmptySourceForListSubtype(String methodName, Class<?> parameterType) {\n\t\t\tvar results = execute(methodName, parameterType);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForSet() {\n\t\t\tvar results = execute(\"testWithEmptySourceForSet\", Set.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.10\n\t\t */\n\t\t@ParameterizedTest(name = \"{1}\")\n\t\t@CsvSource(textBlock = \"\"\"\n\t\t\t\ttestWithEmptySourceForSortedSet,     java.util.SortedSet\n\t\t\t\ttestWithEmptySourceForNavigableSet,  java.util.NavigableSet\n\t\t\t\ttestWithEmptySourceForHashSet,       java.util.HashSet\n\t\t\t\ttestWithEmptySourceForTreeSet,       java.util.TreeSet\n\t\t\t\ttestWithEmptySourceForLinkedHashSet, java.util.LinkedHashSet\n\t\t\t\t\"\"\")\n\t\tvoid executesWithEmptySourceForSetSubtype(String methodName, Class<?> parameterType) {\n\t\t\tvar results = execute(methodName, parameterType);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForMap() {\n\t\t\tvar results = execute(\"testWithEmptySourceForMap\", Map.class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = {}\")));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.10\n\t\t */\n\t\t@ParameterizedTest(name = \"{1}\")\n\t\t@CsvSource(textBlock = \"\"\"\n\t\t\t\ttestWithEmptySourceForSortedMap,     java.util.SortedMap\n\t\t\t\ttestWithEmptySourceForNavigableMap,  java.util.NavigableMap\n\t\t\t\ttestWithEmptySourceForHashMap,       java.util.HashMap\n\t\t\t\ttestWithEmptySourceForTreeMap,       java.util.TreeMap\n\t\t\t\ttestWithEmptySourceForLinkedHashMap, java.util.LinkedHashMap\n\t\t\t\t\"\"\")\n\t\tvoid executesWithEmptySourceForMapSubtype(String methodName, Class<?> parameterType) {\n\t\t\tvar results = execute(methodName, parameterType);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = {}\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForOneDimensionalPrimitiveArray() {\n\t\t\tvar results = execute(\"testWithEmptySourceForOneDimensionalPrimitiveArray\", int[].class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForOneDimensionalStringArray() {\n\t\t\tvar results = execute(\"testWithEmptySourceForOneDimensionalStringArray\", String[].class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForTwoDimensionalPrimitiveArray() {\n\t\t\tvar results = execute(\"testWithEmptySourceForTwoDimensionalPrimitiveArray\", int[][].class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithEmptySourceForTwoDimensionalStringArray() {\n\t\t\tvar results = execute(\"testWithEmptySourceForTwoDimensionalStringArray\", String[][].class);\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(event(test(), displayName(\"[1] argument = []\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithEmptySourceWithZeroFormalParameters() {\n\t\t\tvar methodName = \"testWithEmptySourceWithZeroFormalParameters\";\n\t\t\texecute(methodName).containerEvents().failed().assertEventsMatchExactly(//\n\t\t\t\tevent(container(methodName), //\n\t\t\t\t\tfinishedWithFailure(//\n\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class), //\n\t\t\t\t\t\tmessage(msg -> msg.matches(\n\t\t\t\t\t\t\t\"@EmptySource cannot provide an empty argument to method .+: no formal parameters declared.\")))));\n\t\t}\n\n\t\t@ParameterizedTest(name = \"{1}\")\n\t\t@CsvSource(textBlock = \"\"\"\n\t\t\t\ttestWithEmptySourceForPrimitive,                int\n\t\t\t\ttestWithEmptySourceForUnsupportedReferenceType, java.lang.Integer\n\t\t\t\t\"\"\")\n\t\tvoid failsWithEmptySourceForUnsupportedType(String methodName, Class<?> parameterType) {\n\t\t\texecute(methodName, parameterType).containerEvents().failed().assertEventsMatchExactly(//\n\t\t\t\tevent(container(methodName), //\n\t\t\t\t\tfinishedWithFailure(//\n\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class), //\n\t\t\t\t\t\tmessage(msg -> msg.matches(\"@EmptySource cannot provide an empty argument to method .+: \\\\[\"\n\t\t\t\t\t\t\t\t+ parameterType.getName() + \"] is not a supported type.\"))//\n\t\t\t\t\t)));\n\t\t}\n\n\t\t@Test\n\t\tvoid testWithEmptySourceForUnsupportedReferenceTypeFromAttribute() {\n\t\t\tvar methodName = \"testWithEmptySourceForUnsupportedReferenceTypeFromAttribute\";\n\t\t\texecute(methodName, String.class).containerEvents().failed().assertEventsMatchExactly(//\n\t\t\t\tevent(container(methodName), //\n\t\t\t\t\tfinishedWithFailure(//\n\t\t\t\t\t\tinstanceOf(PreconditionViolationException.class), //\n\t\t\t\t\t\tmessage(\n\t\t\t\t\t\t\t\"@EmptySource cannot provide an empty argument for 'type': [java.lang.Integer] is not a supported type.\")//\n\t\t\t\t\t)));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(EmptySourceTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\t@Nested\n\tclass NullAndEmptySourceIntegrationTests {\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForString() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForString\", String.class);\n\t\t\tassertNullAndEmptyString(results);\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForStringAndTestInfo() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForStringAndTestInfo\", String.class, TestInfo.class);\n\t\t\tassertNullAndEmptyString(results);\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForList() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForList\", List.class);\n\t\t\tassertNullAndEmpty(results);\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForArrayList() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForArrayList\", ArrayList.class);\n\t\t\tassertNullAndEmpty(results);\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForOneDimensionalPrimitiveArray() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForOneDimensionalPrimitiveArray\", int[].class);\n\t\t\tassertNullAndEmpty(results);\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithNullAndEmptySourceForTwoDimensionalStringArray() {\n\t\t\tvar results = execute(\"testWithNullAndEmptySourceForTwoDimensionalStringArray\", String[][].class);\n\t\t\tassertNullAndEmpty(results);\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(NullAndEmptySourceTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\n\t\tprivate void assertNullAndEmptyString(EngineExecutionResults results) {\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(//\n\t\t\t\tevent(test(), displayName(\"[1] argument = null\")), //\n\t\t\t\tevent(test(), displayName(\"[2] argument = \\\"\\\"\"))//\n\t\t\t);\n\t\t}\n\n\t\tprivate void assertNullAndEmpty(EngineExecutionResults results) {\n\t\t\tresults.testEvents().succeeded().assertEventsMatchExactly(//\n\t\t\t\tevent(test(), displayName(\"[1] argument = null\")), //\n\t\t\t\tevent(test(), displayName(\"[2] argument = []\"))//\n\t\t\t);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass MethodSourceIntegrationTests {\n\n\t\t@Test\n\t\tvoid emptyMethodSource() {\n\t\t\texecute(\"emptyMethodSource\", String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"empty method source\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid oneDimensionalPrimitiveArray() {\n\t\t\texecute(\"oneDimensionalPrimitiveArray\", int.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"1\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"2\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid twoDimensionalPrimitiveArray() {\n\t\t\texecute(\"twoDimensionalPrimitiveArray\", int[].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[1, 2]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[3, 4]\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid oneDimensionalObjectArray() {\n\t\t\texecute(\"oneDimensionalObjectArray\", Object.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid oneDimensionalStringArray() {\n\t\t\texecute(\"oneDimensionalStringArray\", String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"two\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid twoDimensionalObjectArray() {\n\t\t\texecute(\"twoDimensionalObjectArray\", String.class, int.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:4\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid twoDimensionalStringArray() {\n\t\t\texecute(\"twoDimensionalStringArray\", String.class, String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:two\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:four\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfOneDimensionalPrimitiveArrays() {\n\t\t\texecute(\"streamOfOneDimensionalPrimitiveArrays\", int[].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[1, 2]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[3, 4]\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfTwoDimensionalPrimitiveArrays() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\"streamOfTwoDimensionalPrimitiveArrays\");\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\"streamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays\");\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfTwoDimensionalPrimitiveArraysWrappedInArguments() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\"streamOfTwoDimensionalPrimitiveArraysWrappedInArguments\");\n\t\t}\n\n\t\tprivate void assertStreamOfTwoDimensionalPrimitiveArrays(String methodName) {\n\t\t\texecute(methodName, int[][].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[1, 2], [3, 4]]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[5, 6], [7, 8]]\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfOneDimensionalObjectArrays() {\n\t\t\texecute(\"streamOfOneDimensionalObjectArrays\", String.class, int.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:4\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.3.2\n\t\t */\n\t\t@Test\n\t\tvoid streamOfTwoDimensionalObjectArrays() {\n\t\t\texecute(\"streamOfTwoDimensionalObjectArrays\", Object[][].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[one, 2], [three, 4]]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[five, 6], [seven, 8]]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid reportsContainerWithAssumptionFailureInMethodSourceAsAborted() {\n\t\t\texecute(\"assumptionFailureInMethodSourceFactoryMethod\", String.class).allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(\"test-template:assumptionFailureInMethodSourceFactoryMethod\"), //\n\t\t\t\t\t\tabortedWithReason(instanceOf(TestAbortedException.class), message(\"nothing to test\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid namedParameters() {\n\t\t\texecute(\"namedParameters\", String.class).allEvents().assertThatEvents() //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"cool name\"), finishedWithFailure(message(\"parameter value\")))) //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"default name\"), finishedWithFailure(message(\"default name\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid nameParametersAlias() {\n\t\t\texecute(\"namedParametersAlias\", String.class).allEvents().assertThatEvents() //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"cool name\"), finishedWithFailure(message(\"parameter value\")))) //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"default name\"), finishedWithFailure(message(\"default name\"))));\n\t\t}\n\n\t\t/**\n\t\t * @since 5.9.1\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3001\">GitHub - Issue #3001</a>\n\t\t */\n\t\t@Test\n\t\tvoid duplicateMethodNames() {\n\t\t\t// It is sufficient to assert that 8 tests started and finished, because\n\t\t\t// without the fix for #3001 the 4 parameterized tests would fail. In\n\t\t\t// other words, we're not really testing the support for @RepeatedTest\n\t\t\t// and @TestFactory, but their presence also contributes to the bug\n\t\t\t// reported in #3001.\n\t\t\texecuteTestsForClass(DuplicateMethodNamesMethodSourceTestCase.class)//\n\t\t\t\t\t.testEvents()//\n\t\t\t\t\t.assertStatistics(stats -> stats.started(8).failed(0).finished(8));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(MethodSourceTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 5.11\n\t */\n\t@Nested\n\tclass FieldSourceIntegrationTests {\n\n\t\t@Test\n\t\tvoid oneDimensionalPrimitiveArray() {\n\t\t\texecute(\"oneDimensionalPrimitiveArray\", int.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"1\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"2\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid twoDimensionalPrimitiveArray() {\n\t\t\texecute(\"twoDimensionalPrimitiveArray\", int[].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[1, 2]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[3, 4]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid oneDimensionalObjectArray() {\n\t\t\texecute(\"oneDimensionalObjectArray\", Object.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid oneDimensionalStringArray() {\n\t\t\texecute(\"oneDimensionalStringArray\", String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"two\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid twoDimensionalObjectArray() {\n\t\t\texecute(\"twoDimensionalObjectArray\", String.class, int.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:4\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid twoDimensionalStringArray() {\n\t\t\texecute(\"twoDimensionalStringArray\", String.class, String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:two\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:four\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfOneDimensionalPrimitiveArrays() {\n\t\t\texecute(\"supplierOfStreamOfOneDimensionalPrimitiveArrays\", int[].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[1, 2]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[3, 4]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArrays() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\"supplierOfStreamOfTwoDimensionalPrimitiveArrays\");\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\n\t\t\t\t\"supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays\");\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments() {\n\t\t\tassertStreamOfTwoDimensionalPrimitiveArrays(\n\t\t\t\t\"supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments\");\n\t\t}\n\n\t\tprivate void assertStreamOfTwoDimensionalPrimitiveArrays(String methodName) {\n\t\t\texecute(methodName, int[][].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[1, 2], [3, 4]]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[5, 6], [7, 8]]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfOneDimensionalObjectArrays() {\n\t\t\texecute(\"supplierOfStreamOfOneDimensionalObjectArrays\", String.class, int.class).testEvents()//\n\t\t\t\t\t.assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"one:2\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"three:4\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid supplierOfStreamOfTwoDimensionalObjectArrays() {\n\t\t\texecute(\"supplierOfStreamOfTwoDimensionalObjectArrays\", Object[][].class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[one, 2], [three, 4]]\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"[[five, 6], [seven, 8]]\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid listOfNamedParameters() {\n\t\t\texecute(\"listOfNamedParameters\", String.class).allEvents().assertThatEvents() //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"cool name\"), finishedWithFailure(message(\"parameter value\")))) //\n\t\t\t\t\t.haveAtLeast(1,\n\t\t\t\t\t\tevent(test(), displayName(\"default name\"), finishedWithFailure(message(\"default name\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid nonStaticFieldInTopLevelTestClass() {\n\t\t\tClass<?> testClass = BaseLifecyclePerClassFieldSourceTestCase.class;\n\t\t\texecute(testClass, \"test\", String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"base-1\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"base-2\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid nonStaticFieldInSubclassTakesPrecedenceOverFieldInSuperclass() {\n\t\t\tClass<?> testClass = SubclassOfBaseLifecyclePerClassFieldSourceTestCase.class;\n\t\t\texecute(testClass, \"test\", String.class).testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"sub-1\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"sub-2\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid nonStaticFieldInNestedTestClass() {\n\t\t\tClass<?> testClass = EnclosingFieldSourceTestCase.NestedLifecyclePerClassFieldSourceTestCase.class;\n\t\t\texecute(testClass, \"nonStaticFieldSource\", String.class)//\n\t\t\t\t\t.testEvents().assertThatEvents()//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"apple\"))))//\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"banana\"))));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn execute(FieldSourceTestCase.class, methodName, methodParameterTypes);\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(Class<?> testClass, String methodName,\n\t\t\t\tClass<?>... methodParameterTypes) {\n\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(testClass, methodName, methodParameterTypes);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass UnusedArgumentsIntegrationTests {\n\n\t\t@Test\n\t\tvoid executesWithArgumentsSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(\"testWithTwoUnusedStringArgumentsProvider\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithCsvSourceContainingUnusedArguments() {\n\t\t\tvar results = execute(\"testWithCsvSourceContainingUnusedArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithCsvFileSourceContainingUnusedArguments() {\n\t\t\tvar results = execute(\"testWithCsvFileSourceContainingUnusedArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithMethodSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(\"testWithMethodSourceProvidingUnusedArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithFieldSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(\"testWithFieldSourceProvidingUnusedArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(UnusedArgumentsTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass UnusedArgumentsWithStrictArgumentsCountIntegrationTests {\n\n\t\t@Test\n\t\tvoid failsWithArgumentsSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithTwoUnusedStringArgumentsProvider\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedTest consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused1]\".formatted()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithMethodSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithMethodSourceProvidingUnusedArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedTest consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused1]\".formatted()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotationAttribute() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithStrictArgumentCountValidation\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedTest consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused1]\".formatted()))));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsWithCsvSourceUnusedArgumentsButExecutesRemainingArgumentsWhereThereIsNoUnusedArgument() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithCsvSourceContainingDifferentNumbersOfArguments\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedTest consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused1]\".formatted())))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithCsvSourceUnusedArgumentsAndArgumentCountValidationAnnotationAttribute() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithNoneArgumentCountValidation\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithMethodSourceProvidingUnusedArguments() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.STRICT, RepeatableSourcesTestCase.class,\n\t\t\t\t\"testWithRepeatableCsvSource\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] argument = a\"), finishedWithFailure(message(\"a\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = b\"), finishedWithFailure(message(\"b\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid evaluatesArgumentsAtMostOnce() {\n\t\t\tvar results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class,\n\t\t\t\t\"testWithEvaluationReportingArgumentsProvider\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(finishedWithFailure(message(\n\t\t\t\t\t\t\"Configuration error: @ParameterizedTest consumes 1 parameter but there were 2 arguments provided.%nNote: the provided arguments were [foo, unused]\".formatted()))));\n\t\t\tresults.allEvents().reportingEntryPublished().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(EventConditions.reportEntry(Map.of(\"evaluated\", \"true\"))));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(ArgumentCountValidationMode configurationValue, Class<?> javaClass,\n\t\t\t\tString methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn EngineTestKit.engine(new JupiterTestEngine()) //\n\t\t\t\t\t.selectors(selectMethod(javaClass, methodName, methodParameterTypes)) //\n\t\t\t\t\t.configurationParameter(ArgumentCountValidator.ARGUMENT_COUNT_VALIDATION_KEY,\n\t\t\t\t\t\tconfigurationValue.name().toLowerCase()) //\n\t\t\t\t\t.execute();\n\t\t}\n\t}\n\n\t@Nested\n\tclass RepeatableSourcesIntegrationTests {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableCsvFileSource\", \"testWithRepeatableCsvFileSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableCsvFileSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] column1 = foo, column2 = 1\"),\n\t\t\t\t\t\tfinishedWithFailure(message(\"foo 1\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[5] FRUIT = apple, RANK = 1\"),\n\t\t\t\t\t\tfinishedWithFailure(message(\"apple 1\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableCsvSource\", \"testWithRepeatableCsvSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableCsvSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] argument = a\"), finishedWithFailure(message(\"a\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = b\"), finishedWithFailure(message(\"b\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableMethodSource\", \"testWithRepeatableMethodSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableMethodSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = some\"), finishedWithFailure(message(\"some\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = other\"), finishedWithFailure(message(\"other\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableEnumSource\", \"testWithRepeatableEnumSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableEnumSource(String methodName) {\n\t\t\tvar results = execute(methodName, Action.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = FOO\"), finishedWithFailure(message(\"FOO\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = BAR\"), finishedWithFailure(message(\"BAR\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableValueSource\", \"testWithRepeatableValueSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableValueSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableFieldSource\", \"testWithRepeatableFieldSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableFieldSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = some\"), finishedWithFailure(message(\"some\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = other\"), finishedWithFailure(message(\"other\"))));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"testWithRepeatableArgumentsSource\",\n\t\t\t\t\"testWithRepeatableArgumentsSourceAsMetaAnnotation\" })\n\t\tvoid executesWithRepeatableArgumentsSource(String methodName) {\n\t\t\tvar results = execute(methodName, String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[1] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[2] argument = bar\"), finishedWithFailure(message(\"bar\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[3] argument = foo\"), finishedWithFailure(message(\"foo\")))) //\n\t\t\t\t\t.haveExactly(1,\n\t\t\t\t\t\tevent(test(), displayName(\"[4] argument = bar\"), finishedWithFailure(message(\"bar\"))));\n\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithSameRepeatableAnnotationMultipleTimes() {\n\t\t\tvar results = execute(\"testWithSameRepeatableAnnotationMultipleTimes\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(test(), started())) //\n\t\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"foo\"))));\n\t\t}\n\n\t\t@Test\n\t\tvoid executesWithDifferentRepeatableAnnotations() {\n\t\t\tvar results = execute(\"testWithDifferentRepeatableAnnotations\", String.class);\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[1] argument = a\"), finishedWithFailure(message(\"a\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = b\"), finishedWithFailure(message(\"b\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[3] argument = c\"), finishedWithFailure(message(\"c\")))) //\n\t\t\t\t\t.haveExactly(1, event(test(), displayName(\"[4] argument = d\"), finishedWithFailure(message(\"d\"))));\n\t\t}\n\n\t\tprivate EngineExecutionResults execute(String methodName, Class<?>... methodParameterTypes) {\n\t\t\treturn ParameterizedTestIntegrationTests.this.execute(RepeatableSourcesTestCase.class, methodName,\n\t\t\t\tmethodParameterTypes);\n\t\t}\n\t}\n\n\t@Test\n\tvoid closeAutoCloseableArgumentsAfterTest() {\n\t\tvar results = execute(\"testWithAutoCloseableArgument\", AutoCloseableArgument.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedSuccessfully()));\n\n\t\tassertEquals(2, AutoCloseableArgument.closeCounter);\n\t}\n\n\t@Test\n\tvoid doNotCloseAutoCloseableArgumentsAfterTestWhenDisabled() {\n\t\tvar results = execute(\"testWithAutoCloseableArgumentButDisabledCleanup\", AutoCloseableArgument.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedSuccessfully()));\n\n\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t}\n\n\t@Test\n\tvoid closeAutoCloseableArgumentsAfterTestDespiteEarlyFailure() {\n\t\tvar results = execute(FailureInBeforeEachTestCase.class, \"test\", AutoCloseableArgument.class);\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(message(\"beforeEach\"))));\n\n\t\tassertEquals(2, AutoCloseableArgument.closeCounter);\n\t}\n\n\t@Test\n\tvoid executesTwoIterationsBasedOnIterationAndUniqueIdSelector() {\n\t\tvar methodId = uniqueIdForTestTemplateMethod(TestCase.class, \"testWithThreeIterations(int)\");\n\t\tvar results = executeTests(selectUniqueId(appendTestTemplateInvocationSegment(methodId, 3)),\n\t\t\tselectIteration(selectMethod(TestCase.class, \"testWithThreeIterations\", \"int\"), 1));\n\n\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t.haveExactly(2, event(test(), finishedWithFailure())) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[2] argument = 3\"), finishedWithFailure())) //\n\t\t\t\t.haveExactly(1, event(test(), displayName(\"[3] argument = 5\"), finishedWithFailure()));\n\t}\n\n\t@Nested\n\tclass SpiParameterInjectionIntegrationTests {\n\n\t\t@Test\n\t\tvoid injectsParametersIntoArgumentsProviderConstructor() {\n\t\t\texecute(SpiParameterInjectionTestCase.class, \"argumentsProviderWithConstructorParameter\", String.class) //\n\t\t\t\t\t.testEvents() //\n\t\t\t\t\t.assertStatistics(it -> it.succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\tvoid injectsParametersIntoArgumentConverterConstructor() {\n\t\t\texecute(SpiParameterInjectionTestCase.class, \"argumentConverterWithConstructorParameter\", String.class) //\n\t\t\t\t\t.testEvents() //\n\t\t\t\t\t.assertStatistics(it -> it.succeeded(1));\n\t\t}\n\n\t\t@Test\n\t\tvoid injectsParametersIntoArgumentsAggregatorConstructor() {\n\t\t\texecute(SpiParameterInjectionTestCase.class, \"argumentsAggregatorWithConstructorParameter\", String.class) //\n\t\t\t\t\t.testEvents() //\n\t\t\t\t\t.assertStatistics(it -> it.succeeded(1));\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class TestCase {\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(TwoSingleStringArgumentsProvider.class)\n\t\tvoid testWithTwoSingleStringArgumentsProvider(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource({ \"foo\", \"bar\" })\n\t\tvoid testWithCsvSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(name = \"{0}\")\n\t\t@CsvSource({ \"'üñåé'\", \"'\\n'\", \"'\\r'\", \"'\\u0007'\", \"😱\", \"'Zero\\u200BWidth\\u200BSpaces'\" })\n\t\tvoid testWithCsvSourceAndSpecialCharacters(String argument) {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, name = \"{0} and {1}\")\n\t\t@CsvSource({ \"foo, 23\", \"bar, 42\" })\n\t\tvoid testWithCustomName(String argument, int i) {\n\t\t\tfail(argument + \", \" + i);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(shorts = { 1, 2 })\n\t\tvoid testWithPrimitiveWideningConversion(double num) {\n\t\t\tfail(\"num: \" + num);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = { \"book 1\", \"book 2\" })\n\t\tvoid testWithImplicitGenericConverter(Book book) {\n\t\t\tfail(book.title);\n\t\t}\n\n\t\t@ParameterizedTest(name = \"{0}\")\n\t\t@ValueSource(strings = { \"record 1\", \"record 2\" })\n\t\tvoid testWithImplicitGenericConverterWithCharSequenceConstructor(Record record) {\n\t\t\tfail(record.title.toString());\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = { \"O\", \"XXX\" })\n\t\tvoid testWithExplicitConverter(@ConvertWith(StringLengthConverter.class) int length) {\n\t\t\tfail(\"length: \" + length);\n\t\t}\n\n\t\t@ParameterizedTest(name = \"  \\t   \")\n\t\t@ValueSource(strings = \"not important\")\n\t\tvoid testWithEmptyName(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(ints = 42)\n\t\tvoid testWithErroneousConverter(@ConvertWith(ErroneousConverter.class) Object ignored) {\n\t\t\tfail(\"this should never be called\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, name = \"{0,number,#.####}\")\n\t\t@ValueSource(doubles = Math.PI)\n\t\tvoid testWithMessageFormat(double argument) {\n\t\t\tfail(String.valueOf(argument));\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource({ \"ab, cd\", \"ef, gh\" })\n\t\tvoid testWithAggregator(@AggregateWith(StringAggregator.class) String concatenation) {\n\t\t\tfail(\"concatenation: \" + concatenation);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource(value = { \" ab , cd\", \"ef ,gh\" }, ignoreLeadingAndTrailingWhitespace = false)\n\t\tvoid testWithIgnoreLeadingAndTrailingWhitespaceSetToFalseForCsvSource(String argument1, String argument2) {\n\t\t\tfail(\"arguments: '\" + argument1 + \"', '\" + argument2 + \"'\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource(value = { \" ab , cd\", \"ef ,gh\" }, ignoreLeadingAndTrailingWhitespace = true)\n\t\tvoid testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvSource(String argument1, String argument2) {\n\t\t\tfail(\"arguments: '\" + argument1 + \"', '\" + argument2 + \"'\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvFileSource(resources = \"provider/leading-trailing-spaces.csv\", ignoreLeadingAndTrailingWhitespace = false)\n\t\tvoid testWithIgnoreLeadingAndTrailingWhitespaceSetToFalseForCsvFileSource(String argument1, String argument2) {\n\t\t\tfail(\"arguments: '\" + argument1 + \"', '\" + argument2 + \"'\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvFileSource(resources = \"provider/leading-trailing-spaces.csv\", ignoreLeadingAndTrailingWhitespace = true)\n\t\tvoid testWithIgnoreLeadingAndTrailingWhitespaceSetToTrueForCsvFileSource(String argument1, String argument2) {\n\t\t\tfail(\"arguments: '\" + argument1 + \"', '\" + argument2 + \"'\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(AutoCloseableArgumentProvider.class)\n\t\tvoid testWithAutoCloseableArgument(AutoCloseableArgument autoCloseable) {\n\t\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, autoCloseArguments = false)\n\t\t@ArgumentsSource(AutoCloseableArgumentProvider.class)\n\t\tvoid testWithAutoCloseableArgumentButDisabledCleanup(AutoCloseableArgument autoCloseable) {\n\t\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(ints = { 2, 3, 5 })\n\t\tvoid testWithThreeIterations(int argument) {\n\t\t\tfail(\"argument: \" + argument);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class NullSourceTestCase {\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid testWithNullSourceForString(String argument) {\n\t\t\tfail(String.valueOf(argument));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid testWithNullSourceForStringAndTestInfo(String argument, TestInfo testInfo) {\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t\tfail(String.valueOf(argument));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid testWithNullSourceForNumber(Number argument) {\n\t\t\tfail(String.valueOf(argument));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid testWithNullSourceWithZeroFormalParameters() {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid testWithNullSourceForPrimitive(int argument) {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class EmptySourceTestCase {\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForString(String argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource(type = String.class)\n\t\tvoid testWithEmptySourceForStringFromAnnotationAttribute(Object argument) {\n\t\t\tassertThat(argument).asInstanceOf(InstanceOfAssertFactories.STRING).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForStringAndTestInfo(String argument, TestInfo testInfo) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForIterable(Iterable<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForIterator(Iterator<?> argument) {\n\t\t\tassertThat(argument).isExhausted();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForListIterator(ListIterator<?> argument) {\n\t\t\tassertThat(argument).isExhausted();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForCollection(Collection<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForList(List<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForArrayList(ArrayList<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForLinkedList(LinkedList<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForSet(Set<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForSortedSet(SortedSet<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForNavigableSet(NavigableSet<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForHashSet(HashSet<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForTreeSet(TreeSet<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForLinkedHashSet(LinkedHashSet<?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForMap(Map<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForSortedMap(SortedMap<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForNavigableMap(NavigableMap<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForHashMap(HashMap<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForTreeMap(TreeMap<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForLinkedHashMap(LinkedHashMap<?, ?> argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForOneDimensionalPrimitiveArray(int[] argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForOneDimensionalStringArray(String[] argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForTwoDimensionalPrimitiveArray(int[][] argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForTwoDimensionalStringArray(String[][] argument) {\n\t\t\tassertThat(argument).isEmpty();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceWithZeroFormalParameters() {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForPrimitive(int argument) {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource\n\t\tvoid testWithEmptySourceForUnsupportedReferenceType(Integer argument) {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@EmptySource(type = Integer.class)\n\t\tvoid testWithEmptySourceForUnsupportedReferenceTypeFromAttribute(String argument) {\n\t\t\tfail(\"should not have been executed\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\tstatic class NullAndEmptySourceTestCase {\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForString(String argument) {\n\t\t\tassertTrue(argument == null || argument.isEmpty());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForStringAndTestInfo(String argument, TestInfo testInfo) {\n\t\t\tassertTrue(argument == null || argument.isEmpty());\n\t\t\tassertThat(testInfo).isNotNull();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForList(List<?> argument) {\n\t\t\tassertTrue(argument == null || argument.isEmpty());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForArrayList(ArrayList<?> argument) {\n\t\t\tassertTrue(argument == null || argument.isEmpty());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForOneDimensionalPrimitiveArray(int[] argument) {\n\t\t\tassertTrue(argument == null || argument.length == 0);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullAndEmptySource\n\t\tvoid testWithNullAndEmptySourceForTwoDimensionalStringArray(String[][] argument) {\n\t\t\tassertTrue(argument == null || argument.length == 0);\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class MethodSourceTestCase {\n\n\t\t@Target(ElementType.METHOD)\n\t\t@Retention(RUNTIME)\n\t\t@ParameterizedTest(quoteTextArguments = false, name = \"{arguments}\")\n\t\t@MethodSource\n\t\t@interface MethodSourceTest {\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(0)\n\t\tvoid emptyMethodSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(1)\n\t\tvoid oneDimensionalPrimitiveArray(int x) {\n\t\t\tfail(\"\" + x);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(2)\n\t\tvoid twoDimensionalPrimitiveArray(int[] array) {\n\t\t\tfail(Arrays.toString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(3)\n\t\tvoid oneDimensionalObjectArray(Object o) {\n\t\t\tfail(\"\" + o);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(4)\n\t\tvoid oneDimensionalStringArray(String s) {\n\t\t\tfail(s);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(5)\n\t\tvoid twoDimensionalObjectArray(String s, int x) {\n\t\t\tfail(s + \":\" + x);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(6)\n\t\tvoid twoDimensionalStringArray(String s1, String s2) {\n\t\t\tfail(s1 + \":\" + s2);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(7)\n\t\tvoid streamOfOneDimensionalPrimitiveArrays(int[] array) {\n\t\t\tfail(Arrays.toString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(8)\n\t\tvoid streamOfTwoDimensionalPrimitiveArrays(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(9)\n\t\tvoid streamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(10)\n\t\tvoid streamOfTwoDimensionalPrimitiveArraysWrappedInArguments(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(11)\n\t\tvoid streamOfOneDimensionalObjectArrays(String s, int x) {\n\t\t\tfail(s + \":\" + x);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(12)\n\t\tvoid streamOfTwoDimensionalObjectArrays(Object[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(13)\n\t\tvoid namedParameters(String string) {\n\t\t\tfail(string);\n\t\t}\n\n\t\t@MethodSourceTest\n\t\t@Order(14)\n\t\tvoid namedParametersAlias(String string) {\n\t\t\tfail(string);\n\t\t}\n\n\t\t// ---------------------------------------------------------------------\n\n\t\tstatic Stream<Arguments> emptyMethodSource() {\n\t\t\treturn Stream.of(arguments(\"empty method source\"));\n\t\t}\n\n\t\tstatic int[] oneDimensionalPrimitiveArray() {\n\t\t\treturn new int[] { 1, 2 };\n\t\t}\n\n\t\tstatic int[][] twoDimensionalPrimitiveArray() {\n\t\t\treturn new int[][] { { 1, 2 }, { 3, 4 } };\n\t\t}\n\n\t\tstatic Object[] oneDimensionalObjectArray() {\n\t\t\treturn new Object[] { \"one\", 2, \"three\" };\n\t\t}\n\n\t\tstatic Object[] oneDimensionalStringArray() {\n\t\t\treturn new Object[] { \"one\", \"two\" };\n\t\t}\n\n\t\tstatic Object[][] twoDimensionalObjectArray() {\n\t\t\treturn new Object[][] { { \"one\", 2 }, { \"three\", 4 } };\n\t\t}\n\n\t\tstatic String[][] twoDimensionalStringArray() {\n\t\t\treturn new String[][] { { \"one\", \"two\" }, { \"three\", \"four\" } };\n\t\t}\n\n\t\tstatic Stream<int[]> streamOfOneDimensionalPrimitiveArrays() {\n\t\t\treturn Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 });\n\t\t}\n\n\t\tstatic Stream<int[][]> streamOfTwoDimensionalPrimitiveArrays() {\n\t\t\treturn Stream.of(new int[][] { { 1, 2 }, { 3, 4 } }, new int[][] { { 5, 6 }, { 7, 8 } });\n\t\t}\n\n\t\tstatic Stream<Object[]> streamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays() {\n\t\t\treturn Stream.of(new Object[] { new int[][] { { 1, 2 }, { 3, 4 } } },\n\t\t\t\tnew Object[] { new int[][] { { 5, 6 }, { 7, 8 } } });\n\t\t}\n\n\t\tstatic Stream<Arguments> streamOfTwoDimensionalPrimitiveArraysWrappedInArguments() {\n\t\t\treturn Stream.of(arguments((Object) new int[][] { { 1, 2 }, { 3, 4 } }),\n\t\t\t\targuments((Object) new int[][] { { 5, 6 }, { 7, 8 } }));\n\t\t}\n\n\t\tstatic Stream<Object[]> streamOfOneDimensionalObjectArrays() {\n\t\t\treturn Stream.of(new Object[] { \"one\", 2 }, new Object[] { \"three\", 4 });\n\t\t}\n\n\t\tstatic Stream<Object[][]> streamOfTwoDimensionalObjectArrays() {\n\t\t\treturn Stream.of(new Object[][] { { \"one\", 2 }, { \"three\", 4 } },\n\t\t\t\tnew Object[][] { { \"five\", 6 }, { \"seven\", 8 } });\n\t\t}\n\n\t\tstatic Stream<Arguments> namedParameters() {\n\t\t\treturn Stream.of(arguments(Named.of(\"cool name\", \"parameter value\")), arguments(\"default name\"));\n\t\t}\n\n\t\tstatic Stream<Arguments> namedParametersAlias() {\n\t\t\treturn Stream.of(arguments(named(\"cool name\", \"parameter value\")), arguments(\"default name\"));\n\t\t}\n\n\t\t// ---------------------------------------------------------------------\n\n\t\t@MethodSourceTest\n\t\tvoid assumptionFailureInMethodSourceFactoryMethod(String test) {\n\t\t}\n\n\t\tstatic List<String> assumptionFailureInMethodSourceFactoryMethod() {\n\t\t\tAssumptions.abort(\"nothing to test\");\n\t\t\treturn List.of();\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 5.9.1\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3001\">GitHub - Issue #3001</a>\n\t */\n\tstatic class DuplicateMethodNamesMethodSourceTestCase {\n\n\t\t@ParameterizedTest\n\t\t@MethodSource\n\t\tvoid test(String value) {\n\t\t\ttest(1, value);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@MethodSource(\"test\")\n\t\tvoid anotherTest(String value) {\n\t\t\tassertTrue(test(value, 1));\n\t\t}\n\n\t\t@RepeatedTest(2)\n\t\tvoid test(TestReporter testReporter) {\n\t\t\tassertNotNull(testReporter);\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> test(TestInfo testInfo) {\n\t\t\treturn test().map(value -> dynamicTest(value, () -> test(1, value)));\n\t\t}\n\n\t\t// neither a test method nor a factory method.\n\t\t// intentionally void.\n\t\tprivate void test(int expectedLength, String value) {\n\t\t\tassertEquals(expectedLength, value.length());\n\t\t}\n\n\t\t// neither a test method nor a factory method.\n\t\t// intentionally non-void and also not convertible to a Stream.\n\t\tprivate boolean test(String value, int expectedLength) {\n\t\t\treturn (value.length() == expectedLength);\n\t\t}\n\n\t\t// legitimate factory method.\n\t\tprivate static Stream<String> test() {\n\t\t\treturn Stream.of(\"a\", \"b\");\n\t\t}\n\n\t}\n\n\t@TestMethodOrder(OrderAnnotation.class)\n\tstatic class FieldSourceTestCase {\n\n\t\t@Target(ElementType.METHOD)\n\t\t@Retention(RUNTIME)\n\t\t@ParameterizedTest(quoteTextArguments = false, name = \"{arguments}\")\n\t\t@FieldSource\n\t\t@interface FieldSourceTest {\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(1)\n\t\tvoid oneDimensionalPrimitiveArray(int x) {\n\t\t\tfail(\"\" + x);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(2)\n\t\tvoid twoDimensionalPrimitiveArray(int[] array) {\n\t\t\tfail(Arrays.toString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(3)\n\t\tvoid oneDimensionalObjectArray(Object o) {\n\t\t\tfail(\"\" + o);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(4)\n\t\tvoid oneDimensionalStringArray(String s) {\n\t\t\tfail(s);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(5)\n\t\tvoid twoDimensionalObjectArray(String s, int x) {\n\t\t\tfail(s + \":\" + x);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(6)\n\t\tvoid twoDimensionalStringArray(String s1, String s2) {\n\t\t\tfail(s1 + \":\" + s2);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(7)\n\t\tvoid supplierOfStreamOfOneDimensionalPrimitiveArrays(int[] array) {\n\t\t\tfail(Arrays.toString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(8)\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArrays(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(9)\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(10)\n\t\tvoid supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments(int[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(11)\n\t\tvoid supplierOfStreamOfOneDimensionalObjectArrays(String s, int x) {\n\t\t\tfail(s + \":\" + x);\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(12)\n\t\tvoid supplierOfStreamOfTwoDimensionalObjectArrays(Object[][] array) {\n\t\t\tfail(Arrays.deepToString(array));\n\t\t}\n\n\t\t@FieldSourceTest\n\t\t@Order(13)\n\t\tvoid listOfNamedParameters(String string) {\n\t\t\tfail(string);\n\t\t}\n\n\t\t// ---------------------------------------------------------------------\n\n\t\tstatic int[] oneDimensionalPrimitiveArray = new int[] { 1, 2 };\n\n\t\tstatic int[][] twoDimensionalPrimitiveArray = new int[][] { { 1, 2 }, { 3, 4 } };\n\n\t\tstatic Object[] oneDimensionalObjectArray = new Object[] { \"one\", 2, \"three\" };\n\n\t\tstatic Object[] oneDimensionalStringArray = new Object[] { \"one\", \"two\" };\n\n\t\tstatic Object[][] twoDimensionalObjectArray = new Object[][] { { \"one\", 2 }, { \"three\", 4 } };\n\n\t\tstatic String[][] twoDimensionalStringArray = new String[][] { { \"one\", \"two\" }, { \"three\", \"four\" } };\n\n\t\tstatic Supplier<Stream<int[]>> supplierOfStreamOfOneDimensionalPrimitiveArrays = //\n\t\t\t() -> Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 });\n\n\t\tstatic Supplier<Stream<int[][]>> supplierOfStreamOfTwoDimensionalPrimitiveArrays = //\n\t\t\t() -> Stream.of(new int[][] { { 1, 2 }, { 3, 4 } }, new int[][] { { 5, 6 }, { 7, 8 } });\n\n\t\tstatic Supplier<Stream<Object[]>> supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInObjectArrays = () -> Stream.of(\n\t\t\tnew Object[] { new int[][] { { 1, 2 }, { 3, 4 } } }, new Object[] { new int[][] { { 5, 6 }, { 7, 8 } } });\n\n\t\tstatic Supplier<Stream<Arguments>> supplierOfStreamOfTwoDimensionalPrimitiveArraysWrappedInArguments = () -> Stream.of(\n\t\t\targuments((Object) new int[][] { { 1, 2 }, { 3, 4 } }),\n\t\t\targuments((Object) new int[][] { { 5, 6 }, { 7, 8 } }));\n\n\t\tstatic Supplier<Stream<Object[]>> supplierOfStreamOfOneDimensionalObjectArrays = () -> Stream.of(\n\t\t\tnew Object[] { \"one\", 2 }, new Object[] { \"three\", 4 });\n\n\t\tstatic Supplier<Stream<Object[][]>> supplierOfStreamOfTwoDimensionalObjectArrays = () -> Stream.of(\n\t\t\tnew Object[][] { { \"one\", 2 }, { \"three\", 4 } }, new Object[][] { { \"five\", 6 }, { \"seven\", 8 } });\n\n\t\tstatic List<Arguments> listOfNamedParameters = //\n\t\t\tList.of(arguments(named(\"cool name\", \"parameter value\")), arguments(\"default name\"));\n\n\t}\n\n\t@TestInstance(PER_CLASS)\n\tstatic class BaseLifecyclePerClassFieldSourceTestCase {\n\n\t\tfinal List<String> field = List.of(\"base-1\", \"base-2\");\n\n\t\t@ParameterizedTest\n\t\t@FieldSource(\"field\")\n\t\tvoid test(String value) {\n\t\t\tfail(value);\n\t\t}\n\t}\n\n\tstatic class SubclassOfBaseLifecyclePerClassFieldSourceTestCase extends BaseLifecyclePerClassFieldSourceTestCase {\n\n\t\tfinal List<String> field = List.of(\"sub-1\", \"sub-2\");\n\n\t\t@ParameterizedTest\n\t\t@FieldSource(\"field\")\n\t\t@Override\n\t\tvoid test(String value) {\n\t\t\tfail(value);\n\t\t}\n\t}\n\n\tstatic class EnclosingFieldSourceTestCase {\n\n\t\t@Nested\n\t\t@TestInstance(Lifecycle.PER_CLASS)\n\t\tclass NestedLifecyclePerClassFieldSourceTestCase {\n\n\t\t\t// Non-static field\n\t\t\tfinal List<String> fruits = List.of(\"apple\", \"banana\");\n\n\t\t\t@ParameterizedTest\n\t\t\t@FieldSource(\"fruits\")\n\t\t\tvoid nonStaticFieldSource(String fruit) {\n\t\t\t\tfail(fruit);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class UnusedArgumentsTestCase {\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(TwoUnusedStringArgumentsProvider.class)\n\t\tvoid testWithTwoUnusedStringArgumentsProvider(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource({ \"foo, unused1\", \"bar, unused2\" })\n\t\tvoid testWithCsvSourceContainingUnusedArguments(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvFileSource(resources = \"two-column.csv\")\n\t\tvoid testWithCsvFileSourceContainingUnusedArguments(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"unusedArgumentsProviderMethod\")\n\t\tvoid testWithMethodSourceProvidingUnusedArguments(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\tstatic Stream<Arguments> unusedArgumentsProviderMethod() {\n\t\t\treturn Stream.of(arguments(\"foo\", \"unused1\"), arguments(\"bar\", \"unused2\"));\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@FieldSource(\"unusedArgumentsProviderField\")\n\t\tvoid testWithFieldSourceProvidingUnusedArguments(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\tstatic Supplier<Stream<Arguments>> unusedArgumentsProviderField = //\n\t\t\t() -> Stream.of(arguments(\"foo\", \"unused1\"), arguments(\"bar\", \"unused2\"));\n\n\t\t@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)\n\t\t@CsvSource({ \"foo, unused1\" })\n\t\tvoid testWithStrictArgumentCountValidation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, argumentCountValidation = ArgumentCountValidationMode.NONE)\n\t\t@CsvSource({ \"foo, unused1\" })\n\t\tvoid testWithNoneArgumentCountValidation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource({ \"foo, unused1\", \"bar\" })\n\t\tvoid testWithCsvSourceContainingDifferentNumbersOfArguments(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ArgumentsSource(EvaluationReportingArgumentsProvider.class)\n\t\tvoid testWithEvaluationReportingArgumentsProvider(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\tprivate static class EvaluationReportingArgumentsProvider implements ArgumentsProvider {\n\n\t\t\t@Override\n\t\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.of(() -> {\n\t\t\t\t\tcontext.publishReportEntry(\"evaluated\", \"true\");\n\t\t\t\t\treturn List.of(\"foo\", \"unused\").toArray();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class LifecycleTestCase {\n\n\t\tprivate static final List<String> lifecycleEvents = new ArrayList<>();\n\t\tprivate static final Set<String> testMethods = new LinkedHashSet<>();\n\n\t\tLifecycleTestCase(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"constructor:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"beforeAll:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@AfterAll\n\t\tstatic void afterAll(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"afterAll:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@BeforeEach\n\t\tvoid beforeEach(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"beforeEach:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach(TestInfo testInfo) {\n\t\t\tlifecycleEvents.add(\"afterEach:\" + testInfo.getDisplayName());\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"providerMethod\")\n\t\tvoid test1(String argument, TestInfo testInfo) {\n\t\t\tperformTest(argument, testInfo);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"providerMethod\")\n\t\tvoid test2(String argument, TestInfo testInfo) {\n\t\t\tperformTest(argument, testInfo);\n\t\t}\n\n\t\tprivate void performTest(String argument, TestInfo testInfo) {\n\t\t\tvar testMethod = testInfo.getTestMethod().orElseThrow().getName();\n\t\t\ttestMethods.add(testMethod);\n\t\t\tlifecycleEvents.add(testMethod + \":\" + testInfo.getDisplayName());\n\t\t\tfail(argument);\n\t\t}\n\n\t\tstatic Stream<String> providerMethod() {\n\t\t\tlifecycleEvents.add(\"providerMethod\");\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\n\t}\n\n\tstatic class RepeatableSourcesTestCase {\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvFileSource(resources = \"two-column.csv\")\n\t\t@CsvFileSource(resources = \"two-column-with-headers.csv\", delimiter = '|', useHeadersInDisplayName = true, nullValues = \"NIL\")\n\t\tvoid testWithRepeatableCsvFileSource(String column1, String column2) {\n\t\t\tfail(\"%s %s\".formatted(column1, column2));\n\t\t}\n\n\t\t@CsvFileSource(resources = \"two-column.csv\")\n\t\t@CsvFileSource(resources = \"two-column-with-headers.csv\", delimiter = '|', useHeadersInDisplayName = true, nullValues = \"NIL\")\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoCsvFileSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoCsvFileSources\n\t\tvoid testWithRepeatableCsvFileSourceAsMetaAnnotation(String column1, String column2) {\n\t\t\tfail(\"%s %s\".formatted(column1, column2));\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@CsvSource({ \"a\" })\n\t\t@CsvSource({ \"b\" })\n\t\tvoid testWithRepeatableCsvSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@CsvSource({ \"a\" })\n\t\t@CsvSource({ \"b\" })\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoCsvSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoCsvSources\n\t\tvoid testWithRepeatableCsvSourceAsMetaAnnotation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@EnumSource(SmartAction.class)\n\t\t@EnumSource(QuickAction.class)\n\t\tvoid testWithRepeatableEnumSource(Action argument) {\n\t\t\tfail(argument.toString());\n\t\t}\n\n\t\t@EnumSource(SmartAction.class)\n\t\t@EnumSource(QuickAction.class)\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoEnumSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoEnumSources\n\t\tvoid testWithRepeatableEnumSourceAsMetaAnnotation(Action argument) {\n\t\t\tfail(argument.toString());\n\t\t}\n\n\t\tinterface Action {\n\t\t}\n\n\t\tprivate enum SmartAction implements Action {\n\t\t\tFOO\n\t\t}\n\n\t\tprivate enum QuickAction implements Action {\n\t\t\tBAR\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"someArgumentsMethodSource\")\n\t\t@MethodSource(\"otherArgumentsMethodSource\")\n\t\tvoid testWithRepeatableMethodSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@MethodSource(\"someArgumentsMethodSource\")\n\t\t@MethodSource(\"otherArgumentsMethodSource\")\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoMethodSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoMethodSources\n\t\tvoid testWithRepeatableMethodSourceAsMetaAnnotation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\tpublic static Stream<Arguments> someArgumentsMethodSource() {\n\t\t\treturn Stream.of(Arguments.of(\"some\"));\n\t\t}\n\n\t\tpublic static Stream<Arguments> otherArgumentsMethodSource() {\n\t\t\treturn Stream.of(Arguments.of(\"other\"));\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@FieldSource(\"someArgumentsContainer\")\n\t\t@FieldSource(\"otherArgumentsContainer\")\n\t\tvoid testWithRepeatableFieldSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@FieldSource(\"someArgumentsContainer\")\n\t\t@FieldSource(\"otherArgumentsContainer\")\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoFieldSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoFieldSources\n\t\tvoid testWithRepeatableFieldSourceAsMetaAnnotation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\tstatic List<String> someArgumentsContainer = List.of(\"some\");\n\t\tstatic List<String> otherArgumentsContainer = List.of(\"other\");\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"bar\")\n\t\tvoid testWithRepeatableValueSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"bar\")\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoValueSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoValueSources\n\t\tvoid testWithRepeatableValueSourceAsMetaAnnotation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"foo\")\n\t\t@ValueSource(strings = \"foo\")\n\t\tvoid testWithSameRepeatableAnnotationMultipleTimes(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"a\")\n\t\t@ValueSource(strings = \"b\")\n\t\t@CsvSource({ \"c\" })\n\t\t@CsvSource({ \"d\" })\n\t\tvoid testWithDifferentRepeatableAnnotations(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(TwoSingleStringArgumentsProvider.class)\n\t\t@ArgumentsSource(TwoUnusedStringArgumentsProvider.class)\n\t\tvoid testWithRepeatableArgumentsSource(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\n\t\t@ArgumentsSource(TwoSingleStringArgumentsProvider.class)\n\t\t@ArgumentsSource(TwoUnusedStringArgumentsProvider.class)\n\t\t@Retention(RUNTIME)\n\t\t@interface TwoArgumentsSources {\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@TwoArgumentsSources\n\t\tvoid testWithRepeatableArgumentsSourceAsMetaAnnotation(String argument) {\n\t\t\tfail(argument);\n\t\t}\n\t}\n\n\tstatic class SpiParameterInjectionTestCase {\n\n\t\t@RegisterExtension\n\t\tstatic final ParameterResolver spiParameterResolver = new ParameterResolver() {\n\n\t\t\t@Override\n\t\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\treturn parameterContext.getDeclaringExecutable() instanceof Constructor //\n\t\t\t\t\t\t&& String.class.equals(parameterContext.getParameter().getType());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\t\tthrows ParameterResolutionException {\n\t\t\t\treturn \"resolved value\";\n\t\t\t}\n\t\t};\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(ArgumentsProviderWithConstructorParameter.class)\n\t\tvoid argumentsProviderWithConstructorParameter(String argument) {\n\t\t\tassertEquals(\"resolved value\", argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"value\")\n\t\tvoid argumentConverterWithConstructorParameter(\n\t\t\t\t@ConvertWith(ArgumentConverterWithConstructorParameter.class) String argument) {\n\t\t\tassertEquals(\"resolved value\", argument);\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"value\")\n\t\tvoid argumentsAggregatorWithConstructorParameter(\n\t\t\t\t@AggregateWith(ArgumentsAggregatorWithConstructorParameter.class) String argument) {\n\t\t\tassertEquals(\"resolved value\", argument);\n\t\t}\n\n\t\trecord ArgumentsProviderWithConstructorParameter(String value) implements ArgumentsProvider {\n\n\t\t\t@Override\n\t\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\t\tExtensionContext context) {\n\t\t\t\treturn Stream.of(arguments(value));\n\t\t\t}\n\t\t}\n\n\t\trecord ArgumentConverterWithConstructorParameter(String value) implements ArgumentConverter {\n\n\t\t\t@Override\n\t\t\tpublic Object convert(@Nullable Object source, ParameterContext context)\n\t\t\t\t\tthrows ArgumentConversionException {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\n\t\tstatic class ArgumentsAggregatorWithConstructorParameter extends SimpleArgumentsAggregator {\n\n\t\t\tprivate final String value;\n\n\t\t\tArgumentsAggregatorWithConstructorParameter(String value) {\n\t\t\t\tthis.value = value;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\t\t\t\treturn this.value;\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class ZeroInvocationsTestCase {\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"zeroArgumentsProvider\")\n\t\tvoid testThatRequiresInvocations(String argument) {\n\t\t\tfail(\"This test should not be executed, because no arguments are provided.\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, allowZeroInvocations = true)\n\t\t@MethodSource(\"zeroArgumentsProvider\")\n\t\tvoid testThatDoesNotRequireInvocations(String argument) {\n\t\t\tfail(\"This test should not be executed, because no arguments are provided.\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false, allowZeroInvocations = true)\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\tvoid testThatHasNoArgumentsSource(String argument) {\n\t\t\tfail(\"This test should not be executed, because no arguments source is declared.\");\n\t\t}\n\n\t\tpublic static Stream<Arguments> zeroArgumentsProvider() {\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tstatic class LocaleConversionTestCase {\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"en-US\")\n\t\tvoid testWithBcp47(Locale locale) {\n\t\t\tassertEquals(\"en\", locale.getLanguage());\n\t\t\tassertEquals(\"US\", locale.getCountry());\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ValueSource(strings = \"en-US\")\n\t\tvoid testWithIso639(@ConvertWith(Iso639Converter.class) Locale locale) {\n\t\t\tassertEquals(\"en-us\", locale.getLanguage());\n\t\t\tassertEquals(\"\", locale.getCountry());\n\t\t}\n\n\t\t@NullMarked\n\t\tstatic class Iso639Converter extends TypedArgumentConverter<String, Locale> {\n\n\t\t\tIso639Converter() {\n\t\t\t\tsuper(String.class, Locale.class);\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"deprecation\")\n\t\t\t@Override\n\t\t\tprotected Locale convert(@Nullable String source) throws ArgumentConversionException {\n\t\t\t\treturn new Locale(requireNonNull(source));\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate static class TwoSingleStringArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(arguments(\"foo\"), arguments(\"bar\"));\n\t\t}\n\t}\n\n\tprivate static class TwoUnusedStringArgumentsProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(arguments(\"foo\", \"unused1\"), arguments(\"bar\", \"unused2\"));\n\t\t}\n\t}\n\n\tprivate static class StringLengthConverter implements ArgumentConverter {\n\n\t\t@Override\n\t\tpublic Object convert(@Nullable Object source, ParameterContext context) throws ArgumentConversionException {\n\t\t\treturn String.valueOf(source).length();\n\t\t}\n\t}\n\n\tprivate static class StringAggregator extends SimpleArgumentsAggregator {\n\n\t\t@Override\n\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\t\t\treturn accessor.getString(0) + accessor.getString(1);\n\t\t}\n\t}\n\n\tprivate static class ErroneousConverter implements ArgumentConverter {\n\n\t\t@Override\n\t\tpublic Object convert(@Nullable Object source, ParameterContext context) throws ArgumentConversionException {\n\t\t\tthrow new ArgumentConversionException(\"something went horribly wrong\");\n\t\t}\n\t}\n\n\tprivate static class AutoCloseableArgumentProvider implements ArgumentsProvider {\n\n\t\t@Override\n\t\tpublic Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context) {\n\t\t\treturn Stream.of(arguments(new AutoCloseableArgument(), Named.of(\"unused\", new AutoCloseableArgument())));\n\t\t}\n\t}\n\n\tstatic class AutoCloseableArgument implements AutoCloseable {\n\n\t\tstatic int closeCounter = 0;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tcloseCounter++;\n\t\t}\n\t}\n\n\tstatic class Book {\n\n\t\tprivate final String title;\n\n\t\tprivate Book(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\tstatic Book factory(String title) {\n\t\t\treturn new Book(title);\n\t\t}\n\t}\n\n\trecord Record(CharSequence title) {\n\t}\n\n\tstatic class FailureInBeforeEachTestCase {\n\n\t\t@BeforeEach\n\t\tvoid beforeEach() {\n\t\t\tfail(\"beforeEach\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@ArgumentsSource(AutoCloseableArgumentProvider.class)\n\t\tvoid test(AutoCloseableArgument autoCloseable) {\n\t\t\tassertNotNull(autoCloseable);\n\t\t\tassertEquals(0, AutoCloseableArgument.closeCounter);\n\t\t}\n\t}\n\n\tstatic class ExceptionInStaticInitializerTestCase {\n\n\t\tstatic {\n\t\t\t//noinspection ConstantValue\n\t\t\tif (true) {\n\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t}\n\t\t}\n\n\t\tprivate static Stream<String> getArguments() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\n\t\t@ParameterizedTest(quoteTextArguments = false)\n\t\t@MethodSource(\"getArguments\")\n\t\tvoid test(String value) {\n\t\t\tfail(\"should not be called: \" + value);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * Test suite for JUnit Jupiter parameterized test support.\n *\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 5.0\n */\n@Suite\n@SelectPackages(\"org.junit.jupiter.params\")\n@IncludeClassNamePatterns(\".*Tests?\")\n@IncludeEngines(\"junit-jupiter\")\nclass ParameterizedTestSuite {\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/AggregatorIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.stream.Collectors.toMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.IntStream;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInfo;\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.converter.ArgumentConversionException;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n/**\n * Integration tests for {@link ArgumentsAccessor}, {@link AggregateWith},\n * and {@link ArgumentsAggregator}.\n *\n * @since 5.2\n */\npublic class AggregatorIntegrationTests {\n\n\t@ParameterizedTest\n\t@CsvSource({ //\n\t\t\t\"Jane, Doe, 1980-04-16, F, red\", //\n\t\t\t\"Jack, Smith, 2000-11-22, M, blue\" //\n\t})\n\tvoid personAggregator(@AggregateWith(PersonAggregator.class) Person person) {\n\t\ttestPersonAggregator(person);\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ //\n\t\t\t\"Jane, Doe, 1980-04-16, F, red\", //\n\t\t\t\"Jack, Smith, 2000-11-22, M, blue\" //\n\t})\n\tvoid personAggregatorRegisteredViaCustomAnnotation(@CsvToPerson Person person) {\n\t\ttestPersonAggregator(person);\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ //\n\t\t\t\"42 Peachtree Street, Atlanta, 30318\", //\n\t\t\t\"99 Peachtree Road, Atlanta, 30318\"//\n\t})\n\tvoid addressAggregator(@CsvToAddress Address address) {\n\t\ttestAddressAggregator(address);\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ //\n\t\t\t\"Jane, Doe, 1980-04-16, F, 42 Peachtree Street, Atlanta, 30318, red\", //\n\t\t\t\"Jack, Smith, 2000-11-22, M, 99 Peachtree Road, Atlanta, 30318, blue\"//\n\t})\n\tvoid personAggregatorAndAddressAggregator(@CsvToPerson Person person,\n\t\t\t@CsvToAddress @StartIndex(4) Address address) {\n\n\t\ttestPersonAggregator(person);\n\t\ttestAddressAggregator(address);\n\t}\n\n\t@ParameterizedTest(name = \"Mixed Mode #1: {arguments}\")\n\t@CsvSource({ //\n\t\t\t\"gh-11111111, Jane, Doe, 1980-04-16, F, 42 Peachtree Street, Atlanta, 30318, red\", //\n\t\t\t\"gh-22222222, Jack, Smith, 2000-11-22, M, 99 Peachtree Road, Atlanta, 30318, blue\"//\n\t})\n\tvoid mixedMode(String issueNumber, @CsvToPerson @StartIndex(1) Person person,\n\t\t\t@CsvToAddress @StartIndex(5) Address address, TestInfo testInfo) {\n\n\t\tassertThat(issueNumber).startsWith(\"gh-\");\n\t\ttestPersonAggregator(person);\n\t\ttestAddressAggregator(address);\n\t\tassertThat(testInfo.getDisplayName()).startsWith(\"Mixed Mode #1\");\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"cat, bird, mouse\", \"mouse, cat, bird\", \"mouse, bird, cat\" })\n\tvoid mapAggregator(@AggregateWith(MapAggregator.class) Map<String, Integer> map) {\n\t\tassertThat(map).containsOnly(entry(\"cat\", 3), entry(\"bird\", 4), entry(\"mouse\", 5));\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid argumentsAccessor(ArgumentsAccessor arguments) {\n\t\tassertEquals(55, IntStream.range(0, arguments.size()).map(arguments::getInteger).sum());\n\t}\n\n\t@ParameterizedTest(name = \"2 ArgumentsAccessors: {arguments}\")\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid argumentsAccessors(ArgumentsAccessor arguments1, ArgumentsAccessor arguments2) {\n\t\tassertArrayEquals(arguments1.toArray(), arguments2.toArray());\n\t}\n\n\t@ParameterizedTest(name = \"ArgumentsAccessor and TestInfo: {arguments}\")\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid argumentsAccessorAndTestInfo(ArgumentsAccessor arguments, TestInfo testInfo) {\n\t\tassertEquals(55, IntStream.range(0, arguments.size()).map(arguments::getInteger).sum());\n\t\tassertThat(testInfo.getDisplayName()).startsWith(\"ArgumentsAccessor and TestInfo\");\n\t}\n\n\t@ParameterizedTest(name = \"Indexed Arguments and ArgumentsAccessor: {arguments}\")\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid indexedArgumentsAndArgumentsAccessor(int num1, int num2, ArgumentsAccessor arguments) {\n\t\tassertEquals(1, num1);\n\t\tassertEquals(2, num2);\n\t\tassertEquals(55, IntStream.range(0, arguments.size()).map(arguments::getInteger).sum());\n\t}\n\n\t@ParameterizedTest(name = \"Indexed Arguments, ArgumentsAccessor, and TestInfo: {arguments}\")\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid indexedArgumentsArgumentsAccessorAndTestInfo(int num1, int num2, ArgumentsAccessor arguments,\n\t\t\tTestInfo testInfo) {\n\n\t\tassertEquals(1, num1);\n\t\tassertEquals(2, num2);\n\t\tassertEquals(55, IntStream.range(0, arguments.size()).map(arguments::getInteger).sum());\n\t\tassertThat(testInfo.getDisplayName()).startsWith(\"Indexed Arguments, ArgumentsAccessor, and TestInfo\");\n\t}\n\n\t@ParameterizedTest(name = \"Indexed Arguments, 2 ArgumentsAccessors, and TestInfo: {arguments}\")\n\t@CsvSource({ \"1, 2, 3, 4, 5, 6, 7, 8, 9, 10\" })\n\tvoid indexedArgumentsArgumentsAccessorsAndTestInfo(int num1, int num2, ArgumentsAccessor arguments1,\n\t\t\tArgumentsAccessor arguments2, TestInfo testInfo) {\n\n\t\tassertEquals(1, num1);\n\t\tassertEquals(2, num2);\n\t\tassertArrayEquals(arguments1.toArray(), arguments2.toArray());\n\t\tassertEquals(55, IntStream.range(0, arguments1.size()).map(arguments1::getInteger).sum());\n\t\tassertThat(testInfo.getDisplayName()).startsWith(\"Indexed Arguments, 2 ArgumentsAccessors, and TestInfo\");\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"foo, bar\" })\n\tvoid nullAggregator(@AggregateWith(NullAggregator.class) Person person) {\n\t\tassertNull(person);\n\t}\n\n\t@Test\n\tvoid reportsExceptionForErroneousAggregator() {\n\t\tvar results = execute(\n\t\t\tselectMethod(ErroneousTestCases.class, \"testWithErroneousAggregator\", Object.class.getName()));\n\n\t\tresults.testEvents().assertThatEvents()//\n\t\t\t\t.haveExactly(1, event(test(), finishedWithFailure(instanceOf(ParameterResolutionException.class), //\n\t\t\t\t\tmessage(\"Error aggregating arguments for parameter at index 0: something went horribly wrong\"))));\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ //\n\t\t\t\"first\", //\n\t\t\t\"second\" //\n\t})\n\tvoid argumentsAccessorInvocationIndex(ArgumentsAccessor arguments) {\n\t\tif (\"first\".equals(arguments.getString(0))) {\n\t\t\tassertEquals(1, arguments.getInvocationIndex());\n\t\t}\n\t\tif (\"second\".equals(arguments.getString(0))) {\n\t\t\tassertEquals(2, arguments.getInvocationIndex());\n\t\t}\n\t}\n\n\tprivate void testPersonAggregator(Person person) {\n\t\tif (person.firstName.equals(\"Jane\")) {\n\t\t\tassertEquals(\"Jane Doe\", person.getFullName());\n\t\t\tassertEquals(1980, person.dateOfBirth.getYear());\n\t\t\tassertEquals(Gender.F, person.gender);\n\t\t}\n\n\t\tif (person.firstName.equals(\"Jack\")) {\n\t\t\tassertEquals(\"Jack Smith\", person.getFullName());\n\t\t\tassertEquals(2000, person.dateOfBirth.getYear());\n\t\t\tassertEquals(Gender.M, person.gender);\n\t\t}\n\t}\n\n\tprivate void testAddressAggregator(Address address) {\n\t\tassertThat(address.street).contains(\"Peachtree\");\n\t\tassertEquals(\"Atlanta\", address.city);\n\t\tassertEquals(30318, address.zipCode);\n\t}\n\n\tprivate EngineExecutionResults execute(DiscoverySelector... selectors) {\n\t\treturn EngineTestKit.execute(\"junit-jupiter\", request().selectors(selectors).build());\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@NullUnmarked\n\tpublic static class Person {\n\n\t\tfinal String firstName;\n\t\tfinal String lastName;\n\t\tfinal Gender gender;\n\t\tfinal LocalDate dateOfBirth;\n\n\t\tPerson(String firstName, String lastName, LocalDate dateOfBirth, Gender gender) {\n\t\t\tthis.firstName = firstName;\n\t\t\tthis.lastName = lastName;\n\t\t\tthis.gender = gender;\n\t\t\tthis.dateOfBirth = dateOfBirth;\n\t\t}\n\n\t\tString getFullName() {\n\t\t\treturn this.firstName + \" \" + this.lastName;\n\t\t}\n\t}\n\n\tenum Gender {\n\t\tF, M\n\t}\n\n\t@NullUnmarked\n\trecord Address(String street, String city, int zipCode) {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@interface StartIndex {\n\t\tint value();\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AggregateWith(PersonAggregator.class)\n\tpublic @interface CsvToPerson {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.PARAMETER)\n\t@AggregateWith(AddressAggregator.class)\n\t@interface CsvToAddress {\n\t}\n\n\tstatic class PersonAggregator extends SimpleArgumentsAggregator {\n\n\t\t@Override\n\t\tprotected Person aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\tint startIndex = context.findAnnotation(StartIndex.class).map(StartIndex::value).orElse(0);\n\n\t\t\t// @formatter:off\n\t\t\treturn new Person(\n\t\t\t\taccessor.getString(startIndex + 0),\n\t\t\t\taccessor.getString(startIndex + 1),\n\t\t\t\taccessor.get(startIndex + 2, LocalDate.class),\n\t\t\t\taccessor.get(startIndex + 3, Gender.class)\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\tstatic class AddressAggregator extends SimpleArgumentsAggregator {\n\n\t\t@Override\n\t\tpublic Address aggregateArguments(ArgumentsAccessor arguments, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\tint startIndex = context.findAnnotation(StartIndex.class).map(StartIndex::value).orElse(0);\n\n\t\t\t// @formatter:off\n\t\t\treturn new Address(\n\t\t\t\targuments.getString(startIndex + 0),\n\t\t\t\targuments.getString(startIndex + 1),\n\t\t\t\trequireNonNull(arguments.getInteger(startIndex + 2))\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t/**\n\t * Maps from String to length of String.\n\t */\n\tstatic class MapAggregator extends SimpleArgumentsAggregator {\n\n\t\t@Override\n\t\tprotected Map<String, Integer> aggregateArguments(ArgumentsAccessor arguments, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\t// @formatter:off\n\t\t\treturn IntStream.range(0, arguments.size())\n\t\t\t\t\t.mapToObj(arguments::getString)\n\t\t\t\t\t.collect(toMap(s -> s, String::length));\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\tstatic class NullAggregator extends SimpleArgumentsAggregator {\n\t\t@Override\n\t\tprotected @Nullable Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) {\n\t\t\tPreconditions.condition(!targetType.isPrimitive(), () -> \"only supports reference types\");\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tstatic class ErroneousAggregator extends SimpleArgumentsAggregator {\n\t\t@Override\n\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\t\t\tthrow new ArgumentsAggregationException(\"something went horribly wrong\");\n\t\t}\n\t}\n\n\tstatic class ErroneousTestCases {\n\t\t@ParameterizedTest\n\t\t@ValueSource(ints = 42)\n\t\tvoid testWithErroneousAggregator(@AggregateWith(ErroneousAggregator.class) Object ignored) {\n\t\t\tfail(\"this should never be called\");\n\t\t}\n\t}\n\n\t@Test\n\t@ResourceLock(\"InstanceCountingConverter.instanceCount\")\n\tvoid aggregatorIsInstantiatedOnlyOnce() {\n\t\tInstanceCountingAggregator.instanceCount = 0;\n\t\tCountingTestCase.output.clear();\n\n\t\texecute(selectMethod(CountingTestCase.class, \"testWithCountingConverterAggregator\",\n\t\t\tint.class.getName() + \",\" + Object.class.getName()));\n\n\t\tassertThat(InstanceCountingAggregator.instanceCount).isEqualTo(1);\n\t\tassertThat(CountingTestCase.output)//\n\t\t\t\t.containsExactly(\"noisy test(1, enigma)\", \"noisy test(2, enigma)\", \"noisy test(3, enigma)\");\n\t}\n\n\t@Test\n\t@ResourceLock(\"InstanceCountingConverter.instanceCount\")\n\tvoid converterIsInstantiatedOnlyOnce() {\n\t\tInstanceCountingConverter.instanceCount = 0;\n\t\tCountingTestCase.output.clear();\n\n\t\texecute(selectMethod(CountingTestCase.class, \"testWithCountingConverterAggregator\",\n\t\t\tint.class.getName() + \",\" + Object.class.getName()));\n\n\t\tassertThat(InstanceCountingConverter.instanceCount).isEqualTo(1);\n\t\tassertThat(CountingTestCase.output)//\n\t\t\t\t.containsExactly(\"noisy test(1, enigma)\", \"noisy test(2, enigma)\", \"noisy test(3, enigma)\");\n\t}\n\n\tstatic class InstanceCountingConverter implements ArgumentConverter {\n\t\tstatic int instanceCount;\n\n\t\tInstanceCountingConverter() {\n\t\t\tinstanceCount++;\n\t\t}\n\n\t\t@Override\n\t\tpublic @Nullable Object convert(@Nullable Object source, ParameterContext context)\n\t\t\t\tthrows ArgumentConversionException {\n\t\t\treturn source;\n\t\t}\n\t}\n\n\tstatic class InstanceCountingAggregator extends SimpleArgumentsAggregator {\n\t\tstatic int instanceCount;\n\n\t\tInstanceCountingAggregator() {\n\t\t\tinstanceCount++;\n\t\t}\n\n\t\t@Override\n\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\t\t\treturn \"enigma\";\n\t\t}\n\t}\n\n\tstatic class CountingTestCase {\n\n\t\tstatic final List<String> output = new ArrayList<>();\n\n\t\t@SuppressWarnings(\"JUnitMalformedDeclaration\")\n\t\t@ParameterizedTest\n\t\t@ValueSource(ints = { 1, 2, 3 })\n\t\tvoid testWithCountingConverterAggregator(@ConvertWith(InstanceCountingConverter.class) int i,\n\t\t\t\t@AggregateWith(InstanceCountingAggregator.class) Object o) {\n\n\t\t\toutput.add(\"noisy test(\" + i + \", \" + o + \")\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/aggregator/DefaultArgumentsAccessorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertIterableEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.Arrays;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link DefaultArgumentsAccessor}.\n *\n * @since 5.2\n */\nclass DefaultArgumentsAccessorTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid argumentsMustNotBeNull() {\n\t\tassertPreconditionViolationFor(() -> defaultArgumentsAccessor(1, (Object[]) null));\n\t}\n\n\t@Test\n\tvoid indexMustNotBeNegative() {\n\t\tArgumentsAccessor arguments = defaultArgumentsAccessor(1, 1, 2);\n\t\tassertPreconditionViolationFor(() -> arguments.get(-1)).withMessage(\"index must be >= 0 and < 2\");\n\t}\n\n\t@Test\n\tvoid indexMustBeSmallerThanLength() {\n\t\tArgumentsAccessor arguments = defaultArgumentsAccessor(1, 1, 2);\n\t\tassertPreconditionViolationFor(() -> arguments.get(2)).withMessage(\"index must be >= 0 and < 2\");\n\t}\n\n\t@Test\n\tvoid getNull() {\n\t\tassertNull(defaultArgumentsAccessor(1, new @Nullable Object[] { null }).get(0));\n\t}\n\n\t@Test\n\tvoid getWithNullCastToWrapperType() {\n\t\tassertNull(defaultArgumentsAccessor(1, (Object[]) new @Nullable Integer[] { null }).get(0, Integer.class));\n\t}\n\n\t@Test\n\tvoid get() {\n\t\tassertEquals(1, defaultArgumentsAccessor(1, 1).get(0));\n\t}\n\n\t@Test\n\tvoid getWithCast() {\n\t\tassertEquals(Integer.valueOf(1), defaultArgumentsAccessor(1, 1).get(0, Integer.class));\n\t\tassertEquals(Character.valueOf('A'), defaultArgumentsAccessor(1, 'A').get(0, Character.class));\n\t}\n\n\t@Test\n\tvoid getWithCastToPrimitiveType() {\n\t\tException exception = assertThrows(ArgumentAccessException.class,\n\t\t\t() -> defaultArgumentsAccessor(1, 1).get(0, int.class));\n\t\tassertThat(exception.getMessage()).isEqualTo(\n\t\t\t\"Argument at index [0] with value [1] and type [java.lang.Integer] could not be converted or cast to type [int].\");\n\n\t\texception = assertThrows(ArgumentAccessException.class,\n\t\t\t() -> defaultArgumentsAccessor(1, new @Nullable Object[] { null }).get(0, int.class));\n\t\tassertThat(exception.getMessage()).isEqualTo(\n\t\t\t\"Argument at index [0] with value [null] and type [null] could not be converted or cast to type [int].\");\n\t}\n\n\t@Test\n\tvoid getWithCastToIncompatibleType() {\n\t\tException exception = assertThrows(ArgumentAccessException.class,\n\t\t\t() -> defaultArgumentsAccessor(1, 1).get(0, Character.class));\n\t\tassertThat(exception.getMessage()).isEqualTo(\n\t\t\t\"Argument at index [0] with value [1] and type [java.lang.Integer] could not be converted or cast to type [java.lang.Character].\");\n\t}\n\n\t@Test\n\tvoid getCharacter() {\n\t\tassertEquals(Character.valueOf('A'), defaultArgumentsAccessor(1, 'A', 'B').getCharacter(0));\n\t}\n\n\t@Test\n\tvoid getBoolean() {\n\t\tassertEquals(Boolean.TRUE, defaultArgumentsAccessor(1, true, false).getBoolean(0));\n\t}\n\n\t@Test\n\tvoid getByte() {\n\t\tassertEquals(Byte.valueOf((byte) 42), defaultArgumentsAccessor(1, (byte) 42).getByte(0));\n\t}\n\n\t@Test\n\tvoid getShort() {\n\t\tassertEquals(Short.valueOf((short) 42), defaultArgumentsAccessor(1, (short) 42).getShort(0));\n\t}\n\n\t@Test\n\tvoid getInteger() {\n\t\tassertEquals(Integer.valueOf(42), defaultArgumentsAccessor(1, 42).getInteger(0));\n\t}\n\n\t@Test\n\tvoid getLong() {\n\t\tassertEquals(Long.valueOf(42L), defaultArgumentsAccessor(1, 42L).getLong(0));\n\t}\n\n\t@Test\n\tvoid getFloat() {\n\t\tassertEquals(Float.valueOf(42.0f), defaultArgumentsAccessor(1, 42.0f).getFloat(0));\n\t}\n\n\t@Test\n\tvoid getDouble() {\n\t\tassertEquals(Double.valueOf(42.0), defaultArgumentsAccessor(1, 42.0).getDouble(0));\n\t}\n\n\t@Test\n\tvoid getString() {\n\t\tassertEquals(\"foo\", defaultArgumentsAccessor(1, \"foo\", \"bar\").getString(0));\n\t}\n\n\t@Test\n\tvoid toArray() {\n\t\tvar arguments = defaultArgumentsAccessor(1, \"foo\", \"bar\");\n\t\tvar copy = arguments.toArray();\n\t\tassertArrayEquals(new String[] { \"foo\", \"bar\" }, copy);\n\n\t\t// Modify local copy:\n\t\tcopy[0] = \"Boom!\";\n\t\tassertEquals(\"foo\", arguments.toArray()[0]);\n\t}\n\n\t@Test\n\tvoid toList() {\n\t\tvar arguments = defaultArgumentsAccessor(1, \"foo\", \"bar\");\n\t\tvar copy = arguments.toList();\n\t\tassertIterableEquals(Arrays.asList(\"foo\", \"bar\"), copy);\n\n\t\t// Modify local copy:\n\t\tassertThrows(UnsupportedOperationException.class, () -> copy.set(0, \"Boom!\"));\n\t}\n\n\t@Test\n\tvoid size() {\n\t\tassertEquals(0, defaultArgumentsAccessor(1).size());\n\t\tassertEquals(1, defaultArgumentsAccessor(1, 42).size());\n\t\tassertEquals(5, defaultArgumentsAccessor(1, 'a', 'b', 'c', 'd', 'e').size());\n\t}\n\n\tprivate static DefaultArgumentsAccessor defaultArgumentsAccessor(int invocationIndex,\n\t\t\t@Nullable Object... arguments) {\n\t\tvar classLoader = DefaultArgumentsAccessorTests.class.getClassLoader();\n\t\treturn DefaultArgumentsAccessor.create(invocationIndex, classLoader, arguments);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static void foo() {\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/converter/DefaultArgumentConverterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getClassLoader;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.doReturn;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.support.conversion.ConversionException;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\n\n/**\n * Unit tests for {@link DefaultArgumentConverter}.\n *\n * @since 5.0\n */\nclass DefaultArgumentConverterTests {\n\n\tprivate final DefaultArgumentConverter underTest = spy(DefaultArgumentConverter.INSTANCE);\n\n\t@Test\n\tvoid isAwareOfNull() {\n\t\tassertConverts(null, Object.class, null);\n\t\tassertConverts(null, String.class, null);\n\t\tassertConverts(null, Boolean.class, null);\n\t}\n\n\t@Test\n\tvoid isAwareOfWrapperTypesForPrimitiveTypes() {\n\t\tassertConverts(true, boolean.class, true);\n\t\tassertConverts(false, boolean.class, false);\n\t\tassertConverts((byte) 1, byte.class, (byte) 1);\n\t\tassertConverts('o', char.class, 'o');\n\t\tassertConverts((short) 1, short.class, (short) 1);\n\t\tassertConverts(1, int.class, 1);\n\t\tassertConverts(1L, long.class, 1L);\n\t\tassertConverts(1.0f, float.class, 1.0f);\n\t\tassertConverts(1.0d, double.class, 1.0d);\n\t}\n\n\t@Test\n\tvoid isAwareOfWideningConversions() {\n\t\tassertConverts((byte) 1, short.class, (byte) 1);\n\t\tassertConverts((short) 1, int.class, (short) 1);\n\t\tassertConverts((char) 1, int.class, (char) 1);\n\t\tassertConverts((byte) 1, long.class, (byte) 1);\n\t\tassertConverts(1, long.class, 1);\n\t\tassertConverts((char) 1, float.class, (char) 1);\n\t\tassertConverts(1, float.class, 1);\n\t\tassertConverts(1L, double.class, 1L);\n\t\tassertConverts(1.0f, double.class, 1.0f);\n\t}\n\n\t@ParameterizedTest(name = \"[{index}] {0}\")\n\t@ValueSource(classes = { char.class, boolean.class, short.class, byte.class, int.class, long.class, float.class,\n\t\t\tdouble.class, void.class })\n\tvoid throwsExceptionForNullToPrimitiveTypeConversion(Class<?> type) {\n\t\tassertThatExceptionOfType(ArgumentConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(null, type)) //\n\t\t\t\t.withMessage(\"Cannot convert null to primitive value of type \" + type.getCanonicalName());\n\n\t\tverify(underTest, never()).convert(any(), any(), any(ClassLoader.class));\n\t}\n\n\t@Test\n\tvoid throwsExceptionForNonStringsConversion() {\n\t\tassertThatExceptionOfType(ArgumentConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(new Enigma(), String.class)) //\n\t\t\t\t.withMessage(\"No built-in converter for source type %s and target type java.lang.String\",\n\t\t\t\t\tEnigma.class.getName());\n\n\t\tverify(underTest, never()).convert(any(), any(), any(ClassLoader.class));\n\t}\n\n\t@Test\n\tvoid delegatesStringsConversion() {\n\t\tdoReturn(null).when(underTest).convert(any(), any(), any(ClassLoader.class));\n\n\t\tconvert(\"value\", int.class);\n\n\t\tverify(underTest).convert(\"value\", int.class, getClassLoader(DefaultArgumentConverterTests.class));\n\t}\n\n\t@Test\n\tvoid throwsExceptionForDelegatedConversionFailure() {\n\t\tConversionException exception = new ConversionException(\"fail\");\n\t\tdoThrow(exception).when(underTest).convert(any(), any(), any(ClassLoader.class));\n\n\t\tassertThatExceptionOfType(ArgumentConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"value\", int.class)) //\n\t\t\t\t.withCause(exception) //\n\t\t\t\t.withMessage(exception.getMessage());\n\n\t\tverify(underTest).convert(\"value\", int.class, getClassLoader(DefaultArgumentConverterTests.class));\n\t}\n\n\t@Test\n\tvoid delegatesStringToClassWithCustomTypeFromDifferentClassLoaderConversion() throws Exception {\n\t\tString customTypeName = Enigma.class.getName();\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(Enigma.class)) {\n\t\t\tvar customType = testClassLoader.loadClass(customTypeName);\n\t\t\tassertThat(customType.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar declaringExecutable = ReflectionSupport.findMethod(customType, \"foo\").orElseThrow();\n\t\t\tassertThat(declaringExecutable.getDeclaringClass().getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tdoReturn(customType).when(underTest).convert(any(), any(), any(ClassLoader.class));\n\n\t\t\tvar clazz = (Class<?>) convert(customTypeName, Class.class, testClassLoader);\n\t\t\tassertThat(clazz).isNotEqualTo(Enigma.class);\n\t\t\tassertThat(clazz).isNotNull().isEqualTo(customType);\n\t\t\tassertThat(clazz.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tverify(underTest).convert(customTypeName, Class.class, testClassLoader);\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate void assertConverts(@Nullable Object input, Class<?> targetClass, @Nullable Object expectedOutput) {\n\t\tvar result = convert(input, targetClass);\n\n\t\tassertThat(result) //\n\t\t\t\t.describedAs(input + \" --(\" + targetClass.getName() + \")--> \" + expectedOutput) //\n\t\t\t\t.isEqualTo(expectedOutput);\n\n\t\tverify(underTest, never()).convert(any(), any(), any(ClassLoader.class));\n\t}\n\n\tprivate @Nullable Object convert(@Nullable Object input, Class<?> targetClass) {\n\t\treturn convert(input, targetClass, ClassLoaderUtils.getClassLoader(getClass()));\n\t}\n\n\tprivate @Nullable Object convert(@Nullable Object input, Class<?> targetClass, ClassLoader classLoader) {\n\t\treturn underTest.convert(input, targetClass, classLoader);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static void foo() {\n\t}\n\n\tprivate static class Enigma {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid foo() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/converter/JavaTimeArgumentConverterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static java.time.ZoneOffset.UTC;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.time.chrono.ChronoLocalDate;\nimport java.time.chrono.ChronoLocalDateTime;\nimport java.time.chrono.ChronoZonedDateTime;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\nclass JavaTimeArgumentConverterTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToChronoLocalDate() {\n\t\tassertThat(convert(\"01.02.2017\", \"dd.MM.yyyy\", ChronoLocalDate.class)) //\n\t\t\t\t.isEqualTo(LocalDate.of(2017, 2, 1));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToChronoLocalDateTime() {\n\t\tassertThat(convert(\"01.02.2017 12:34:56.789\", \"dd.MM.yyyy HH:mm:ss.SSS\", ChronoLocalDateTime.class)) //\n\t\t\t\t.isEqualTo(LocalDateTime.of(2017, 2, 1, 12, 34, 56, 789_000_000));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToChronoZonedDateTime() {\n\t\tassertThat(convert(\"01.02.2017 12:34:56.789 Z\", \"dd.MM.yyyy HH:mm:ss.SSS X\", ChronoZonedDateTime.class)) //\n\t\t\t\t.isEqualTo(ZonedDateTime.of(2017, 2, 1, 12, 34, 56, 789_000_000, UTC));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToLocalDate() {\n\t\tassertThat(convert(\"01.02.2017\", \"dd.MM.yyyy\", LocalDate.class)) //\n\t\t\t\t.isEqualTo(LocalDate.of(2017, 2, 1));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToLocalDateTime() {\n\t\tassertThat(convert(\"01.02.2017 12:34:56.789\", \"dd.MM.yyyy HH:mm:ss.SSS\", LocalDateTime.class)) //\n\t\t\t\t.isEqualTo(LocalDateTime.of(2017, 2, 1, 12, 34, 56, 789_000_000));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToLocalTime() {\n\t\tassertThat(convert(\"12:34:56.789\", \"HH:mm:ss.SSS\", LocalTime.class)) //\n\t\t\t\t.isEqualTo(LocalTime.of(12, 34, 56, 789_000_000));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToOffsetDateTime() {\n\t\tassertThat(convert(\"01.02.2017 12:34:56.789 +02\", \"dd.MM.yyyy HH:mm:ss.SSS X\", OffsetDateTime.class)) //\n\t\t\t\t.isEqualTo(OffsetDateTime.of(2017, 2, 1, 12, 34, 56, 789_000_000, ZoneOffset.ofHours(2)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToOffsetTime() {\n\t\tassertThat(convert(\"12:34:56.789 -02\", \"HH:mm:ss.SSS X\", OffsetTime.class)) //\n\t\t\t\t.isEqualTo(OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.ofHours(-2)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToYear() {\n\t\tassertThat(convert(\"2017\", \"yyyy\", Year.class)) //\n\t\t\t\t.isEqualTo(Year.of(2017));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToYearMonth() {\n\t\tassertThat(convert(\"03/2017\", \"MM/yyyy\", YearMonth.class)) //\n\t\t\t\t.isEqualTo(YearMonth.of(2017, 3));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsStringToZonedDateTime() {\n\t\tassertThat(convert(\"01.02.2017 12:34:56.789 Europe/Berlin\", \"dd.MM.yyyy HH:mm:ss.SSS VV\", ZonedDateTime.class)) //\n\t\t\t\t.isEqualTo(ZonedDateTime.of(2017, 2, 1, 12, 34, 56, 789_000_000, ZoneId.of(\"Europe/Berlin\")));\n\t}\n\n\t@Test\n\tvoid throwsExceptionOnInvalidTargetType() {\n\t\tvar exception = assertThrows(ArgumentConversionException.class, () -> convert(\"2017\", \"yyyy\", Integer.class));\n\n\t\tassertThat(exception).hasMessage(\"Cannot convert to java.lang.Integer: 2017\");\n\t}\n\n\t/**\n\t * @since 5.12\n\t */\n\t@Test\n\tvoid throwsExceptionOnNullParameterWithoutNullable() {\n\t\tvar exception = assertThrows(ArgumentConversionException.class,\n\t\t\t() -> convert(null, \"dd.MM.yyyy\", LocalDate.class));\n\n\t\tassertThat(exception).hasMessage(\n\t\t\t\"Cannot convert null to java.time.LocalDate; consider setting 'nullable = true'\");\n\t}\n\n\t/**\n\t * @since 5.12\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid convertsNullableParameter() {\n\t\tassertThat(convert(null, \"dd.MM.yyyy\", true, LocalDate.class)).isNull();\n\t}\n\n\tprivate @Nullable Object convert(@Nullable Object input, String pattern, Class<?> targetClass) {\n\t\treturn convert(input, pattern, false, targetClass);\n\t}\n\n\tprivate @Nullable Object convert(@Nullable Object input, String pattern, boolean nullable, Class<?> targetClass) {\n\t\tvar converter = new JavaTimeArgumentConverter();\n\t\tvar annotation = mock(JavaTimeConversionPattern.class);\n\t\twhen(annotation.value()).thenReturn(pattern);\n\t\twhen(annotation.nullable()).thenReturn(nullable);\n\n\t\treturn converter.convert(input, targetClass, annotation);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/converter/TypedArgumentConverterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Parameter;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.NullSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\n\n/**\n * Tests for {@link TypedArgumentConverter}.\n *\n * @since 5.7\n */\nclass TypedArgumentConverterTests {\n\n\t@Nested\n\tclass UnitTests {\n\n\t\tprivate final StringLengthArgumentConverter converter = new StringLengthArgumentConverter();\n\n\t\t/**\n\t\t * @since 5.8\n\t\t */\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid preconditions() {\n\t\t\tassertPreconditionViolationNotNullFor(\"sourceType\",\n\t\t\t\t() -> new StringLengthArgumentConverter(null, Integer.class));\n\n\t\t\tassertPreconditionViolationNotNullFor(\"targetType\",\n\t\t\t\t() -> new StringLengthArgumentConverter(String.class, null));\n\t\t}\n\n\t\t@Test\n\t\tvoid convertsSourceToTarget() {\n\t\t\tassertAll(//\n\t\t\t\t() -> assertConverts(\"abcd\", 4), //\n\t\t\t\t() -> assertConverts(\"\", 0), //\n\t\t\t\t() -> assertConverts(null, 0)//\n\t\t\t);\n\t\t}\n\n\t\tprivate void assertConverts(@Nullable String input, int expected) {\n\t\t\tassertThat(this.converter.convert(input)).isEqualTo(expected);\n\t\t}\n\n\t\t@Test\n\t\tvoid sourceTypeMismatch() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToBoolean\", Boolean.class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(Boolean.TRUE, parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert objects of type [java.lang.Boolean]. \"\n\t\t\t\t\t\t\t+ \"Only source objects of type [java.lang.String] are supported.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid sourceTypeMismatchForArrayType() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToByteArray\", Byte[].class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(new String[][] {}, parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert objects of type [java.lang.String[][]]. \"\n\t\t\t\t\t\t\t+ \"Only source objects of type [java.lang.String] are supported.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid sourceTypeMismatchForPrimitiveArrayType() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToByteArray\", Byte[].class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(new byte[0], parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert objects of type [byte[]]. \"\n\t\t\t\t\t\t\t+ \"Only source objects of type [java.lang.String] are supported.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid targetTypeMismatch() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToBoolean\", Boolean.class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(\"enigma\", parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert to type [java.lang.Boolean]. \"\n\t\t\t\t\t\t\t+ \"Only target type [java.lang.Integer] is supported.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid targetTypeMismatchForArrayType() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToByteArray\", Byte[].class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(\"enigma\", parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert to type [java.lang.Byte[]]. \"\n\t\t\t\t\t\t\t+ \"Only target type [java.lang.Integer] is supported.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid targetTypeMismatchForPrimitiveArrayType() {\n\t\t\tParameter parameter = findParameterOfMethod(\"stringToPrimitiveByteArray\", byte[].class);\n\t\t\tParameterContext parameterContext = parameterContext(parameter);\n\t\t\tassertThatExceptionOfType(ArgumentConversionException.class)//\n\t\t\t\t\t.isThrownBy(() -> this.converter.convert(\"enigma\", parameterContext))//\n\t\t\t\t\t.withMessage(\"StringLengthArgumentConverter cannot convert to type [byte[]]. \"\n\t\t\t\t\t\t\t+ \"Only target type [java.lang.Integer] is supported.\");\n\t\t}\n\n\t\tprivate ParameterContext parameterContext(Parameter parameter) {\n\t\t\tParameterContext parameterContext = mock();\n\t\t\twhen(parameterContext.getParameter()).thenReturn(parameter);\n\t\t\treturn parameterContext;\n\t\t}\n\n\t\tprivate Parameter findParameterOfMethod(String methodName, Class<?>... parameterTypes) {\n\t\t\tMethod method = ReflectionSupport.findMethod(getClass(), methodName, parameterTypes).get();\n\t\t\treturn method.getParameters()[0];\n\t\t}\n\n\t\tvoid stringToBoolean(Boolean b) {\n\t\t}\n\n\t\tvoid stringToByteArray(Byte[] array) {\n\t\t}\n\n\t\tvoid stringToPrimitiveByteArray(byte[] array) {\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 5.8\n\t */\n\t@Nested\n\tclass IntegrationTests {\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid nullStringToInteger(@StringLength Integer length) {\n\t\t\tassertThat(length).isEqualTo(0);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid nullStringToPrimitiveInt(@StringLength int length) {\n\t\t\tassertThat(length).isEqualTo(0);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@NullSource\n\t\tvoid nullStringToPrimitiveLong(@StringLength long length) {\n\t\t\tassertThat(length).isEqualTo(0);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = \"enigma\")\n\t\tvoid stringToInteger(@StringLength Integer length) {\n\t\t\tassertThat(length).isEqualTo(6);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = \"enigma\")\n\t\tvoid stringToPrimitiveInt(@StringLength int length) {\n\t\t\tassertThat(length).isEqualTo(6);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = \"enigma\")\n\t\tvoid stringToPrimitiveLong(@StringLength long length) {\n\t\t\tassertThat(length).isEqualTo(6);\n\t\t}\n\n\t}\n\n\t@Target(ElementType.PARAMETER)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ConvertWith(StringLengthArgumentConverter.class)\n\tprivate @interface StringLength {\n\t}\n\n\t@NullMarked\n\tprivate static class StringLengthArgumentConverter extends TypedArgumentConverter<String, Integer> {\n\n\t\tStringLengthArgumentConverter() {\n\t\t\tthis(String.class, Integer.class);\n\t\t}\n\n\t\tStringLengthArgumentConverter(Class<String> sourceType, Class<Integer> targetType) {\n\t\t\tsuper(sourceType, targetType);\n\t\t}\n\n\t\t@Override\n\t\tprotected Integer convert(@Nullable String source) throws ArgumentConversionException {\n\t\t\treturn (source != null ? source.length() : 0);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/AnnotationBasedArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.params.provider.MockCsvAnnotationBuilder.csvSource;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.atMostOnce;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\n@DisplayName(\"AnnotationBasedArgumentsProvider\")\nclass AnnotationBasedArgumentsProviderTests {\n\n\tprivate final AnnotationBasedArgumentsProvider<CsvSource> annotationBasedArgumentsProvider = new AnnotationBasedArgumentsProvider<>() {\n\t\t@Override\n\t\tprotected Stream<? extends Arguments> provideArguments(\n\t\t\t\torg.junit.jupiter.params.support.ParameterDeclarations parameters, ExtensionContext context,\n\t\t\t\tCsvSource annotation) {\n\t\t\treturn Stream.of(Arguments.of(annotation));\n\t\t}\n\t};\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\t@DisplayName(\"should throw exception when null annotation is provided to accept method\")\n\tvoid shouldThrowExceptionWhenNullAnnotationIsProvidedToAccept() {\n\t\tassertThatThrownBy(() -> annotationBasedArgumentsProvider.accept(null)) //\n\t\t\t\t.hasMessage(\"annotation must not be null\");\n\t}\n\n\t@Test\n\t@DisplayName(\"should invoke the provideArguments template method with the accepted annotation\")\n\tvoid shouldInvokeTemplateMethodWithTheAnnotationProvidedToAccept() {\n\t\tvar spiedProvider = spy(annotationBasedArgumentsProvider);\n\t\tvar parameters = mock(org.junit.jupiter.params.support.ParameterDeclarations.class);\n\t\tvar extensionContext = mock(ExtensionContext.class);\n\t\tvar annotation = csvSource(\"0\", \"1\", \"2\");\n\n\t\tannotationBasedArgumentsProvider.accept(annotation);\n\t\tannotationBasedArgumentsProvider.provideArguments(parameters, extensionContext);\n\n\t\tverify(spiedProvider, atMostOnce()).provideArguments(eq(parameters), eq(extensionContext), eq(annotation));\n\t}\n\n\t@Test\n\t@DisplayName(\"should invoke the provideArguments template method for every accepted annotation\")\n\tvoid shouldInvokeTemplateMethodForEachAnnotationProvided() {\n\t\tvar parameters = mock(ParameterDeclarations.class);\n\t\tvar extensionContext = mock(ExtensionContext.class);\n\t\tvar foo = csvSource(\"foo\");\n\t\tvar bar = csvSource(\"bar\");\n\n\t\tannotationBasedArgumentsProvider.accept(foo);\n\t\tannotationBasedArgumentsProvider.accept(bar);\n\n\t\tvar arguments = annotationBasedArgumentsProvider.provideArguments(parameters, extensionContext).toList();\n\n\t\tassertThat(arguments).hasSize(2);\n\t\tassertThat(arguments.getFirst().get()[0]).isEqualTo(foo);\n\t\tassertThat(arguments.get(1).get()[0]).isEqualTo(bar);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ArgumentsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.jupiter.params.provider.Arguments.of;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link Arguments}.\n *\n * @since 5.0\n */\nclass ArgumentsTests {\n\n\t@Test\n\tvoid ofSupportsVarargs() {\n\t\tvar arguments = of(1, \"2\", 3.0);\n\n\t\tassertArrayEquals(new Object[] { 1, \"2\", 3.0 }, arguments.get());\n\t}\n\n\t@Test\n\tvoid argumentsSupportsVarargs() {\n\t\tvar arguments = arguments(1, \"2\", 3.0);\n\n\t\tassertArrayEquals(new Object[] { 1, \"2\", 3.0 }, arguments.get());\n\t}\n\n\t@Test\n\tvoid ofReturnsSameArrayUsedForCreating() {\n\t\tObject[] input = { 1, \"2\", 3.0 };\n\n\t\tvar arguments = of(input);\n\n\t\tassertThat(arguments.get()).isSameAs(input);\n\t}\n\n\t@Test\n\tvoid argumentsReturnsSameArrayUsedForCreating() {\n\t\tObject[] input = { 1, \"2\", 3.0 };\n\n\t\tvar arguments = arguments(input);\n\n\t\tassertThat(arguments.get()).isSameAs(input);\n\t}\n\n\t@Test\n\tvoid fromSupportsCollection() {\n\t\tCollection<@Nullable Object> input = Arrays.asList(1, \"two\", null, 3.0);\n\t\tvar arguments = Arguments.from(input);\n\n\t\tassertArrayEquals(new Object[] { 1, \"two\", null, 3.0 }, arguments.get());\n\t}\n\n\t@Test\n\tvoid fromSupportsIterable() {\n\t\tvar input = new IterableWithNullableElements(1, \"two\", null, 3.0);\n\t\tvar arguments = Arguments.from(input);\n\n\t\tassertArrayEquals(new Object[] { 1, \"two\", null, 3.0 }, arguments.get());\n\t}\n\n\t@Test\n\tvoid fromSupportsListDefensiveCopy() {\n\t\tvar input = new ArrayList<@Nullable Object>(asList(1, \"two\", null, 3.0));\n\t\tvar arguments = Arguments.from(input);\n\n\t\t// Modify input\n\t\tinput.set(1, \"changed\");\n\t\tinput.add(\"new\");\n\n\t\t// Assert that arguments are unchanged\n\t\tassertArrayEquals(new Object[] { 1, \"two\", null, 3.0 }, arguments.get());\n\t}\n\n\t@Test\n\tvoid argumentsFromSupportsCollection() {\n\t\tCollection<@Nullable Object> input = asList(\"a\", 2, null);\n\t\tvar arguments = Arguments.argumentsFrom(input);\n\n\t\tassertArrayEquals(new Object[] { \"a\", 2, null }, arguments.get());\n\t}\n\n\t@Test\n\tvoid argumentsFromSupportsIterable() {\n\t\tvar input = new IterableWithNullableElements(\"a\", 2, null);\n\t\tvar arguments = Arguments.argumentsFrom(input);\n\n\t\tassertArrayEquals(new Object[] { \"a\", 2, null }, arguments.get());\n\t}\n\n\t@Test\n\tvoid argumentSetSupportsCollection() {\n\t\tCollection<@Nullable Object> input = asList(\"x\", null, 42);\n\t\tvar argumentSet = Arguments.argumentSetFrom(\"list-test\", input);\n\n\t\tassertArrayEquals(new Object[] { \"x\", null, 42 }, argumentSet.get());\n\t\tassertThat(argumentSet.getName()).isEqualTo(\"list-test\");\n\t}\n\n\t@Test\n\tvoid argumentSetSupportsIterable() {\n\t\tvar input = new IterableWithNullableElements(\"x\", null, 42);\n\t\tvar argumentSet = Arguments.argumentSetFrom(\"list-test\", input);\n\n\t\tassertArrayEquals(new Object[] { \"x\", null, 42 }, argumentSet.get());\n\t\tassertThat(argumentSet.getName()).isEqualTo(\"list-test\");\n\t}\n\n\t@Test\n\tvoid toListReturnsMutableListOfArguments() {\n\t\tvar arguments = Arguments.of(\"a\", 2, null);\n\n\t\tvar result = arguments.toList();\n\n\t\tassertThat(result).containsExactly(\"a\", 2, null); // preserves content\n\t\tresult.add(\"extra\"); // confirms mutability\n\t\tassertThat(result).contains(\"extra\");\n\t}\n\n\t@Test\n\tvoid toListDoesNotAffectInternalArgumentsState() {\n\t\tvar arguments = Arguments.of(\"a\", 2, null);\n\n\t\tvar result = arguments.toList();\n\t\tresult.add(\"extra\"); // mutate the returned list\n\n\t\t// Confirm that internal state was not modified\n\t\tvar freshCopy = arguments.toList();\n\t\tassertThat(freshCopy).containsExactly(\"a\", 2, null);\n\t}\n\n\t@Test\n\tvoid toListWorksOnEmptyArguments() {\n\t\tvar arguments = Arguments.of();\n\n\t\tvar result = arguments.toList();\n\n\t\tassertThat(result).isEmpty();\n\t\tresult.add(\"extra\");\n\t\tassertThat(result).containsExactly(\"extra\");\n\t}\n\n\tprivate static final class IterableWithNullableElements implements Iterable<@Nullable Object> {\n\n\t\tprivate final Collection<@Nullable Object> collection;\n\n\t\tprivate IterableWithNullableElements(@Nullable Object... items) {\n\t\t\tthis.collection = asList(items);\n\t\t}\n\n\t\t@Override\n\t\tpublic Iterator<@Nullable Object> iterator() {\n\t\t\treturn collection.iterator();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.params.provider.MockCsvAnnotationBuilder.csvSource;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.ThrowingConsumer;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.support.ParameterNameAndArgument;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * @since 5.0\n */\nclass CsvArgumentsProviderTests {\n\n\t@Test\n\tvoid throwsExceptionForBlankLines() {\n\t\tvar annotation = csvSource(\"foo\", \"bar\", \" \");\n\n\t\tassertThatExceptionOfType(JUnitException.class)//\n\t\t\t\t.isThrownBy(() -> provideArguments(annotation).toArray())//\n\t\t\t\t.withMessage(\"CSV record at index 3 must not be blank\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfNeitherValueNorTextBlockIsDeclared() {\n\t\tvar annotation = csvSource().build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessage(\"@CsvSource must be declared with either `value` or `textBlock` but not both\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfValueAndTextBlockAreDeclared() {\n\t\tvar annotation = csvSource().lines(\"foo\").textBlock(\"\"\"\n\t\t\t\tbar\n\t\t\t\tbaz\n\t\t\t\t\"\"\").build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessage(\"@CsvSource must be declared with either `value` or `textBlock` but not both\");\n\t}\n\n\t@Test\n\tvoid providesSingleArgument() {\n\t\tvar annotation = csvSource(\"foo\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"));\n\t}\n\n\t@Test\n\tvoid providesSingleArgumentFromTextBlock() {\n\t\tvar annotation = csvSource().textBlock(\"foo\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"));\n\t}\n\n\t@Test\n\tvoid providesMultipleArguments() {\n\t\tvar annotation = csvSource(\"foo\", \"bar\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesMultipleArgumentsFromTextBlock() {\n\t\tvar annotation = csvSource().textBlock(\"\"\"\n\t\t\t\tfoo\n\t\t\t\tbar\n\t\t\t\t\"\"\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid splitsAndTrimsArguments() {\n\t\tvar annotation = csvSource(\" foo , bar \");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"));\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3824\">GitHub issue #3824</a>\n\t */\n\t@Test\n\tvoid trimsLeadingWhitespaceAndControlCharacters() {\n\t\tvar annotation = csvSource(\"'', 1\", \"\\t'',\\b2\", \"'',\\u00003\", \" '', \\t 4\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\", \"1\"), array(\"\", \"2\"), array(\"\", \"3\"), array(\"\", \"4\"));\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3824\">GitHub issue #3824</a>\n\t */\n\t@Test\n\tvoid trimsTrailingWhitespaceAndControlCharacters() {\n\t\tvar annotation = csvSource(\"1 ,'' \", \"2\\t,''\\b\", \"3   ,''\\u0000\", \"4,'' \\t \");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"1\", \"\"), array(\"2\", \"\"), array(\"3\", \"\"), array(\"4\", \"\"));\n\t}\n\n\t@Test\n\tvoid preservesLeadingAndTrailingWhitespaceAndControlCharactersWhenRequested() {\n\t\tvar annotation = csvSource().lines(\" 1 , a \", \"\\t2\\b, b   \", \"\\u00003\\u0007,c \", \"4, \\t d \\t \") //\n\t\t\t\t.ignoreLeadingAndTrailingWhitespace(false).build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(//\n\t\t\tarray(\" 1 \", \" a \"), //\n\t\t\tarray(\"\\t2\\b\", \" b   \"), //\n\t\t\tarray(\"\\u00003\\u0007\", \"c \"), //\n\t\t\tarray(\"4\", \" \\t d \\t \"));\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3824\">GitHub issue #3824</a>\n\t */\n\t@Test\n\tvoid trimVsStripSemanticsWithUnquotedText() {\n\t\t// \\u0000 (null character) removed by trim(), preserved by strip()\n\t\t// \\u00A0 (non-breaking space) preserved by trim(), removed by strip()\n\n\t\tvar annotation = csvSource().lines(\"\\u0000, \\u0000foo\\u0000, \\u00A0bar\\u00A0\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\", \"foo\", \"\\u00A0bar\\u00A0\"));\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3824\">GitHub issue #3824</a>\n\t */\n\t@Test\n\tvoid trimVsStripSemanticsWithQuotedText() {\n\t\t// \\u0000 (null character) removed by trim(), preserved by strip()\n\t\t// \\u00A0 (non-breaking space) preserved by trim(), removed by strip()\n\n\t\tvar annotation = csvSource().lines(\"'\\u0000', '\\u0000 foo \\u0000', '\\t\\u00A0bar\\u0000'\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\\u0000\", \"\\u0000 foo \\u0000\", \"\\t\\u00A0bar\\u0000\"));\n\t}\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3824\">GitHub issue #3824</a>\n\t */\n\t@Test\n\tvoid trimVsStripSemanticsWithUnquotedAndQuotedText() {\n\t\t// \\u0000 (null character) removed by trim(), preserved by strip()\n\t\t// \\u00A0 (non-breaking space) preserved by trim(), removed by strip()\n\n\t\tvar annotation = csvSource().lines(\"\\u0000'\\u0000 foo', \\u00A0' bar\\u0000'\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\\u0000 foo\", \"\\u00A0' bar\\u0000'\"));\n\t}\n\n\t@Test\n\tvoid understandsQuotes() {\n\t\tvar annotation = csvSource(\"'foo, bar'\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo, bar\"));\n\t}\n\n\t@Test\n\tvoid understandsQuotesInTextBlock() {\n\t\tvar annotation = csvSource().textBlock(\"\"\"\n\t\t\t\t'foo, bar'\n\t\t\t\t\"\"\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo, bar\"));\n\t}\n\n\t@Test\n\tvoid understandsCustomQuotes() {\n\t\tvar annotation = csvSource().quoteCharacter('~').lines(\"~foo, bar~\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo, bar\"));\n\t}\n\n\t@Test\n\tvoid understandsCustomQuotesInTextBlock() {\n\t\tvar annotation = csvSource().quoteCharacter('\"').textBlock(\"\"\"\n\t\t\t\t\t\"foo, bar\"\n\t\t\t\t\"\"\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo, bar\"));\n\t}\n\n\t@Test\n\tvoid understandsEscapeCharacters() {\n\t\tvar annotation = csvSource(\"'foo or ''bar''', baz\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo or 'bar'\", \"baz\"));\n\t}\n\n\t@Test\n\tvoid understandsEscapeCharactersWithCustomQuoteCharacter() {\n\t\tvar annotation = csvSource().quoteCharacter('~').lines(\"~foo or ~~bar~~~, baz\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo or ~bar~\", \"baz\"));\n\t}\n\n\t@Test\n\tvoid doesNotTrimSpacesInsideQuotes() {\n\t\tvar annotation = csvSource(\"''\", \"'   '\", \"'blank '\", \"' not blank   '\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\"), array(\"   \"), array(\"blank \"), array(\" not blank   \"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsWithCharacterDelimiter() {\n\t\tvar annotation = csvSource().delimiter('|').lines(\"foo|bar\", \"bar|foo\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"bar\", \"foo\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsWithStringDelimiter() {\n\t\tvar annotation = csvSource().delimiterString(\"~~~\").lines(\"foo~~~ bar\", \"bar~~~ foo\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"bar\", \"foo\"));\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfBothDelimitersAreSimultaneouslySet() {\n\t\tvar annotation = csvSource().delimiter('|').delimiterString(\"~~~\").build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessageStartingWith(\"The delimiter and delimiterString attributes cannot be set simultaneously in\")//\n\t\t\t\t.withMessageContaining(\"CsvSource\");\n\t}\n\n\t@Test\n\tvoid defaultEmptyValueAndDefaultNullValue() {\n\t\tvar annotation = csvSource(\"'', null, ,, apple\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\", \"null\", null, null, \"apple\"));\n\t}\n\n\t@Test\n\tvoid customEmptyValueAndDefaultNullValue() {\n\t\tvar annotation = csvSource().emptyValue(\"EMPTY\").lines(\"'', null, ,, apple\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"EMPTY\", \"null\", null, null, \"apple\"));\n\t}\n\n\t@Test\n\tvoid customNullValues() {\n\t\tvar annotation = csvSource().nullValues(\"N/A\", \"NIL\", \"null\")//\n\t\t\t\t.lines(\"apple, , NIL, '', N/A, banana, null\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"apple\", null, null, \"\", null, \"banana\", null));\n\t}\n\n\t@Test\n\tvoid customNullValueInHeader() {\n\t\tvar annotation = csvSource().useHeadersInDisplayName(true).nullValues(\"NIL\").textBlock(\"\"\"\n\t\t\t\tFRUIT, NIL\n\t\t\t\tapple, 1\n\t\t\t\t\"\"\").build();\n\n\t\tassertThat(headersToValues(annotation)).containsExactly(array(\"FRUIT = apple\", \"null = 1\"));\n\t}\n\n\t@Test\n\tvoid convertsEmptyValuesToNullInLinesAfterFirstLine() {\n\t\tvar annotation = csvSource(\"'', ''\", \" , \");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"\", \"\"), array(null, null));\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfSourceExceedsMaxCharsPerColumnConfig() {\n\t\tvar annotation = csvSource().lines(\"413\").maxCharsPerColumn(2).build();\n\n\t\tassertThatExceptionOfType(CsvParsingException.class)//\n\t\t\t\t.isThrownBy(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessageStartingWith(\"Failed to parse CSV input configured via Mock for CsvSource\")//\n\t\t\t\t.havingRootCause().satisfies(isCsvParseException());\n\t}\n\n\t@Test\n\tvoid providesArgumentWithDefaultMaxCharsPerColumnConfig() {\n\t\tvar annotation = csvSource().lines(\"0\".repeat(4096)).delimiter(';').build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments.toArray()).hasSize(1);\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenSourceExceedsDefaultMaxCharsPerColumnConfig() {\n\t\tvar annotation = csvSource().lines(\"0\".repeat(4097)).delimiter(';').build();\n\n\t\tassertThatExceptionOfType(CsvParsingException.class)//\n\t\t\t\t.isThrownBy(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessageStartingWith(\"Failed to parse CSV input configured via Mock for CsvSource\")//\n\t\t\t\t.havingRootCause().satisfies(isCsvParseException());\n\t}\n\n\t@Test\n\tvoid providesArgumentsForExceedsSourceWithCustomMaxCharsPerColumnConfig() {\n\t\tvar annotation = csvSource().lines(\"0\".repeat(4097)).maxCharsPerColumn(4097).build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments.toArray()).hasSize(1);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { Integer.MIN_VALUE, -2, 0 })\n\tvoid throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumberOrMinusOne(int maxCharsPerColumn) {\n\t\tvar annotation = csvSource().lines(\"41\").maxCharsPerColumn(maxCharsPerColumn).build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessageStartingWith(\"maxCharsPerColumn must be a positive number or -1: \" + maxCharsPerColumn);\n\t}\n\n\t@Test\n\tvoid ignoresCommentCharacterWhenUsingValueAttribute() {\n\t\tvar annotation = csvSource(\"#foo\", \"#bar,baz\", \"baz,#quux\");\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"#foo\"), array(\"#bar\", \"baz\"), array(\"baz\", \"#quux\"));\n\t}\n\n\t@Test\n\tvoid honorsCommentCharacterWhenUsingTextBlockAttribute() {\n\t\tvar annotation = csvSource().textBlock(\"\"\"\n\t\t\t\t#foo\n\t\t\t\tbar, #baz\n\t\t\t\t'#bar', baz\n\t\t\t\t\"\"\").build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"bar\", \"#baz\"), array(\"#bar\", \"baz\"));\n\t}\n\n\t@Test\n\tvoid honorsCustomCommentCharacter() {\n\t\tvar annotation = csvSource().textBlock(\"\"\"\n\t\t\t\t*foo\n\t\t\t\tbar, *baz\n\t\t\t\t'*bar', baz\n\t\t\t\t\"\"\").commentCharacter('*').build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"bar\", \"*baz\"), array(\"*bar\", \"baz\"));\n\t}\n\n\t@Test\n\tvoid doesNotThrowExceptionWhenDelimiterAndCommentCharacterTheSameWhenUsingValueAttribute() {\n\t\tvar annotation = csvSource().lines(\"foo#bar\").delimiter('#').commentCharacter('#').build();\n\n\t\tvar arguments = provideArguments(annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"invalidDelimiterAndQuoteCharacterCombinations\")\n\tvoid doesNotThrowExceptionWhenDelimiterAndCommentCharacterAreTheSameWhenUsingValueAttribute(Object delimiter,\n\t\t\tchar quoteCharacter) {\n\n\t\tvar builder = csvSource().lines(\"foo\").quoteCharacter(quoteCharacter);\n\n\t\tvar annotation = delimiter instanceof Character c //\n\t\t\t\t? builder.delimiter(c).build() //\n\t\t\t\t: builder.delimiterString(delimiter.toString()).build();\n\n\t\tvar message = \"delimiter or delimiterString: '%s' and quoteCharacter: '%s' must differ\";\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny()) //\n\t\t\t\t.withMessage(message.formatted(delimiter, quoteCharacter));\n\t}\n\n\tstatic Stream<Arguments> invalidDelimiterAndQuoteCharacterCombinations() {\n\t\treturn Stream.of(\n\t\t\t// delimiter\n\t\t\tArguments.of('*', '*'), //\n\t\t\t// delimiterString\n\t\t\tArguments.of(\"*\", '*'));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"invalidDelimiterQuoteCharacterAndCommentCharacterCombinations\")\n\tvoid throwsExceptionWhenControlCharactersAreTheSameWhenUsingTextBlockAttribute(Object delimiter,\n\t\t\tchar quoteCharacter, char commentCharacter) {\n\n\t\tvar builder = csvSource().textBlock(\"\"\"\n\t\t\t\tfoo\"\"\").quoteCharacter(quoteCharacter).commentCharacter(commentCharacter);\n\n\t\tvar annotation = delimiter instanceof Character c //\n\t\t\t\t? builder.delimiter(c).build() //\n\t\t\t\t: builder.delimiterString(delimiter.toString()).build();\n\n\t\tvar message = \"delimiter or delimiterString: '%s', quoteCharacter: '%s', and commentCharacter: '%s' \" + //\n\t\t\t\t\"must all differ\";\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny()) //\n\t\t\t\t.withMessage(message.formatted(delimiter, quoteCharacter, commentCharacter));\n\t}\n\n\tstatic Stream<Arguments> invalidDelimiterQuoteCharacterAndCommentCharacterCombinations() {\n\t\treturn Stream.of(\n\t\t\t// delimiter\n\t\t\tArguments.of('#', '#', '#'), //\n\t\t\tArguments.of('#', '#', '*'), //\n\t\t\tArguments.of('*', '#', '#'), //\n\t\t\tArguments.of('#', '*', '#'), //\n\t\t\t// delimiterString\n\t\t\tArguments.of(\"#\", '#', '*'), //\n\t\t\tArguments.of(\"#\", '*', '#') //\n\t\t);\n\t}\n\n\t@Test\n\tvoid supportsCsvHeadersWhenUsingTextBlockAttribute() {\n\t\tvar annotation = csvSource().useHeadersInDisplayName(true).textBlock(\"\"\"\n\t\t\t\tFRUIT, RANK\n\t\t\t\tapple, 1\n\t\t\t\tbanana, 2\n\t\t\t\t\"\"\").build();\n\n\t\tassertThat(headersToValues(annotation)).containsExactly(//\n\t\t\tarray(\"FRUIT = apple\", \"RANK = 1\"), //\n\t\t\tarray(\"FRUIT = banana\", \"RANK = 2\")//\n\t\t);\n\t}\n\n\t@Test\n\tvoid supportsCsvHeadersWhenUsingValueAttribute() {\n\t\tvar annotation = csvSource().useHeadersInDisplayName(true)//\n\t\t\t\t.lines(\"FRUIT, RANK\", \"apple, 1\", \"banana, 2\").build();\n\n\t\tassertThat(headersToValues(annotation)).containsExactly(//\n\t\t\tarray(\"FRUIT = apple\", \"RANK = 1\"), //\n\t\t\tarray(\"FRUIT = banana\", \"RANK = 2\")//\n\t\t);\n\t}\n\n\tprivate Stream<String[]> headersToValues(CsvSource csvSource) {\n\t\tvar arguments = provideArguments(csvSource);\n\t\treturn arguments.map(array -> {\n\t\t\tString[] strings = new String[array.length];\n\t\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\t\tif (array[i] instanceof ParameterNameAndArgument parameterNameAndArgument) {\n\t\t\t\t\tstrings[i] = parameterNameAndArgument.getName() + \" = \" + parameterNameAndArgument.getPayload();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalStateException(\"Unexpected argument type: \" + array[i].getClass().getName());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn strings;\n\t\t});\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfColumnCountExceedsHeaderCount() {\n\t\tvar annotation = csvSource().useHeadersInDisplayName(true).textBlock(\"\"\"\n\t\t\t\tFRUIT, RANK\n\t\t\t\tapple, 1\n\t\t\t\tbanana, 2, BOOM!\n\t\t\t\t\"\"\").build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation).findAny())//\n\t\t\t\t.withMessage(\n\t\t\t\t\t\"The number of columns (3) exceeds the number of supplied headers (2) in CSV record: [banana, 2, BOOM!]\");\n\t}\n\n\tprivate Stream<Object[]> provideArguments(CsvSource annotation) {\n\t\tvar provider = new CsvArgumentsProvider();\n\t\tprovider.accept(annotation);\n\t\treturn provider.provideArguments(mock(), mock(ExtensionContext.class)).map(Arguments::get);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> @Nullable T[] array(@Nullable T... elements) {\n\t\treturn elements;\n\t}\n\n\tstatic ThrowingConsumer<Throwable> isCsvParseException() {\n\t\treturn ex -> ex.getClass().getName().contains(\"de.siegmar.fastcsv.reader.CsvParseException\");\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/CsvFileArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.params.provider.CsvArgumentsProviderTests.isCsvParseException;\nimport static org.junit.jupiter.params.provider.MockCsvAnnotationBuilder.csvFileSource;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotEmptyFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\nimport static org.mockito.Mockito.doCallRealMethod;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvFileArgumentsProvider.InputStreamProvider;\nimport org.junit.jupiter.params.support.ParameterNameAndArgument;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * @since 5.0\n */\nclass CsvFileArgumentsProviderTests {\n\n\t@Test\n\tvoid providesArgumentsForEachSupportedLineSeparator() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, bar \\n baz, qux \\r quux, corge \\r\\n grault, garply\");\n\n\t\tassertThat(arguments).containsExactly(//\n\t\t\tarray(\"foo\", \"bar\"), //\n\t\t\tarray(\"baz\", \"qux\"), //\n\t\t\tarray(\"quux\", \"corge\"), //\n\t\t\tarray(\"grault\", \"garply\")//\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsForNewlineAndComma() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, bar \\n baz, qux \\n\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"baz\", \"qux\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsForCarriageReturnAndSemicolon() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiter(';')//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo; bar \\r baz; qux\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"baz\", \"qux\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsWithCustomQuoteCharacter() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.quoteCharacter('\\'')//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, 'bar \\\"and\\\" baz', qux \\n 'lemon lime', banana, apple\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar \\\"and\\\" baz\", \"qux\"),\n\t\t\tarray(\"lemon lime\", \"banana\", \"apple\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsWithStringDelimiter() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiterString(\",\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, bar \\n baz, qux \\n\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"baz\", \"qux\"));\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfBothDelimitersAreSimultaneouslySet() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.delimiterString(\";\")//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation, \"foo\").findAny())//\n\t\t\t\t.withMessageStartingWith(\"The delimiter and delimiterString attributes cannot be set simultaneously in\")//\n\t\t\t\t.withMessageContaining(\"CsvFileSource\");\n\t}\n\n\t@Test\n\tvoid ignoresCommentedOutEntries() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, bar \\n#baz, qux\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"));\n\t}\n\n\t@Test\n\tvoid honorsCustomCommentCharacter() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.commentCharacter(';')//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"foo, bar \\n;baz, qux\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"));\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"org.junit.jupiter.params.provider.CsvArgumentsProviderTests#\"\n\t\t\t+ \"invalidDelimiterQuoteCharacterAndCommentCharacterCombinations\")\n\tvoid throwsExceptionWhenControlCharactersNotDiffer(Object delimiter, char quoteCharacter, char commentCharacter) {\n\t\tvar builder = csvFileSource().resources(\"test.csv\") //\n\t\t\t\t.quoteCharacter(quoteCharacter).commentCharacter(commentCharacter);\n\n\t\tvar annotation = delimiter instanceof Character c //\n\t\t\t\t? builder.delimiter(c).build() //\n\t\t\t\t: builder.delimiterString(delimiter.toString()).build();\n\n\t\tvar message = \"delimiter or delimiterString: '%s', quoteCharacter: '%s', and commentCharacter: '%s' \"\n\t\t\t\t+ \"must all differ\";\n\t\tassertPreconditionViolationFor(() -> provideArguments(annotation, \"foo\").findAny()) //\n\t\t\t\t.withMessage(message.formatted(delimiter, quoteCharacter, commentCharacter));\n\t}\n\n\t@Test\n\tvoid closesInputStreamForClasspathResource() {\n\t\tvar closed = new AtomicBoolean(false);\n\t\tInputStream inputStream = new ByteArrayInputStream(\"foo\".getBytes()) {\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tclosed.set(true);\n\t\t\t}\n\t\t};\n\t\tvar annotation = csvFileSource().resources(\"test.csv\").build();\n\n\t\tvar arguments = provideArguments(inputStream, annotation);\n\n\t\tassertThat(arguments.count()).isEqualTo(1);\n\t\tassertThat(closed.get()).describedAs(\"closed\").isTrue();\n\t}\n\n\t@Test\n\tvoid closesInputStreamForFile(@TempDir Path tempDir) {\n\t\tvar closed = new AtomicBoolean(false);\n\t\tInputStream inputStream = new ByteArrayInputStream(\"foo\".getBytes()) {\n\n\t\t\t@Override\n\t\t\tpublic void close() {\n\t\t\t\tclosed.set(true);\n\t\t\t}\n\t\t};\n\t\tvar annotation = csvFileSource().files(tempDir.resolve(\"test.csv\").toAbsolutePath().toString()).build();\n\n\t\tvar arguments = provideArguments(inputStream, annotation);\n\n\t\tassertThat(arguments.count()).isEqualTo(1);\n\t\tassertThat(closed.get()).describedAs(\"closed\").isTrue();\n\t}\n\n\t@Test\n\tvoid readsFromSingleClasspathResource() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"\"));\n\t}\n\n\t@Test\n\tvoid readsFromSingleFileWithAbsolutePath(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"single-column.csv\", tempDir.resolve(\"single-column.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"\"));\n\t}\n\n\t@Test\n\tvoid readsFromClasspathResourcesAndFiles(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"single-column.csv\", tempDir.resolve(\"single-column.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).hasSize(2 * 5);\n\t}\n\n\t@Test\n\tvoid readsFromSingleFileWithRelativePath() throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"single-column.csv\", Path.of(\"single-column.csv\"));\n\t\ttry {\n\t\t\tvar annotation = csvFileSource()//\n\t\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t\t.files(csvFile.getFileName().toString())//\n\t\t\t\t\t.build();\n\n\t\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"\"));\n\t\t}\n\t\tfinally {\n\t\t\tFiles.delete(csvFile);\n\t\t}\n\t}\n\n\t@Test\n\tvoid readsFromSingleClasspathResourceWithCustomEmptyValue() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.emptyValue(\"EMPTY\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"EMPTY\"));\n\t}\n\n\t@Test\n\tvoid readsFromMultipleClasspathResources() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\", \"single-column.csv\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).hasSize(10);\n\t}\n\n\t@Test\n\tvoid readsFromSingleClasspathResourceWithHeaders() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.numLinesToSkip(1)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"\"));\n\t}\n\n\t@Test\n\tvoid readsFromSingleClasspathResourceWithMoreHeadersThanLines() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.numLinesToSkip(10)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).isEmpty();\n\t}\n\n\t@Test\n\tvoid readsFromMultipleClasspathResourcesWithHeaders() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\", \"single-column.csv\")//\n\t\t\t\t.numLinesToSkip(1)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"bar\"), array(\"baz\"), array(\"qux\"), array(\"\"), array(\"bar\"),\n\t\t\tarray(\"baz\"), array(\"qux\"), array(\"\"));\n\t}\n\n\t@Test\n\tvoid supportsCsvHeadersInDisplayNames() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"single-column.csv\")//\n\t\t\t\t.useHeadersInDisplayName(true)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tStream<String[]> argumentsAsStrings = arguments.map(array -> {\n\t\t\tString[] strings = new String[array.length];\n\t\t\tfor (int i = 0; i < array.length; i++) {\n\t\t\t\tif (array[i] instanceof ParameterNameAndArgument parameterNameAndArgument) {\n\t\t\t\t\tstrings[i] = parameterNameAndArgument.getName() + \" = \" + parameterNameAndArgument.getPayload();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthrow new IllegalStateException(\"Unexpected argument type: \" + array[i].getClass().getName());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn strings;\n\t\t});\n\n\t\tassertThat(argumentsAsStrings).containsExactly(array(\"foo = bar\"), array(\"foo = baz\"), array(\"foo = qux\"),\n\t\t\tarray(\"foo = \"));\n\t}\n\n\t@Test\n\tvoid throwsExceptionForMissingClasspathResource() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"/does-not-exist.csv\")//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray())//\n\t\t\t\t.withMessageContaining(\"Classpath resource [/does-not-exist.csv] does not exist\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionForBlankClasspathResource() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"    \")//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Classpath resource [    ]\",\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\t}\n\n\t@Test\n\tvoid throwsExceptionForMissingFile() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.files(\"does-not-exist.csv\")//\n\t\t\t\t.build();\n\n\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\n\t\tassertThat(exception).hasMessageContaining(\"File [does-not-exist.csv] could not be read\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionForBlankFile() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.files(\"    \")//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"File [    ]\",\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\t}\n\n\t@Test\n\tvoid throwsExceptionIfResourcesAndFilesAreEmpty() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources()//\n\t\t\t\t.files()//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationNotEmptyFor(\"Resources or files\",\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\t}\n\n\t@Test\n\tvoid throwsExceptionForInvalidCharset() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"Bogus-Charset\")//\n\t\t\t\t.resources(\"/bogus-charset.csv\")//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray())//\n\t\t\t\t.withMessageContaining(\"The charset supplied in Mock for CsvFileSource\")//\n\t\t\t\t.withMessageEndingWith(\"is invalid\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionForInvalidCsvFormat() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"broken.csv\")//\n\t\t\t\t.build();\n\n\t\tvar exception = assertThrows(CsvParsingException.class,\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\n\t\tassertThat(exception)//\n\t\t\t\t.hasMessageStartingWith(\"Failed to parse CSV input configured via Mock for CsvFileSource\")//\n\t\t\t\t.rootCause().satisfies(isCsvParseException());\n\t}\n\n\t@Test\n\tvoid emptyValueIsAnEmptyWithCustomNullValueString() {\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.resources(\"test.csv\")//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.nullValues(\"NIL\")//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(annotation, \"apple, , NIL, ''\\nNIL, NIL, foo, bar\");\n\n\t\tassertThat(arguments).containsExactly(array(\"apple\", null, null, \"''\"), array(null, null, \"foo\", \"bar\"));\n\t}\n\n\t@Test\n\tvoid readsLineFromDefaultMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"default-max-chars.csv\", tempDir.resolve(\"default-max-chars.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"default-max-chars.csv\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).hasSize(2);\n\t}\n\n\t@Test\n\tvoid readsLineFromExceedsMaxCharsFileWithCustomExplicitConfig(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"exceeds-default-max-chars.csv\",\n\t\t\ttempDir.resolve(\"exceeds-default-max-chars.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"exceeds-default-max-chars.csv\")//\n\t\t\t\t.maxCharsPerColumn(4097)//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).hasSize(2);\n\t}\n\n\t@Test\n\tvoid readsLineFromExceedsMaxCharsFileWithCustomUnlimitedConfig(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = tempDir.resolve(\"test.csv\");\n\t\ttry (var out = Files.newBufferedWriter(csvFile)) {\n\t\t\tvar chunks = 10;\n\t\t\tvar chunk = \"a\".repeat(8192);\n\t\t\tfor (long i = 0; i < chunks; i++) {\n\t\t\t\tout.write(chunk);\n\t\t\t}\n\t\t}\n\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.maxCharsPerColumn(-1)//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new CsvFileArgumentsProvider(), annotation);\n\n\t\tassertThat(arguments).hasSize(1);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { Integer.MIN_VALUE, -2, 0 })\n\tvoid throwsExceptionWhenMaxCharsPerColumnIsNotPositiveNumberOrMinusOne(int maxCharsPerColumn, @TempDir Path tempDir)\n\t\t\tthrows Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"exceeds-default-max-chars.csv\",\n\t\t\ttempDir.resolve(\"exceeds-default-max-chars.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"exceeds-default-max-chars.csv\")//\n\t\t\t\t.maxCharsPerColumn(maxCharsPerColumn)//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(new CsvFileArgumentsProvider(), annotation).findAny())//\n\t\t\t\t.withMessageStartingWith(\"maxCharsPerColumn must be a positive number or -1: \" + maxCharsPerColumn);\n\t}\n\n\t@Test\n\tvoid throwsExceptionForExceedsMaxCharsFileWithDefaultConfig(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"exceeds-default-max-chars.csv\",\n\t\t\ttempDir.resolve(\"exceeds-default-max-chars.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"exceeds-default-max-chars.csv\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.build();\n\n\t\tvar exception = assertThrows(CsvParsingException.class,\n\t\t\t() -> provideArguments(new CsvFileArgumentsProvider(), annotation).toArray());\n\n\t\tassertThat(exception)//\n\t\t\t\t.hasMessageStartingWith(\"Failed to parse CSV input configured via Mock for CsvFileSource\")//\n\t\t\t\t.rootCause().satisfies(isCsvParseException());\n\t}\n\n\t@Test\n\tvoid ignoresLeadingAndTrailingSpaces(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"leading-trailing-spaces.csv\",\n\t\t\ttempDir.resolve(\"leading-trailing-spaces.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"leading-trailing-spaces.csv\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.ignoreLeadingAndTrailingWhitespace(true)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new ByteArrayInputStream(Files.readAllBytes(csvFile)), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\"ab\", \"cd\"), array(\"ef\", \"gh\"));\n\t}\n\n\t@Test\n\tvoid trimsLeadingAndTrailingSpaces(@TempDir Path tempDir) throws Exception {\n\t\tvar csvFile = writeClasspathResourceToFile(\"leading-trailing-spaces.csv\",\n\t\t\ttempDir.resolve(\"leading-trailing-spaces.csv\"));\n\t\tvar annotation = csvFileSource()//\n\t\t\t\t.encoding(\"ISO-8859-1\")//\n\t\t\t\t.resources(\"leading-trailing-spaces.csv\")//\n\t\t\t\t.files(csvFile.toAbsolutePath().toString())//\n\t\t\t\t.delimiter(',')//\n\t\t\t\t.ignoreLeadingAndTrailingWhitespace(false)//\n\t\t\t\t.build();\n\n\t\tvar arguments = provideArguments(new ByteArrayInputStream(Files.readAllBytes(csvFile)), annotation);\n\n\t\tassertThat(arguments).containsExactly(array(\" ab \", \" cd\"), array(\"ef \", \"gh\"));\n\t}\n\n\tprivate Stream<Object[]> provideArguments(CsvFileSource annotation, String content) {\n\t\treturn provideArguments(new ByteArrayInputStream(content.getBytes(UTF_8)), annotation);\n\t}\n\n\tprivate Stream<Object[]> provideArguments(InputStream inputStream, CsvFileSource annotation) {\n\t\tvar provider = new CsvFileArgumentsProvider(new InputStreamProvider() {\n\t\t\t@Override\n\t\t\tpublic InputStream openClasspathResource(Class<?> baseClass, String path) {\n\t\t\t\tassertThat(path).isEqualTo(annotation.resources()[0]);\n\t\t\t\treturn inputStream;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic InputStream openFile(String path) {\n\t\t\t\tassertThat(path).isEqualTo(annotation.files()[0]);\n\t\t\t\treturn inputStream;\n\t\t\t}\n\t\t});\n\t\treturn provideArguments(provider, annotation);\n\t}\n\n\tprivate Stream<Object[]> provideArguments(CsvFileArgumentsProvider provider, CsvFileSource annotation) {\n\t\tprovider.accept(annotation);\n\t\tvar context = mock(ExtensionContext.class);\n\t\twhen(context.getTestClass()).thenReturn(Optional.of(CsvFileArgumentsProviderTests.class));\n\t\tdoCallRealMethod().when(context).getRequiredTestClass();\n\t\treturn provider.provideArguments(mock(), context).map(Arguments::get);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\tprivate static <T> @Nullable T[] array(@Nullable T... elements) {\n\t\treturn elements;\n\t}\n\n\tprivate static Path writeClasspathResourceToFile(String name, Path target) throws Exception {\n\t\ttry (var in = CsvFileArgumentsProviderTests.class.getResourceAsStream(name)) {\n\t\t\tFiles.copy(in, target);\n\t\t}\n\t\treturn target;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;\nimport static org.junit.jupiter.params.provider.EnumArgumentsProviderTests.EnumWithFourConstants.BAR;\nimport static org.junit.jupiter.params.provider.EnumArgumentsProviderTests.EnumWithFourConstants.BAZ;\nimport static org.junit.jupiter.params.provider.EnumArgumentsProviderTests.EnumWithFourConstants.FOO;\nimport static org.junit.jupiter.params.provider.EnumArgumentsProviderTests.EnumWithFourConstants.QUX;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Arrays;\nimport java.util.Optional;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.params.provider.EnumSource.Mode;\nimport org.junit.jupiter.params.support.ParameterDeclaration;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\n\n/**\n * @since 5.0\n */\nclass EnumArgumentsProviderTests {\n\n\tfinal ParameterDeclarations parameters = mock();\n\tfinal ExtensionContext extensionContext = mock();\n\n\t@Test\n\tvoid providesAllEnumConstants() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class);\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO }, new Object[] { BAR }, new Object[] { BAZ },\n\t\t\tnew Object[] { QUX });\n\t}\n\n\t@Test\n\tvoid provideSingleEnumConstant() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"FOO\");\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO });\n\t}\n\n\t@Test\n\tvoid provideAllEnumConstantsWithNamingAll() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"FOO\", \"BAR\");\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO }, new Object[] { BAR });\n\t}\n\n\t@Test\n\tvoid duplicateConstantNameIsDetected() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> provideArguments(EnumWithFourConstants.class, \"FOO\", \"BAR\", \"FOO\").findAny())//\n\t\t\t\t\t.withMessageContaining(\"Duplicate enum constant name(s) found\");\n\t}\n\n\t@Test\n\tvoid invalidConstantNameIsDetected() {\n\t\tassertPreconditionViolationFor(() -> provideArguments(EnumWithFourConstants.class, \"FO0\", \"B4R\").findAny())//\n\t\t\t\t.withMessageContaining(\"Invalid enum constant name(s) in\");\n\t}\n\n\t@Test\n\tvoid invalidPatternIsDetected() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> provideArguments(EnumWithFourConstants.class, Mode.MATCH_ALL, \"(\", \")\").findAny())//\n\t\t\t\t\t.withMessageContaining(\"Pattern compilation failed\");\n\t}\n\n\t@Test\n\tvoid providesEnumConstantsBasedOnTestMethod() {\n\t\torg.junit.jupiter.params.support.ParameterDeclaration firstParameterDeclaration = mock();\n\t\twhen(firstParameterDeclaration.getParameterType()).thenAnswer(__ -> EnumWithFourConstants.class);\n\t\twhen(parameters.getFirst()).thenReturn(Optional.of(firstParameterDeclaration));\n\n\t\tvar arguments = provideArguments(NullEnum.class);\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO }, new Object[] { BAR }, new Object[] { BAZ },\n\t\t\tnew Object[] { QUX });\n\t}\n\n\t@Test\n\tvoid incorrectParameterTypeIsDetected() {\n\t\tParameterDeclaration firstParameterDeclaration = mock();\n\t\twhen(firstParameterDeclaration.getParameterType()).thenAnswer(__ -> Object.class);\n\t\twhen(parameters.getFirst()).thenReturn(Optional.of(firstParameterDeclaration));\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(NullEnum.class).findAny())//\n\t\t\t\t.withMessageStartingWith(\"First parameter must reference an Enum type\");\n\t}\n\n\t@Test\n\tvoid methodsWithoutParametersAreDetected() {\n\t\twhen(parameters.getSourceElementDescription()).thenReturn(\"method\");\n\n\t\tassertPreconditionViolationFor(() -> provideArguments(NullEnum.class).findAny())//\n\t\t\t\t.withMessageStartingWith(\"There must be at least one declared parameter for method\");\n\t}\n\n\t@Test\n\tvoid providesEnumConstantsStartingFromBar() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"BAR\", \"\", Mode.INCLUDE);\n\n\t\tassertThat(arguments).containsExactly(new Object[] { BAR }, new Object[] { BAZ }, new Object[] { QUX });\n\t}\n\n\t@Test\n\tvoid providesEnumConstantsEndingAtBaz() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"\", \"BAZ\", Mode.INCLUDE);\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO }, new Object[] { BAR }, new Object[] { BAZ });\n\t}\n\n\t@Test\n\tvoid providesEnumConstantsFromBarToBaz() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"BAR\", \"BAZ\", Mode.INCLUDE);\n\n\t\tassertThat(arguments).containsExactly(new Object[] { BAR }, new Object[] { BAZ });\n\t}\n\n\t@Test\n\tvoid providesEnumConstantsFromFooToBazWhileExcludingBar() {\n\t\tvar arguments = provideArguments(EnumWithFourConstants.class, \"FOO\", \"BAZ\", Mode.EXCLUDE, \"BAR\");\n\n\t\tassertThat(arguments).containsExactly(new Object[] { FOO }, new Object[] { BAZ });\n\t}\n\n\t@Test\n\tvoid providesNoEnumConstant() {\n\t\tvar arguments = provideArguments(EnumWithNoConstant.class);\n\n\t\tassertThat(arguments).isEmpty();\n\t}\n\n\t@Test\n\tvoid invalidConstantNameIsDetectedInRange() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> provideArguments(EnumWithFourConstants.class, \"FOO\", \"BAZ\", Mode.EXCLUDE, \"QUX\").findAny())//\n\t\t\t\t\t.withMessageContaining(\"Invalid enum constant name(s) in\");\n\t}\n\n\t@Test\n\tvoid invalidStartingRangeIsDetected() {\n\t\tassertThatIllegalArgumentException()//\n\t\t\t\t.isThrownBy(() -> provideArguments(EnumWithFourConstants.class, \"B4R\", \"\", Mode.INCLUDE).findAny())//\n\t\t\t\t.withMessageContaining(\"No enum constant\");\n\t}\n\n\t@Test\n\tvoid invalidEndingRangeIsDetected() {\n\t\tassertThatIllegalArgumentException()//\n\t\t\t\t.isThrownBy(() -> provideArguments(EnumWithFourConstants.class, \"\", \"B4R\", Mode.INCLUDE).findAny())//\n\t\t\t\t.withMessageContaining(\"No enum constant\");\n\t}\n\n\t@Test\n\tvoid invalidRangeOrderIsDetected() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> provideArguments(EnumWithFourConstants.class, \"BAR\", \"FOO\", Mode.INCLUDE).findAny())//\n\t\t\t\t\t.withMessageContaining(\"Invalid enum range\");\n\t}\n\n\t@Test\n\tvoid invalidRangeIsDetectedWhenEnumWithNoConstantIsProvided() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> provideArguments(EnumWithNoConstant.class, \"BAR\", \"FOO\", Mode.INCLUDE).findAny())//\n\t\t\t\t\t.withMessageContaining(\"No enum constant\");\n\t}\n\n\tstatic class TestCase {\n\t\tvoid methodWithoutParameters() {\n\t\t}\n\t}\n\n\tenum EnumWithFourConstants {\n\t\tFOO, BAR, BAZ, QUX\n\t}\n\n\tenum EnumWithNoConstant {\n\t}\n\n\tprivate <E extends Enum<E>> Stream<Object[]> provideArguments(Class<E> enumClass, String... names) {\n\t\treturn provideArguments(enumClass, Mode.INCLUDE, names);\n\t}\n\n\tprivate <E extends Enum<E>> Stream<Object[]> provideArguments(Class<E> enumClass, Mode mode, String... names) {\n\t\treturn provideArguments(enumClass, \"\", \"\", mode, names);\n\t}\n\n\tprivate <E extends Enum<E>> Stream<Object[]> provideArguments(Class<E> enumClass, String from, String to, Mode mode,\n\t\t\tString... names) {\n\t\tvar annotation = mock(EnumSource.class);\n\t\twhen(annotation.value()).thenAnswer(__ -> enumClass);\n\t\twhen(annotation.from()).thenReturn(from);\n\t\twhen(annotation.to()).thenReturn(to);\n\t\twhen(annotation.mode()).thenReturn(mode);\n\t\twhen(annotation.names()).thenReturn(names);\n\t\twhen(annotation.toString()).thenReturn(\n\t\t\t\"@EnumSource(value=%s.class, from=%s, to=%s, mode=%s, names=%s)\".formatted(enumClass.getSimpleName(), from,\n\t\t\t\tto, mode, Arrays.toString(names)));\n\n\t\tvar provider = new EnumArgumentsProvider();\n\t\tprovider.accept(annotation);\n\t\treturn provider.provideArguments(parameters, extensionContext).map(Arguments::get);\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/EnumSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static java.util.stream.Collectors.toSet;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.INCLUDE;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ANY;\nimport static org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_NONE;\nimport static org.junit.jupiter.params.provider.EnumSourceTests.EnumWithThreeConstants.BAR;\nimport static org.junit.jupiter.params.provider.EnumSourceTests.EnumWithThreeConstants.BAZ;\nimport static org.junit.jupiter.params.provider.EnumSourceTests.EnumWithThreeConstants.FOO;\n\nimport java.util.EnumSet;\nimport java.util.Set;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 5.0\n */\nclass EnumSourceTests {\n\n\t@Test\n\tvoid includeNamesWithAll() {\n\t\tassertAll(\"include names with all\", //\n\t\t\t() -> assertTrue(INCLUDE.select(FOO, allOf(EnumWithThreeConstants::name))),\n\t\t\t() -> assertTrue(INCLUDE.select(BAR, allOf(EnumWithThreeConstants::name))),\n\t\t\t() -> assertTrue(INCLUDE.select(BAZ, allOf(EnumWithThreeConstants::name))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid includeNamesWithSingleton() {\n\t\tassertAll(\"include names with singleton\", //\n\t\t\t() -> assertTrue(INCLUDE.select(FOO, Set.of(FOO.name()))),\n\t\t\t() -> assertTrue(INCLUDE.select(BAR, Set.of(BAR.name()))),\n\t\t\t() -> assertTrue(INCLUDE.select(BAZ, Set.of(BAZ.name()))) //\n\t\t);\n\t\tassertAll(\"include names with singleton complement\", //\n\t\t\t() -> assertFalse(INCLUDE.select(BAR, Set.of(FOO.name()))),\n\t\t\t() -> assertFalse(INCLUDE.select(BAZ, Set.of(FOO.name()))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid excludeNames() {\n\t\tassertAll(\"exclude name with none excluded\", //\n\t\t\t() -> assertTrue(EXCLUDE.select(FOO, Set.of())), //\n\t\t\t() -> assertTrue(EXCLUDE.select(BAR, Set.of())), //\n\t\t\t() -> assertTrue(EXCLUDE.select(BAZ, Set.of())) //\n\t\t);\n\t\tassertAll(\"exclude name with FOO excluded\", //\n\t\t\t() -> assertFalse(EXCLUDE.select(FOO, Set.of(FOO.name()))),\n\t\t\t() -> assertTrue(EXCLUDE.select(BAR, Set.of(FOO.name()))),\n\t\t\t() -> assertTrue(EXCLUDE.select(BAZ, Set.of(FOO.name()))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid matchesAll() {\n\t\tassertAll(\"matches all\", //\n\t\t\t() -> assertTrue(MATCH_ALL.select(FOO, Set.of(\"F..\"))),\n\t\t\t() -> assertTrue(MATCH_ALL.select(BAR, Set.of(\"B..\"))),\n\t\t\t() -> assertTrue(MATCH_ALL.select(BAZ, Set.of(\"B..\"))) //\n\t\t);\n\t\tassertAll(\"matches all fails if not all match\", //\n\t\t\t() -> assertFalse(MATCH_ALL.select(FOO, Set.of(\"F..\", \".\"))),\n\t\t\t() -> assertFalse(MATCH_ALL.select(BAR, Set.of(\"B..\", \".\"))),\n\t\t\t() -> assertFalse(MATCH_ALL.select(BAZ, Set.of(\"B..\", \".\"))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid matchesAny() {\n\t\tassertAll(\"matches any\", //\n\t\t\t() -> assertTrue(MATCH_ANY.select(FOO, Set.of(\"B..\", \"^F.*\"))),\n\t\t\t() -> assertTrue(MATCH_ANY.select(BAR, Set.of(\"B\", \"B.\", \"B..\"))),\n\t\t\t() -> assertTrue(MATCH_ANY.select(BAZ, Set.of(\"^.+[zZ]$\"))));\n\t}\n\n\t@Test\n\tvoid matchesNone() {\n\t\tassertAll(\"matches none fails if any match\", //\n\t\t\t() -> assertFalse(MATCH_NONE.select(FOO, Set.of(\"F..\"))),\n\t\t\t() -> assertFalse(MATCH_NONE.select(FOO, Set.of(\"B..\", \"F..\"))),\n\t\t\t() -> assertFalse(MATCH_NONE.select(BAZ, Set.of(\"B.\", \"F.\", \"^.+[zZ]$\"))));\n\n\t\tassertAll(\"matches none\", //\n\t\t\t() -> assertTrue(MATCH_NONE.select(FOO, Set.of())), //\n\t\t\t() -> assertTrue(MATCH_NONE.select(FOO, Set.of(\"F.\"))),\n\t\t\t() -> assertTrue(MATCH_NONE.select(FOO, Set.of(\"B..\"))),\n\t\t\t() -> assertTrue(MATCH_NONE.select(BAZ, Set.of(\".\", \"B.\", \"F.\"))));\n\t}\n\n\tenum EnumWithThreeConstants {\n\t\tFOO, BAR, BAZ\n\n\t}\n\n\tstatic Set<String> allOf(Function<EnumWithThreeConstants, String> mapper) {\n\t\treturn EnumSet.allOf(EnumWithThreeConstants.class).stream().map(mapper).collect(toSet());\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/FieldArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryWithDefaultExtensions;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethod;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.doCallRealMethod;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Supplier;\nimport java.util.stream.DoubleStream;\nimport java.util.stream.IntStream;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.execution.DefaultExecutableInvoker;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.support.ParameterDeclarations;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.test.TestClassLoader;\n\n/**\n * Unit tests for {@link FieldArgumentsProvider}.\n *\n * @since 5.11\n */\nclass FieldArgumentsProviderTests {\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplier() {\n\t\tvar arguments = provideArguments(\"stringStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIntStreamSupplier() {\n\t\tvar arguments = provideArguments(\"intStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(1), array(2));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingLongStreamSupplier() {\n\t\tvar arguments = provideArguments(\"longStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(1L), array(2L));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingDoubleStreamSupplier() {\n\t\tvar arguments = provideArguments(\"doubleStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(1.2), array(3.4));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplierOfIntArrays() {\n\t\tvar arguments = provideArguments(\"intArrayStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tnew Object[] { new int[] { 1, 2 } }, //\n\t\t\tnew Object[] { new int[] { 3, 4 } } //\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplierOfTwoDimensionalIntArrays() {\n\t\tvar arguments = provideArguments(\"twoDimensionalIntArrayStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tarray((Object) new int[][] { { 1, 2 }, { 2, 3 } }), //\n\t\t\tarray((Object) new int[][] { { 4, 5 }, { 5, 6 } }) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplierOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplierOfTwoDimensionalObjectArrays() {\n\t\tvar arguments = provideArguments(\"twoDimensionalObjectArrayStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tarray((Object) array(array(\"a\", 1), array(\"b\", 2))), //\n\t\t\tarray((Object) array(array(\"c\", 3), array(\"d\", 4))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamSupplierOfArguments() {\n\t\tvar arguments = provideArguments(\"argumentsStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIterable() {\n\t\tvar arguments = provideArguments(\"stringIterable\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingMultipleFields() {\n\t\tvar arguments = provideArguments(\"stringStreamSupplier\", \"stringIterable\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIterableOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayIterable\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingListOfStrings() {\n\t\tvar arguments = provideArguments(\"stringList\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingListOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayList\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsFromNonStaticFieldWhenStaticIsNotRequired() {\n\t\tvar lifecyclePerClass = true;\n\t\tvar arguments = provideArguments(NonStaticTestCase.class, lifecyclePerClass, \"nonStaticStringStreamSupplier\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingDefaultFieldName() {\n\t\tvar testClass = DefaultFieldNameTestCase.class;\n\t\tvar methodName = \"testDefaultFieldName\";\n\t\tvar testMethod = findMethod(testClass, methodName, String.class).get();\n\n\t\tvar arguments = provideArguments(testClass, testMethod, false, new String[0]);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalField() {\n\t\tvar arguments = provideArguments(ExternalFields.class.getName() + \"#strings\");\n\n\t\tassertThat(arguments).containsExactly(array(\"string1\"), array(\"string2\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFieldInTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(TestCase.class, ExternalFields.class)) {\n\t\t\tvar testClass = testClassLoader.loadClass(TestCase.class.getName());\n\t\t\tvar fullyQualifiedFieldName = ExternalFields.class.getName() + \"#strings\";\n\n\t\t\tassertThat(testClass.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar arguments = provideArguments(testClass, false, fullyQualifiedFieldName);\n\t\t\tassertThat(arguments).containsExactly(array(\"string1\"), array(\"string2\"));\n\n\t\t\tvar field = FieldArgumentsProvider.findField(testClass, fullyQualifiedFieldName);\n\t\t\tassertThat(field).isNotNull();\n\t\t\tassertThat(field.getName()).isEqualTo(\"strings\");\n\n\t\t\tvar declaringClass = field.getDeclaringClass();\n\t\t\tassertThat(declaringClass.getName()).isEqualTo(ExternalFields.class.getName());\n\t\t\tassertThat(declaringClass).isNotEqualTo(ExternalFields.class);\n\t\t\tassertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader);\n\t\t}\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFieldFromStaticNestedClass() {\n\t\tvar arguments = provideArguments(ExternalFields.Nested.class.getName() + \"#strings\");\n\n\t\tassertThat(arguments).containsExactly(array(\"nested string1\"), array(\"nested string2\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalAndInternalFieldsCombined() {\n\t\tvar arguments = provideArguments(\"stringStreamSupplier\", ExternalFields.class.getName() + \"#strings\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"string1\"), array(\"string2\"));\n\t}\n\n\t@Nested\n\tclass PrimitiveArrays {\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingBooleanArray() {\n\t\t\tvar arguments = provideArguments(\"booleanArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(Boolean.TRUE), array(Boolean.FALSE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingByteArray() {\n\t\t\tvar arguments = provideArguments(\"byteArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array((byte) 1), array(Byte.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingCharArray() {\n\t\t\tvar arguments = provideArguments(\"charArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array((char) 1), array(Character.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingDoubleArray() {\n\t\t\tvar arguments = provideArguments(\"doubleArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(1d), array(Double.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFloatArray() {\n\t\t\tvar arguments = provideArguments(\"floatArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(1f), array(Float.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingIntArray() {\n\t\t\tvar arguments = provideArguments(\"intArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(47), array(Integer.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLongArray() {\n\t\t\tvar arguments = provideArguments(\"longArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(47L), array(Long.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingShortArray() {\n\t\t\tvar arguments = provideArguments(\"shortArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array((short) 47), array(Short.MIN_VALUE));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ObjectArrays {\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingObjectArray() {\n\t\t\tvar arguments = provideArguments(\"objectArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(42), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingStringArray() {\n\t\t\tvar arguments = provideArguments(\"stringArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsing2dStringArray() {\n\t\t\tvar arguments = provideArguments(\"twoDimensionalStringArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\", \"bar\"), array(\"baz\", \"qux\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsing2dObjectArray() {\n\t\t\tvar arguments = provideArguments(\"twoDimensionalObjectArray\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ErrorCases {\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenNonStaticLocalFieldIsReferencedWithLifecyclePerMethodSemantics() {\n\t\t\tvar lifecyclePerClass = false;\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(NonStaticTestCase.class, lifecyclePerClass,\n\t\t\t\t\"nonStaticStringStreamSupplier\").toArray())//\n\t\t\t\t\t\t.withMessageContainingAll(\"Field '\",\n\t\t\t\t\t\t\t\"' must be static: local @FieldSource fields must be static \",\n\t\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \",\n\t\t\t\t\t\t\t\"external @FieldSource fields must always be static.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenNonStaticExternalFieldIsReferencedWithLifecyclePerMethodSemantics() {\n\t\t\tvar factoryClass = NonStaticTestCase.class.getName();\n\t\t\tvar field = factoryClass + \"#nonStaticStringStreamSupplier\";\n\t\t\tvar lifecyclePerClass = false;\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(TestCase.class, lifecyclePerClass, field).toArray())//\n\t\t\t\t\t.withMessageContainingAll(\"Field '\", \"' must be static: local @FieldSource fields must be static \",\n\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \",\n\t\t\t\t\t\t\"external @FieldSource fields must always be static.\");\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenNonStaticExternalFieldIsReferencedWithLifecyclePerClassSemantics() {\n\t\t\tvar factoryClass = NonStaticTestCase.class.getName();\n\t\t\tvar field = factoryClass + \"#nonStaticStringStreamSupplier\";\n\t\t\tboolean lifecyclePerClass = true;\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(TestCase.class, lifecyclePerClass, field).toArray())//\n\t\t\t\t\t.withMessageContainingAll(\"Field '\", \"' must be static: local @FieldSource fields must be static \",\n\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \",\n\t\t\t\t\t\t\"external @FieldSource fields must always be static.\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"org.example.MyUtils\", \"org.example.MyUtils#\", \"#fieldName\" })\n\t\tvoid throwsExceptionWhenFullyQualifiedFieldNameSyntaxIsInvalid(String fieldName) {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(fieldName).toArray())//\n\t\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\t\t[%s] is not a valid fully qualified field name: \\\n\t\t\t\t\t\t\tit must start with a fully qualified class name followed by a \\\n\t\t\t\t\t\t\t'#' and then the field name.\"\"\", fieldName, TestCase.class.getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenClassForExternalFieldCannotBeLoaded() {\n\t\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t\t() -> provideArguments(\"com.example.NonExistentClass#strings\").toArray());\n\n\t\t\tassertThat(exception.getMessage()).isEqualTo(\"Could not load class [com.example.NonExistentClass]\");\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFieldDoesNotExist() {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(\"nonExistentField\").toArray())//\n\t\t\t\t\t.withMessage(\"Could not find field named [nonExistentField] in class [%s]\",\n\t\t\t\t\t\tTestCase.class.getName());\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"nonExistentField\", \"strings()\" })\n\t\tvoid throwsExceptionWhenExternalFieldDoesNotExist(String fieldName) {\n\t\t\tString factoryClass = ExternalFields.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(factoryClass + \"#\" + fieldName).toArray())//\n\t\t\t\t\t.withMessage(\"Could not find field named [%s] in class [%s]\", fieldName, factoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFieldHasNullValue() {\n\t\t\tString field = \"nullList\";\n\t\t\tString factoryClass = TestCase.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(field).toArray())//\n\t\t\t\t\t.withMessage(\"The value of field [%s] in class [%s] must not be null\", field, factoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFieldHasInvalidReturnType() {\n\t\t\tString field = \"object\";\n\t\t\tString factoryClass = TestCase.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(field).toArray())//\n\t\t\t\t\t.withMessage(\"The value of field [%s] in class [%s] must be convertible to a Stream\", field,\n\t\t\t\t\t\tfactoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenExternalFieldHasInvalidReturnType() {\n\t\t\tString factoryClass = ExternalFields.class.getName();\n\t\t\tString fieldName = \"object\";\n\t\t\tString field = factoryClass + \"#\" + fieldName;\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(TestCase.class, false, field).toArray())//\n\t\t\t\t\t.withMessage(\"The value of field [%s] in class [%s] must be convertible to a Stream\", fieldName,\n\t\t\t\t\t\tfactoryClass);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"stream\", \"intStream\", \"longStream\", \"doubleStream\" })\n\t\tvoid throwsExceptionWhenLocalFieldHasStreamReturnType(String field) {\n\t\t\tString factoryClass = TestCase.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(field).toArray())//\n\t\t\t\t\t.withMessage(\"The value of field [%s] in class [%s] must not be a stream\", field, factoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFieldHasIteratorReturnType() {\n\t\t\tString field = \"iterator\";\n\t\t\tString factoryClass = TestCase.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(field).toArray())//\n\t\t\t\t\t.withMessage(\"The value of field [%s] in class [%s] must not be an Iterator\", field, factoryClass);\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static Object[] array(Object... objects) {\n\t\treturn objects;\n\t}\n\n\tprivate static Stream<Object[]> provideArguments(String... fieldNames) {\n\t\treturn provideArguments(TestCase.class, false, fieldNames);\n\t}\n\n\tprivate static Stream<Object[]> provideArguments(Class<?> testClass, boolean allowNonStaticMethod,\n\t\t\tString... fieldNames) {\n\n\t\t// Ensure we have a non-null test method, even if it's not a real test method.\n\t\t// If this throws an exception, make sure that the supplied test class defines a \"void test()\" method.\n\t\tMethod testMethod = ReflectionSupport.findMethod(testClass, \"test\").get();\n\t\treturn provideArguments(testClass, testMethod, allowNonStaticMethod, fieldNames);\n\t}\n\n\tprivate static Stream<Object[]> provideArguments(Class<?> testClass, Method testMethod,\n\t\t\tboolean allowNonStaticMethod, String... fieldNames) {\n\n\t\tvar extensionRegistry = createRegistryWithDefaultExtensions(mock());\n\t\tvar fieldSource = mock(FieldSource.class);\n\n\t\twhen(fieldSource.value()).thenReturn(fieldNames);\n\n\t\tvar parameters = mock(ParameterDeclarations.class);\n\t\tvar extensionContext = mock(ExtensionContext.class);\n\t\twhen(extensionContext.getTestClass()).thenReturn(Optional.of(testClass));\n\t\twhen(extensionContext.getTestMethod()).thenReturn(Optional.of(testMethod));\n\t\twhen(extensionContext.getExecutableInvoker()).thenReturn(\n\t\t\tnew DefaultExecutableInvoker(extensionContext, extensionRegistry));\n\n\t\tdoCallRealMethod().when(extensionContext).getRequiredTestMethod();\n\t\tdoCallRealMethod().when(extensionContext).getRequiredTestClass();\n\n\t\tvar testInstance = allowNonStaticMethod ? ReflectionSupport.newInstance(testClass) : null;\n\t\twhen(extensionContext.getTestInstance()).thenReturn(Optional.ofNullable(testInstance));\n\n\t\tvar lifeCycle = allowNonStaticMethod ? Lifecycle.PER_CLASS : Lifecycle.PER_METHOD;\n\t\twhen(extensionContext.getTestInstanceLifecycle()).thenReturn(Optional.of(lifeCycle));\n\n\t\tvar provider = new FieldArgumentsProvider();\n\t\tprovider.accept(fieldSource);\n\t\treturn provider.provideArguments(parameters, extensionContext).map(Arguments::get);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class DefaultFieldNameTestCase {\n\n\t\t// Test\n\t\tvoid testDefaultFieldName(String param) {\n\t\t}\n\n\t\t// Field\n\t\tstatic List<String> testDefaultFieldName = List.of(\"foo\", \"bar\");\n\t}\n\n\tstatic class TestCase {\n\n\t\tvoid test() {\n\t\t}\n\n\t\t// --- Invalid ---------------------------------------------------------\n\n\t\t@Nullable\n\t\tstatic List<String> nullList = null;\n\n\t\tstatic Object object = -1;\n\n\t\tstatic Stream<String> stream = Stream.of(\"foo\", \"bar\");\n\n\t\tstatic DoubleStream doubleStream = DoubleStream.of(1.2, 3.4);\n\n\t\tstatic IntStream intStream = IntStream.of(1, 2);\n\n\t\tstatic LongStream longStream = LongStream.of(1L, 2L);\n\n\t\tstatic Iterator<String> iterator = List.of(\"foo\", \"bar\").iterator();\n\n\t\t// --- Stream Supplier -------------------------------------------------\n\n\t\tstatic Supplier<Stream<String>> stringStreamSupplier = () -> Stream.of(\"foo\", \"bar\");\n\n\t\tstatic Supplier<DoubleStream> doubleStreamSupplier = () -> DoubleStream.of(1.2, 3.4);\n\n\t\tstatic Supplier<LongStream> longStreamSupplier = () -> LongStream.of(1L, 2L);\n\n\t\tstatic Supplier<IntStream> intStreamSupplier = () -> IntStream.of(1, 2);\n\n\t\tstatic Supplier<Stream<int[]>> intArrayStreamSupplier = //\n\t\t\t() -> Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 });\n\n\t\tstatic Supplier<Stream<int[][]>> twoDimensionalIntArrayStreamSupplier = //\n\t\t\t() -> Stream.of(new int[][] { { 1, 2 }, { 2, 3 } }, new int[][] { { 4, 5 }, { 5, 6 } });\n\n\t\tstatic Supplier<Stream<Object[]>> objectArrayStreamSupplier = //\n\t\t\t() -> Stream.of(new Object[] { \"foo\", 42 }, new Object[] { \"bar\", 23 });\n\n\t\tstatic Supplier<Stream<Object[][]>> twoDimensionalObjectArrayStreamSupplier = //\n\t\t\t() -> Stream.of(new Object[][] { { \"a\", 1 }, { \"b\", 2 } }, new Object[][] { { \"c\", 3 }, { \"d\", 4 } });\n\n\t\tstatic Supplier<Stream<Arguments>> argumentsStreamSupplier = //\n\t\t\t() -> objectArrayStreamSupplier.get().map(Arguments::of);\n\n\t\t// --- Collection / Iterable -------------------------------------------\n\n\t\tstatic List<String> stringList = List.of(\"foo\", \"bar\");\n\n\t\tstatic List<Object[]> objectArrayList = List.of(array(\"foo\", 42), array(\"bar\", 23));\n\n\t\tstatic Iterable<String> stringIterable = stringList::iterator;\n\n\t\tstatic Iterable<Object[]> objectArrayIterable = objectArrayList::iterator;\n\n\t\t// --- Array of primitives ---------------------------------------------\n\n\t\tstatic boolean[] booleanArray = new boolean[] { true, false };\n\n\t\tstatic byte[] byteArray = new byte[] { (byte) 1, Byte.MIN_VALUE };\n\n\t\tstatic char[] charArray = new char[] { (char) 1, Character.MIN_VALUE };\n\n\t\tstatic double[] doubleArray = new double[] { 1d, Double.MIN_VALUE };\n\n\t\tstatic float[] floatArray = new float[] { 1f, Float.MIN_VALUE };\n\n\t\tstatic int[] intArray = new int[] { 47, Integer.MIN_VALUE };\n\n\t\tstatic long[] longArray = new long[] { 47L, Long.MIN_VALUE };\n\n\t\tstatic short[] shortArray = new short[] { (short) 47, Short.MIN_VALUE };\n\n\t\t// --- Array of objects ------------------------------------------------\n\n\t\tstatic Object[] objectArray = new Object[] { 42, \"bar\" };\n\n\t\tstatic String[] stringArray = new String[] { \"foo\", \"bar\" };\n\n\t\tstatic String[][] twoDimensionalStringArray = new String[][] { { \"foo\", \"bar\" }, { \"baz\", \"qux\" } };\n\n\t\tstatic Object[][] twoDimensionalObjectArray = new Object[][] { { \"foo\", 42 }, { \"bar\", 23 } };\n\n\t}\n\n\t// This test case mimics @TestInstance(Lifecycle.PER_CLASS)\n\tstatic class NonStaticTestCase {\n\n\t\tvoid test() {\n\t\t}\n\n\t\tSupplier<Stream<String>> nonStaticStringStreamSupplier = () -> Stream.of(\"foo\", \"bar\");\n\t}\n\n\tstatic class ExternalFields {\n\n\t\tstatic Object object = -1;\n\n\t\tstatic List<String> strings = List.of(\"string1\", \"string2\");\n\n\t\tstatic class Nested {\n\n\t\t\tstatic List<String> strings = List.of(\"nested string1\", \"nested string2\");\n\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MethodArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.engine.extension.MutableExtensionRegistry.createRegistryWithDefaultExtensions;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.ReflectionUtils.findMethod;\nimport static org.mockito.Mockito.doCallRealMethod;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.stream.Collectors;\nimport java.util.stream.DoubleStream;\nimport java.util.stream.IntStream;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.engine.execution.DefaultExecutableInvoker;\nimport org.junit.jupiter.engine.extension.MutableExtensionRegistry;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 5.0\n */\nclass MethodArgumentsProviderTests {\n\n\tprivate @Nullable MutableExtensionRegistry extensionRegistry;\n\n\t@Test\n\tvoid providesArgumentsUsingStream() {\n\t\tvar arguments = provideArguments(\"stringStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingDoubleStream() {\n\t\tvar arguments = provideArguments(\"doubleStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(1.2), array(3.4));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingLongStream() {\n\t\tvar arguments = provideArguments(\"longStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(1L), array(2L));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIntStream() {\n\t\tvar arguments = provideArguments(\"intStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(1), array(2));\n\t}\n\n\t/**\n\t * @since 5.3.2\n\t */\n\t@Test\n\tvoid providesArgumentsUsingStreamOfIntArrays() {\n\t\tvar arguments = provideArguments(\"intArrayStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tnew Object[] { new int[] { 1, 2 } }, //\n\t\t\tnew Object[] { new int[] { 3, 4 } } //\n\t\t);\n\t}\n\n\t/**\n\t * @since 5.3.2\n\t */\n\t@Test\n\tvoid providesArgumentsUsingStreamOfTwoDimensionalIntArrays() {\n\t\tvar arguments = provideArguments(\"twoDimensionalIntArrayStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tarray((Object) new int[][] { { 1, 2 }, { 2, 3 } }), //\n\t\t\tarray((Object) new int[][] { { 4, 5 }, { 5, 6 } }) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t/**\n\t * @since 5.3.2\n\t */\n\t@Test\n\tvoid providesArgumentsUsingStreamOfTwoDimensionalObjectArrays() {\n\t\tvar arguments = provideArguments(\"twoDimensionalObjectArrayStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly( //\n\t\t\tarray((Object) array(array(\"a\", 1), array(\"b\", 2))), //\n\t\t\tarray((Object) array(array(\"c\", 3), array(\"d\", 4))) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingStreamOfArguments() {\n\t\tvar arguments = provideArguments(\"argumentsStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIterable() {\n\t\tvar arguments = provideArguments(\"stringIterableProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIterator() {\n\t\tvar arguments = provideArguments(\"stringIteratorProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingMultipleFactoryMethods() {\n\t\tvar arguments = provideArguments(\"stringStreamProvider\", \"stringIterableProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingIterableOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayIterableProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingListOfStrings() {\n\t\tvar arguments = provideArguments(\"stringArrayListProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingListOfObjectArrays() {\n\t\tvar arguments = provideArguments(\"objectArrayListProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenNonStaticLocalFactoryMethodIsReferencedWithLifecyclePerMethodSemantics() {\n\t\tvar lifecyclePerClass = false;\n\t\tassertPreconditionViolationFor(//\n\t\t\t() -> provideArguments(NonStaticTestCase.class, lifecyclePerClass,\n\t\t\t\t\"nonStaticStringStreamProvider\").toArray())//\n\t\t\t\t\t\t.withMessageContainingAll(//\n\t\t\t\t\t\t\t\"Method '\", //\n\t\t\t\t\t\t\t\"' must be static: local factory methods must be static \", //\n\t\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \", //\n\t\t\t\t\t\t\t\"external factory methods must always be static.\"//\n\t\t\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenNonStaticExternalFactoryMethodIsReferencedWithLifecyclePerMethodSemantics() {\n\t\tvar factoryClass = NonStaticTestCase.class.getName();\n\t\tvar factoryMethod = factoryClass + \"#nonStaticStringStreamProvider\";\n\t\tvar lifecyclePerClass = false;\n\t\tassertPreconditionViolationFor(//\n\t\t\t() -> provideArguments(TestCase.class, lifecyclePerClass, factoryMethod).toArray())//\n\t\t\t\t\t.withMessageContainingAll(//\n\t\t\t\t\t\t\"Method '\", //\n\t\t\t\t\t\t\"' must be static: local factory methods must be static \", //\n\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \", //\n\t\t\t\t\t\t\"external factory methods must always be static.\"//\n\t\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenNonStaticExternalFactoryMethodIsReferencedWithLifecyclePerClassSemantics() {\n\t\tvar factoryClass = NonStaticTestCase.class.getName();\n\t\tvar factoryMethod = factoryClass + \"#nonStaticStringStreamProvider\";\n\t\tboolean lifecyclePerClass = true;\n\t\tassertPreconditionViolationFor(//\n\t\t\t() -> provideArguments(TestCase.class, lifecyclePerClass, factoryMethod).toArray())//\n\t\t\t\t\t.withMessageContainingAll(//\n\t\t\t\t\t\t\"Method '\", //\n\t\t\t\t\t\t\"' must be static: local factory methods must be static \", //\n\t\t\t\t\t\t\"unless the PER_CLASS @TestInstance lifecycle mode is used; \", //\n\t\t\t\t\t\t\"external factory methods must always be static.\"//\n\t\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid providesArgumentsFromNonStaticFactoryMethodWhenStaticIsNotRequired() {\n\t\tvar arguments = provideArguments(NonStaticTestCase.class, true, \"nonStaticStringStreamProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingDefaultFactoryMethodName() {\n\t\tvar testClass = DefaultFactoryMethodNameTestCase.class;\n\t\tvar methodName = \"testDefaultFactoryMethodName\";\n\t\tvar testMethod = findMethod(testClass, methodName, String.class).get();\n\n\t\tvar arguments = provideArguments(testClass, testMethod, false, \"\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFactoryMethod() {\n\t\tvar arguments = provideArguments(ExternalFactoryMethods.class.getName() + \"#stringsProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"string1\"), array(\"string2\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFactoryMethodInTypeFromDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(TestCase.class, ExternalFactoryMethods.class)) {\n\t\t\tvar testClass = testClassLoader.loadClass(TestCase.class.getName());\n\t\t\tvar testMethod = ReflectionUtils.findMethod(testClass, \"test\").get();\n\t\t\tvar fullyQualifiedMethodName = ExternalFactoryMethods.class.getName() + \"#stringsProvider\";\n\n\t\t\tassertThat(testClass.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar arguments = provideArguments(testClass, false, fullyQualifiedMethodName);\n\t\t\tassertThat(arguments).containsExactly(array(\"string1\"), array(\"string2\"));\n\n\t\t\tvar factoryMethod = MethodArgumentsProvider.findFactoryMethodByFullyQualifiedName(testClass,\n\t\t\t\tOptional.of(testMethod), fullyQualifiedMethodName);\n\t\t\tassertThat(factoryMethod).isNotNull();\n\t\t\tassertThat(factoryMethod.getName()).isEqualTo(\"stringsProvider\");\n\t\t\tassertThat(factoryMethod.getParameterTypes()).isEmpty();\n\n\t\t\tvar declaringClass = factoryMethod.getDeclaringClass();\n\t\t\tassertThat(declaringClass.getName()).isEqualTo(ExternalFactoryMethods.class.getName());\n\t\t\tassertThat(declaringClass).isNotEqualTo(ExternalFactoryMethods.class);\n\t\t\tassertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader);\n\t\t}\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFactoryMethodWithParentheses() {\n\t\tvar arguments = provideArguments(ExternalFactoryMethods.class.getName() + \"#stringsProvider()\");\n\n\t\tassertThat(arguments).containsExactly(array(\"string1\"), array(\"string2\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalFactoryMethodFromStaticNestedClass() {\n\t\tvar arguments = provideArguments(ExternalFactoryMethods.class.getName() + \"$Nested#stringsProvider()\");\n\n\t\tassertThat(arguments).containsExactly(array(\"nested string1\"), array(\"nested string2\"));\n\t}\n\n\t@Test\n\tvoid providesArgumentsUsingExternalAndInternalFactoryMethodsCombined() {\n\t\tvar arguments = provideArguments(\"stringStreamProvider\",\n\t\t\tExternalFactoryMethods.class.getName() + \"#stringsProvider\");\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"), array(\"string1\"), array(\"string2\"));\n\t}\n\n\t@Nested\n\tclass PrimitiveArrays {\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingBooleanArray() {\n\t\t\tvar arguments = provideArguments(\"booleanArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(Boolean.TRUE), array(Boolean.FALSE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingByteArray() {\n\t\t\tvar arguments = provideArguments(\"byteArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array((byte) 1), array(Byte.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingCharArray() {\n\t\t\tvar arguments = provideArguments(\"charArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array((char) 1), array(Character.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingDoubleArray() {\n\t\t\tvar arguments = provideArguments(\"doubleArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(1d), array(Double.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFloatArray() {\n\t\t\tvar arguments = provideArguments(\"floatArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(1f), array(Float.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingIntArray() {\n\t\t\tvar arguments = provideArguments(\"intArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(47), array(Integer.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLongArray() {\n\t\t\tvar arguments = provideArguments(\"longArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(47L), array(Long.MIN_VALUE));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingShortArray() {\n\t\t\tvar arguments = provideArguments(\"shortArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array((short) 47), array(Short.MIN_VALUE));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ObjectArrays {\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingObjectArray() {\n\t\t\tvar arguments = provideArguments(\"objectArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(42), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingStringArray() {\n\t\t\tvar arguments = provideArguments(\"stringArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsing2dObjectArray() {\n\t\t\tvar arguments = provideArguments(\"twoDimensionalObjectArrayProvider\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\", 42), array(\"bar\", 23));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ParameterResolution {\n\n\t\tprivate final Method testMethod = findMethod(TestCase.class, \"test\").get();\n\n\t\t@BeforeEach\n\t\tvoid registerParameterResolver() {\n\t\t\textensionRegistry = createRegistryWithDefaultExtensions(mock());\n\t\t\textensionRegistry.registerExtension(StringResolver.class);\n\t\t\textensionRegistry.registerExtension(StringArrayResolver.class);\n\t\t\textensionRegistry.registerExtension(IntArrayResolver.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsInferringDefaultFactoryMethodThatAcceptsArgument() {\n\t\t\tMethod testMethod = findMethod(TestCase.class, \"overloadedStringStreamProvider\", Object.class).get();\n\t\t\tString factoryMethodName = \"\"; // signals to use default\n\t\t\tvar arguments = provideArguments(testMethod, factoryMethodName);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingSimpleNameForFactoryMethodThatAcceptsArgumentWithoutSpecifyingParameterList() {\n\t\t\tvar arguments = provideArguments(\"stringStreamProviderWithParameter\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFullyQualifiedNameForFactoryMethodThatAcceptsArgumentWithoutSpecifyingParameterList() {\n\t\t\tvar arguments = provideArguments(TestCase.class.getName() + \"#stringStreamProviderWithParameter\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFullyQualifiedNameSpecifyingParameter() {\n\t\t\tvar arguments = provideArguments(\n\t\t\t\tTestCase.class.getName() + \"#stringStreamProviderWithParameter(java.lang.String)\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLocalQualifiedNameSpecifyingParameter() {\n\t\t\tvar arguments = provideArguments(testMethod, \"stringStreamProviderWithParameter(java.lang.String)\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFullyQualifiedNameForOverloadedFactoryMethodSpecifyingEmptyParameterList() {\n\t\t\tvar arguments = provideArguments(\n\t\t\t\tTestCase.class.getName() + \"#stringStreamProviderWithOrWithoutParameter()\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLocalQualifiedNameForOverloadedFactoryMethodSpecifyingEmptyParameterList() {\n\t\t\tvar arguments = provideArguments(this.testMethod, \"stringStreamProviderWithOrWithoutParameter()\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFullyQualifiedNameForOverloadedFactoryMethodSpecifyingParameter() {\n\t\t\tvar arguments = provideArguments(\n\t\t\t\tTestCase.class.getName() + \"#stringStreamProviderWithOrWithoutParameter(java.lang.String)\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLocalQualifiedNameForOverloadedFactoryMethodSpecifyingParameter() {\n\t\t\tvar arguments = provideArguments(testMethod,\n\t\t\t\t\"stringStreamProviderWithOrWithoutParameter(java.lang.String)\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!\"), array(\"bar!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid failsToProvideArgumentsUsingFullyQualifiedNameSpecifyingInvalidParameterType() {\n\t\t\tString method = TestCase.class.getName() + \"#stringStreamProviderWithParameter(example.FooBar)\";\n\t\t\tvar exception = assertThrows(JUnitException.class, () -> provideArguments(method).toArray());\n\n\t\t\tassertThat(exception).hasMessage(\"\"\"\n\t\t\t\t\tFailed to load parameter type [example.FooBar] for method [stringStreamProviderWithParameter] \\\n\t\t\t\t\tin class [org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase].\"\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid failsToProvideArgumentsUsingLocalQualifiedNameSpecifyingInvalidParameterType() {\n\t\t\tvar method = \"stringStreamProviderWithParameter(example.FooBar)\";\n\t\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t\t() -> provideArguments(this.testMethod, method).toArray());\n\n\t\t\tassertThat(exception).hasMessage(\"\"\"\n\t\t\t\t\tFailed to load parameter type [example.FooBar] for method [stringStreamProviderWithParameter] \\\n\t\t\t\t\tin class [org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase].\"\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid failsToProvideArgumentsUsingFullyQualifiedNameSpecifyingIncorrectParameterType() {\n\t\t\tString method = TestCase.class.getName() + \"#stringStreamProviderWithParameter(java.lang.Integer)\";\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(method).toArray())//\n\t\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\t\tCould not find factory method [stringStreamProviderWithParameter(java.lang.Integer)] in \\\n\t\t\t\t\t\t\tclass [org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase]\"\"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid failsToProvideArgumentsUsingLocalQualifiedNameSpecifyingIncorrectParameterType() {\n\t\t\tvar method = \"stringStreamProviderWithParameter(java.lang.Integer)\";\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(this.testMethod, method).toArray())//\n\t\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\t\tCould not find factory method [stringStreamProviderWithParameter(java.lang.Integer)] in \\\n\t\t\t\t\t\t\tclass [org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase]\"\"\");\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = {\n\t\t\t\t\"org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase#stringStreamProviderWithArrayParameter(java.lang.String[])\",\n\t\t\t\t\"org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase#stringStreamProviderWithArrayParameter([Ljava.lang.String;)\", })\n\t\tvoid providesArgumentsUsingFullyQualifiedNameSpecifyingObjectArrayParameter(String method) {\n\t\t\tvar arguments = provideArguments(method);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo :)\"), array(\"bar :)\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { //\n\t\t\t\t\"stringStreamProviderWithArrayParameter(java.lang.String[])\",\n\t\t\t\t\"stringStreamProviderWithArrayParameter([Ljava.lang.String;)\" })\n\t\tvoid providesArgumentsUsingLocalQualifiedNameSpecifyingObjectArrayParameter(String method) {\n\t\t\tvar arguments = provideArguments(this.testMethod, method);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo :)\"), array(\"bar :)\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = {\n\t\t\t\t\"org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase#stringStreamProviderWithArrayParameter(int[])\",\n\t\t\t\t\"org.junit.jupiter.params.provider.MethodArgumentsProviderTests$TestCase#stringStreamProviderWithArrayParameter([I)\", })\n\t\tvoid providesArgumentsUsingFullyQualifiedNameSpecifyingPrimitiveArrayParameter(String method) {\n\t\t\tvar arguments = provideArguments(method);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo 42\"), array(\"bar 42\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { //\n\t\t\t\t\"stringStreamProviderWithArrayParameter(int[])\", //\n\t\t\t\t\"stringStreamProviderWithArrayParameter([I)\" })\n\t\tvoid providesArgumentsUsingLocalQualifiedNameSpecifyingPrimitiveArrayParameter(String method) {\n\t\t\tvar arguments = provideArguments(this.testMethod, method);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo 42\"), array(\"bar 42\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"java.lang.String,java.lang.String\", \"java.lang.String, java.lang.String\",\n\t\t\t\t\"java.lang.String,    java.lang.String\" })\n\t\tvoid providesArgumentsUsingFullyQualifiedNameSpecifyingMultipleParameters(String params) {\n\t\t\tvar method = TestCase.class.getName() + \"#stringStreamProviderWithOrWithoutParameter(\" + params + \")\";\n\t\t\tvar arguments = provideArguments(method);\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!!\"), array(\"bar!!\"));\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"java.lang.String,java.lang.String\", \"java.lang.String, java.lang.String\",\n\t\t\t\t\"java.lang.String,    java.lang.String\" })\n\t\tvoid providesArgumentsUsingLocalQualifiedNameSpecifyingMultipleParameters(String params) {\n\t\t\tvar arguments = provideArguments(this.testMethod,\n\t\t\t\t\"stringStreamProviderWithOrWithoutParameter(\" + params + \")\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo!!\"), array(\"bar!!\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingFullyQualifiedNameForOverloadedFactoryMethodWhenParameterListIsNotSpecified() {\n\t\t\tvar arguments = provideArguments(TestCase.class.getName() + \"#stringStreamProviderWithOrWithoutParameter\");\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid providesArgumentsUsingLocalQualifiedNameForOverloadedFactoryMethodWhenParameterListIsNotSpecified() {\n\t\t\tvar arguments = provideArguments(\"stringStreamProviderWithOrWithoutParameter\").toArray();\n\n\t\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ErrorCases {\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenFullyQualifiedMethodNameSyntaxIsInvalid() {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(\"org.example.wrongSyntax\").toArray())//\n\t\t\t\t\t.withMessage(//\n\t\t\t\t\t\t\"[org.example.wrongSyntax] is not a valid fully qualified method name: \"//\n\t\t\t\t\t\t\t\t+ \"it must start with a fully qualified class name followed by a '#' and then the method name, \"//\n\t\t\t\t\t\t\t\t+ \"optionally followed by a parameter list enclosed in parentheses.\"//\n\t\t\t\t\t);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenClassForExternalFactoryMethodCannotBeLoaded() {\n\t\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t\t() -> provideArguments(\"com.example.NonExistentClass#stringsProvider\").toArray());\n\n\t\t\tassertThat(exception.getMessage()).isEqualTo(\"Could not load class [com.example.NonExistentClass]\");\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenExternalFactoryMethodDoesNotExist() {\n\t\t\tString factoryClass = ExternalFactoryMethods.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(factoryClass + \"#nonExistentMethod\").toArray())//\n\t\t\t\t\t.withMessage(\"Could not find factory method [nonExistentMethod] in class [%s]\", factoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFactoryMethodDoesNotExist() {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(\"nonExistentMethod\").toArray())//\n\t\t\t\t\t.withMessage(\"Could not find factory method [nonExistentMethod] in class [%s]\",\n\t\t\t\t\t\tTestCase.class.getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenExternalFactoryMethodAcceptingSingleArgumentDoesNotExist() {\n\t\t\tString factoryClass = ExternalFactoryMethods.class.getName();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(factoryClass + \"#nonExistentMethod(int)\").toArray())//\n\t\t\t\t\t.withMessage(\"Could not find factory method [nonExistentMethod(int)] in class [%s]\", factoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFactoryMethodAcceptingSingleArgumentDoesNotExist() {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(\"nonExistentMethod(int)\").toArray())//\n\t\t\t\t\t.withMessage(\"Could not find factory method [nonExistentMethod(int)] in class [%s]\",\n\t\t\t\t\t\tTestCase.class.getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenExternalFactoryMethodAcceptingMultipleArgumentsDoesNotExist() {\n\t\t\tString factoryClass = ExternalFactoryMethods.class.getName();\n\n\t\t\tassertPreconditionViolationFor(\n\t\t\t\t() -> provideArguments(factoryClass + \"#nonExistentMethod(int, java.lang.String)\").toArray())//\n\t\t\t\t\t\t.withMessage(\n\t\t\t\t\t\t\t\"Could not find factory method [nonExistentMethod(int, java.lang.String)] in class [%s]\",\n\t\t\t\t\t\t\tfactoryClass);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFactoryMethodAcceptingMultipleArgumentsDoesNotExist() {\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(\"nonExistentMethod(java.lang.String,int)\").toArray())//\n\t\t\t\t\t.withMessage(\n\t\t\t\t\t\t\"Could not find factory method [nonExistentMethod(java.lang.String,int)] in class [%s]\",\n\t\t\t\t\t\tTestCase.class.getName());\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenExternalFactoryMethodHasInvalidReturnType() {\n\t\t\tString testClass = TestCase.class.getName();\n\t\t\tString factoryClass = ExternalFactoryMethods.class.getName();\n\t\t\tString factoryMethod = factoryClass + \"#factoryWithInvalidReturnType\";\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(TestCase.class, false, factoryMethod).toArray())//\n\t\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\t\tCould not find valid factory method [%s] for test class [%s] \\\n\t\t\t\t\t\t\tbut found the following invalid candidate: \\\n\t\t\t\t\t\t\tstatic java.lang.Object %s.factoryWithInvalidReturnType()\\\n\t\t\t\t\t\t\t\"\"\".formatted(factoryMethod, testClass, factoryClass));\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenLocalFactoryMethodHasInvalidReturnType() {\n\t\t\tString testClass = TestCase.class.getName();\n\t\t\tString factoryClass = testClass;\n\t\t\tString factoryMethod = \"factoryWithInvalidReturnType\";\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(factoryMethod).toArray())//\n\t\t\t\t\t.withMessage(\"\"\"\n\t\t\t\t\t\t\tCould not find valid factory method [%s] for test class [%s] \\\n\t\t\t\t\t\t\tbut found the following invalid candidate: \\\n\t\t\t\t\t\t\tstatic java.lang.Object %s.factoryWithInvalidReturnType()\\\n\t\t\t\t\t\t\t\"\"\".formatted(factoryMethod, factoryClass, factoryClass));\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenMultipleDefaultFactoryMethodCandidatesExist() {\n\t\t\tvar testClass = MultipleDefaultFactoriesTestCase.class;\n\t\t\tvar methodName = \"test\";\n\t\t\tvar testMethod = findMethod(testClass, methodName, String.class).get();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(testClass, testMethod, false, \"\").toArray())//\n\t\t\t\t\t.withMessageContainingAll(//\n\t\t\t\t\t\t\"2 factory methods named [test] were found in class [\", testClass.getName() + \"]: \", //\n\t\t\t\t\t\t\"$MultipleDefaultFactoriesTestCase.test()\", //\n\t\t\t\t\t\t\"$MultipleDefaultFactoriesTestCase.test(int)\"//\n\t\t\t\t\t);\n\t\t}\n\n\t\t@Test\n\t\tvoid throwsExceptionWhenMultipleInvalidDefaultFactoryMethodCandidatesExist() {\n\t\t\tvar testClass = MultipleInvalidDefaultFactoriesTestCase.class;\n\t\t\tvar methodName = \"test\";\n\t\t\tvar testMethod = findMethod(testClass, methodName, String.class).get();\n\n\t\t\tassertPreconditionViolationFor(() -> provideArguments(testClass, testMethod, false, \"\").toArray())//\n\t\t\t\t\t.withMessageContainingAll(//\n\t\t\t\t\t\t\"Could not find valid factory method [test] in class [\", testClass.getName() + \"]\", //\n\t\t\t\t\t\t\"but found the following invalid candidates: \", //\n\t\t\t\t\t\t\"$MultipleInvalidDefaultFactoriesTestCase.test()\", //\n\t\t\t\t\t\t\"$MultipleInvalidDefaultFactoriesTestCase.test(int)\"//\n\t\t\t\t\t);\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static Object[] array(Object... objects) {\n\t\treturn objects;\n\t}\n\n\tprivate Stream<@Nullable Object[]> provideArguments(String... factoryMethodNames) {\n\t\treturn provideArguments(TestCase.class, false, factoryMethodNames);\n\t}\n\n\tprivate Stream<@Nullable Object[]> provideArguments(Method testMethod, String factoryMethodName) {\n\t\treturn provideArguments(TestCase.class, testMethod, false, factoryMethodName);\n\t}\n\n\tprivate Stream<@Nullable Object[]> provideArguments(Class<?> testClass, boolean allowNonStaticMethod,\n\t\t\tString... factoryMethodNames) {\n\n\t\t// Ensure we have a non-null test method, even if it's not a real test method.\n\t\t// If this throws an exception, make sure that the supplied test class defines a \"void test()\" method.\n\t\tMethod testMethod = ReflectionUtils.findMethod(testClass, \"test\").get();\n\t\treturn provideArguments(testClass, testMethod, allowNonStaticMethod, factoryMethodNames);\n\t}\n\n\tprivate Stream<@Nullable Object[]> provideArguments(Class<?> testClass, Method testMethod,\n\t\t\tboolean allowNonStaticMethod, String... factoryMethodNames) {\n\n\t\tvar methodSource = mock(MethodSource.class);\n\n\t\twhen(methodSource.value()).thenReturn(factoryMethodNames);\n\n\t\tvar extensionContext = mock(ExtensionContext.class);\n\t\twhen(extensionContext.getTestClass()).thenReturn(Optional.of(testClass));\n\t\twhen(extensionContext.getTestMethod()).thenReturn(Optional.of(testMethod));\n\t\twhen(extensionContext.getExecutableInvoker()).thenReturn(getExecutableInvoker(extensionContext));\n\n\t\tdoCallRealMethod().when(extensionContext).getRequiredTestClass();\n\n\t\tvar testInstance = allowNonStaticMethod ? ReflectionUtils.newInstance(testClass) : null;\n\t\twhen(extensionContext.getTestInstance()).thenReturn(Optional.ofNullable(testInstance));\n\n\t\tvar lifeCycle = allowNonStaticMethod ? Lifecycle.PER_CLASS : Lifecycle.PER_METHOD;\n\t\twhen(extensionContext.getTestInstanceLifecycle()).thenReturn(Optional.of(lifeCycle));\n\n\t\tvar provider = new MethodArgumentsProvider();\n\t\tprovider.accept(methodSource);\n\t\treturn provider.provideArguments(mock(), extensionContext).map(Arguments::get);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tprivate DefaultExecutableInvoker getExecutableInvoker(ExtensionContext extensionContext) {\n\t\treturn new DefaultExecutableInvoker(extensionContext, extensionRegistry);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class DefaultFactoryMethodNameTestCase {\n\n\t\t// Test\n\t\tvoid testDefaultFactoryMethodName(String param) {\n\t\t}\n\n\t\t// Factory\n\t\tstatic Stream<String> testDefaultFactoryMethodName() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\t}\n\n\tstatic class MultipleDefaultFactoriesTestCase {\n\n\t\t// Test\n\t\tvoid test(String param) {\n\t\t}\n\n\t\t// Factory\n\t\tstatic Stream<String> test() {\n\t\t\treturn Stream.of();\n\t\t}\n\n\t\t// Another Factory\n\t\tstatic Stream<Integer> test(int num) {\n\t\t\treturn Stream.of();\n\t\t}\n\t}\n\n\t@NullUnmarked\n\tstatic class MultipleInvalidDefaultFactoriesTestCase {\n\n\t\t// Test\n\t\tvoid test(String param) {\n\t\t}\n\n\t\t// NOT a Factory\n\t\tstatic String test() {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Also NOT a Factory\n\t\tstatic Object test(int num) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tstatic class TestCase {\n\n\t\tvoid test() {\n\t\t}\n\n\t\t// --- Invalid ---------------------------------------------------------\n\n\t\tstatic Object factoryWithInvalidReturnType() {\n\t\t\treturn -1;\n\t\t}\n\n\t\t// --- Stream ----------------------------------------------------------\n\n\t\tstatic Stream<String> stringStreamProvider() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithParameter(String parameter) {\n\t\t\treturn Stream.of(\"foo\" + parameter, \"bar\" + parameter);\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithArrayParameter(String[] parameter) {\n\t\t\tString suffix = Arrays.stream(parameter).collect(Collectors.joining());\n\t\t\treturn Stream.of(\"foo \" + suffix, \"bar \" + suffix);\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithArrayParameter(int[] parameter) {\n\t\t\treturn stringStreamProviderWithArrayParameter(\n\t\t\t\tArrays.stream(parameter).mapToObj(String::valueOf).toArray(String[]::new));\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithOrWithoutParameter() {\n\t\t\treturn stringStreamProvider();\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithOrWithoutParameter(String parameter) {\n\t\t\treturn stringStreamProviderWithParameter(parameter);\n\t\t}\n\n\t\tstatic Stream<String> stringStreamProviderWithOrWithoutParameter(String parameter1, String parameter2) {\n\t\t\treturn stringStreamProviderWithParameter(parameter1 + parameter2);\n\t\t}\n\n\t\t// Overloaded method, but not a valid return type for a factory method\n\t\tstatic void stringStreamProviderWithOrWithoutParameter(String parameter1, int parameter2) {\n\t\t}\n\n\t\t// @ParameterizedTest\n\t\t// @MethodSource // use default, inferred factory method\n\t\tvoid overloadedStringStreamProvider(Object parameter) {\n\t\t\t// test implementation\n\t\t}\n\n\t\t// Default factory method for overloadedStringStreamProvider(Object)\n\t\tstatic Stream<String> overloadedStringStreamProvider(String parameter) {\n\t\t\treturn stringStreamProviderWithParameter(parameter);\n\t\t}\n\n\t\tstatic DoubleStream doubleStreamProvider() {\n\t\t\treturn DoubleStream.of(1.2, 3.4);\n\t\t}\n\n\t\tstatic LongStream longStreamProvider() {\n\t\t\treturn LongStream.of(1L, 2L);\n\t\t}\n\n\t\tstatic IntStream intStreamProvider() {\n\t\t\treturn IntStream.of(1, 2);\n\t\t}\n\n\t\tstatic Stream<int[]> intArrayStreamProvider() {\n\t\t\treturn Stream.of(new int[] { 1, 2 }, new int[] { 3, 4 });\n\t\t}\n\n\t\tstatic Stream<int[][]> twoDimensionalIntArrayStreamProvider() {\n\t\t\treturn Stream.of(new int[][] { { 1, 2 }, { 2, 3 } }, new int[][] { { 4, 5 }, { 5, 6 } });\n\t\t}\n\n\t\tstatic Stream<Object[]> objectArrayStreamProvider() {\n\t\t\treturn Stream.of(new Object[] { \"foo\", 42 }, new Object[] { \"bar\", 23 });\n\t\t}\n\n\t\tstatic Stream<Object[][]> twoDimensionalObjectArrayStreamProvider() {\n\t\t\treturn Stream.of(new Object[][] { { \"a\", 1 }, { \"b\", 2 } }, new Object[][] { { \"c\", 3 }, { \"d\", 4 } });\n\t\t}\n\n\t\tstatic Stream<Arguments> argumentsStreamProvider() {\n\t\t\treturn objectArrayStreamProvider().map(Arguments::of);\n\t\t}\n\n\t\t// --- Iterable / Collection -------------------------------------------\n\n\t\tstatic Iterable<String> stringIterableProvider() {\n\t\t\treturn TestCase::stringIteratorProvider;\n\t\t}\n\n\t\tstatic Iterable<Object[]> objectArrayIterableProvider() {\n\t\t\treturn objectArrayListProvider();\n\t\t}\n\n\t\tstatic List<String> stringArrayListProvider() {\n\t\t\treturn Arrays.asList(\"foo\", \"bar\");\n\t\t}\n\n\t\tstatic List<Object[]> objectArrayListProvider() {\n\t\t\treturn Arrays.asList(array(\"foo\", 42), array(\"bar\", 23));\n\t\t}\n\n\t\t// --- Iterator --------------------------------------------------------\n\n\t\tstatic Iterator<String> stringIteratorProvider() {\n\t\t\treturn Arrays.asList(\"foo\", \"bar\").iterator();\n\t\t}\n\n\t\t// --- Array of primitives ---------------------------------------------\n\n\t\tstatic boolean[] booleanArrayProvider() {\n\t\t\treturn new boolean[] { true, false };\n\t\t}\n\n\t\tstatic byte[] byteArrayProvider() {\n\t\t\treturn new byte[] { (byte) 1, Byte.MIN_VALUE };\n\t\t}\n\n\t\tstatic char[] charArrayProvider() {\n\t\t\treturn new char[] { (char) 1, Character.MIN_VALUE };\n\t\t}\n\n\t\tstatic double[] doubleArrayProvider() {\n\t\t\treturn new double[] { 1d, Double.MIN_VALUE };\n\t\t}\n\n\t\tstatic float[] floatArrayProvider() {\n\t\t\treturn new float[] { 1f, Float.MIN_VALUE };\n\t\t}\n\n\t\tstatic int[] intArrayProvider() {\n\t\t\treturn new int[] { 47, Integer.MIN_VALUE };\n\t\t}\n\n\t\tstatic long[] longArrayProvider() {\n\t\t\treturn new long[] { 47L, Long.MIN_VALUE };\n\t\t}\n\n\t\tstatic short[] shortArrayProvider() {\n\t\t\treturn new short[] { (short) 47, Short.MIN_VALUE };\n\t\t}\n\n\t\t// --- Array of objects ------------------------------------------------\n\n\t\tstatic Object[] objectArrayProvider() {\n\t\t\treturn new Object[] { 42, \"bar\" };\n\t\t}\n\n\t\tstatic String[] stringArrayProvider() {\n\t\t\treturn new String[] { \"foo\", \"bar\" };\n\t\t}\n\n\t\tstatic Object[][] twoDimensionalObjectArrayProvider() {\n\t\t\treturn new Object[][] { { \"foo\", 42 }, { \"bar\", 23 } };\n\t\t}\n\n\t}\n\n\t// This test case mimics @TestInstance(Lifecycle.PER_CLASS)\n\tstatic class NonStaticTestCase {\n\n\t\tvoid test() {\n\t\t}\n\n\t\tStream<String> nonStaticStringStreamProvider() {\n\t\t\treturn Stream.of(\"foo\", \"bar\");\n\t\t}\n\t}\n\n\tstatic class ExternalFactoryMethods {\n\n\t\tstatic Object factoryWithInvalidReturnType() {\n\t\t\treturn -1;\n\t\t}\n\n\t\tstatic Stream<String> stringsProvider() {\n\t\t\treturn Stream.of(\"string1\", \"string2\");\n\t\t}\n\n\t\tstatic class Nested {\n\n\t\t\tstatic Stream<String> stringsProvider() {\n\t\t\t\treturn Stream.of(\"nested string1\", \"nested string2\");\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class StringResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == String.class;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn \"!\";\n\t\t}\n\t}\n\n\tstatic class StringArrayResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == String[].class;\n\t\t}\n\n\t\t@Override\n\t\tpublic String[] resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn new String[] { \":\", \")\" };\n\t\t}\n\t}\n\n\tstatic class IntArrayResolver implements ParameterResolver {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn parameterContext.getParameter().getType() == int[].class;\n\t\t}\n\n\t\t@Override\n\t\tpublic int[] resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\t\treturn new int[] { 4, 2 };\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/MockCsvAnnotationBuilder.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.annotation.Annotation;\n\n/**\n * @since 5.6\n */\nabstract class MockCsvAnnotationBuilder<A extends Annotation, B extends MockCsvAnnotationBuilder<A, B>> {\n\n\tstatic CsvSource csvSource(String... lines) {\n\t\treturn csvSource().lines(lines).build();\n\t}\n\n\tstatic MockCsvSourceBuilder csvSource() {\n\t\treturn new MockCsvSourceBuilder();\n\t}\n\n\tstatic MockCsvFileSourceBuilder csvFileSource() {\n\t\treturn new MockCsvFileSourceBuilder();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate boolean useHeadersInDisplayName = false;\n\tprivate char quoteCharacter = '\\0';\n\tprotected char delimiter = '\\0';\n\tprotected String delimiterString = \"\";\n\tprotected String emptyValue = \"\";\n\tprotected String[] nullValues = new String[0];\n\tprotected int maxCharsPerColumn = 4096;\n\tprotected boolean ignoreLeadingAndTrailingWhitespace = true;\n\tprivate char commentCharacter = '#';\n\n\tprivate MockCsvAnnotationBuilder() {\n\t}\n\n\tprotected abstract B getSelf();\n\n\tB useHeadersInDisplayName(boolean useHeadersInDisplayName) {\n\t\tthis.useHeadersInDisplayName = useHeadersInDisplayName;\n\t\treturn getSelf();\n\t}\n\n\tB quoteCharacter(char quoteCharacter) {\n\t\tthis.quoteCharacter = quoteCharacter;\n\t\treturn getSelf();\n\t}\n\n\tB delimiter(char delimiter) {\n\t\tthis.delimiter = delimiter;\n\t\treturn getSelf();\n\t}\n\n\tB delimiterString(String delimiterString) {\n\t\tthis.delimiterString = delimiterString;\n\t\treturn getSelf();\n\t}\n\n\tB emptyValue(String emptyValue) {\n\t\tthis.emptyValue = emptyValue;\n\t\treturn getSelf();\n\t}\n\n\tB nullValues(String... nullValues) {\n\t\tthis.nullValues = nullValues;\n\t\treturn getSelf();\n\t}\n\n\tB maxCharsPerColumn(int maxCharsPerColumn) {\n\t\tthis.maxCharsPerColumn = maxCharsPerColumn;\n\t\treturn getSelf();\n\t}\n\n\tB ignoreLeadingAndTrailingWhitespace(boolean ignoreLeadingAndTrailingWhitespace) {\n\t\tthis.ignoreLeadingAndTrailingWhitespace = ignoreLeadingAndTrailingWhitespace;\n\t\treturn getSelf();\n\t}\n\n\tB commentCharacter(char commentCharacter) {\n\t\tthis.commentCharacter = commentCharacter;\n\t\treturn getSelf();\n\t}\n\n\tabstract A build();\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class MockCsvSourceBuilder extends MockCsvAnnotationBuilder<CsvSource, MockCsvSourceBuilder> {\n\n\t\tprivate String[] lines = new String[0];\n\t\tprivate String textBlock = \"\";\n\n\t\tprivate MockCsvSourceBuilder() {\n\t\t\tsuper.quoteCharacter = '\\'';\n\t\t}\n\n\t\t@Override\n\t\tprotected MockCsvSourceBuilder getSelf() {\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvSourceBuilder lines(String... lines) {\n\t\t\tthis.lines = lines;\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvSourceBuilder textBlock(String textBlock) {\n\t\t\tthis.textBlock = textBlock;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tCsvSource build() {\n\t\t\tvar annotation = mock(CsvSource.class);\n\n\t\t\t// Common\n\t\t\twhen(annotation.useHeadersInDisplayName()).thenReturn(super.useHeadersInDisplayName);\n\t\t\twhen(annotation.quoteCharacter()).thenReturn(super.quoteCharacter);\n\t\t\twhen(annotation.delimiter()).thenReturn(super.delimiter);\n\t\t\twhen(annotation.delimiterString()).thenReturn(super.delimiterString);\n\t\t\twhen(annotation.emptyValue()).thenReturn(super.emptyValue);\n\t\t\twhen(annotation.nullValues()).thenReturn(super.nullValues);\n\t\t\twhen(annotation.maxCharsPerColumn()).thenReturn(super.maxCharsPerColumn);\n\t\t\twhen(annotation.ignoreLeadingAndTrailingWhitespace()).thenReturn(super.ignoreLeadingAndTrailingWhitespace);\n\t\t\twhen(annotation.commentCharacter()).thenReturn(super.commentCharacter);\n\n\t\t\t// @CsvSource\n\t\t\twhen(annotation.value()).thenReturn(this.lines);\n\t\t\twhen(annotation.textBlock()).thenReturn(this.textBlock);\n\n\t\t\treturn annotation;\n\t\t}\n\n\t}\n\n\tstatic class MockCsvFileSourceBuilder extends MockCsvAnnotationBuilder<CsvFileSource, MockCsvFileSourceBuilder> {\n\n\t\tprivate String[] resources = {};\n\t\tprivate String[] files = {};\n\t\tprivate String encoding = \"UTF-8\";\n\t\tprivate int numLinesToSkip = 0;\n\n\t\tprivate MockCsvFileSourceBuilder() {\n\t\t\tsuper.quoteCharacter = '\"';\n\t\t}\n\n\t\t@Override\n\t\tprotected MockCsvFileSourceBuilder getSelf() {\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvFileSourceBuilder resources(String... resources) {\n\t\t\tthis.resources = resources;\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvFileSourceBuilder files(String... files) {\n\t\t\tthis.files = files;\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvFileSourceBuilder encoding(String encoding) {\n\t\t\tthis.encoding = encoding;\n\t\t\treturn this;\n\t\t}\n\n\t\tMockCsvFileSourceBuilder numLinesToSkip(int numLinesToSkip) {\n\t\t\tthis.numLinesToSkip = numLinesToSkip;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tCsvFileSource build() {\n\t\t\tvar annotation = mock(CsvFileSource.class);\n\n\t\t\t// Common\n\t\t\twhen(annotation.useHeadersInDisplayName()).thenReturn(super.useHeadersInDisplayName);\n\t\t\twhen(annotation.quoteCharacter()).thenReturn(super.quoteCharacter);\n\t\t\twhen(annotation.delimiter()).thenReturn(super.delimiter);\n\t\t\twhen(annotation.delimiterString()).thenReturn(super.delimiterString);\n\t\t\twhen(annotation.emptyValue()).thenReturn(super.emptyValue);\n\t\t\twhen(annotation.nullValues()).thenReturn(super.nullValues);\n\t\t\twhen(annotation.maxCharsPerColumn()).thenReturn(super.maxCharsPerColumn);\n\t\t\twhen(annotation.ignoreLeadingAndTrailingWhitespace()).thenReturn(super.ignoreLeadingAndTrailingWhitespace);\n\t\t\twhen(annotation.commentCharacter()).thenReturn(super.commentCharacter);\n\n\t\t\t// @CsvFileSource\n\t\t\twhen(annotation.resources()).thenReturn(this.resources);\n\t\t\twhen(annotation.files()).thenReturn(this.files);\n\t\t\twhen(annotation.encoding()).thenReturn(this.encoding);\n\t\t\twhen(annotation.numLinesToSkip()).thenReturn(this.numLinesToSkip);\n\n\t\t\treturn annotation;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/provider/ValueArgumentsProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.provider;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.0\n */\nclass ValueArgumentsProviderTests {\n\n\t@Test\n\tvoid multipleInputsAreNotAllowed() {\n\t\tassertPreconditionViolationFor(() -> provideArguments(new short[1], new byte[0], new int[1], new long[0],\n\t\t\tnew float[0], new double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]).findAny())//\n\t\t\t\t\t.withMessageContaining(\n\t\t\t\t\t\t\"Exactly one type of input must be provided in the @ValueSource annotation, but there were 2\");\n\t}\n\n\t@Test\n\tvoid onlyEmptyInputsAreNotAllowed() {\n\t\tassertPreconditionViolationFor(() -> provideArguments(new short[0], new byte[0], new int[0], new long[0],\n\t\t\tnew float[0], new double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]).findAny())//\n\t\t\t\t\t.withMessageContaining(\n\t\t\t\t\t\t\"Exactly one type of input must be provided in the @ValueSource annotation, but there were 0\");\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid providesShorts() {\n\t\tvar arguments = provideArguments(new short[] { 23, 42 }, new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array((short) 23), array((short) 42));\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid providesBytes() {\n\t\tvar arguments = provideArguments(new short[0], new byte[] { 23, 42 }, new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array((byte) 23), array((byte) 42));\n\t}\n\n\t@Test\n\tvoid providesInts() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[] { 23, 42 }, new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(23), array(42));\n\t}\n\n\t@Test\n\tvoid providesLongs() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[] { 23, 42 }, new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(23L), array(42L));\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid providesFloats() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0],\n\t\t\tnew float[] { 23.32F, 42.24F }, new double[0], new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(23.32F), array(42.24F));\n\t}\n\n\t@Test\n\tvoid providesDoubles() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[] { 23.32, 42.24 }, new char[0], new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(23.32), array(42.24));\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid providesChars() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[] { 'a', 'b', 'c' }, new boolean[0], new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array('a'), array('b'), array('c'));\n\t}\n\n\t/**\n\t * @since 5.5\n\t */\n\t@Test\n\tvoid providesBooleans() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[] { true, false }, new String[0], new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(true), array(false));\n\t}\n\n\t@Test\n\tvoid providesStrings() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[] { \"foo\", \"bar\" }, new Class<?>[0]);\n\n\t\tassertThat(arguments).containsExactly(array(\"foo\"), array(\"bar\"));\n\t}\n\n\t/**\n\t * @since 5.1\n\t */\n\t@Test\n\tvoid providesClasses() {\n\t\tvar arguments = provideArguments(new short[0], new byte[0], new int[0], new long[0], new float[0],\n\t\t\tnew double[0], new char[0], new boolean[0], new String[0], new Class<?>[] { Integer.class, getClass() });\n\n\t\tassertThat(arguments).containsExactly(array(Integer.class), array(getClass()));\n\t}\n\n\tprivate static Stream<Object[]> provideArguments(short[] shorts, byte[] bytes, int[] ints, long[] longs,\n\t\t\tfloat[] floats, double[] doubles, char[] chars, boolean[] booleans, String[] strings, Class<?>[] classes) {\n\n\t\tvar annotation = mock(ValueSource.class);\n\t\twhen(annotation.shorts()).thenReturn(shorts);\n\t\twhen(annotation.bytes()).thenReturn(bytes);\n\t\twhen(annotation.ints()).thenReturn(ints);\n\t\twhen(annotation.longs()).thenReturn(longs);\n\t\twhen(annotation.floats()).thenReturn(floats);\n\t\twhen(annotation.doubles()).thenReturn(doubles);\n\t\twhen(annotation.chars()).thenReturn(chars);\n\t\twhen(annotation.booleans()).thenReturn(booleans);\n\t\twhen(annotation.strings()).thenReturn(strings);\n\t\twhen(annotation.classes()).thenReturn(classes);\n\n\t\tvar provider = new ValueArgumentsProvider();\n\t\tprovider.accept(annotation);\n\t\treturn provider.provideArguments(mock(), mock(ExtensionContext.class)).map(Arguments::get);\n\t}\n\n\tprivate static Object[] array(Object... objects) {\n\t\treturn objects;\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/support/AnnotationConsumerInitializerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.jupiter.params.support.AnnotationConsumerInitializer.initialize;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.time.LocalDate;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Named;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.converter.AnnotationBasedArgumentConverter;\nimport org.junit.jupiter.params.converter.JavaTimeConversionPattern;\nimport org.junit.jupiter.params.provider.AnnotationBasedArgumentsProvider;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.FieldSource;\nimport org.junit.platform.commons.JUnitException;\n\n@DisplayName(\"AnnotationConsumerInitializer\")\nclass AnnotationConsumerInitializerTests {\n\n\t@Test\n\t@DisplayName(\"should initialize annotation consumer\")\n\tvoid shouldInitializeAnnotationConsumer() throws NoSuchMethodException {\n\t\tvar instance = new SomeAnnotationConsumer();\n\t\tvar method = SubjectClass.class.getDeclaredMethod(\"foo\");\n\t\tvar initialisedAnnotationConsumer = initialize(method, instance);\n\n\t\tassertThat(initialisedAnnotationConsumer.annotation) //\n\t\t\t\t.isInstanceOfSatisfying(CsvSource.class, //\n\t\t\t\t\tsource -> assertThat(source.value()).containsExactly(\"a\", \"b\"));\n\t}\n\n\t@ParameterizedTest\n\t@FieldSource(\"argumentsProviders\")\n\t@DisplayName(\"should initialize annotation-based ArgumentsProvider\")\n\tvoid shouldInitializeAnnotationBasedArgumentsProvider(AbstractAnnotationBasedArgumentsProvider instance)\n\t\t\tthrows NoSuchMethodException {\n\t\tvar method = SubjectClass.class.getDeclaredMethod(\"foo\");\n\t\tvar initialisedAnnotationConsumer = initialize(method, instance);\n\n\t\tinitialisedAnnotationConsumer.provideArguments(mock(), mock(ExtensionContext.class)).findAny();\n\n\t\tassertThat(initialisedAnnotationConsumer.annotations) //\n\t\t\t\t.hasSize(1) //\n\t\t\t\t.element(0) //\n\t\t\t\t.isInstanceOfSatisfying(CsvSource.class, //\n\t\t\t\t\tsource -> assertThat(source.value()).containsExactly(\"a\", \"b\"));\n\t}\n\n\t@Test\n\t@DisplayName(\"should initialize annotation-based ArgumentConverter\")\n\tvoid shouldInitializeAnnotationBasedArgumentConverter() throws NoSuchMethodException {\n\t\tvar instance = new SomeAnnotationBasedArgumentConverter();\n\t\tvar parameter = SubjectClass.class.getDeclaredMethod(\"bar\", LocalDate.class).getParameters()[0];\n\t\tvar initialisedAnnotationConsumer = initialize(parameter, instance);\n\n\t\tParameterContext parameterContext = mock();\n\t\twhen(parameterContext.getParameter()).thenReturn(parameter);\n\t\tinitialisedAnnotationConsumer.convert(\"source\", parameterContext);\n\n\t\tassertThat(initialisedAnnotationConsumer.annotation) //\n\t\t\t\t.isInstanceOfSatisfying(JavaTimeConversionPattern.class, //\n\t\t\t\t\tannotation -> assertThat(annotation.value()).isEqualTo(\"pattern\"));\n\t}\n\n\t@Test\n\t@DisplayName(\"should throw exception when method is not annotated\")\n\tvoid shouldThrowExceptionWhenMethodIsNotAnnotated() throws NoSuchMethodException {\n\t\tvar instance = new SomeAnnotationConsumer();\n\t\tvar method = SubjectClass.class.getDeclaredMethod(\"noAnnotation\", String.class);\n\n\t\tassertThatThrownBy(() -> initialize(method, instance)).isInstanceOf(JUnitException.class);\n\t}\n\n\t@Test\n\t@DisplayName(\"should throw exception when parameter is not annotated\")\n\tvoid shouldThrowExceptionWhenParameterIsNotAnnotated() throws NoSuchMethodException {\n\t\tvar instance = new SomeAnnotationConsumer();\n\t\tvar parameter = SubjectClass.class.getDeclaredMethod(\"noAnnotation\", String.class).getParameters()[0];\n\n\t\tassertThatThrownBy(() -> initialize(parameter, instance)).isInstanceOf(JUnitException.class);\n\t}\n\n\t@ParameterizedTest\n\t@FieldSource(\"argumentsProviders\")\n\tvoid shouldInitializeForEachAnnotations(AbstractAnnotationBasedArgumentsProvider provider)\n\t\t\tthrows NoSuchMethodException {\n\t\tvar instance = spy(provider);\n\t\tvar method = SubjectClass.class.getDeclaredMethod(\"repeatableAnnotation\", String.class);\n\n\t\tinitialize(method, instance);\n\n\t\tverify(instance, times(2)).accept(any(CsvSource.class));\n\t}\n\n\tstatic Supplier<List<Named<? extends AbstractAnnotationBasedArgumentsProvider>>> argumentsProviders = () -> List.of( //\n\t\tNamed.of(\"current\", new SomeAnnotationBasedArgumentsProvider()), //\n\t\tNamed.of(\"deprecated\", new DeprecatedAnnotationBasedArgumentsProvider()) //\n\t);\n\n\tprivate static abstract class AbstractAnnotationBasedArgumentsProvider\n\t\t\textends AnnotationBasedArgumentsProvider<CsvSource> {\n\n\t\tList<CsvSource> annotations = new ArrayList<>();\n\n\t}\n\n\tprivate static class SomeAnnotationBasedArgumentsProvider extends AbstractAnnotationBasedArgumentsProvider {\n\n\t\t@Override\n\t\tprotected Stream<? extends Arguments> provideArguments(ParameterDeclarations parameters,\n\t\t\t\tExtensionContext context, CsvSource annotation) {\n\t\t\tannotations.add(annotation);\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tprivate static class DeprecatedAnnotationBasedArgumentsProvider extends AbstractAnnotationBasedArgumentsProvider {\n\n\t\t@Override\n\t\t@SuppressWarnings(\"deprecation\")\n\t\tprotected Stream<? extends Arguments> provideArguments(ExtensionContext context, CsvSource annotation) {\n\t\t\tannotations.add(annotation);\n\t\t\treturn Stream.empty();\n\t\t}\n\t}\n\n\tprivate static class SomeAnnotationBasedArgumentConverter\n\t\t\textends AnnotationBasedArgumentConverter<JavaTimeConversionPattern> {\n\n\t\t@Nullable\n\t\tJavaTimeConversionPattern annotation;\n\n\t\t@Override\n\t\tprotected @Nullable Object convert(@Nullable Object source, Class<?> targetType,\n\t\t\t\tJavaTimeConversionPattern annotation) {\n\t\t\tthis.annotation = annotation;\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class SomeAnnotationConsumer implements AnnotationConsumer<CsvSource> {\n\n\t\t@Nullable\n\t\tCsvSource annotation;\n\n\t\t@Override\n\t\tpublic void accept(CsvSource csvSource) {\n\t\t\tannotation = csvSource;\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static class SubjectClass {\n\n\t\t@CsvSource({ \"a\", \"b\" })\n\t\tvoid foo() {\n\t\t}\n\n\t\tvoid bar(@JavaTimeConversionPattern(\"pattern\") LocalDate date) {\n\t\t}\n\n\t\tvoid noAnnotation(String param) {\n\t\t}\n\n\t\t@CsvSource(\"a\")\n\t\t@CsvSource(\"b\")\n\t\tvoid repeatableAnnotation(String param) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/java/org/junit/jupiter/params/support/DeprecatedParameterInfoIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.support;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeClassTemplateInvocationCallback;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * @since 5.13\n */\nclass DeprecatedParameterInfoIntegrationTests extends AbstractJupiterTestEngineTests {\n\n\t@Test\n\tvoid storesParameterInfoInExtensionContextStoreOnDifferentLevels() {\n\t\tvar results = executeTestsForClass(TestCase.class);\n\n\t\tresults.allEvents().debug().assertStatistics(stats -> stats.started(7).succeeded(7));\n\t}\n\n\t@ParameterizedClass\n\t@ValueSource(ints = 1)\n\t@ExtendWith(ParameterInfoConsumingExtension.class)\n\trecord TestCase(int i) {\n\n\t\t@Nested\n\t\t@ParameterizedClass\n\t\t@ValueSource(ints = 2)\n\t\tclass Inner {\n\n\t\t\t@Parameter\n\t\t\tint j;\n\n\t\t\t@ParameterizedTest\n\t\t\t@ValueSource(ints = 3)\n\t\t\tvoid test(int k) {\n\t\t\t\tassertEquals(1, i);\n\t\t\t\tassertEquals(2, j);\n\t\t\t\tassertEquals(3, k);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"removal\")\n\t@NullMarked\n\tprivate static class ParameterInfoConsumingExtension\n\t\t\timplements BeforeClassTemplateInvocationCallback, BeforeEachCallback {\n\n\t\t@Override\n\t\tpublic void beforeClassTemplateInvocation(ExtensionContext parameterizedClassInvocationContext) {\n\t\t\tif (TestCase.Inner.class.equals(parameterizedClassInvocationContext.getRequiredTestClass())) {\n\t\t\t\tassertParameterInfo(parameterizedClassInvocationContext, \"j\", 2);\n\n\t\t\t\tvar nestedParameterizedClassContext = parameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\t\tassertParameterInfo(nestedParameterizedClassContext, \"i\", 1);\n\n\t\t\t\tparameterizedClassInvocationContext = nestedParameterizedClassContext.getParent().orElseThrow();\n\t\t\t}\n\n\t\t\tassertParameterInfo(parameterizedClassInvocationContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassContext = parameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertNull(ParameterInfo.get(outerParameterizedClassContext));\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeEach(ExtensionContext parameterizedTestInvocationContext) {\n\t\t\tassertParameterInfo(parameterizedTestInvocationContext, \"k\", 3);\n\n\t\t\tvar parameterizedTestContext = parameterizedTestInvocationContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(parameterizedTestContext, \"j\", 2);\n\n\t\t\tvar nestedParameterizedClassInvocationContext = parameterizedTestContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(nestedParameterizedClassInvocationContext, \"j\", 2);\n\n\t\t\tvar nestedParameterizedClassContext = nestedParameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(nestedParameterizedClassContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassInvocationContext = nestedParameterizedClassContext.getParent().orElseThrow();\n\t\t\tassertParameterInfo(outerParameterizedClassInvocationContext, \"i\", 1);\n\n\t\t\tvar outerParameterizedClassContext = outerParameterizedClassInvocationContext.getParent().orElseThrow();\n\t\t\tassertNull(ParameterInfo.get(outerParameterizedClassContext));\n\t\t}\n\n\t\tprivate static void assertParameterInfo(ExtensionContext context, String parameterName, int argumentValue) {\n\t\t\tvar parameterInfo = ParameterInfo.get(context);\n\t\t\tassertNotNull(parameterInfo);\n\t\t\tvar declaration = parameterInfo.getDeclarations().get(0).orElseThrow();\n\t\t\tassertEquals(parameterName, declaration.getParameterName().orElseThrow());\n\t\t\tassertEquals(int.class, declaration.getParameterType());\n\t\t\tassertEquals(argumentValue, parameterInfo.getArguments().getInteger(0));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/GenericInlineValueClassTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.MethodSource\n\n/**\n * Tests for generic inline value classes.\n * These work because they compile to Object in JVM, bypassing strict type validation.\n */\nclass GenericInlineValueClassTests {\n    @MethodSource(\"resultProvider\")\n    @ParameterizedTest\n    fun testResult(result: Result<String>) {\n        assertEquals(\"success\", result.getOrThrow())\n    }\n\n    @MethodSource(\"multipleResultsProvider\")\n    @ParameterizedTest\n    fun testMultipleResults(\n        result1: Result<String>,\n        result2: Result<Int>\n    ) {\n        assertEquals(\"data\", result1.getOrThrow())\n        assertEquals(42, result2.getOrThrow())\n    }\n\n    @MethodSource(\"nullableResultProvider\")\n    @ParameterizedTest\n    fun testNullableResult(result: Result<String>?) {\n        assertEquals(\"test\", result?.getOrNull())\n    }\n\n    @MethodSource(\"customGenericProvider\")\n    @ParameterizedTest\n    fun testCustomGenericContainer(container: Container<String>) {\n        assertEquals(\"content\", container.value)\n    }\n\n    companion object {\n        @JvmStatic\n        fun resultProvider() =\n            listOf(\n                Arguments.of(Result.success(\"success\"))\n            )\n\n        @JvmStatic\n        fun multipleResultsProvider() =\n            listOf(\n                Arguments.of(\n                    Result.success(\"data\"),\n                    Result.success(42)\n                )\n            )\n\n        @JvmStatic\n        fun nullableResultProvider() =\n            listOf(\n                Arguments.of(Result.success(\"test\"))\n            )\n\n        @JvmStatic\n        fun customGenericProvider() =\n            listOf(\n                Arguments.of(Container(\"content\"))\n            )\n    }\n}\n\n@JvmInline\nvalue class Container<T>(\n    val value: T\n)\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinAssertTimeoutAssertionsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.assertTimeout\nimport org.junit.jupiter.api.assertTimeoutPreemptively\nimport org.junit.jupiter.api.fail\nimport org.junit.platform.commons.util.ExceptionUtils\nimport org.opentest4j.AssertionFailedError\nimport java.time.Duration.ofMillis\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.atomic.AtomicBoolean\n\n/**\n * Unit tests for Kotlin-specific `assertTimeout*` assertions.\n *\n * @since 5.5\n */\ninternal class KotlinAssertTimeoutAssertionsTests {\n    // --- executable ----------------------------------------------------------\n\n    @Test\n    fun assertTimeoutForExecutableThatCompletesBeforeTheTimeout() {\n        changed.get().set(false)\n        assertTimeout(ofMillis(500)) { changed.get().set(true) }\n        assertTrue(changed.get().get(), \"should have executed in the same thread\")\n        assertTimeout(ofMillis(500), \"message\") { }\n        assertTimeout(ofMillis(500), \"message\") { }\n    }\n\n    @Test\n    fun assertTimeoutForExecutableThatThrowsAnException() {\n        val exception =\n            assertThrows<RuntimeException> {\n                assertTimeout<Any>(ofMillis(500)) {\n                    throw RuntimeException(\"not this time\")\n                }\n            }\n        assertMessageEquals(exception, \"not this time\")\n    }\n\n    @Test\n    fun assertTimeoutForExecutableThatThrowsAnAssertionFailedError() {\n        val exception =\n            assertThrows<AssertionFailedError> {\n                assertTimeout<Any>(ofMillis(500)) { fail(\"enigma\") }\n            }\n        assertMessageEquals(exception, \"enigma\")\n    }\n\n    @Test\n    fun assertTimeoutForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10)) { this.nap() }\n            }\n        assertMessageStartsWith(error, \"execution exceeded timeout of 10 ms by\")\n    }\n\n    @Test\n    fun assertTimeoutWithMessageForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10), \"Tempus Fugit\") { this.nap() }\n            }\n        assertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\")\n    }\n\n    @Test\n    fun assertTimeoutWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10), { \"Tempus\" + \" \" + \"Fugit\" }) { this.nap() }\n            }\n        assertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\")\n    }\n\n    // --- supplier ------------------------------------------------------------\n\n    @Test\n    fun assertTimeoutForSupplierThatCompletesBeforeTheTimeout() {\n        changed.get().set(false)\n        val result =\n            assertTimeout(ofMillis(500)) {\n                changed.get().set(true)\n                \"Tempus Fugit\"\n            }\n        assertTrue(changed.get().get(), \"should have executed in the same thread\")\n        assertEquals(\"Tempus Fugit\", result)\n        assertEquals(\"Tempus Fugit\", assertTimeout(ofMillis(500), \"message\") { \"Tempus Fugit\" })\n        assertEquals(\"Tempus Fugit\", assertTimeout(ofMillis(500), { \"message\" }, { \"Tempus Fugit\" }))\n    }\n\n    @Test\n    fun assertTimeoutForSupplierThatThrowsAnException() {\n        val exception =\n            assertThrows<RuntimeException> {\n                assertTimeout(ofMillis(500)) {\n                    ExceptionUtils.throwAsUncheckedException(RuntimeException(\"not this time\"))\n                }\n            }\n        assertMessageEquals(exception, \"not this time\")\n    }\n\n    @Test\n    fun assertTimeoutForSupplierThatThrowsAnAssertionFailedError() {\n        val exception =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(500)) {\n                    fail(\"enigma\")\n                }\n            }\n        assertMessageEquals(exception, \"enigma\")\n    }\n\n    @Test\n    fun assertTimeoutForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10)) {\n                    nap()\n                }\n            }\n        assertMessageStartsWith(error, \"execution exceeded timeout of 10 ms by\")\n    }\n\n    @Test\n    fun assertTimeoutWithMessageForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10), \"Tempus Fugit\") {\n                    nap()\n                }\n            }\n        assertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\")\n    }\n\n    @Test\n    fun assertTimeoutWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeout(ofMillis(10), { \"Tempus\" + \" \" + \"Fugit\" }) {\n                    nap()\n                }\n            }\n        assertMessageStartsWith(error, \"Tempus Fugit ==> execution exceeded timeout of 10 ms by\")\n    }\n\n    @Test\n    fun `assertTimeout with value assignment in lambda`() {\n        val value: Int\n\n        assertTimeout(ofMillis(500)) {\n            value = 10 // Val can be assigned in the message supplier lambda.\n            assertEquals(10, value)\n        }\n    }\n\n    @Test\n    fun `assertTimeout with message and value assignment in lambda`() {\n        var value = 0\n\n        assertTimeout(ofMillis(500), \"message\") { value = 10 }\n\n        assertEquals(10, value)\n    }\n\n    @Test\n    fun `assertTimeout with message supplier and value assignment in lambda`() {\n        var value = 0\n\n        @Suppress(\"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\")\n        val valueInMessageSupplier: Int\n\n        assertTimeout(\n            timeout = ofMillis(500),\n            message = {\n                valueInMessageSupplier = 20 // Val can be assigned in the message supplier lambda.\n                \"message\"\n            },\n            executable = { value = 10 }\n        )\n\n        assertEquals(10, value)\n    }\n\n    // -- executable - preemptively ---\n\n    @Test\n    fun assertTimeoutPreemptivelyForExecutableThatCompletesBeforeTheTimeout() {\n        changed.get().set(false)\n        assertTimeoutPreemptively(ofMillis(500)) { changed.get().set(true) }\n        assertFalse(changed.get().get(), \"should have executed in a different thread\")\n        assertTimeoutPreemptively(ofMillis(500), \"message\") {}\n        assertTimeoutPreemptively(ofMillis(500), { \"message\" }) {}\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForExecutableThatThrowsAnException() {\n        val exception =\n            assertThrows<RuntimeException> {\n                assertTimeoutPreemptively<Any>(ofMillis(500)) { throw RuntimeException(\"not this time\") }\n            }\n        assertMessageEquals(exception, \"not this time\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForExecutableThatThrowsAnAssertionFailedError() {\n        val exception =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively<Any>(ofMillis(500)) { fail(\"enigma\") }\n            }\n        assertMessageEquals(exception, \"enigma\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10)) { waitForInterrupt() }\n            }\n        assertMessageEquals(error, \"execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyWithMessageForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10), \"Tempus Fugit\") { waitForInterrupt() }\n            }\n        assertMessageEquals(error, \"Tempus Fugit ==> execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyWithMessageSupplierForExecutableThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10), { \"Tempus\" + \" \" + \"Fugit\" }) { waitForInterrupt() }\n            }\n        assertMessageEquals(error, \"Tempus Fugit ==> execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyWithMessageSupplierForExecutableThatCompletesBeforeTheTimeout() {\n        assertTimeoutPreemptively(ofMillis(500), { \"Tempus\" + \" \" + \"Fugit\" }) {}\n    }\n\n    // -- supplier - preemptively ---\n\n    @Test\n    fun assertTimeoutPreemptivelyForSupplierThatCompletesBeforeTheTimeout() {\n        changed.get().set(false)\n        val result =\n            assertTimeoutPreemptively(ofMillis(500)) {\n                changed.get().set(true)\n                \"Tempus Fugit\"\n            }\n        assertFalse(changed.get().get(), \"should have executed in a different thread\")\n        assertEquals(\"Tempus Fugit\", result)\n        assertEquals(\"Tempus Fugit\", assertTimeoutPreemptively(ofMillis(500), \"message\") { \"Tempus Fugit\" })\n        assertEquals(\"Tempus Fugit\", assertTimeoutPreemptively(ofMillis(500), { \"message\" }) { \"Tempus Fugit\" })\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForSupplierThatThrowsAnException() {\n        val exception =\n            assertThrows<RuntimeException> {\n                assertTimeoutPreemptively(ofMillis(500)) {\n                    ExceptionUtils.throwAsUncheckedException(RuntimeException(\"not this time\"))\n                }\n            }\n        assertMessageEquals(exception, \"not this time\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForSupplierThatThrowsAnAssertionFailedError() {\n        val exception =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(500)) {\n                    fail(\"enigma\")\n                }\n            }\n        assertMessageEquals(exception, \"enigma\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10)) {\n                    waitForInterrupt()\n                }\n            }\n        assertMessageEquals(error, \"execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyWithMessageForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10), \"Tempus Fugit\") {\n                    waitForInterrupt()\n                }\n            }\n        assertMessageEquals(error, \"Tempus Fugit ==> execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun assertTimeoutPreemptivelyWithMessageSupplierForSupplierThatCompletesAfterTheTimeout() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertTimeoutPreemptively(ofMillis(10), { \"Tempus\" + \" \" + \"Fugit\" }) {\n                    waitForInterrupt()\n                }\n            }\n        assertMessageEquals(error, \"Tempus Fugit ==> execution timed out after 10 ms\")\n    }\n\n    @Test\n    fun `assertTimeoutPreemptively with message supplier and value initialization in lambda`() {\n        @Suppress(\"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\")\n        val valueInMessageSupplier: Int\n\n        assertTimeoutPreemptively(\n            timeout = ofMillis(500),\n            message = {\n                valueInMessageSupplier = 20 // Val can be assigned in the message supplier lambda.\n                \"message\"\n            },\n            executable = {}\n        )\n    }\n\n    /**\n     * Take a nap for 100 milliseconds.\n     */\n    private fun nap() {\n        val start = System.nanoTime()\n        // workaround for imprecise clocks (yes, Windows, I'm talking about you)\n        do {\n            Thread.sleep(100)\n        } while (System.nanoTime() - start < 100_000_000L)\n    }\n\n    private fun waitForInterrupt() {\n        try {\n            CountDownLatch(1).await()\n        } catch (ignore: InterruptedException) {\n            // ignore\n        }\n    }\n\n    companion object {\n        private val changed = ThreadLocal.withInitial { AtomicBoolean(false) }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinAssertionsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport kotlinx.coroutines.runBlocking\nimport org.junit.jupiter.api.AssertionTestUtils\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith\nimport org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.DynamicContainer.dynamicContainer\nimport org.junit.jupiter.api.DynamicNode\nimport org.junit.jupiter.api.DynamicTest.dynamicTest\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestFactory\nimport org.junit.jupiter.api.assertAll\nimport org.junit.jupiter.api.assertDoesNotThrow\nimport org.junit.jupiter.api.assertInstanceOf\nimport org.junit.jupiter.api.assertNull\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.assertThrowsExactly\nimport org.junit.jupiter.api.fail\nimport org.opentest4j.AssertionFailedError\nimport org.opentest4j.MultipleFailuresError\nimport java.util.stream.Stream\nimport kotlin.reflect.KClass\n\n/**\n * Unit tests for JUnit Jupiter [org.junit.jupiter.api] top-level assertion functions.\n */\nclass KotlinAssertionsTests {\n    // Bonus: no null check tests as these get handled by the compiler!\n\n    @Test\n    fun `assertAll with functions that do not throw exceptions`() {\n        assertAll(Stream.of({ assertTrue(true) }, { assertFalse(false) }))\n        assertAll(\"heading\", Stream.of({ assertTrue(true) }, { assertFalse(false) }))\n        assertAll(setOf({ assertTrue(true) }, { assertFalse(false) }))\n        assertAll(\"heading\", setOf({ assertTrue(true) }, { assertFalse(false) }))\n        assertAll({ assertTrue(true) }, { assertFalse(false) })\n        assertAll(\"heading\", { assertTrue(true) }, { assertFalse(false) })\n    }\n\n    @Test\n    fun `assertAll with functions that throw AssertionErrors`() {\n        val multipleFailuresError =\n            assertThrows<MultipleFailuresError> {\n                assertAll(\n                    { assertFalse(true) },\n                    { assertFalse(true) }\n                )\n            }\n        assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class)\n    }\n\n    @Test\n    fun `assertThrows and fail`() {\n        assertThrows<AssertionError> { fail(\"message\") }\n        assertThrows<AssertionError> { fail(\"message\", AssertionError()) }\n        assertThrows<AssertionError> { fail(\"message\", null) }\n        assertThrows<AssertionError>(\"should fail\") { fail({ \"message\" }) }\n        assertThrows<AssertionError>({ \"should fail\" }) { fail(AssertionError()) }\n        assertThrows<AssertionError>({ \"should fail\" }) { fail(null as Throwable?) }\n    }\n\n    @Test\n    fun assertThrowsExactly() {\n        assertThrowsExactly<AssertionFailedError> { fail(\"message\") }\n        assertThrowsExactly<AssertionFailedError>(\"should fail\") { fail(\"message\") }\n        assertThrowsExactly<AssertionFailedError>({ \"should fail\" }) { fail(\"message\") }\n    }\n\n    @Test\n    fun `expected context exception testing`() =\n        runBlocking<Unit> {\n            assertThrows<AssertionError>(\"Should fail async\") {\n                suspend { fail(\"Should fail async\") }()\n            }\n        }\n\n    @TestFactory\n    fun `assertDoesNotThrow behaves as expected`(): Stream<out DynamicNode> =\n        Stream.of(\n            dynamicContainer(\n                \"succeeds when no exception thrown\",\n                Stream.of(\n                    dynamicTest(\"for no arguments variant\") {\n                        val actual = assertDoesNotThrow { 1 }\n                        assertEquals(1, actual)\n                    },\n                    dynamicTest(\"for no arguments variant (suspended)\") {\n                        runBlocking {\n                            val actual = assertDoesNotThrow { suspend { 1 }() }\n                            assertEquals(1, actual)\n                        }\n                    },\n                    dynamicTest(\"for message variant\") {\n                        val actual = assertDoesNotThrow(\"message\") { 2 }\n                        assertEquals(2, actual)\n                    },\n                    dynamicTest(\"for message variant (suspended)\") {\n                        runBlocking {\n                            val actual = assertDoesNotThrow(\"message\") { suspend { 2 }() }\n                            assertEquals(2, actual)\n                        }\n                    },\n                    dynamicTest(\"for message supplier variant\") {\n                        val actual = assertDoesNotThrow({ \"message\" }) { 3 }\n                        assertEquals(3, actual)\n                    },\n                    dynamicTest(\"for message supplier variant (suspended)\") {\n                        runBlocking {\n                            val actual = assertDoesNotThrow({ \"message\" }) { suspend { 3 }() }\n                            assertEquals(3, actual)\n                        }\n                    }\n                )\n            ),\n            dynamicContainer(\n                \"fails when an exception is thrown\",\n                Stream.of(\n                    dynamicTest(\"for no arguments variant\") {\n                        val exception =\n                            assertThrows<AssertionError> {\n                                assertDoesNotThrow {\n                                    fail(\"fail\")\n                                }\n                            }\n                        assertMessageEquals(\n                            exception,\n                            \"Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                        )\n                    },\n                    dynamicTest(\"for no arguments variant (suspended)\") {\n                        runBlocking {\n                            val exception =\n                                assertThrows<AssertionError> {\n                                    assertDoesNotThrow {\n                                        suspend { fail(\"fail\") }()\n                                    }\n                                }\n                            assertMessageEquals(\n                                exception,\n                                \"Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                            )\n                        }\n                    },\n                    dynamicTest(\"for message variant\") {\n                        val exception =\n                            assertThrows<AssertionError> {\n                                assertDoesNotThrow(\"Does not throw\") {\n                                    fail(\"fail\")\n                                }\n                            }\n                        assertMessageEquals(\n                            exception,\n                            \"Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                        )\n                    },\n                    dynamicTest(\"for message variant (suspended)\") {\n                        runBlocking {\n                            val exception =\n                                assertThrows<AssertionError> {\n                                    assertDoesNotThrow(\"Does not throw\") {\n                                        suspend { fail(\"fail\") }()\n                                    }\n                                }\n                            assertMessageEquals(\n                                exception,\n                                \"Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                            )\n                        }\n                    },\n                    dynamicTest(\"for message supplier variant\") {\n                        val exception =\n                            assertThrows<AssertionError> {\n                                assertDoesNotThrow({ \"Does not throw\" }) {\n                                    fail(\"fail\")\n                                }\n                            }\n                        assertMessageEquals(\n                            exception,\n                            \"Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                        )\n                    },\n                    dynamicTest(\"for message supplier variant (suspended)\") {\n                        runBlocking {\n                            val exception =\n                                assertThrows<AssertionError> {\n                                    assertDoesNotThrow({ \"Does not throw\" }) {\n                                        suspend { fail(\"fail\") }()\n                                    }\n                                }\n                            assertMessageEquals(\n                                exception,\n                                \"Does not throw ==> Unexpected exception thrown: org.opentest4j.AssertionFailedError: fail\"\n                            )\n                        }\n                    }\n                )\n            )\n        )\n\n    @Test\n    fun `assertAll with stream of functions that throw AssertionErrors`() {\n        val multipleFailuresError =\n            assertThrows<MultipleFailuresError>(\"Should have thrown multiple errors\") {\n                assertAll(Stream.of({ assertFalse(true) }, { assertFalse(true) }))\n            }\n        assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class)\n    }\n\n    @Test\n    fun `assertAll with collection of functions that throw AssertionErrors`() {\n        val multipleFailuresError =\n            assertThrows<MultipleFailuresError>(\"Should have thrown multiple errors\") {\n                assertAll(setOf({ assertFalse(true) }, { assertFalse(true) }))\n            }\n        assertExpectedExceptionTypes(multipleFailuresError, AssertionFailedError::class, AssertionFailedError::class)\n    }\n\n    @Test\n    fun `assertThrows with function that does not throw an exception`() {\n        val assertionMessage = \"This will not throw an exception\"\n        val error =\n            assertThrows<AssertionFailedError>(\"assertThrows did not throw the correct exception\") {\n                assertThrows<IllegalStateException>(assertionMessage) { }\n                // This should never execute:\n                expectAssertionFailedError()\n            }\n        assertMessageStartsWith(error, assertionMessage)\n    }\n\n    @Test\n    fun `assertInstanceOf succeeds`() {\n        assertInstanceOf<RandomAccess>(listOf(\"whatever\"))\n        assertInstanceOf<RandomAccess>(listOf(\"whatever\"), \"No random access\")\n        assertInstanceOf<RandomAccess>(listOf(\"whatever\")) { \"No random access\" }\n    }\n\n    @Test\n    fun `assertInstanceOf fails wrong type value`() {\n        val result =\n            assertThrows<AssertionError> {\n                assertInstanceOf<String>(StringBuilder(), \"Should be a String\")\n            }\n        assertMessageStartsWith(result, \"Should be a String\")\n    }\n\n    @Test\n    fun `assertInstanceOf fails null value`() {\n        val result =\n            assertThrows<AssertionError> {\n                assertInstanceOf<String>(null, \"Should be a String\")\n            }\n        assertMessageStartsWith(result, \"Should be a String\")\n    }\n\n    @Test\n    fun `assertInstanceOf with compiler smart cast`() {\n        val maybeString: Any = \"string\"\n\n        assertInstanceOf<String>(maybeString)\n        assertFalse(maybeString.isEmpty()) // A smart cast to a String object.\n    }\n\n    @Test\n    fun `assertInstanceOf with compiler nullable smart cast`() {\n        val maybeString: Any? = \"string\"\n\n        assertInstanceOf<String>(maybeString)\n        assertFalse(maybeString.isEmpty()) // A smart cast to a non-nullable String object.\n    }\n\n    @Test\n    fun `assertInstanceOf with a null value`() {\n        val error =\n            assertThrows<AssertionFailedError> {\n                assertInstanceOf<String>(null)\n            }\n\n        assertMessageStartsWith(error, \"Unexpected null value\")\n    }\n\n    @Test\n    fun `assertInstanceOf with message and compiler smart cast`() {\n        val maybeString: Any = \"string\"\n\n        assertInstanceOf<String>(maybeString, \"maybeString is not an instance of String\")\n        assertFalse(maybeString.isEmpty()) // A smart cast to a String object.\n    }\n\n    @Test\n    fun `assertInstanceOf with message supplier and compiler smart cast`() {\n        val maybeString: Any = \"string\"\n\n        @Suppress(\"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\")\n        val valueInMessageSupplier: Int\n\n        assertInstanceOf<String>(maybeString) {\n            valueInMessageSupplier = 20 // Val can be assigned in the message supplier lambda.\n\n            \"maybeString is not an instance of String\"\n        }\n\n        assertFalse(maybeString.isEmpty()) // A smart cast to a String object.\n    }\n\n    @Test\n    fun `assertNull with compiler smart cast`() {\n        val nullableString: String? = null\n\n        assertNull(nullableString)\n        // Even safe call is not allowed because compiler knows that nullableString is always null.\n        // nullableString?.isEmpty()\n    }\n\n    @Test\n    fun `assertNull with message and compiler smart cast`() {\n        val nullableString: String? = null\n\n        assertNull(nullableString, \"nullableString is not null\")\n        // Even safe call is not allowed because compiler knows that nullableString is always null.\n        // nullableString?.isEmpty()\n    }\n\n    @Test\n    fun `assertNull with message supplier and compiler smart cast`() {\n        val nullableString: String? = null\n\n        @Suppress(\"ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE\")\n        val valueInMessageSupplier: Int\n\n        assertNull(nullableString) {\n            valueInMessageSupplier = 20 // Val can be assigned in the message supplier lambda.\n\n            \"nullableString is not null\"\n        }\n\n        // Even safe call is not allowed because compiler knows that nullableString is always null.\n        // nullableString?.isEmpty()\n    }\n\n    companion object {\n        fun assertExpectedExceptionTypes(\n            multipleFailuresError: MultipleFailuresError,\n            vararg exceptionTypes: KClass<out Throwable>\n        ) = AssertionTestUtils.assertExpectedExceptionTypes(\n            multipleFailuresError,\n            *exceptionTypes.map { it.java }.toTypedArray()\n        )\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinDynamicTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.DynamicTest\nimport org.junit.jupiter.api.DynamicTest.dynamicTest\nimport org.junit.jupiter.api.Nested\nimport org.junit.jupiter.api.TestFactory\nimport java.math.BigDecimal\nimport java.math.BigDecimal.ONE\nimport java.math.MathContext\nimport java.math.BigInteger as BigInt\nimport java.math.RoundingMode as Rounding\n\n/**\n * Unit tests for JUnit Jupiter [org.junit.jupiter.api.TestFactory] use in kotlin classes.\n *\n * @since 5.12\n */\nclass KotlinDynamicTests {\n    @Nested\n    inner class SequenceReturningTestFactoryTests {\n        @TestFactory\n        fun `Dynamic tests returned as Kotlin sequence`() =\n            generateSequence(0) { it + 2 }\n                .map { dynamicTest(\"$it should be even\") { assertEquals(0, it % 2) } }\n                .take(10)\n\n        @TestFactory\n        fun `Consecutive fibonacci nr ratios, should converge to golden ratio as n increases`(): Sequence<DynamicTest> {\n            val scale = 5\n            val goldenRatio =\n                (ONE + 5.toBigDecimal().sqrt(MathContext(scale + 10, Rounding.HALF_UP)))\n                    .divide(2.toBigDecimal(), scale, Rounding.HALF_UP)\n\n            fun shouldApproximateGoldenRatio(\n                cur: BigDecimal,\n                next: BigDecimal\n            ) = next.divide(cur, scale, Rounding.HALF_UP).let {\n                dynamicTest(\"$cur / $next = $it should approximate the golden ratio in $scale decimals\") {\n                    assertEquals(goldenRatio, it)\n                }\n            }\n            return generateSequence(BigInt.ONE to BigInt.ONE) { (cur, next) -> next to cur + next }\n                .map { (cur) -> cur.toBigDecimal() }\n                .zipWithNext(::shouldApproximateGoldenRatio)\n                .drop(14)\n                .take(10)\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinFailAssertionsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport org.junit.jupiter.api.AssertionTestUtils.assertEmptyMessage\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageContains\nimport org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.fail\nimport org.opentest4j.AssertionFailedError\nimport java.util.stream.Stream\n\nclass KotlinFailAssertionsTests {\n    @Test\n    fun `fail with string`() {\n        val message = \"test\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(message)\n            }\n        assertMessageEquals(ex, message)\n    }\n\n    @Test\n    fun `fail with message supplier`() {\n        val message = \"test\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail { message }\n            }\n        assertMessageEquals(ex, message)\n    }\n\n    @Test\n    fun `fail with null string`() {\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(null as String?)\n            }\n        assertEmptyMessage(ex)\n    }\n\n    @Test\n    fun `fail with null message supplier`() {\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(null as (() -> String)?)\n            }\n        assertEmptyMessage(ex)\n    }\n\n    @Test\n    fun `fail with string and throwable`() {\n        val message = \"message\"\n        val throwableCause = \"cause\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(message, Throwable(throwableCause))\n            }\n        assertMessageEquals(ex, message)\n        val cause = ex.cause\n        assertMessageContains(cause!!, throwableCause)\n    }\n\n    @Test\n    fun `fail with throwable`() {\n        val throwableCause = \"cause\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(Throwable(throwableCause))\n            }\n        assertEmptyMessage(ex)\n        val cause = ex.cause\n        assertMessageContains(cause!!, throwableCause)\n    }\n\n    @Test\n    fun `fail with string and null throwable`() {\n        val message = \"message\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(message, null)\n            }\n        assertMessageEquals(ex, message)\n        if (ex.cause != null) {\n            throw AssertionError(\"Cause should have been null\")\n        }\n    }\n\n    @Test\n    fun `fail with null string and throwable`() {\n        val throwableCause = \"cause\"\n        val ex =\n            assertThrows<AssertionFailedError> {\n                fail(null, Throwable(throwableCause))\n            }\n        assertEmptyMessage(ex)\n        val cause = ex.cause\n        assertMessageContains(cause!!, throwableCause)\n    }\n\n    @Test\n    fun `fail usable as a stream expression`() {\n        val count =\n            Stream\n                .empty<Any>()\n                .peek { _ -> fail(\"peek should never be called\") }\n                .filter { _ -> fail(\"filter should never be called\", Throwable(\"cause\")) }\n                .map { _ -> fail(Throwable(\"map should never be called\")) }\n                .sorted { _, _ -> fail { \"sorted should never be called\" } }\n                .count()\n        assertEquals(0L, count)\n    }\n\n    @Test\n    fun `fail usable as a sequence expression`() {\n        val count =\n            emptyList<Any>()\n                .asSequence()\n                .onEach { _ -> fail(\"peek should never be called\") }\n                .filter { _ -> fail(\"filter should never be called\", Throwable(\"cause\")) }\n                .map { _ -> fail(Throwable(\"map should never be called\")) }\n                .count()\n        assertEquals(0, count)\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinSuspendFunctionsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport kotlinx.coroutines.runBlocking\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.AfterAll\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.DynamicTest\nimport org.junit.jupiter.api.DynamicTest.dynamicTest\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestFactory\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS\nimport org.junit.jupiter.api.TestReporter\nimport org.junit.jupiter.engine.AbstractJupiterTestEngineTests\nimport org.junit.jupiter.params.AfterParameterizedClassInvocation\nimport org.junit.jupiter.params.BeforeParameterizedClassInvocation\nimport org.junit.jupiter.params.Parameter\nimport org.junit.jupiter.params.ParameterizedClass\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.ValueSource\nimport org.junit.platform.engine.reporting.ReportEntry\nimport org.junit.platform.testkit.engine.EngineExecutionResults\nimport java.util.stream.Stream\n\nclass KotlinSuspendFunctionsTests : AbstractJupiterTestEngineTests() {\n    @Test\n    fun suspendingTestMethodsAreSupported() {\n        val results = executeTestsForClass(TestMethodTestCase::class)\n        assertAllTestsPassed(results, 1)\n        assertThat(getPublishedEvents(results)).containsExactly(\"test\")\n    }\n\n    @Test\n    fun suspendingOpenTestMethodsAreSupported() {\n        val results = executeTestsForClass(OpenTestMethodTestCase::class)\n        assertAllTestsPassed(results, 1)\n        assertThat(getPublishedEvents(results)).containsExactly(\"test\")\n    }\n\n    @Test\n    fun suspendingTestTemplateMethodsAreSupported() {\n        val results = executeTestsForClass(TestTemplateTestCase::class)\n        assertAllTestsPassed(results, 2)\n        assertThat(getPublishedEvents(results)).containsExactly(\"foo\", \"bar\")\n    }\n\n    @Test\n    fun suspendingTestFactoryMethodsAreSupported() {\n        val results = executeTestsForClass(TestFactoryTestCase::class)\n        assertAllTestsPassed(results, 2)\n        assertThat(getPublishedEvents(results)).containsExactly(\"test\", \"foo\", \"bar\")\n    }\n\n    @Test\n    fun suspendingLifecycleMethodsAreSupported() {\n        val results = executeTestsForClass(LifecycleMethodsTestCase::class)\n        assertAllTestsPassed(results, 1)\n        assertThat(getPublishedEvents(results)).containsExactly(\"beforeAll\", \"beforeEach\", \"test\", \"afterEach\", \"afterAll\")\n    }\n\n    @Test\n    fun suspendingParameterizedLifecycleMethodsAreSupported() {\n        val results = executeTestsForClass(ParameterizedLifecycleMethodsTestCase::class)\n        assertAllTestsPassed(results, 2)\n        assertThat(\n            getPublishedEvents(results)\n        ).containsExactly(\"beforeInvocation[1]\", \"test[1]\", \"afterInvocation[1]\", \"beforeInvocation[2]\", \"test[2]\", \"afterInvocation[2]\")\n    }\n\n    private fun assertAllTestsPassed(\n        results: EngineExecutionResults,\n        numTests: Long\n    ) {\n        results.testEvents().assertStatistics {\n            it.started(numTests).succeeded(numTests)\n        }\n    }\n\n    private fun getPublishedEvents(results: EngineExecutionResults) =\n        results\n            .allEvents()\n            .reportingEntryPublished() //\n            .map { it.getRequiredPayload(ReportEntry::class.java) } //\n            .map(ReportEntry::getKeyValuePairs)\n            .map { it[\"value\"] }\n\n    @Suppress(\"JUnitMalformedDeclaration\")\n    class TestMethodTestCase {\n        @Test\n        suspend fun test(reporter: TestReporter) {\n            suspendingPublish(reporter, \"test\")\n        }\n    }\n\n    @Suppress(\"JUnitMalformedDeclaration\")\n    class TestTemplateTestCase {\n        @ParameterizedTest\n        @ValueSource(strings = [\"foo\", \"bar\"])\n        suspend fun test(\n            message: String,\n            reporter: TestReporter\n        ) {\n            suspendingPublish(reporter, message)\n        }\n    }\n\n    class TestFactoryTestCase {\n        @TestFactory\n        suspend fun test(reporter: TestReporter): Stream<DynamicTest> {\n            suspendingPublish(reporter, \"test\")\n            return Stream.of(\"foo\", \"bar\").map {\n                dynamicTest(it) {\n                    runBlocking {\n                        suspendingPublish(reporter, it)\n                    }\n                }\n            }\n        }\n    }\n\n    @Suppress(\"JUnitMalformedDeclaration\")\n    @TestInstance(PER_CLASS)\n    class LifecycleMethodsTestCase {\n        @BeforeAll\n        suspend fun beforeAll(reporter: TestReporter) {\n            suspendingPublish(reporter, \"beforeAll\")\n        }\n\n        @BeforeEach\n        suspend fun beforeEach(reporter: TestReporter) {\n            suspendingPublish(reporter, \"beforeEach\")\n        }\n\n        @Test\n        suspend fun test(reporter: TestReporter) {\n            suspendingPublish(reporter, \"test\")\n        }\n\n        @AfterEach\n        suspend fun afterEach(reporter: TestReporter) {\n            suspendingPublish(reporter, \"afterEach\")\n        }\n\n        @AfterAll\n        suspend fun afterAll(reporter: TestReporter) {\n            suspendingPublish(reporter, \"afterAll\")\n        }\n    }\n\n    @Suppress(\"JUnitMalformedDeclaration\", \"RedundantSuspendModifier\")\n    @ParameterizedClass\n    @ValueSource(ints = [1, 2])\n    @TestInstance(PER_CLASS)\n    class ParameterizedLifecycleMethodsTestCase {\n        @BeforeParameterizedClassInvocation\n        suspend fun beforeInvocation() {}\n\n        @BeforeParameterizedClassInvocation\n        suspend fun beforeInvocation(\n            parameter: Int,\n            reporter: TestReporter\n        ) {\n            suspendingPublish(reporter, \"beforeInvocation[$parameter]\")\n        }\n\n        @AfterParameterizedClassInvocation\n        suspend fun afterInvocation() {}\n\n        @AfterParameterizedClassInvocation\n        suspend fun afterInvocation(\n            parameter: Int,\n            reporter: TestReporter\n        ) {\n            suspendingPublish(reporter, \"afterInvocation[$parameter]\")\n        }\n\n        @Parameter\n        var parameter: Int = 0\n\n        @Test\n        suspend fun test(reporter: TestReporter) {\n            suspendingPublish(reporter, \"test[$parameter]\")\n        }\n    }\n\n    @Suppress(\"JUnitMalformedDeclaration\")\n    open class OpenTestMethodTestCase {\n        @Test // https://github.com/junit-team/junit-framework/issues/5102\n        open suspend fun `check getRandomPositiveInt`(reporter: TestReporter) {\n            suspendingPublish(reporter, \"test\")\n        }\n    }\n\n    @Suppress(\"RedundantSuspendModifier\")\n    companion object {\n        suspend fun suspendingPublish(\n            reporter: TestReporter,\n            message: String\n        ) {\n            reporter.publishEntry(message)\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/PrimitiveWrapperInlineValueClassTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.api.kotlin\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Disabled\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.Arguments\nimport org.junit.jupiter.params.provider.MethodSource\n\n/**\n * Tests for primitive-wrapper inline value classes.\n *\n * Currently disabled: These fail because Kotlin compiles them to primitives\n * (UInt→int, UserId→long), causing JUnit's type validation to fail before\n * reaching the invocation logic.\n *\n * Supporting these would require modifications to JUnit's core type validation system.\n *\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/5081\">Issue #5081</a>\n */\n@Disabled(\"Primitive-wrapper inline value classes are not yet supported\")\nclass PrimitiveWrapperInlineValueClassTests {\n    @MethodSource(\"uintProvider\")\n    @ParameterizedTest\n    fun testUInt(value: UInt) {\n        assertEquals(42u, value)\n    }\n\n    @MethodSource(\"userIdProvider\")\n    @ParameterizedTest\n    fun testUserId(userId: UserId) {\n        assertEquals(123L, userId.value)\n    }\n\n    @MethodSource(\"emailProvider\")\n    @ParameterizedTest\n    fun testEmail(email: Email) {\n        assertEquals(\"test@example.com\", email.value)\n    }\n\n    companion object {\n        @JvmStatic\n        fun uintProvider() = listOf(Arguments.of(42u))\n\n        @JvmStatic\n        fun userIdProvider() = listOf(Arguments.of(UserId(123L)))\n\n        @JvmStatic\n        fun emailProvider() = listOf(Arguments.of(Email(\"test@example.com\")))\n    }\n}\n\n@JvmInline\nvalue class UserId(\n    val value: Long\n)\n\n@JvmInline\nvalue class Email(\n    val value: String\n)\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/ArbitraryNamingKotlinTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nimport org.junit.jupiter.api.Test\n\nclass ArbitraryNamingKotlinTestCase {\n    companion object {\n        @JvmField\n        val METHOD_NAME = \"\\uD83E\\uDD86 ~|~test with a really, (really) terrible name & that needs to be changed!~|~\"\n    }\n\n    @Suppress(\"DANGEROUS_CHARACTERS\")\n    @Test\n    fun `🦆 ~|~test with a really, (really) terrible name & that needs to be changed!~|~`() { }\n\n    @Test\n    fun `test name ends with parentheses()`() { }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerClassKotlinTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nimport org.junit.jupiter.api.AfterAll\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInstance\nimport org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS\n\n@TestInstance(PER_CLASS)\nclass InstancePerClassKotlinTestCase {\n    companion object {\n        @JvmField\n        val TEST_INSTANCES: MutableMap<Any, MutableMap<String, Int>> = HashMap()\n    }\n\n    @BeforeAll\n    fun beforeAll() {\n        increment(\"beforeAll\")\n    }\n\n    @BeforeEach\n    fun beforeEach() {\n        increment(\"beforeEach\")\n    }\n\n    @AfterEach\n    fun afterEach() {\n        increment(\"afterEach\")\n    }\n\n    @AfterAll\n    fun afterAll() {\n        increment(\"afterAll\")\n    }\n\n    @Test\n    fun firstTest() {\n        increment(\"test\")\n    }\n\n    @Test\n    fun secondTest() {\n        increment(\"test\")\n    }\n\n    private fun increment(name: String) {\n        TEST_INSTANCES\n            .computeIfAbsent(this, { _ -> HashMap() })\n            .compute(name, { _, oldValue -> (oldValue ?: 0) + 1 })\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/InstancePerMethodKotlinTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nimport org.junit.jupiter.api.AfterAll\nimport org.junit.jupiter.api.AfterEach\nimport org.junit.jupiter.api.BeforeAll\nimport org.junit.jupiter.api.BeforeEach\nimport org.junit.jupiter.api.Test\n\nclass InstancePerMethodKotlinTestCase {\n    companion object {\n        @JvmField\n        val TEST_INSTANCES: MutableMap<Any, MutableMap<String, Int>> = LinkedHashMap()\n\n        @JvmStatic\n        @BeforeAll\n        fun beforeAll() {\n            increment(this, \"beforeAll\")\n        }\n\n        @JvmStatic\n        @AfterAll\n        fun afterAll() {\n            increment(this, \"afterAll\")\n        }\n\n        private fun increment(\n            instance: Any,\n            name: String\n        ) {\n            TEST_INSTANCES\n                .computeIfAbsent(instance, { _ -> LinkedHashMap() })\n                .compute(name, { _, oldValue -> (oldValue ?: 0) + 1 })\n        }\n    }\n\n    @BeforeEach\n    fun beforeEach() {\n        increment(this, \"beforeEach\")\n    }\n\n    @AfterEach\n    fun afterEach() {\n        increment(this, \"afterEach\")\n    }\n\n    @Test\n    fun firstTest() {\n        increment(this, \"test\")\n    }\n\n    @Test\n    fun secondTest() {\n        increment(this, \"test\")\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/KotlinDefaultImplsTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nimport org.junit.jupiter.api.Test\n\nclass KotlinDefaultImplsTestCase {\n    class DefaultImpls {\n        @Test\n        fun test() {\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/KotlinInterfaceImplementationTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nclass KotlinInterfaceImplementationTestCase : KotlinInterfaceTestCase\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/engine/kotlin/KotlinInterfaceTestCase.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.engine.kotlin\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInfo\n\ninterface KotlinInterfaceTestCase {\n    @Test\n    fun test(testInfo: TestInfo) {\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedClassKotlinIntegrationTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.TestInfo\nimport org.junit.jupiter.params.provider.ValueSource\nimport org.junit.platform.engine.discovery.DiscoverySelectors.selectClass\nimport org.junit.platform.testkit.engine.EngineTestKit\n\nclass ParameterizedClassKotlinIntegrationTests {\n    @Test\n    fun supportsDataClasses() {\n        val results =\n            EngineTestKit\n                .engine(\"junit-jupiter\")\n                .selectors(selectClass(TestCase::class.java))\n                .execute()\n\n        results.containerEvents().assertStatistics {\n            it.started(4).succeeded(4)\n        }\n        results.testEvents().assertStatistics {\n            it.started(4).succeeded(2).failed(2)\n        }\n    }\n\n    @ParameterizedClass\n    @ValueSource(ints = [-1, 1])\n    data class TestCase(\n        val value: Int,\n        val testInfo: TestInfo\n    ) {\n        @Test\n        fun test1() {\n            assertEquals(\"test1()\", testInfo.displayName)\n            assertTrue(value < 0, \"negative\")\n        }\n\n        @Test\n        fun test2() {\n            assertEquals(\"test2()\", testInfo.displayName)\n            assertTrue(value < 0, \"negative\")\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedInvocationNameFormatterIntegrationTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.TestInfo\nimport org.junit.jupiter.params.provider.ValueSource\n\nclass ParameterizedInvocationNameFormatterIntegrationTests {\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @ParameterizedTest\n    fun defaultDisplayName(\n        param: String,\n        info: TestInfo\n    ) {\n        if (param.equals(\"foo\")) {\n            assertEquals(\"[1] param = \\\"foo\\\"\", info.displayName)\n        } else {\n            assertEquals(\"[2] param = \\\"bar\\\"\", info.displayName)\n        }\n    }\n\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @ParameterizedTest(name = \"{0}\")\n    fun `1st argument`(\n        param: String,\n        info: TestInfo\n    ) {\n        if (param.equals(\"foo\")) {\n            assertEquals(\"\\\"foo\\\"\", info.displayName)\n        } else {\n            assertEquals(\"\\\"bar\\\"\", info.displayName)\n        }\n    }\n\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @ParameterizedTest(name = \"{displayName}\")\n    fun `it's an {enigma} '{0}'`(\n        @Suppress(\"UNUSED_PARAMETER\") param: String,\n        info: TestInfo\n    ) {\n        assertEquals(\"it's an {enigma} '{0}'(String, TestInfo)\", info.displayName)\n    }\n\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @ParameterizedTest(name = \"{displayName} - {0}\")\n    fun `displayName and 1st 'argument'`(\n        param: String,\n        info: TestInfo\n    ) {\n        if (param.equals(\"foo\")) {\n            assertEquals(\"displayName and 1st 'argument'(String, TestInfo) - \\\"foo\\\"\", info.displayName)\n        } else {\n            assertEquals(\"displayName and 1st 'argument'(String, TestInfo) - \\\"bar\\\"\", info.displayName)\n        }\n    }\n\n    @ValueSource(strings = [\"foo\", \"bar\"])\n    @ParameterizedTest(name = \"{0} - {displayName}\", quoteTextArguments = false)\n    fun `1st 'argument' and displayName`(\n        param: String,\n        info: TestInfo\n    ) {\n        if (param.equals(\"foo\")) {\n            assertEquals(\"foo - 1st 'argument' and displayName(String, TestInfo)\", info.displayName)\n        } else {\n            assertEquals(\"bar - 1st 'argument' and displayName(String, TestInfo)\", info.displayName)\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/ParameterizedTestKotlinSequenceIntegrationTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.params.provider.Arguments.arguments\nimport org.junit.jupiter.params.provider.FieldSource\nimport org.junit.jupiter.params.provider.MethodSource\nimport java.time.Month\n\n/**\n * Tests for Kotlin compatibility of ParameterizedTest\n */\nobject ParameterizedTestKotlinSequenceIntegrationTests {\n    @ParameterizedTest\n    @MethodSource(\"dataProvidedByKotlinSequenceMethod\")\n    fun `a method source can be supplied by a Sequence-returning method`(\n        value: Int,\n        month: Month\n    ) {\n        assertEquals(value, month.value)\n    }\n\n    @JvmStatic\n    private fun dataProvidedByKotlinSequenceMethod() = dataProvidedByKotlinSequenceField\n\n    @JvmStatic\n    val dataProvidedByKotlinSequenceField =\n        sequenceOf(\n            arguments(1, Month.JANUARY),\n            arguments(3, Month.MARCH),\n            arguments(8, Month.AUGUST),\n            arguments(5, Month.MAY),\n            arguments(12, Month.DECEMBER)\n        )\n\n    @ParameterizedTest\n    @FieldSource(\"dataProvidedByKotlinSequenceField\")\n    fun `a field source can be supplied by a Sequence-typed field`(\n        value: Int,\n        month: Month\n    ) {\n        assertEquals(value, month.value)\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/ArgumentsAccessorKotlinTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.aggregator\n\nimport org.assertj.core.api.Assertions.assertThat\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.extension.ExtensionContext\nimport org.mockito.Mockito.mock\n\n/**\n * Unit tests for using [ArgumentsAccessor] from Kotlin.\n */\nclass ArgumentsAccessorKotlinTests {\n    @Test\n    fun `get() with reified type and index`() {\n        assertEquals(1, defaultArgumentsAccessor(1, 1).get<Int>(0))\n        assertEquals('A', defaultArgumentsAccessor(1, 'A').get<Char>(0))\n    }\n\n    @Test\n    fun `get() with reified type and index for incompatible type`() {\n        val exception =\n            assertThrows<ArgumentAccessException> {\n                defaultArgumentsAccessor(1, Integer.valueOf(1)).get<Char>(0)\n            }\n\n        assertThat(exception).hasMessage(\n            \"Argument at index [0] with value [1] and type [java.lang.Integer] could not be converted or cast to type [java.lang.Character].\"\n        )\n    }\n\n    @Test\n    fun `get() with index`() {\n        assertEquals(1, defaultArgumentsAccessor(1, 1).get(0))\n        assertEquals('A', defaultArgumentsAccessor(1, 'A').get(0))\n    }\n\n    @Suppress(\"PLATFORM_CLASS_MAPPED_TO_KOTLIN\")\n    @Test\n    fun `get() with index and class reference`() {\n        assertEquals(1, defaultArgumentsAccessor(1, 1).get(0, Integer::class.java))\n        assertEquals('A', defaultArgumentsAccessor(1, 'A').get(0, Character::class.java))\n    }\n\n    fun defaultArgumentsAccessor(\n        invocationIndex: Int,\n        vararg arguments: Any\n    ): DefaultArgumentsAccessor {\n        val context = mock(ExtensionContext::class.java)\n        val classLoader = ArgumentsAccessorKotlinTests::class.java.classLoader\n        return DefaultArgumentsAccessor.create(invocationIndex, classLoader, arguments)\n    }\n\n    fun foo() {\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/aggregator/DisplayNameTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\n@file:Suppress(\"unused\")\n\npackage org.junit.jupiter.params.aggregator\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.TestInfo\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.provider.MethodSource\n\n// https://github.com/junit-team/junit-framework/issues/1836\nobject DisplayNameTests {\n    @JvmStatic\n    fun data() =\n        arrayOf(\n            arrayOf<Any>(\"A\", 1),\n            arrayOf<Any>(\"B\", 2),\n            arrayOf<Any>(\"C\", 3),\n            arrayOf<Any>(\"\", 4), // empty is okay\n            arrayOf<Any?>(null, 5) // null was the problem\n        )\n\n    @ParameterizedTest\n    @MethodSource(\"data\")\n    fun test(\n        str: String?,\n        number: Int,\n        info: TestInfo\n    ) {\n        if (str == null) {\n            assertEquals(\"[$number] str = null, number = $number\", info.displayName)\n        } else {\n            assertEquals(\"[$number] str = \\\"$str\\\", number = $number\", info.displayName)\n        }\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/kotlin/org/junit/jupiter/params/converter/TypedArgumentConverterKotlinTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.params.converter\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertNull\nimport org.junit.jupiter.api.extension.ParameterContext\nimport org.mockito.Mockito.mock\nimport org.mockito.Mockito.`when`\n\nclass TypedArgumentConverterKotlinTests {\n    @Test\n    fun converts() {\n        val parameterContext = mock(ParameterContext::class.java)\n        val parameter = this.javaClass.getDeclaredMethod(\"foo\", String::class.java).parameters[0]\n        `when`(parameterContext.parameter).thenReturn(parameter)\n\n        assertNull(NullableTypeConverter().convert(null, parameterContext))\n        assertEquals(\"null\", NonNullableTypeConverter().convert(null, parameterContext))\n    }\n\n    @Suppress(\"unused\")\n    private fun foo(param: String) = Unit\n\n    class NullableTypeConverter : TypedArgumentConverter<String, String?>(String::class.java, String::class.java) {\n        override fun convert(source: String?) = source\n    }\n\n    class NonNullableTypeConverter : TypedArgumentConverter<String, String>(String::class.java, String::class.java) {\n        override fun convert(source: String?) = source.toString()\n    }\n}\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/META-INF/services/org.junit.jupiter.api.extension.Extension",
    "content": "org.junit.jupiter.engine.extension.ServiceLoaderExtension\norg.junit.jupiter.engine.extension.ConfigLoaderExtension\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.extensions.autodetection.enabled=true\n\njunit.platform.output.capture.stdout=true\njunit.platform.output.capture.stderr=true\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/log4j2-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\"\n\t\t\t   xmlns=\"https://logging.apache.org/xml/ns\"\n\t\t\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t   xsi:schemaLocation=\"https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd\">\n\t<Appenders>\n\t\t<Console name=\"Console\" target=\"SYSTEM_OUT\">\n\t\t\t<PatternLayout pattern=\"%d{HH:mm:ss.SSSSSS} [%-18t] %-5level %logger{1.} - %msg%n\"/>\n\t\t</Console>\n\t</Appenders>\n\t<Loggers>\n\t\t<Logger name=\"org.junit\" level=\"WARN\"/>\n\t\t<Logger name=\"org.junit.jupiter.api\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.jupiter.engine\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.DiscoveryIssueNotifier\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.listeners.discovery.LoggingLauncherDiscoveryListener\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.jupiter.engine.extension.MutableExtensionRegistry\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.jupiter.engine.extension.TempDirectory$CloseablePath\" level=\"OFF\"/>\n\t\t<Root level=\"ERROR\">\n\t\t\t<AppenderRef ref=\"Console\"/>\n\t\t</Root>\n\t</Loggers>\n</Configuration>\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/broken.csv",
    "content": "# more than 512 columns\nx,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/default-max-chars.csv",
    "content": "\"41,6,8469,0,22,6336,9177,null,3,15,31,5734,6509,8770,9379,2,4,10,20,28,39,1490,5775,6410,6986,8493,8823,9298,9975,1,null,null,5,8,12,16,21,26,29,34,40,506,4491,5737,6034,6384,6434,6853,8178,8492,8503,8779,9106,9283,9339,9774,9982,null,null,null,null,7,9,11,13,null,17,null,null,23,27,null,30,32,35,null,null,176,837,3317,5715,5735,5743,5944,6310,6338,6398,6425,6436,6802,6917,7473,8397,8472,null,8494,8711,8776,8782,9009,9153,9210,9289,9336,9371,9400,9953,9980,9993,null,null,null,null,null,null,null,14,null,18,null,25,null,null,null,null,null,33,null,36,53,316,739,1409,3011,3924,4845,5730,null,5736,5741,5754,5807,5968,6056,6329,6337,6345,6387,6400,6420,6428,6435,6467,6575,6814,6911,6970,7111,7761,8283,8461,8470,8473,null,8496,8674,8722,8773,8778,8780,8803,8924,9078,9111,9172,9185,9270,9285,9296,9307,9337,9369,9376,9391,9633,9933,9967,9979,9981,9989,9994,null,null,null,19,24,null,null,null,null,37,46,140,199,505,669,814,1281,1430,1976,3096,3627,3934,4646,5455,5719,5733,null,null,5740,5742,5752,5758,5790,5828,5948,5974,6051,6278,6326,6331,null,null,6339,6365,6385,6388,6399,6405,6417,6424,6426,6433,null,null,6441,6492,6572,6739,6808,6845,6867,6915,6931,6985,7086,7463,7758,7797,8179,8349,8421,8467,null,8471,null,8476,8495,8497,8576,8708,8719,8761,8771,8774,8777,null,null,8781,8793,8806,8882,8937,9054,9084,9107,9134,9166,9175,9181,9187,9255,9282,9284,9287,9292,9297,9305,9319,null,9338,9342,9370,9375,9378,9380,9396,9546,9713,9798,9946,9956,9973,9978,null,null,null,9986,9991,null,9999,null,null,null,null,null,38,45,49,111,175,196,203,352,null,631,673,799,832,1056,1366,1415,1480,1819,2471,3016,3223,3532,3770,3931,4018,4592,4835,5233,5477,5718,5728,5732,null,5739,null,null,null,5745,5753,5756,5766,5785,5803,5823,5875,5946,5950,5971,5994,6043,6052,6124,6298,6317,6328,6330,6332,null,6341,6348,6369,null,6386,null,6396,null,null,6404,6408,6415,6419,6422,null,null,6427,6430,null,6440,6458,6471,6503,6520,6574,6721,6750,6807,6810,6843,6849,6863,6893,6912,6916,6919,6947,6983,null,6993,7088,7124,7466,7703,7759,7787,7962,null,8195,8331,8362,8411,8453,8462,8468,null,null,8474,8482,null,null,null,8500,8573,8619,8682,8710,8712,8721,8755,8767,null,8772,null,8775,null,null,null,null,8787,8795,8805,8819,8862,8886,8929,8978,9014,9076,9080,9087,null,9110,9112,9139,9163,9170,9173,9176,9178,9183,9186,9188,9240,9267,9278,null,null,null,9286,9288,9290,9293,null,null,9303,9306,9312,9326,null,null,9340,9366,null,null,9374,null,9377,null,null,9383,9393,9397,9493,9601,9652,9726,9789,9905,9942,9947,9954,9959,9969,9974,9976,null,9984,9988,9990,9992,9997,null,null,null,44,null,48,52,92,125,156,null,183,197,202,235,325,366,629,656,670,712,746,802,816,835,1026,1212,1293,1403,1414,1420,1471,1483,1604,1903,2432,2870,3014,3062,3219,3311,3336,3565,3730,3918,3927,3933,3975,4330,4546,4627,4709,4840,5158,5399,5456,5697,5716,null,5727,5729,5731,null,5738,null,5744,5746,null,null,5755,5757,5762,5774,5778,5786,5795,5805,5822,5824,5848,5895,5945,5947,5949,5961,5969,5973,5976,6014,6042,6049,null,6053,6117,6139,6290,6308,6314,6320,6327,null,null,null,null,6335,6340,6344,6346,6354,6366,6383,null,null,6390,6397,6401,null,6406,6409,6413,6416,6418,null,6421,6423,null,null,6429,6431,6438,null,6443,6462,6470,6472,6497,6506,6512,6528,6573,null,6648,6733,6748,6767,6805,null,6809,6813,6828,6844,6847,6852,6854,6864,6875,6910,null,6914,null,null,6918,6922,6944,6968,6976,6984,6991,7047,7087,7091,7112,7260,7464,7467,7656,7740,null,7760,7774,7795,7876,8110,8187,8202,8315,8341,8359,8391,8410,8414,8448,8455,null,8463,null,null,null,8475,8478,8483,8498,8502,8531,8575,8600,8624,8681,8688,8709,null,null,8716,8720,null,8735,8760,8766,8768,null,null,null,null,8784,8791,8794,8797,8804,null,8815,8820,8825,8867,8884,8887,8927,8936,8974,8986,9013,9041,9067,9077,9079,9081,9086,9093,9109,null,null,9119,9138,9144,9159,9164,9167,9171,null,9174,null,null,null,9179,9182,9184,null,null,null,9189,9223,9247,9258,9269,9275,9279,null,null,null,null,null,9291,null,9295,9301,9304,null,null,9310,9315,9324,9330,null,9341,9358,9367,9372,null,null,null,9382,9386,9392,9395,null,9398,9488,9509,9547,9616,9643,9702,94,122\"\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/exceeds-default-max-chars.csv",
    "content": "\"41,6,8469,0,22,6336,9177,null,3,15,31,5734,6509,8770,9379,2,4,10,20,28,39,1490,5775,6410,6986,8493,8823,9298,9975,1,null,null,5,8,12,16,21,26,29,34,40,506,4491,5737,6034,6384,6434,6853,8178,8492,8503,8779,9106,9283,9339,9774,9982,null,null,null,null,7,9,11,13,null,17,null,null,23,27,null,30,32,35,null,null,176,837,3317,5715,5735,5743,5944,6310,6338,6398,6425,6436,6802,6917,7473,8397,8472,null,8494,8711,8776,8782,9009,9153,9210,9289,9336,9371,9400,9953,9980,9993,null,null,null,null,null,null,null,14,null,18,null,25,null,null,null,null,null,33,null,36,53,316,739,1409,3011,3924,4845,5730,null,5736,5741,5754,5807,5968,6056,6329,6337,6345,6387,6400,6420,6428,6435,6467,6575,6814,6911,6970,7111,7761,8283,8461,8470,8473,null,8496,8674,8722,8773,8778,8780,8803,8924,9078,9111,9172,9185,9270,9285,9296,9307,9337,9369,9376,9391,9633,9933,9967,9979,9981,9989,9994,null,null,null,19,24,null,null,null,null,37,46,140,199,505,669,814,1281,1430,1976,3096,3627,3934,4646,5455,5719,5733,null,null,5740,5742,5752,5758,5790,5828,5948,5974,6051,6278,6326,6331,null,null,6339,6365,6385,6388,6399,6405,6417,6424,6426,6433,null,null,6441,6492,6572,6739,6808,6845,6867,6915,6931,6985,7086,7463,7758,7797,8179,8349,8421,8467,null,8471,null,8476,8495,8497,8576,8708,8719,8761,8771,8774,8777,null,null,8781,8793,8806,8882,8937,9054,9084,9107,9134,9166,9175,9181,9187,9255,9282,9284,9287,9292,9297,9305,9319,null,9338,9342,9370,9375,9378,9380,9396,9546,9713,9798,9946,9956,9973,9978,null,null,null,9986,9991,null,9999,null,null,null,null,null,38,45,49,111,175,196,203,352,null,631,673,799,832,1056,1366,1415,1480,1819,2471,3016,3223,3532,3770,3931,4018,4592,4835,5233,5477,5718,5728,5732,null,5739,null,null,null,5745,5753,5756,5766,5785,5803,5823,5875,5946,5950,5971,5994,6043,6052,6124,6298,6317,6328,6330,6332,null,6341,6348,6369,null,6386,null,6396,null,null,6404,6408,6415,6419,6422,null,null,6427,6430,null,6440,6458,6471,6503,6520,6574,6721,6750,6807,6810,6843,6849,6863,6893,6912,6916,6919,6947,6983,null,6993,7088,7124,7466,7703,7759,7787,7962,null,8195,8331,8362,8411,8453,8462,8468,null,null,8474,8482,null,null,null,8500,8573,8619,8682,8710,8712,8721,8755,8767,null,8772,null,8775,null,null,null,null,8787,8795,8805,8819,8862,8886,8929,8978,9014,9076,9080,9087,null,9110,9112,9139,9163,9170,9173,9176,9178,9183,9186,9188,9240,9267,9278,null,null,null,9286,9288,9290,9293,null,null,9303,9306,9312,9326,null,null,9340,9366,null,null,9374,null,9377,null,null,9383,9393,9397,9493,9601,9652,9726,9789,9905,9942,9947,9954,9959,9969,9974,9976,null,9984,9988,9990,9992,9997,null,null,null,44,null,48,52,92,125,156,null,183,197,202,235,325,366,629,656,670,712,746,802,816,835,1026,1212,1293,1403,1414,1420,1471,1483,1604,1903,2432,2870,3014,3062,3219,3311,3336,3565,3730,3918,3927,3933,3975,4330,4546,4627,4709,4840,5158,5399,5456,5697,5716,null,5727,5729,5731,null,5738,null,5744,5746,null,null,5755,5757,5762,5774,5778,5786,5795,5805,5822,5824,5848,5895,5945,5947,5949,5961,5969,5973,5976,6014,6042,6049,null,6053,6117,6139,6290,6308,6314,6320,6327,null,null,null,null,6335,6340,6344,6346,6354,6366,6383,null,null,6390,6397,6401,null,6406,6409,6413,6416,6418,null,6421,6423,null,null,6429,6431,6438,null,6443,6462,6470,6472,6497,6506,6512,6528,6573,null,6648,6733,6748,6767,6805,null,6809,6813,6828,6844,6847,6852,6854,6864,6875,6910,null,6914,null,null,6918,6922,6944,6968,6976,6984,6991,7047,7087,7091,7112,7260,7464,7467,7656,7740,null,7760,7774,7795,7876,8110,8187,8202,8315,8341,8359,8391,8410,8414,8448,8455,null,8463,null,null,null,8475,8478,8483,8498,8502,8531,8575,8600,8624,8681,8688,8709,null,null,8716,8720,null,8735,8760,8766,8768,null,null,null,null,8784,8791,8794,8797,8804,null,8815,8820,8825,8867,8884,8887,8927,8936,8974,8986,9013,9041,9067,9077,9079,9081,9086,9093,9109,null,null,9119,9138,9144,9159,9164,9167,9171,null,9174,null,null,null,9179,9182,9184,null,null,null,9189,9223,9247,9258,9269,9275,9279,null,null,null,null,null,9291,null,9295,9301,9304,null,null,9310,9315,9324,9330,null,9341,9358,9367,9372,null,null,null,9382,9386,9392,9395,null,9398,9488,9509,9547,9616,9643,9702,94,1223\"\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/leading-trailing-spaces.csv",
    "content": " ab , cd\nef ,gh"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/provider/single-column.csv",
    "content": "foo\nbar\nbaz\nqux\n\"\"\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column-with-headers.csv",
    "content": "#---------------------------------\n  FRUIT  | RANK\n#---------------------------------\n  apple  | 1\n#---------------------------------\n  banana | 2\n#---------------------------------\n  cherry | 3.14159265358979323846\n#---------------------------------\n         | 0\n#---------------------------------\n  NIL    | 0\n#---------------------------------\n"
  },
  {
    "path": "jupiter-tests/src/test/resources/org/junit/jupiter/params/two-column.csv",
    "content": "foo,1\nbar,2\nbaz,3\nqux,4\n"
  },
  {
    "path": "platform-tests/platform-tests.gradle.kts",
    "content": "import junitbuild.extensions.capitalized\nimport org.gradle.api.tasks.PathSensitivity.RELATIVE\nimport org.gradle.internal.os.OperatingSystem\nimport org.gradle.plugins.ide.eclipse.model.Classpath\nimport org.gradle.plugins.ide.eclipse.model.SourceFolder\n\nplugins {\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.junit4-compatibility\")\n\tid(\"junitbuild.testing-conventions\")\n\tid(\"junitbuild.jmh-conventions\")\n}\n\nval processStarter by sourceSets.creating {\n\tjava {\n\t\tsrcDir(\"src/processStarter/java\")\n\t}\n}\n\njava {\n\tregisterFeature(processStarter.name) {\n\t\tusingSourceSet(processStarter)\n\t}\n}\n\nval woodstox = configurations.dependencyScope(\"woodstox\")\nval woodstoxRuntimeClasspath = configurations.resolvable(\"woodstoxRuntimeClasspath\") {\n\textendsFrom(configurations.testRuntimeClasspath.get())\n\textendsFrom(woodstox.get())\n}\n\ndependencies {\n\t// --- Things we are testing --------------------------------------------------\n\ttestImplementation(projects.junitPlatformCommons)\n\ttestImplementation(projects.junitPlatformConsole)\n\ttestImplementation(projects.junitPlatformEngine)\n\ttestImplementation(projects.junitPlatformLauncher)\n\ttestImplementation(projects.junitPlatformSuiteEngine)\n\n\t// --- Things we are testing with ---------------------------------------------\n\ttestImplementation(projects.junitPlatformTestkit)\n\ttestImplementation(testFixtures(projects.junitPlatformCommons))\n\ttestImplementation(testFixtures(projects.junitPlatformEngine))\n\ttestImplementation(testFixtures(projects.junitPlatformLauncher))\n\ttestImplementation(projects.junitJupiterEngine)\n\ttestImplementation(testFixtures(projects.junitJupiterEngine))\n\ttestImplementation(testFixtures(projects.junitJupiterParams))\n\ttestImplementation(libs.apiguardian)\n\ttestImplementation(libs.classgraph)\n\ttestImplementation(libs.jfrunit) {\n\t\texclude(group = \"org.junit.vintage\")\n\t}\n\ttestImplementation(libs.joox)\n\ttestImplementation(libs.openTestReporting.tooling.core)\n\ttestImplementation(libs.picocli)\n\ttestImplementation(libs.bundles.xmlunit)\n\ttestImplementation(kotlin(\"stdlib\"))\n\ttestImplementation(testFixtures(projects.junitJupiterApi))\n\ttestImplementation(testFixtures(projects.junitPlatformReporting))\n\ttestImplementation(projects.platformTests) {\n\t\tcapabilities {\n\t\t\trequireFeature(\"process-starter\")\n\t\t}\n\t}\n\n\t// --- Test run-time dependencies ---------------------------------------------\n\tval mavenizedProjects: List<Project> by rootProject\n\tmavenizedProjects.filter { it.path != projects.junitPlatformConsoleStandalone.path }.forEach {\n\t\t// Add all projects to the classpath for tests using classpath scanning\n\t\ttestRuntimeOnly(it)\n\t}\n\ttestRuntimeOnly(libs.groovy) {\n\t\tbecause(\"`ReflectionUtilsTests.findNestedClassesWithInvalidNestedClassFile` needs it\")\n\t}\n\twoodstox(libs.woodstox)\n\n\t// --- https://openjdk.java.net/projects/code-tools/jmh/ ----------------------\n\tjmh(projects.junitJupiterApi)\n\tjmh(libs.junit4)\n\n\t// --- ProcessStarter dependencies --------------------------------------------\n\tprocessStarter.implementationConfigurationName(libs.groovy) {\n\t\tbecause(\"it provides convenience methods to handle process output\")\n\t}\n\tprocessStarter.implementationConfigurationName(libs.commons.io) {\n\t\tbecause(\"it uses TeeOutputStream\")\n\t}\n\tprocessStarter.implementationConfigurationName(libs.opentest4j) {\n\t\tbecause(\"it throws TestAbortedException\")\n\t}\n}\n\njmh {\n\tduplicateClassesStrategy = DuplicatesStrategy.WARN\n\tfork = 1\n\twarmupIterations = 1\n\titerations = 5\n}\n\ntasks {\n\twithType<Test>().configureEach {\n\t\tuseJUnitPlatform {\n\t\t\texcludeTags(\"exclude\")\n\t\t}\n\t\tjvmArgs(\"-Xmx1g\")\n\t\tdevelocity {\n\t\t\ttestDistribution {\n\t\t\t\t// Retry in a new JVM on Windows to improve chances of successful retries when\n\t\t\t\t// cached resources are used (e.g. in ClasspathScannerTests)\n\t\t\t\tretryInSameJvm = !OperatingSystem.current().isWindows\n\t\t\t}\n\t\t}\n\t}\n\ttest {\n\t\t// Additional inputs for remote execution with Test Distribution\n\t\tinputs.dir(\"src/test/resources\").withPathSensitivity(RELATIVE)\n\t}\n\ttest_4_12 {\n\t\tuseJUnitPlatform {\n\t\t\tincludeTags(\"junit4\")\n\t\t}\n\t}\n\tval testWoodstox by registering(Test::class) {\n\t\tval test by testing.suites.existing(JvmTestSuite::class)\n\t\ttestClassesDirs = files(test.map { it.sources.output.classesDirs })\n\t\tclasspath = files(sourceSets.main.map { it.output }) + files(test.map { it.sources.output }) + woodstoxRuntimeClasspath.get()\n\t\tgroup = JavaBasePlugin.VERIFICATION_GROUP\n\t\tsetIncludes(listOf(\"**/org/junit/platform/reporting/**\"))\n\t}\n\tcheck {\n\t\tdependsOn(testWoodstox)\n\t}\n\tnamed<JavaCompile>(processStarter.compileJavaTaskName).configure {\n\t\toptions.release = javaLibrary.testJavaVersion.map { it.majorVersion.toInt() }\n\t}\n\tnamed<Checkstyle>(\"checkstyle${processStarter.name.capitalized()}\").configure {\n\t\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleMain.xml\"))\n\t}\n}\n\neclipse {\n\tclasspath.file.whenMerged {\n\t\tthis as Classpath\n\t\tentries.filterIsInstance<SourceFolder>().forEach {\n\t\t\tif (it.path == \"src/test/resources\") {\n\t\t\t\t// Exclude Foo.java and FooBar.java in the modules-2500 folder.\n\t\t\t\tit.excludes.add(\"**/Foo*.java\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/jmh/java/org/junit/jupiter/jmh/AssertionBenchmarks.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.jupiter.jmh;\n\nimport org.junit.Assert;\nimport org.junit.jupiter.api.Assertions;\nimport org.openjdk.jmh.annotations.Benchmark;\n\n/**\n * JMH benchmarks for assertions.\n *\n * @since 5.1\n */\npublic class AssertionBenchmarks {\n\n\t@Benchmark\n\tpublic void junit4_assertTrue_boolean() {\n\t\tAssert.assertTrue(true);\n\t}\n\n\t@Benchmark\n\tpublic void junitJupiter_assertTrue_boolean() {\n\t\tAssertions.assertTrue(true);\n\t}\n\n\t@Benchmark\n\tpublic void junit4_assertTrue_String_boolean() {\n\t\tAssert.assertTrue(\"message\", true);\n\t}\n\n\t@Benchmark\n\tpublic void junitJupiter_assertTrue_boolean_String() {\n\t\tAssertions.assertTrue(true, \"message\");\n\t}\n\n\t@Benchmark\n\tpublic void junitJupiter_assertTrue_boolean_Supplier() {\n\t\tAssertions.assertTrue(true, () -> \"message\");\n\t}\n\n\t@Benchmark\n\tpublic void junitJupiter_assertTrue_BooleanSupplier_String() {\n\t\tAssertions.assertTrue(() -> true, \"message\");\n\t}\n\n\t@Benchmark\n\tpublic void junitJupiter_assertTrue_BooleanSupplier_Supplier() {\n\t\tAssertions.assertTrue(() -> true, () -> \"message\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/OutputFiles.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.tests.process;\n\nimport java.nio.file.Path;\n\npublic record OutputFiles(Path stdOut, Path stdErr) {\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/ProcessResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.tests.process;\n\nimport java.util.List;\n\npublic record ProcessResult(int exitCode, String stdOut, String stdErr) {\n\n\tpublic List<String> stdOutLines() {\n\t\treturn stdOut.lines().toList();\n\t}\n\n\tpublic List<String> stdErrLines() {\n\t\treturn stdErr.lines().toList();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/ProcessStarter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.tests.process;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.PrintStream;\nimport java.io.UncheckedIOException;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.function.BiFunction;\nimport java.util.stream.Stream;\n\nimport org.apache.commons.io.output.TeeOutputStream;\nimport org.codehaus.groovy.runtime.ProcessGroovyMethods;\n\npublic class ProcessStarter {\n\n\tpublic static final Charset OUTPUT_ENCODING = Charset.forName(System.getProperty(\"native.encoding\"));\n\n\tprivate Path executable;\n\tprivate Path workingDir;\n\tprivate final List<String> arguments = new ArrayList<>();\n\tprivate final Map<String, String> environment = new LinkedHashMap<>();\n\tprivate Optional<OutputFiles> outputFiles = Optional.empty();\n\n\tpublic ProcessStarter executable(Path executable) {\n\t\tthis.executable = executable;\n\t\treturn this;\n\t}\n\n\tpublic ProcessStarter workingDir(Path workingDir) {\n\t\tthis.workingDir = workingDir;\n\t\treturn this;\n\t}\n\n\tpublic ProcessStarter addArguments(String... arguments) {\n\t\tthis.arguments.addAll(List.of(arguments));\n\t\treturn this;\n\t}\n\n\tpublic ProcessStarter putEnvironment(String key, Path value) {\n\t\treturn putEnvironment(key, value.toAbsolutePath().toString());\n\t}\n\n\tpublic ProcessStarter putEnvironment(String key, String value) {\n\t\tenvironment.put(key, value);\n\t\treturn this;\n\t}\n\n\tpublic ProcessStarter putEnvironment(Map<String, String> values) {\n\t\tenvironment.putAll(values);\n\t\treturn this;\n\t}\n\n\tpublic ProcessStarter redirectOutput(OutputFiles outputFiles) {\n\t\tthis.outputFiles = Optional.of(outputFiles);\n\t\treturn this;\n\t}\n\n\tpublic ProcessResult startAndWait() throws InterruptedException {\n\t\treturn start().waitFor();\n\t}\n\n\tpublic WatchedProcess start() {\n\t\tvar command = Stream.concat(Stream.of(executable.toString()), arguments.stream()).toList();\n\t\ttry {\n\t\t\tvar builder = new ProcessBuilder().command(command);\n\t\t\tif (workingDir != null) {\n\t\t\t\tbuilder.directory(workingDir.toFile());\n\t\t\t}\n\t\t\tbuilder.environment().putAll(environment);\n\t\t\tvar process = builder.start();\n\t\t\tvar out = forwardAndCaptureOutput(process, System.out, outputFiles.map(OutputFiles::stdOut),\n\t\t\t\tProcessGroovyMethods::consumeProcessOutputStream);\n\t\t\tvar err = forwardAndCaptureOutput(process, System.err, outputFiles.map(OutputFiles::stdErr),\n\t\t\t\tProcessGroovyMethods::consumeProcessErrorStream);\n\t\t\treturn new WatchedProcess(process, out, err);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to start process: \" + command, e);\n\t\t}\n\t}\n\n\tprivate static WatchedOutput forwardAndCaptureOutput(Process process, PrintStream delegate,\n\t\t\tOptional<Path> outputFile, BiFunction<Process, OutputStream, Thread> captureAction) {\n\t\tvar capturingStream = new ByteArrayOutputStream();\n\t\tOptional<OutputStream> fileStream = outputFile.map(path -> {\n\t\t\ttry {\n\t\t\t\treturn Files.newOutputStream(path);\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(\"Failed to open output file: \" + path, e);\n\t\t\t}\n\t\t});\n\t\tvar attachedStream = tee(delegate, fileStream.map(it -> tee(capturingStream, it)).orElse(capturingStream));\n\t\tvar thread = captureAction.apply(process, attachedStream);\n\t\treturn new WatchedOutput(thread, capturingStream, fileStream);\n\t}\n\n\tprivate static OutputStream tee(OutputStream out, OutputStream branch) {\n\t\treturn new TeeOutputStream(out, branch);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/WatchedOutput.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.tests.process;\n\nimport static org.junit.platform.tests.process.ProcessStarter.OUTPUT_ENCODING;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\nimport java.util.Optional;\n\nrecord WatchedOutput(Thread thread, ByteArrayOutputStream stream, Optional<OutputStream> fileStream) {\n\n\tString streamAsString() {\n\t\treturn stream.toString(OUTPUT_ENCODING);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/WatchedProcess.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.tests.process;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Optional;\n\npublic class WatchedProcess {\n\n\tprivate final Process process;\n\tprivate final WatchedOutput out;\n\tprivate final WatchedOutput err;\n\n\tWatchedProcess(Process process, WatchedOutput out, WatchedOutput err) {\n\t\tthis.process = process;\n\t\tthis.out = out;\n\t\tthis.err = err;\n\t}\n\n\tProcessResult waitFor() throws InterruptedException {\n\t\ttry {\n\t\t\tint exitCode;\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\texitCode = process.waitFor();\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tprocess.destroyForcibly();\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\ttry {\n\t\t\t\t\tout.thread().join();\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\terr.thread().join();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn new ProcessResult(exitCode, out.streamAsString(), err.streamAsString());\n\t\t}\n\t\tfinally {\n\t\t\tprocess.destroyForcibly();\n\t\t\tcloseQuietly(out.fileStream());\n\t\t\tcloseQuietly(err.fileStream());\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"EmptyCatch\")\n\tprivate static void closeQuietly(Optional<OutputStream> fileStream) {\n\t\tif (fileStream.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tfileStream.get().close();\n\t\t}\n\t\tcatch (IOException ignore) {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/processStarter/java/org/junit/platform/tests/process/package-info.java",
    "content": "/**\n * Utilities for working with processes.\n *\n * @since 1.12\n */\n\npackage org.junit.platform.tests.process;\n"
  },
  {
    "path": "platform-tests/src/test/java/DefaultPackageTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Simple test case that is used to verify proper support for classpath scanning\n * within the <em>default</em> package.\n *\n * @since 1.0\n */\n@Disabled(\"Only used reflectively by other tests\")\nclass DefaultPackageTestCase {\n\n\t@Test\n\tvoid test() {\n\t\t// do nothing\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/JUnitPlatformTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * Test suite for the JUnit Platform.\n *\n * <h2>Logging Configuration</h2>\n *\n * <p>In order for our log4j2 configuration to be used in an IDE, you must\n * set the following system property before running any tests &mdash; for\n * example, in <em>Run Configurations</em> in Eclipse.\n *\n * <pre class=\"code\">\n * -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager\n * </pre>\n *\n * @since 1.0\n */\n@Suite\n@SelectPackages(\"org.junit.platform\")\n@IncludeClassNamePatterns(\".*Tests?\")\n@IncludeEngines(\"junit-jupiter\")\nclass JUnitPlatformTestSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n/**\n * Test cases for stacktrace pruning.\n *\n * <p>Note: the package {@code org.junit.platform} this class resides in is\n * chosen on purpose. If it was in {@code org.junit.platform.launcher}\n * stack traces would be fully pruned.\n *\n * @since 5.10\n */\nclass StackTracePruningTests {\n\n\t@Test\n\tvoid shouldPruneStackTraceByDefault() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssertion\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceDoesNotContain(stackTrace,\n\t\t\t\"jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:\");\n\t}\n\n\t@Test\n\tvoid shouldPruneStackTraceWhenEnabled() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssertion\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceDoesNotContain(stackTrace,\n\t\t\t\"jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:\");\n\t}\n\n\t@Test\n\tvoid shouldNotPruneStackTraceWhenDisabled() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"false\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssertion\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceMatch(stackTrace, \"\"\"\n\t\t\t\t\\\\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\\\E.+\n\t\t\t\t>>>>\n\t\t\t\t\\\\Qorg.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:\\\\E.+\n\t\t\t\t>>>>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid shouldAlwaysKeepJupiterAssertionStackTraceElement() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssertion\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceMatch(stackTrace, \"\"\"\n\t\t\t\t\\\\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\\\E.+\n\t\t\t\t>>>>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid shouldAlwaysKeepJupiterAssumptionStackTraceElement() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssumption\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceMatch(stackTrace, \"\"\"\n\t\t\t\t>>>>\n\t\t\t\t\\\\Qorg.junit.jupiter.api.Assumptions.assumeTrue(Assumptions.java:\\\\E.+\n\t\t\t\t>>>>\n\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid shouldKeepExactlyEverythingBetweenTestCallAndFirstAssertionCall() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"failingAssertion\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceMatch(stackTrace,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.failingAssertion(StackTracePruningTests.java:\\\\E.+\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase\",\n\t\t\t\"org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase$NestedTestCase\",\n\t\t\t\"org.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase$NestedTestCase$NestedNestedTestCase\" })\n\tvoid shouldKeepExactlyEverythingAfterLifecycleMethodCall(Class<?> methodClass) {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(methodClass, \"test\")) //\n\t\t\t\t.execute();\n\n\t\tList<StackTraceElement> stackTrace = extractStackTrace(results);\n\n\t\tassertStackTraceMatch(stackTrace,\n\t\t\t\"\"\"\n\t\t\t\t\t\\\\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\\\E.+\n\t\t\t\t\t\\\\Qorg.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase.setUp(StackTracePruningTests.java:\\\\E.+\n\t\t\t\t\t\"\"\");\n\t}\n\n\t@Test\n\tvoid shouldPruneStackTracesOfSuppressedExceptions() {\n\t\tEngineExecutionResults results = EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.configurationParameter(\"junit.platform.stacktrace.pruning.enabled\", \"true\") //\n\t\t\t\t.selectors(selectMethod(FailingTestTestCase.class, \"multipleFailingAssertions\")) //\n\t\t\t\t.execute();\n\n\t\tThrowable throwable = getThrowable(results);\n\n\t\tfor (Throwable suppressed : throwable.getSuppressed()) {\n\t\t\tList<StackTraceElement> stackTrace = Arrays.asList(suppressed.getStackTrace());\n\t\t\tassertStackTraceDoesNotContain(stackTrace,\n\t\t\t\t\"jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:\");\n\t\t}\n\t}\n\n\tprivate static List<StackTraceElement> extractStackTrace(EngineExecutionResults results) {\n\t\treturn Arrays.asList(getThrowable(results).getStackTrace());\n\t}\n\n\tprivate static Throwable getThrowable(EngineExecutionResults results) {\n\t\tvar failedTestEvent = results.testEvents().failed().list().getFirst();\n\t\tvar testResult = failedTestEvent.getRequiredPayload(TestExecutionResult.class);\n\t\treturn testResult.getThrowable().orElseThrow();\n\t}\n\n\tprivate static void assertStackTraceMatch(List<StackTraceElement> stackTrace, String expectedLines) {\n\t\tList<String> stackStraceAsLines = stackTrace.stream() //\n\t\t\t\t.map(StackTraceElement::toString) //\n\t\t\t\t.toList();\n\t\tassertLinesMatch(expectedLines.lines().toList(), stackStraceAsLines);\n\t}\n\n\tprivate static void assertStackTraceDoesNotContain(List<StackTraceElement> stackTrace,\n\t\t\t@SuppressWarnings(\"SameParameterValue\") String element) {\n\t\tString stackStraceAsString = stackTrace.stream() //\n\t\t\t\t.map(StackTraceElement::toString) //\n\t\t\t\t.collect(Collectors.joining());\n\t\tassertThat(stackStraceAsString).doesNotContain(element);\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tstatic class FailingTestTestCase {\n\n\t\t@Test\n\t\tvoid failingAssertion() {\n\t\t\tAssertions.fail();\n\t\t}\n\n\t\t@Test\n\t\tvoid multipleFailingAssertions() {\n\t\t\tAssertions.assertAll(Assertions::fail, Assertions::fail);\n\t\t}\n\n\t\t@Test\n\t\tvoid failingAssumption() {\n\t\t\tAssumptions.assumeTrue(() -> {\n\t\t\t\tthrow new RuntimeException();\n\t\t\t});\n\t\t}\n\n\t}\n\n\tstatic class FailingBeforeEachTestCase {\n\n\t\t@BeforeEach\n\t\tvoid setUp() {\n\t\t\tAssertions.fail();\n\t\t}\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\tclass NestedTestCase {\n\n\t\t\t@Test\n\t\t\tvoid test() {\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass NestedNestedTestCase {\n\n\t\t\t\t@Test\n\t\t\t\tvoid test() {\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/TestEngineTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Test cases for default implementations of {@link org.junit.platform.engine.TestEngine}.\n *\n * <p>Note: the package {@code org.junit.platform} this class resides in is\n * chosen on purpose. If it was in {@code org.junit.platform.engine} the default\n * implementation will pick up values defined by the real Jupiter test engine.\n *\n * @since 1.1\n */\nclass TestEngineTests {\n\n\t@Test\n\tvoid defaults() {\n\t\tTestEngine engine = new DefaultEngine();\n\t\tassertEquals(Optional.empty(), engine.getGroupId());\n\t\tassertEquals(Optional.empty(), engine.getArtifactId());\n\t\tassertEquals(Optional.of(\"DEVELOPMENT\"), engine.getVersion());\n\t}\n\n\tprivate static class DefaultEngine implements TestEngine {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn getClass().getSimpleName();\n\t\t}\n\n\t\t@Override\n\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\tthrow new UnsupportedOperationException(\"discover\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(ExecutionRequest request) {\n\t\t\tthrow new UnsupportedOperationException(\"execute\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/annotation/TestableAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.annotation;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests that indirectly verify that the {@link Testable @Testable}\n * annotation may be declared on any element type.\n *\n * @since 1.7\n */\nclass TestableAnnotationTests {\n\n\t@Test\n\tvoid testMethod() {\n\t\tassertNotNull(new TestableEverywhere().toString());\n\t}\n\n\t@Testable\n\tstatic class TestableEverywhere {\n\n\t\t@Testable\n\t\tfinal int field = 0;\n\n\t\t@Testable\n\t\tTestableEverywhere() {\n\t\t}\n\n\t\t@Testable\n\t\tvoid test(@Testable int parameter) {\n\t\t\t@Testable\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tvar var = \"var\";\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/function/TryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.function;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.JUnitException;\n\nclass TryTests {\n\n\t@Test\n\tvoid successfulTriesCanBeTransformed() throws Exception {\n\t\tvar success = Try.success(\"foo\");\n\n\t\tassertThat(success.get()).isEqualTo(\"foo\");\n\t\tassertThat(success.getNonNull()).isEqualTo(\"foo\");\n\t\tassertThat(success.getOrThrow(RuntimeException::new)).isEqualTo(\"foo\");\n\t\tassertThat(success.getNonNullOrThrow(RuntimeException::new)).isEqualTo(\"foo\");\n\t\tassertThat(success.toOptional()).contains(\"foo\");\n\n\t\tassertThat(success.andThen(v -> {\n\t\t\tassertThat(v).isEqualTo(\"foo\");\n\t\t\treturn Try.success(\"bar\");\n\t\t}).get()).isEqualTo(\"bar\");\n\t\tassertThat(success.andThenTry(v -> {\n\t\t\tassertThat(v).isEqualTo(\"foo\");\n\t\t\treturn \"bar\";\n\t\t}).get()).isEqualTo(\"bar\");\n\n\t\tassertThat(success.orElse(() -> fail(\"should not be called\"))).isSameAs(success);\n\t\tassertThat(success.orElseTry(() -> fail(\"should not be called\"))).isSameAs(success);\n\n\t\tvar value = new AtomicReference<String>();\n\t\tassertThat(success.ifSuccess(value::set)).isSameAs(success);\n\t\tassertThat(value.get()).isEqualTo(\"foo\");\n\t\tassertThat(success.ifFailure(cause -> fail(\"should not be called\"))).isSameAs(success);\n\t}\n\n\t@Test\n\tvoid failedTriesCanBeTransformed() throws Exception {\n\t\tvar cause = new JUnitException(\"foo\");\n\t\tvar failure = Try.failure(cause);\n\n\t\tassertThat(assertThrows(JUnitException.class, failure::get)).isSameAs(cause);\n\t\tassertThat(assertThrows(RuntimeException.class, () -> failure.getOrThrow(RuntimeException::new))).isInstanceOf(\n\t\t\tRuntimeException.class).hasCause(cause);\n\t\tassertThat(failure.toOptional()).isEmpty();\n\n\t\tassertThat(failure.andThen(v -> fail(\"should not be called\"))).isSameAs(failure);\n\t\tassertThat(failure.andThenTry(v -> fail(\"should not be called\"))).isSameAs(failure);\n\n\t\tassertThat(failure.orElse(() -> Try.success(\"bar\")).get()).isEqualTo(\"bar\");\n\t\tassertThat(failure.orElseTry(() -> \"bar\").get()).isEqualTo(\"bar\");\n\n\t\tassertThat(failure.ifSuccess(v -> fail(\"should not be called\"))).isSameAs(failure);\n\t\tvar exception = new AtomicReference<Exception>();\n\t\tassertThat(failure.ifFailure(exception::set)).isSameAs(failure);\n\t\tassertThat(exception.get()).isSameAs(cause);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid successfulTriesCanStoreNull() throws Exception {\n\t\tvar success = Try.success(null);\n\t\tassertThat(success.get()).isNull();\n\t\tassertThrows(JUnitException.class, success::getNonNull);\n\t\tassertThat(success.getOrThrow(RuntimeException::new)).isNull();\n\t\tassertThrows(RuntimeException.class, () -> success.getNonNullOrThrow(RuntimeException::new));\n\t\tassertThat(success.toOptional()).isEmpty();\n\t}\n\n\t@Test\n\tvoid triesWithSameContentAreEqual() {\n\t\tvar cause = new Exception();\n\t\tCallable<Object> failingCallable = () -> {\n\t\t\tthrow cause;\n\t\t};\n\n\t\tvar success = Try.call(() -> \"foo\");\n\t\tassertThat(success).isEqualTo(success).hasSameHashCodeAs(success);\n\t\tassertThat(success).isEqualTo(Try.success(\"foo\"));\n\t\tassertThat(success).isNotEqualTo(Try.failure(cause));\n\n\t\tvar failure = Try.call(failingCallable);\n\t\tassertThat(failure).isEqualTo(failure).hasSameHashCodeAs(failure);\n\t\tassertThat(failure).isNotEqualTo(Try.success(\"foo\"));\n\t\tassertThat(failure).isEqualTo(Try.failure(cause));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid methodPreconditionsAreChecked() {\n\t\tassertThrows(JUnitException.class, () -> Try.call(null));\n\n\t\tvar success = Try.success(\"foo\");\n\t\tassertThrows(JUnitException.class, () -> success.andThen(null));\n\t\tassertThrows(JUnitException.class, () -> success.andThenTry(null));\n\t\tassertThrows(JUnitException.class, () -> success.ifSuccess(null));\n\n\t\tvar failure = Try.failure(new Exception());\n\t\tassertThrows(JUnitException.class, () -> failure.orElse(null));\n\t\tassertThrows(JUnitException.class, () -> failure.orElseTry(null));\n\t\tassertThrows(JUnitException.class, () -> failure.ifFailure(null));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/AnnotationSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.AnnotationUtils;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 1.0\n */\nclass AnnotationSupportTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isAnnotatedPreconditions() {\n\t\tvar optional = Optional.of(Probe.class);\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\", () -> AnnotationSupport.isAnnotated(optional, null));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\", () -> AnnotationSupport.isAnnotated(Probe.class, null));\n\t}\n\n\t@Test\n\tvoid isAnnotatedDelegates() {\n\t\tvar element = Probe.class;\n\t\tvar optional = Optional.of(element);\n\n\t\tassertEquals(AnnotationUtils.isAnnotated(optional, Tag.class),\n\t\t\tAnnotationSupport.isAnnotated(optional, Tag.class));\n\t\tassertEquals(AnnotationUtils.isAnnotated(optional, Override.class),\n\t\t\tAnnotationSupport.isAnnotated(optional, Override.class));\n\n\t\tassertEquals(AnnotationUtils.isAnnotated(element, Tag.class),\n\t\t\tAnnotationSupport.isAnnotated(element, Tag.class));\n\t\tassertEquals(AnnotationUtils.isAnnotated(element, Override.class),\n\t\t\tAnnotationSupport.isAnnotated(element, Override.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotationOnElementPreconditions() {\n\t\tvar optional = Optional.of(Probe.class);\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\", () -> AnnotationSupport.findAnnotation(optional, null));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotation(Probe.class, null));\n\t}\n\n\t@Test\n\tvoid findAnnotationOnElementDelegates() {\n\t\tvar element = Probe.class;\n\t\tvar optional = Optional.of(element);\n\n\t\tassertEquals(AnnotationUtils.findAnnotation(optional, Tag.class),\n\t\t\tAnnotationSupport.findAnnotation(optional, Tag.class));\n\t\tassertEquals(AnnotationUtils.findAnnotation(optional, Override.class),\n\t\t\tAnnotationSupport.findAnnotation(optional, Override.class));\n\n\t\tassertEquals(AnnotationUtils.findAnnotation(element, Tag.class),\n\t\t\tAnnotationSupport.findAnnotation(element, Tag.class));\n\t\tassertEquals(AnnotationUtils.findAnnotation(element, Override.class),\n\t\t\tAnnotationSupport.findAnnotation(element, Override.class));\n\t}\n\n\t@SuppressWarnings({ \"deprecation\", \"DataFlowIssue\" })\n\t@Test\n\tvoid findAnnotationOnClassWithSearchModePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotation(Probe.class, null, SearchOption.INCLUDE_ENCLOSING_CLASSES));\n\t\tassertPreconditionViolationNotNullFor(\"SearchOption\",\n\t\t\t() -> AnnotationSupport.findAnnotation(Probe.class, Override.class, (SearchOption) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotationOnClassWithEnclosingInstanceTypesPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"enclosingInstanceTypes\",\n\t\t\t() -> AnnotationSupport.findAnnotation(Probe.class, Override.class, (List<Class<?>>) null));\n\t}\n\n\t@SuppressWarnings(\"deprecation\")\n\t@Test\n\tvoid findAnnotationOnClassWithSearchModeDelegates() {\n\t\tClass<?> clazz = Probe.class;\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Tag.class, false),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Tag.class, SearchOption.DEFAULT));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Tag.class, true),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Tag.class, SearchOption.INCLUDE_ENCLOSING_CLASSES));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Override.class, false),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Override.class, SearchOption.DEFAULT));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Override.class, true),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Override.class, SearchOption.INCLUDE_ENCLOSING_CLASSES));\n\n\t\tclazz = Probe.InnerClass.class;\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Tag.class, false),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Tag.class, SearchOption.DEFAULT));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Tag.class, true),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Tag.class, SearchOption.INCLUDE_ENCLOSING_CLASSES));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Override.class, false),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Override.class, SearchOption.DEFAULT));\n\t\tassertEquals(AnnotationUtils.findAnnotation(clazz, Override.class, true),\n\t\t\tAnnotationSupport.findAnnotation(clazz, Override.class, SearchOption.INCLUDE_ENCLOSING_CLASSES));\n\t}\n\n\t@NullUnmarked\n\t@Test\n\tvoid findAnnotationOnClassWithEnclosingInstanceTypes() {\n\t\tassertThat(AnnotationSupport.findAnnotation(Probe.class, Tag.class, List.of())) //\n\t\t\t\t.contains(Probe.class.getDeclaredAnnotation(Tag.class));\n\t\tassertThat(AnnotationSupport.findAnnotation(Probe.InnerClass.class, Tag.class, List.of())) //\n\t\t\t\t.isEmpty();\n\t\tassertThat(AnnotationSupport.findAnnotation(Probe.InnerClass.class, Tag.class, List.of(Probe.class))) //\n\t\t\t\t.contains(Probe.class.getDeclaredAnnotation(Tag.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findPublicAnnotatedFieldsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> AnnotationSupport.findPublicAnnotatedFields(null, String.class, FieldMarker.class));\n\t\tassertPreconditionViolationNotNullFor(\"fieldType\",\n\t\t\t() -> AnnotationSupport.findPublicAnnotatedFields(Probe.class, null, FieldMarker.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findPublicAnnotatedFields(Probe.class, String.class, null));\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsDelegates() {\n\t\tassertEquals(AnnotationUtils.findPublicAnnotatedFields(Probe.class, String.class, FieldMarker.class),\n\t\t\tAnnotationSupport.findPublicAnnotatedFields(Probe.class, String.class, FieldMarker.class));\n\t\tassertEquals(AnnotationUtils.findPublicAnnotatedFields(Probe.class, Throwable.class, Override.class),\n\t\t\tAnnotationSupport.findPublicAnnotatedFields(Probe.class, Throwable.class, Override.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedMethodsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> AnnotationSupport.findAnnotatedMethods(null, Tag.class, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedMethods(Probe.class, null, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"HierarchyTraversalMode\",\n\t\t\t() -> AnnotationSupport.findAnnotatedMethods(Probe.class, Tag.class, null));\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsDelegates() {\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedMethods(Probe.class, Tag.class,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tAnnotationSupport.findAnnotatedMethods(Probe.class, Tag.class, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedMethods(Probe.class, Tag.class,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.BOTTOM_UP),\n\t\t\tAnnotationSupport.findAnnotatedMethods(Probe.class, Tag.class, HierarchyTraversalMode.BOTTOM_UP));\n\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedMethods(Probe.class, Override.class,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tAnnotationSupport.findAnnotatedMethods(Probe.class, Override.class, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedMethods(Probe.class, Override.class,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.BOTTOM_UP),\n\t\t\tAnnotationSupport.findAnnotatedMethods(Probe.class, Override.class, HierarchyTraversalMode.BOTTOM_UP));\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsDelegates() throws Throwable {\n\t\tvar bMethod = Probe.class.getDeclaredMethod(\"bMethod\");\n\t\tassertEquals(AnnotationUtils.findRepeatableAnnotations(bMethod, Tag.class),\n\t\t\tAnnotationSupport.findRepeatableAnnotations(bMethod, Tag.class));\n\t\tvar expected = assertPreconditionViolationFor(\n\t\t\t() -> AnnotationUtils.findRepeatableAnnotations(bMethod, Override.class));\n\t\tvar actual = assertPreconditionViolationFor(\n\t\t\t() -> AnnotationSupport.findRepeatableAnnotations(bMethod, Override.class));\n\t\tassertSame(expected.getClass(), actual.getClass(), \"expected same exception class\");\n\t\tassertEquals(expected.toString(), actual.toString(), \"expected equal exception toString representation\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findRepeatableAnnotationsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findRepeatableAnnotations(Probe.class, null));\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsDelegates() {\n\t\tassertEquals(AnnotationUtils.findAnnotatedFields(Probe.class, FieldMarker.class, f -> true),\n\t\t\tAnnotationSupport.findAnnotatedFields(Probe.class, FieldMarker.class));\n\t\tassertEquals(AnnotationUtils.findAnnotatedFields(Probe.class, Override.class, f -> true),\n\t\t\tAnnotationSupport.findAnnotatedFields(Probe.class, Override.class));\n\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedFields(Probe.class, FieldMarker.class, f -> true,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tAnnotationSupport.findAnnotatedFields(Probe.class, FieldMarker.class, f -> true,\n\t\t\t\tHierarchyTraversalMode.TOP_DOWN));\n\t\tassertEquals(\n\t\t\tAnnotationUtils.findAnnotatedFields(Probe.class, Override.class, f -> true,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tAnnotationSupport.findAnnotatedFields(Probe.class, Override.class, f -> true,\n\t\t\t\tHierarchyTraversalMode.TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedFieldsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFields(null, FieldMarker.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFields(Probe.class, null));\n\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> AnnotationSupport.findAnnotatedFields(null, Override.class,\n\t\t\tf -> true, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFields(Probe.class, null, f -> true, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"HierarchyTraversalMode\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFields(Probe.class, Override.class, f -> true, null));\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldValuesForNonStaticFields() {\n\t\tvar instance = new Fields();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(instance, Tag.class)).isEmpty();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(instance, FieldMarker.class))//\n\t\t\t\t.containsExactlyInAnyOrder(\"i1\", \"i2\");\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldValuesForStaticFields() {\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(Fields.class, Tag.class)).isEmpty();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(Fields.class, FieldMarker.class))//\n\t\t\t\t.containsExactlyInAnyOrder(\"s1\", \"s2\");\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldValuesForNonStaticFieldsByType() {\n\t\tvar instance = new Fields();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(instance, FieldMarker.class, Number.class)).isEmpty();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(instance, FieldMarker.class, String.class))//\n\t\t\t\t.containsExactlyInAnyOrder(\"i1\", \"i2\");\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldValuesForStaticFieldsByType() {\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(Fields.class, FieldMarker.class, Number.class)).isEmpty();\n\n\t\tassertThat(AnnotationSupport.findAnnotatedFieldValues(Fields.class, FieldMarker.class, String.class))//\n\t\t\t\t.containsExactlyInAnyOrder(\"s1\", \"s2\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedFieldValuesPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"instance\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues((Object) null, FieldMarker.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(this, null));\n\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(null, FieldMarker.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(Probe.class, null));\n\n\t\tassertPreconditionViolationNotNullFor(\"instance\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues((Object) null, FieldMarker.class, Number.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(this, null, Number.class));\n\t\tassertPreconditionViolationNotNullFor(\"fieldType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(this, FieldMarker.class, null));\n\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(null, FieldMarker.class, Number.class));\n\t\tassertPreconditionViolationNotNullFor(\"annotationType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(Probe.class, null, Number.class));\n\t\tassertPreconditionViolationNotNullFor(\"fieldType\",\n\t\t\t() -> AnnotationSupport.findAnnotatedFieldValues(Probe.class, FieldMarker.class, null));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Target(ElementType.FIELD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface FieldMarker {\n\t}\n\n\t@Tag(\"class-tag\")\n\tstatic class Probe {\n\n\t\t@FieldMarker\n\t\tpublic static String publicAnnotatedStaticField = \"static\";\n\n\t\t@FieldMarker\n\t\tpublic String publicAnnotatedInstanceField = \"instance\";\n\n\t\t@Tag(\"method-tag\")\n\t\tvoid aMethod() {\n\t\t}\n\n\t\t@Tag(\"method-tag-1\")\n\t\t@Tag(\"method-tag-2\")\n\t\tvoid bMethod() {\n\t\t}\n\n\t\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\t\tclass InnerClass {\n\t\t}\n\n\t}\n\n\tstatic class Fields {\n\n\t\t@FieldMarker\n\t\tstatic String staticField1 = \"s1\";\n\n\t\t@FieldMarker\n\t\tstatic String staticField2 = \"s2\";\n\n\t\t@FieldMarker\n\t\tString instanceField1 = \"i1\";\n\n\t\t@FieldMarker\n\t\tString instanceField2 = \"i2\";\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/ClassSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ClassUtils;\n\n/**\n * @since 1.1\n */\nclass ClassSupportTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid nullSafeToStringPreconditions() {\n\t\tFunction<? super Class<?>, ? extends String> mapper = null;\n\t\tassertPreconditionViolationNotNullFor(\"Mapping function\",\n\t\t\t() -> ClassSupport.nullSafeToString(mapper, String.class, List.class));\n\t}\n\n\t@Test\n\tvoid nullSafeToStringDelegates() {\n\t\tassertEquals(ClassUtils.nullSafeToString(String.class, List.class),\n\t\t\tClassSupport.nullSafeToString(String.class, List.class));\n\n\t\tFunction<Class<?>, String> classToStringMapper = aClass -> aClass.getSimpleName() + \"-Test\";\n\t\tassertEquals(ClassUtils.nullSafeToString(classToStringMapper, String.class, List.class),\n\t\t\tClassSupport.nullSafeToString(classToStringMapper, String.class, List.class));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/ModifierSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Unit tests for {@link ModifierSupport}.\n *\n * @since 1.4\n */\nclass ModifierSupportTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isPublicPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isPublic((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isPublic((Member) null));\n\t}\n\n\t@Classes\n\tvoid isPublicDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isPublic(clazz), ModifierSupport.isPublic(clazz));\n\t}\n\n\t@Methods\n\tvoid isPublicDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isPublic(method), ModifierSupport.isPublic(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isPrivatePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isPrivate((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isPrivate((Member) null));\n\t}\n\n\t@Classes\n\tvoid isPrivateDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isPrivate(clazz), ModifierSupport.isPrivate(clazz));\n\t}\n\n\t@Methods\n\tvoid isPrivateDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isPrivate(method), ModifierSupport.isPrivate(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isNotPrivatePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isNotPrivate((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isNotPrivate((Member) null));\n\t}\n\n\t@Classes\n\tvoid isNotPrivateDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isNotPrivate(clazz), ModifierSupport.isNotPrivate(clazz));\n\t}\n\n\t@Methods\n\tvoid isNotPrivateDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isNotPrivate(method), ModifierSupport.isNotPrivate(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isAbstractPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isAbstract((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isAbstract((Member) null));\n\t}\n\n\t@Classes\n\tvoid isAbstractDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isAbstract(clazz), ModifierSupport.isAbstract(clazz));\n\t}\n\n\t@Methods\n\tvoid isAbstractDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isAbstract(method), ModifierSupport.isAbstract(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isNotAbstractPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isNotAbstract((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isNotAbstract((Member) null));\n\t}\n\n\t@Classes\n\tvoid isNotAbstractDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isNotAbstract(clazz), ModifierSupport.isNotAbstract(clazz));\n\t}\n\n\t@Methods\n\tvoid isNotAbstractDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isNotAbstract(method), ModifierSupport.isNotAbstract(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isStaticPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isStatic((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isStatic((Member) null));\n\t}\n\n\t@Classes\n\tvoid isStaticDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isStatic(clazz), ModifierSupport.isStatic(clazz));\n\t}\n\n\t@Methods\n\tvoid isStaticDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isStatic(method), ModifierSupport.isStatic(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isNotStaticPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isNotStatic((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isNotStatic((Member) null));\n\t}\n\n\t@Classes\n\tvoid isNotStaticDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isNotStatic(clazz), ModifierSupport.isNotStatic(clazz));\n\t}\n\n\t@Methods\n\tvoid isNotStaticDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isNotStatic(method), ModifierSupport.isNotStatic(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isFinalPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isFinal((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isFinal((Member) null));\n\t}\n\n\t@Classes\n\tvoid isFinalDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isFinal(clazz), ModifierSupport.isFinal(clazz));\n\t}\n\n\t@Methods\n\tvoid isFinalDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isFinal(method), ModifierSupport.isFinal(method));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid isNotFinalPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ModifierSupport.isNotFinal((Class<?>) null));\n\t\tassertPreconditionViolationNotNullFor(\"Member\", () -> ModifierSupport.isNotFinal((Member) null));\n\t}\n\n\t@Classes\n\tvoid isNotFinalDelegates(Class<?> clazz) {\n\t\tassertEquals(ReflectionUtils.isNotFinal(clazz), ModifierSupport.isNotFinal(clazz));\n\t}\n\n\t@Methods\n\tvoid isNotFinalDelegates(Method method) {\n\t\tassertEquals(ReflectionUtils.isNotFinal(method), ModifierSupport.isNotFinal(method));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t// Intentionally non-static\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tpublic class PublicClass {\n\n\t\tpublic void publicMethod() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tprivate class PrivateClass {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate void privateMethod() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tprotected class ProtectedClass {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprotected void protectedMethod() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tclass PackageVisibleClass {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid packageVisibleMethod() {\n\t\t}\n\t}\n\n\tabstract static class AbstractClass {\n\n\t\tabstract void abstractMethod();\n\t}\n\n\tstatic class StaticClass {\n\n\t\tstatic void staticMethod() {\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tfinal class FinalClass {\n\n\t\t@SuppressWarnings({ \"FinalMethodInFinalClass\", \"RedundantModifier\" })\n\t\tfinal void finalMethod() {\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ParameterizedTest\n\t@ValueSource(classes = { PublicClass.class, PrivateClass.class, ProtectedClass.class, PackageVisibleClass.class,\n\t\t\tAbstractClass.class, StaticClass.class, FinalClass.class })\n\t@interface Classes {\n\t}\n\n\t@Target(ElementType.METHOD)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@ParameterizedTest\n\t@MethodSource(\"methods\")\n\t@interface Methods {\n\t}\n\n\tstatic Stream<Method> methods() throws Exception {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\tPublicClass.class.getMethod(\"publicMethod\"),\n\t\t\tPrivateClass.class.getDeclaredMethod(\"privateMethod\"),\n\t\t\tProtectedClass.class.getDeclaredMethod(\"protectedMethod\"),\n\t\t\tPackageVisibleClass.class.getDeclaredMethod(\"packageVisibleMethod\"),\n\t\t\tAbstractClass.class.getDeclaredMethod(\"abstractMethod\"),\n\t\t\tStaticClass.class.getDeclaredMethod(\"staticMethod\"),\n\t\t\tFinalClass.class.getDeclaredMethod(\"finalMethod\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/ReflectionSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.platform.commons.support.ReflectionSupport.toSupportResourcesList;\nimport static org.junit.platform.commons.support.ReflectionSupport.toSupportResourcesStream;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getDefaultClassLoader;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Predicate;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * @since 1.0\n */\nclass ReflectionSupportTests {\n\n\tprivate static final Predicate<Class<?>> allTypes = type -> true;\n\t@SuppressWarnings(\"removal\")\n\tprivate static final Predicate<Resource> allResources = __ -> true;\n\tprivate static final Predicate<String> allNames = name -> true;\n\tprivate static final Predicate<Method> allMethods = name -> true;\n\tprivate static final Predicate<Field> allFields = name -> true;\n\n\tstatic final String staticField = \"static\";\n\tfinal String instanceField = \"instance\";\n\n\t@Test\n\tvoid tryToLoadClassDelegates() {\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"-\").toOptional(),\n\t\t\tReflectionSupport.tryToLoadClass(\"-\").toOptional());\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"A\").toOptional(),\n\t\t\tReflectionSupport.tryToLoadClass(\"A\").toOptional());\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"java.nio.Bits\"),\n\t\t\tReflectionSupport.tryToLoadClass(\"java.nio.Bits\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid tryToLoadClassPreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Class name\", () -> ReflectionSupport.tryToLoadClass(null));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Class name\", () -> ReflectionSupport.tryToLoadClass(\"\"));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\t@Test\n\tvoid tryToLoadClassWithExplicitClassLoaderDelegates() {\n\t\tClassLoader classLoader = getClass().getClassLoader();\n\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"-\", classLoader).toOptional(),\n\t\t\tReflectionSupport.tryToLoadClass(\"-\", classLoader).toOptional());\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"A\", classLoader).toOptional(),\n\t\t\tReflectionSupport.tryToLoadClass(\"A\", classLoader).toOptional());\n\t\tassertEquals(ReflectionUtils.tryToLoadClass(\"java.nio.Bits\", classLoader),\n\t\t\tReflectionSupport.tryToLoadClass(\"java.nio.Bits\", classLoader));\n\t}\n\n\t/**\n\t * @since 1.10\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid tryToLoadClassWithExplicitClassLoaderPreconditions() {\n\t\tvar cl = getClass().getClassLoader();\n\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Class name\", () -> ReflectionSupport.tryToLoadClass(null, cl));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Class name\", () -> ReflectionSupport.tryToLoadClass(\"\", cl));\n\n\t\tassertPreconditionViolationNotNullFor(\"ClassLoader\", () -> ReflectionSupport.tryToLoadClass(\"int\", null));\n\t}\n\n\t@TestFactory\n\tList<DynamicTest> findAllClassesInClasspathRootDelegates() throws Throwable {\n\t\tList<DynamicTest> tests = new ArrayList<>();\n\t\tList<Path> paths = new ArrayList<>();\n\t\tpaths.add(Path.of(\".\").toRealPath());\n\t\tpaths.addAll(ReflectionUtils.getAllClasspathRootDirectories());\n\t\tfor (var path : paths) {\n\t\t\tvar root = path.toUri();\n\t\t\ttests.add(DynamicTest.dynamicTest(createDisplayName(root),\n\t\t\t\t() -> assertEquals(ReflectionUtils.findAllClassesInClasspathRoot(root, allTypes, allNames),\n\t\t\t\t\tReflectionSupport.findAllClassesInClasspathRoot(root, allTypes, allNames))));\n\t\t}\n\t\treturn tests;\n\t}\n\n\t/**\n\t * @since 1.12\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid tryToGetResourcesPreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Resource name\", () -> ReflectionSupport.tryToGetResources(null));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Resource name\", () -> ReflectionSupport.tryToGetResources(\"\"));\n\t\tassertPreconditionViolationNotNullFor(\"Class loader\",\n\t\t\t() -> ReflectionSupport.tryToGetResources(\"default-package.resource\", null));\n\t\tassertPreconditionViolationNotNullFor(\"Class loader\",\n\t\t\t() -> ReflectionSupport.tryToGetResources(\"default-package.resource\", null));\n\t}\n\n\t/**\n\t * @since 1.12\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Test\n\tvoid tryToGetResources() {\n\t\tassertEquals(\n\t\t\tReflectionUtils.tryToGetResources(\"default-package.resource\").toOptional().map(\n\t\t\t\tReflectionSupport::toSupportResourcesSet),\n\t\t\tReflectionSupport.tryToGetResources(\"default-package.resource\").toOptional());\n\t\tassertEquals(\n\t\t\tReflectionUtils.tryToGetResources(\"default-package.resource\", getDefaultClassLoader()).toOptional().map(\n\t\t\t\tReflectionSupport::toSupportResourcesSet), //\n\t\t\tReflectionSupport.tryToGetResources(\"default-package.resource\", getDefaultClassLoader()).toOptional());\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllClassesInClasspathRootPreconditions() {\n\t\tvar path = Path.of(\".\").toUri();\n\t\tassertPreconditionViolationNotNullFor(\"root\",\n\t\t\t() -> ReflectionSupport.findAllClassesInClasspathRoot(null, allTypes, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"class predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInClasspathRoot(path, null, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"name predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInClasspathRoot(path, allTypes, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@TestFactory\n\tList<DynamicTest> findAllResourcesInClasspathRootDelegates() throws Throwable {\n\t\tList<DynamicTest> tests = new ArrayList<>();\n\t\tList<Path> paths = new ArrayList<>();\n\t\tpaths.add(Path.of(\".\").toRealPath());\n\t\tpaths.addAll(ReflectionUtils.getAllClasspathRootDirectories());\n\t\tfor (var path : paths) {\n\t\t\tvar root = path.toUri();\n\t\t\ttests.add(DynamicTest.dynamicTest(createDisplayName(root), () -> assertThat(toSupportResourcesList(\n\t\t\t\tReflectionUtils.findAllResourcesInClasspathRoot(root, ResourceFilter.of(__ -> true)))) //\n\t\t\t\t\t\t.containsExactlyElementsOf(\n\t\t\t\t\t\t\tReflectionSupport.findAllResourcesInClasspathRoot(root, allResources))));\n\t\t}\n\t\treturn tests;\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid findAllResourcesInClasspathRootPreconditions() {\n\t\tvar path = Path.of(\".\").toUri();\n\t\tassertPreconditionViolationNotNullFor(\"root\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInClasspathRoot(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInClasspathRoot(path, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@TestFactory\n\tList<DynamicTest> streamAllResourcesInClasspathRootDelegates() throws Throwable {\n\t\tList<DynamicTest> tests = new ArrayList<>();\n\t\tList<Path> paths = new ArrayList<>();\n\t\tpaths.add(Path.of(\".\").toRealPath());\n\t\tpaths.addAll(ReflectionUtils.getAllClasspathRootDirectories());\n\t\tfor (var path : paths) {\n\t\t\tvar root = path.toUri();\n\t\t\ttests.add(DynamicTest.dynamicTest(createDisplayName(root), () -> assertThat(toSupportResourcesStream(\n\t\t\t\tReflectionUtils.streamAllResourcesInClasspathRoot(root, ResourceFilter.of(__ -> true)))) //\n\t\t\t\t\t\t.containsExactlyElementsOf(\n\t\t\t\t\t\t\tReflectionSupport.streamAllResourcesInClasspathRoot(root, allResources).toList())));\n\t\t}\n\t\treturn tests;\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid streamAllResourcesInClasspathRootPreconditions() {\n\t\tvar path = Path.of(\".\").toUri();\n\t\tassertPreconditionViolationNotNullFor(\"root\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInClasspathRoot(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInClasspathRoot(path, null));\n\t}\n\n\t@Test\n\tvoid findAllClassesInPackageDelegates() {\n\t\tassertNotEquals(0, ReflectionSupport.findAllClassesInPackage(\"org.junit\", allTypes, allNames).size());\n\t\tassertEquals(ReflectionUtils.findAllClassesInPackage(\"org.junit\", allTypes, allNames),\n\t\t\tReflectionSupport.findAllClassesInPackage(\"org.junit\", allTypes, allNames));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllClassesInPackagePreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"basePackageName\",\n\t\t\t() -> ReflectionSupport.findAllClassesInPackage(null, allTypes, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"class predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInPackage(\"org.junit\", null, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"name predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInPackage(\"org.junit\", allTypes, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Test\n\tvoid findAllResourcesInPackageDelegates() {\n\t\tassertNotEquals(0, ReflectionSupport.findAllResourcesInPackage(\"org.junit\", allResources).size());\n\n\t\tassertEquals(\n\t\t\ttoSupportResourcesList(\n\t\t\t\tReflectionUtils.findAllResourcesInPackage(\"org.junit\", ResourceFilter.of(__ -> true))),\n\t\t\tReflectionSupport.findAllResourcesInPackage(\"org.junit\", allResources));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid findAllResourcesInPackagePreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"basePackageName\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInPackage(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInPackage(\"org.junit\", null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Test\n\tvoid streamAllResourcesInPackageDelegates() {\n\t\tassertNotEquals(0, ReflectionSupport.streamAllResourcesInPackage(\"org.junit\", allResources).count());\n\n\t\tassertEquals(\n\t\t\ttoSupportResourcesStream(\n\t\t\t\tReflectionUtils.streamAllResourcesInPackage(\"org.junit\", ResourceFilter.of(__ -> true))).toList(),\n\t\t\tReflectionSupport.streamAllResourcesInPackage(\"org.junit\", allResources).toList());\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid streamAllResourcesInPackagePreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"basePackageName\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInPackage(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInPackage(\"org.junit\", null));\n\t}\n\n\t@Test\n\tvoid findAllClassesInModuleDelegates() {\n\t\tassertEquals(ReflectionUtils.findAllClassesInModule(\"org.junit.platform.commons\", allTypes, allNames),\n\t\t\tReflectionSupport.findAllClassesInModule(\"org.junit.platform.commons\", allTypes, allNames));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllClassesInModulePreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"Module name\",\n\t\t\t() -> ReflectionSupport.findAllClassesInModule((String) null, allTypes, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"Module\",\n\t\t\t() -> ReflectionSupport.findAllClassesInModule((Module) null, allTypes, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"class predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInModule(\"org.junit.platform.commons\", null, allNames));\n\t\tassertPreconditionViolationNotNullFor(\"name predicate\",\n\t\t\t() -> ReflectionSupport.findAllClassesInModule(\"org.junit.platform.commons\", allTypes, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Test\n\tvoid findAllResourcesInModuleDelegates() {\n\t\tassertEquals(\n\t\t\tReflectionUtils.findAllResourcesInModule(\"org.junit.platform.commons\", ResourceFilter.of(__ -> true)),\n\t\t\tReflectionSupport.findAllResourcesInModule(\"org.junit.platform.commons\", allResources));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid findAllResourcesInModulePreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"Module name\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInModule(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.findAllResourcesInModule(\"org.junit.platform.commons\", null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"removal\")\n\t@Test\n\tvoid streamAllResourcesInModuleDelegates() {\n\t\tassertEquals(\n\t\t\ttoSupportResourcesStream(ReflectionUtils.streamAllResourcesInModule(\"org.junit.platform.commons\",\n\t\t\t\tResourceFilter.of(__ -> true))).toList(),\n\t\t\tReflectionSupport.streamAllResourcesInModule(\"org.junit.platform.commons\", allResources).toList());\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings({ \"DataFlowIssue\", \"removal\" })\n\t@Test\n\tvoid streamAllResourcesInModulePreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"Module name\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInModule(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ReflectionSupport.streamAllResourcesInModule(\"org.junit.platform.commons\", null));\n\t}\n\n\t@Test\n\tvoid newInstanceDelegates() {\n\t\tassertEquals(ReflectionUtils.newInstance(String.class, \"foo\"),\n\t\t\tReflectionSupport.newInstance(String.class, \"foo\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid newInstancePreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ReflectionSupport.newInstance(null));\n\t\tassertPreconditionViolationNotNullFor(\"Argument array\",\n\t\t\t() -> ReflectionSupport.newInstance(String.class, (Object[]) null));\n\t\tassertPreconditionViolationNotNullFor(\"Individual arguments\",\n\t\t\t() -> ReflectionSupport.newInstance(String.class, new Object[] { null }));\n\t}\n\n\t@Test\n\tvoid invokeMethodDelegates() throws Exception {\n\t\tvar method = Boolean.class.getMethod(\"valueOf\", String.class);\n\t\tassertEquals(ReflectionUtils.invokeMethod(method, null, \"true\"),\n\t\t\tReflectionSupport.invokeMethod(method, null, \"true\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid invokeMethodPreconditions() throws Exception {\n\t\tassertPreconditionViolationNotNullFor(\"Method\", () -> ReflectionSupport.invokeMethod(null, null, \"true\"));\n\n\t\tvar method = Boolean.class.getMethod(\"toString\");\n\t\tassertPreconditionViolationFor(() -> ReflectionSupport.invokeMethod(method, null))//\n\t\t\t\t.withMessage(\"Cannot invoke non-static method [\" + method.toGenericString() + \"] on a null target.\");\n\t}\n\n\t@Test\n\tvoid findFieldsDelegates() {\n\t\tassertEquals(\n\t\t\tReflectionUtils.findFields(ReflectionSupportTests.class, allFields,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.BOTTOM_UP),\n\t\t\tReflectionSupport.findFields(ReflectionSupportTests.class, allFields, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertEquals(\n\t\t\tReflectionUtils.findFields(ReflectionSupportTests.class, allFields,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tReflectionSupport.findFields(ReflectionSupportTests.class, allFields, HierarchyTraversalMode.TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findFieldsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findFields(null, allFields, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findFields(null, allFields, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"Predicate\",\n\t\t\t() -> ReflectionSupport.findFields(ReflectionSupportTests.class, null, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertPreconditionViolationNotNullFor(\"Predicate\",\n\t\t\t() -> ReflectionSupport.findFields(ReflectionSupportTests.class, null, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"HierarchyTraversalMode\",\n\t\t\t() -> ReflectionSupport.findFields(ReflectionSupportTests.class, allFields, null));\n\t}\n\n\t@Test\n\tvoid tryToReadFieldValueDelegates() throws Exception {\n\t\tvar staticField = getClass().getDeclaredField(\"staticField\");\n\t\tassertEquals(ReflectionUtils.tryToReadFieldValue(staticField, null),\n\t\t\tReflectionSupport.tryToReadFieldValue(staticField, null));\n\n\t\tvar instanceField = getClass().getDeclaredField(\"instanceField\");\n\t\tassertEquals(ReflectionUtils.tryToReadFieldValue(instanceField, this),\n\t\t\tReflectionSupport.tryToReadFieldValue(instanceField, this));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid tryToReadFieldValuePreconditions() throws Exception {\n\t\tassertPreconditionViolationNotNullFor(\"Field\", () -> ReflectionSupport.tryToReadFieldValue(null, this));\n\n\t\tvar instanceField = getClass().getDeclaredField(\"instanceField\");\n\t\tassertPreconditionViolationFor(() -> ReflectionSupport.tryToReadFieldValue(instanceField, null))//\n\t\t\t\t.withMessageStartingWith(\"Cannot read non-static field\")//\n\t\t\t\t.withMessageEndingWith(\"on a null instance.\");\n\t}\n\n\t@Test\n\tvoid findMethodDelegates() {\n\t\tassertEquals(ReflectionUtils.findMethod(Boolean.class, \"valueOf\", String.class.getName()),\n\t\t\tReflectionSupport.findMethod(Boolean.class, \"valueOf\", String.class.getName()));\n\n\t\tassertEquals(ReflectionUtils.findMethod(Boolean.class, \"valueOf\", String.class),\n\t\t\tReflectionSupport.findMethod(Boolean.class, \"valueOf\", String.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findMethodPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findMethod(null, \"valueOf\", String.class.getName()));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"\", String.class.getName()));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"   \", String.class.getName()));\n\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findMethod(null, \"valueOf\", String.class));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"\", String.class));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"   \", String.class));\n\t\tassertPreconditionViolationNotNullFor(\"Parameter types array\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"valueOf\", (Class<?>[]) null));\n\t\tassertPreconditionViolationNotNullFor(\"Individual parameter types\",\n\t\t\t() -> ReflectionSupport.findMethod(Boolean.class, \"valueOf\", new Class<?>[] { null }));\n\t}\n\n\t@Test\n\tvoid findMethodsDelegates() {\n\t\tassertEquals(\n\t\t\tReflectionUtils.findMethods(ReflectionSupportTests.class, allMethods,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.BOTTOM_UP),\n\t\t\tReflectionSupport.findMethods(ReflectionSupportTests.class, allMethods, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertEquals(\n\t\t\tReflectionUtils.findMethods(ReflectionSupportTests.class, allMethods,\n\t\t\t\tReflectionUtils.HierarchyTraversalMode.TOP_DOWN),\n\t\t\tReflectionSupport.findMethods(ReflectionSupportTests.class, allMethods, HierarchyTraversalMode.TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findMethodsPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findMethods(null, allMethods, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findMethods(null, allMethods, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"Predicate\",\n\t\t\t() -> ReflectionSupport.findMethods(ReflectionSupportTests.class, null, HierarchyTraversalMode.BOTTOM_UP));\n\t\tassertPreconditionViolationNotNullFor(\"Predicate\",\n\t\t\t() -> ReflectionSupport.findMethods(ReflectionSupportTests.class, null, HierarchyTraversalMode.TOP_DOWN));\n\t\tassertPreconditionViolationNotNullFor(\"HierarchyTraversalMode\",\n\t\t\t() -> ReflectionSupport.findMethods(ReflectionSupportTests.class, allMethods, null));\n\t}\n\n\t@Test\n\tvoid findNestedClassesDelegates() {\n\t\tassertEquals(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, ReflectionUtils::isStatic),\n\t\t\tReflectionSupport.findNestedClasses(ClassWithNestedClasses.class, ReflectionUtils::isStatic));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findNestedClassesPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\",\n\t\t\t() -> ReflectionSupport.findNestedClasses(null, ReflectionUtils::isStatic));\n\t\tassertPreconditionViolationNotNullFor(\"Predicate\",\n\t\t\t() -> ReflectionSupport.findNestedClasses(ClassWithNestedClasses.class, null));\n\t}\n\n\tprivate static String createDisplayName(URI root) {\n\t\tvar displayName = root.getPath();\n\t\tif (displayName.length() > 42) {\n\t\t\tdisplayName = \"...\" + displayName.substring(displayName.length() - 42);\n\t\t}\n\t\treturn displayName;\n\t}\n\n\tstatic class ClassWithNestedClasses {\n\n\t\t@SuppressWarnings({ \"InnerClassMayBeStatic\", \"unused\" })\n\t\tclass Nested1 {\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tstatic class Nested2 {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/ResourceInteroperabilityTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.io.Resource;\n\nclass ResourceInteroperabilityTests {\n\n\t@Test\n\tvoid newAndOldResourcesAreLogicallyEquivalent() {\n\t\tvar oldResource = new DefaultResource(\"foo\", URI.create(\"foo\"));\n\t\tvar newResource = Resource.of(\"foo\", URI.create(\"foo\"));\n\t\tvar differentResource = Resource.of(\"foo\", URI.create(\"bar\"));\n\n\t\tassertEqualsAndHashCode(oldResource, newResource, differentResource);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/ResourceSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getDefaultClassLoader;\n\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\nclass ResourceSupportTests {\n\n\tprivate static final ResourceFilter allResources = ResourceFilter.of(__ -> true);\n\n\t/**\n\t * @since 1.12\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid tryToGetResourcesPreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Resource name\", () -> ResourceSupport.tryToGetResources(null));\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"Resource name\", () -> ResourceSupport.tryToGetResources(\"\"));\n\t\tassertPreconditionViolationNotNullFor(\"Class loader\",\n\t\t\t() -> ResourceSupport.tryToGetResources(\"default-package.resource\", null));\n\t\tassertPreconditionViolationNotNullFor(\"Class loader\",\n\t\t\t() -> ResourceSupport.tryToGetResources(\"default-package.resource\", null));\n\t}\n\n\t/**\n\t * @since 1.12\n\t */\n\t@Test\n\tvoid tryToGetResources() {\n\t\tassertEquals(ReflectionUtils.tryToGetResources(\"default-package.resource\").toOptional(),\n\t\t\tResourceSupport.tryToGetResources(\"default-package.resource\").toOptional());\n\t\tassertEquals(\n\t\t\tReflectionUtils.tryToGetResources(\"default-package.resource\", getDefaultClassLoader()).toOptional(), //\n\t\t\tResourceSupport.tryToGetResources(\"default-package.resource\", getDefaultClassLoader()).toOptional());\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@TestFactory\n\tList<DynamicTest> findAllResourcesInClasspathRootDelegates() throws Throwable {\n\t\tList<DynamicTest> tests = new ArrayList<>();\n\t\tList<Path> paths = new ArrayList<>();\n\t\tpaths.add(Path.of(\".\").toRealPath());\n\t\tpaths.addAll(ReflectionUtils.getAllClasspathRootDirectories());\n\t\tfor (var path : paths) {\n\t\t\tvar root = path.toUri();\n\t\t\ttests.add(DynamicTest.dynamicTest(createDisplayName(root),\n\t\t\t\t() -> assertThat(ReflectionUtils.findAllResourcesInClasspathRoot(root, allResources)) //\n\t\t\t\t\t\t.containsExactlyElementsOf(\n\t\t\t\t\t\t\tResourceSupport.findAllResourcesInClasspathRoot(root, allResources))));\n\t\t}\n\t\treturn tests;\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllResourcesInClasspathRootPreconditions() {\n\t\tvar path = Path.of(\".\").toUri();\n\t\tassertPreconditionViolationNotNullFor(\"root\",\n\t\t\t() -> ResourceSupport.findAllResourcesInClasspathRoot(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ResourceSupport.findAllResourcesInClasspathRoot(path, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@TestFactory\n\tList<DynamicTest> streamAllResourcesInClasspathRootDelegates() throws Throwable {\n\t\tList<DynamicTest> tests = new ArrayList<>();\n\t\tList<Path> paths = new ArrayList<>();\n\t\tpaths.add(Path.of(\".\").toRealPath());\n\t\tpaths.addAll(ReflectionUtils.getAllClasspathRootDirectories());\n\t\tfor (var path : paths) {\n\t\t\tvar root = path.toUri();\n\t\t\ttests.add(DynamicTest.dynamicTest(createDisplayName(root),\n\t\t\t\t() -> assertThat(ReflectionUtils.streamAllResourcesInClasspathRoot(root, allResources)) //\n\t\t\t\t\t\t.containsExactlyElementsOf(\n\t\t\t\t\t\t\tResourceSupport.streamAllResourcesInClasspathRoot(root, allResources).toList())));\n\t\t}\n\t\treturn tests;\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamAllResourcesInClasspathRootPreconditions() {\n\t\tvar path = Path.of(\".\").toUri();\n\t\tassertPreconditionViolationNotNullFor(\"root\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInClasspathRoot(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInClasspathRoot(path, null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@Test\n\tvoid findAllResourcesInPackageDelegates() {\n\t\tassertNotEquals(0, ResourceSupport.findAllResourcesInPackage(\"org.junit\", allResources).size());\n\n\t\tassertEquals(ReflectionUtils.findAllResourcesInPackage(\"org.junit\", allResources),\n\t\t\tResourceSupport.findAllResourcesInPackage(\"org.junit\", allResources));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllResourcesInPackagePreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"basePackageName\",\n\t\t\t() -> ResourceSupport.findAllResourcesInPackage(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ResourceSupport.findAllResourcesInPackage(\"org.junit\", null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@Test\n\tvoid streamAllResourcesInPackageDelegates() {\n\t\tassertNotEquals(0, ResourceSupport.streamAllResourcesInPackage(\"org.junit\", allResources).count());\n\n\t\tassertEquals(ReflectionUtils.streamAllResourcesInPackage(\"org.junit\", allResources).toList(),\n\t\t\tResourceSupport.streamAllResourcesInPackage(\"org.junit\", allResources).toList());\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamAllResourcesInPackagePreconditions() {\n\t\tassertPreconditionViolationNotNullOrBlankFor(\"basePackageName\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInPackage(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"resourceFilter\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInPackage(\"org.junit\", null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@Test\n\tvoid findAllResourcesInModuleDelegates() {\n\t\tassertEquals(ReflectionUtils.findAllResourcesInModule(\"org.junit.platform.commons\", allResources),\n\t\t\tResourceSupport.findAllResourcesInModule(\"org.junit.platform.commons\", allResources));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllResourcesInModulePreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"Module name\",\n\t\t\t() -> ResourceSupport.findAllResourcesInModule((String) null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"Module\",\n\t\t\t() -> ResourceSupport.findAllResourcesInModule((Module) null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"Resource filter\",\n\t\t\t() -> ResourceSupport.findAllResourcesInModule(\"org.junit.platform.commons\", null));\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@Test\n\tvoid streamAllResourcesInModuleDelegates() {\n\t\tassertEquals(ReflectionUtils.streamAllResourcesInModule(\"org.junit.platform.commons\", allResources).toList(),\n\t\t\tResourceSupport.streamAllResourcesInModule(\"org.junit.platform.commons\", allResources).toList());\n\t}\n\n\t/**\n\t * @since 1.11\n\t */\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid streamAllResourcesInModulePreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"Module name\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInModule(null, allResources));\n\t\tassertPreconditionViolationNotNullFor(\"Resource filter\",\n\t\t\t() -> ResourceSupport.streamAllResourcesInModule(\"org.junit.platform.commons\", null));\n\t}\n\n\tprivate static String createDisplayName(URI root) {\n\t\tvar displayName = root.getPath();\n\t\tif (displayName.length() > 42) {\n\t\t\tdisplayName = \"...\" + displayName.substring(displayName.length() - 42);\n\t\t}\n\t\treturn displayName;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/conversion/ConversionSupportTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.LocalTime;\nimport java.time.MonthDay;\nimport java.time.OffsetDateTime;\nimport java.time.OffsetTime;\nimport java.time.Period;\nimport java.time.Year;\nimport java.time.YearMonth;\nimport java.time.ZoneId;\nimport java.time.ZoneOffset;\nimport java.time.ZonedDateTime;\nimport java.util.Currency;\nimport java.util.Locale;\nimport java.util.UUID;\nimport java.util.concurrent.TimeUnit;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.commons.util.ClassLoaderUtils;\n\n/**\n * Unit tests for {@link ConversionSupport}.\n *\n * @since 5.12\n */\nclass ConversionSupportTests {\n\n\t@Test\n\tvoid isAwareOfNull() {\n\t\tassertConverts(null, Object.class, null);\n\t\tassertConverts(null, String.class, null);\n\t\tassertConverts(null, Boolean.class, null);\n\t}\n\n\t@Test\n\tvoid convertsStringsToPrimitiveTypes() {\n\t\tassertConverts(\"true\", boolean.class, true);\n\t\tassertConverts(\"false\", boolean.class, false);\n\t\tassertConverts(\"o\", char.class, 'o');\n\t\tassertConverts(\"1\", byte.class, (byte) 1);\n\t\tassertConverts(\"1_0\", byte.class, (byte) 10);\n\t\tassertConverts(\"1\", short.class, (short) 1);\n\t\tassertConverts(\"1_2\", short.class, (short) 12);\n\t\tassertConverts(\"42\", int.class, 42);\n\t\tassertConverts(\"700_050_000\", int.class, 700_050_000);\n\t\tassertConverts(\"42\", long.class, 42L);\n\t\tassertConverts(\"4_2\", long.class, 42L);\n\t\tassertConverts(\"42.23\", float.class, 42.23f);\n\t\tassertConverts(\"42.2_3\", float.class, 42.23f);\n\t\tassertConverts(\"42.23\", double.class, 42.23);\n\t\tassertConverts(\"42.2_3\", double.class, 42.23);\n\t}\n\n\t@Test\n\tvoid convertsStringsToPrimitiveWrapperTypes() {\n\t\tassertConverts(\"true\", Boolean.class, true);\n\t\tassertConverts(\"false\", Boolean.class, false);\n\t\tassertConverts(\"o\", Character.class, 'o');\n\t\tassertConverts(\"1\", Byte.class, (byte) 1);\n\t\tassertConverts(\"1_0\", Byte.class, (byte) 10);\n\t\tassertConverts(\"1\", Short.class, (short) 1);\n\t\tassertConverts(\"1_2\", Short.class, (short) 12);\n\t\tassertConverts(\"42\", Integer.class, 42);\n\t\tassertConverts(\"700_050_000\", Integer.class, 700_050_000);\n\t\tassertConverts(\"42\", Long.class, 42L);\n\t\tassertConverts(\"4_2\", Long.class, 42L);\n\t\tassertConverts(\"42.23\", Float.class, 42.23f);\n\t\tassertConverts(\"42.2_3\", Float.class, 42.23f);\n\t\tassertConverts(\"42.23\", Double.class, 42.23);\n\t\tassertConverts(\"42.2_3\", Double.class, 42.23);\n\t}\n\n\t@ParameterizedTest(name = \"[{index}] {0}\")\n\t@ValueSource(classes = { char.class, boolean.class, short.class, byte.class, int.class, long.class, float.class,\n\t\t\tdouble.class, void.class })\n\tvoid throwsExceptionForNullToPrimitiveTypeConversion(Class<?> type) {\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(null, type)) //\n\t\t\t\t.withMessage(\"Cannot convert null to primitive value of type \" + type.getCanonicalName());\n\t}\n\n\t@ParameterizedTest(name = \"[{index}] {0}\")\n\t@ValueSource(classes = { Boolean.class, Character.class, Short.class, Byte.class, Integer.class, Long.class,\n\t\t\tFloat.class, Double.class })\n\tvoid throwsExceptionWhenConvertingTheWordNullToPrimitiveWrapperType(Class<?> type) {\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"null\", type)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"null\\\" to type \" + type.getCanonicalName());\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"NULL\", type)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"NULL\\\" to type \" + type.getCanonicalName());\n\t}\n\n\t@Test\n\tvoid throwsExceptionOnInvalidStringForPrimitiveTypes() {\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"ab\", char.class)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"ab\\\" to type char\") //\n\t\t\t\t.havingCause() //\n\t\t\t\t.withMessage(\"String must have length of 1: ab\");\n\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"tru\", boolean.class)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"tru\\\" to type boolean\") //\n\t\t\t\t.havingCause() //\n\t\t\t\t.withMessage(\"String must be 'true' or 'false' (ignoring case): tru\");\n\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"null\", boolean.class)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"null\\\" to type boolean\") //\n\t\t\t\t.havingCause() //\n\t\t\t\t.withMessage(\"String must be 'true' or 'false' (ignoring case): null\");\n\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"NULL\", boolean.class)) //\n\t\t\t\t.withMessage(\"Failed to convert String \\\"NULL\\\" to type boolean\") //\n\t\t\t\t.havingCause() //\n\t\t\t\t.withMessage(\"String must be 'true' or 'false' (ignoring case): NULL\");\n\t}\n\n\t@Test\n\tvoid throwsExceptionWhenImplicitConversionIsUnsupported() {\n\t\tassertThatExceptionOfType(ConversionException.class) //\n\t\t\t\t.isThrownBy(() -> convert(\"foo\", Enigma.class)) //\n\t\t\t\t.withMessage(\"No built-in converter for source type java.lang.String and target type %s\",\n\t\t\t\t\tEnigma.class.getName());\n\t}\n\n\t/**\n\t * @since 5.4\n\t */\n\t@Test\n\t@SuppressWarnings(\"OctalInteger\") // We test parsing octal integers here as well as hex.\n\tvoid convertsEncodedStringsToIntegralTypes() {\n\t\tassertConverts(\"0x1f\", byte.class, (byte) 0x1F);\n\t\tassertConverts(\"-0x1F\", byte.class, (byte) -0x1F);\n\t\tassertConverts(\"010\", byte.class, (byte) 010);\n\n\t\tassertConverts(\"0x1f00\", short.class, (short) 0x1F00);\n\t\tassertConverts(\"-0x1F00\", short.class, (short) -0x1F00);\n\t\tassertConverts(\"01000\", short.class, (short) 01000);\n\n\t\tassertConverts(\"0x1f000000\", int.class, 0x1F000000);\n\t\tassertConverts(\"-0x1F000000\", int.class, -0x1F000000);\n\t\tassertConverts(\"010000000\", int.class, 010000000);\n\n\t\tassertConverts(\"0x1f000000000\", long.class, 0x1F000000000L);\n\t\tassertConverts(\"-0x1F000000000\", long.class, -0x1F000000000L);\n\t\tassertConverts(\"0100000000000\", long.class, 0100000000000L);\n\t}\n\n\t@Test\n\tvoid convertsStringsToEnumConstants() {\n\t\tassertConverts(\"DAYS\", TimeUnit.class, TimeUnit.DAYS);\n\t}\n\n\t// --- java.io and java.nio ------------------------------------------------\n\n\t@Test\n\tvoid convertsStringToCharset() {\n\t\tassertConverts(\"ISO-8859-1\", Charset.class, StandardCharsets.ISO_8859_1);\n\t\tassertConverts(\"UTF-8\", Charset.class, StandardCharsets.UTF_8);\n\t}\n\n\t@Test\n\tvoid convertsStringToFile() {\n\t\tassertConverts(\"file\", File.class, new File(\"file\"));\n\t\tassertConverts(\"/file\", File.class, new File(\"/file\"));\n\t\tassertConverts(\"/some/file\", File.class, new File(\"/some/file\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToPath() {\n\t\tassertConverts(\"path\", Path.class, Path.of(\"path\"));\n\t\tassertConverts(\"/path\", Path.class, Path.of(\"/path\"));\n\t\tassertConverts(\"/some/path\", Path.class, Path.of(\"/some/path\"));\n\t}\n\n\t// --- java.lang -----------------------------------------------------------\n\n\t@Test\n\tvoid convertsStringToClass() {\n\t\tassertConverts(\"java.lang.Integer\", Class.class, Integer.class);\n\t\tassertConverts(\"java.lang.Void\", Class.class, Void.class);\n\t\tassertConverts(\"java.lang.Thread$State\", Class.class, Thread.State.class);\n\t\tassertConverts(\"byte\", Class.class, byte.class);\n\t\tassertConverts(\"void\", Class.class, void.class);\n\t\tassertConverts(\"char[]\", Class.class, char[].class);\n\t\tassertConverts(\"java.lang.Long[][]\", Class.class, Long[][].class);\n\t\tassertConverts(\"[[[I\", Class.class, int[][][].class);\n\t\tassertConverts(\"[[Ljava.lang.String;\", Class.class, String[][].class);\n\t}\n\n\t@Test\n\tvoid convertsStringToClassWithCustomTypeFromDifferentClassLoader() throws Exception {\n\t\tString customTypeName = Enigma.class.getName();\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(Enigma.class)) {\n\t\t\tvar customType = testClassLoader.loadClass(customTypeName);\n\t\t\tassertThat(customType.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar declaringExecutable = ReflectionSupport.findMethod(customType, \"foo\").orElseThrow();\n\t\t\tassertThat(declaringExecutable.getDeclaringClass().getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar clazz = (Class<?>) convert(customTypeName, Class.class, classLoader(declaringExecutable));\n\t\t\tassertThat(clazz).isNotNull().isNotEqualTo(Enigma.class).isEqualTo(customType);\n\t\t\tassertThat(clazz.getClassLoader()).isSameAs(testClassLoader);\n\t\t}\n\t}\n\n\t// --- java.math -----------------------------------------------------------\n\n\t@Test\n\tvoid convertsStringToBigDecimal() {\n\t\tassertConverts(\"123.456e789\", BigDecimal.class, new BigDecimal(\"123.456e789\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToBigInteger() {\n\t\tassertConverts(\"1234567890123456789\", BigInteger.class, new BigInteger(\"1234567890123456789\"));\n\t}\n\n\t// --- java.net ------------------------------------------------------------\n\n\t@Test\n\tvoid convertsStringToURI() {\n\t\tassertConverts(\"https://docs.oracle.com/en/java/javase/12/\", URI.class,\n\t\t\tURI.create(\"https://docs.oracle.com/en/java/javase/12/\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToURL() throws Exception {\n\t\tassertConverts(\"https://junit.org\", URL.class, URI.create(\"https://junit.org\").toURL());\n\t}\n\n\t// --- java.time -----------------------------------------------------------\n\n\t@Test\n\tvoid convertsStringsToJavaTimeInstances() {\n\t\tassertConverts(\"PT1234.5678S\", Duration.class, Duration.ofSeconds(1234, 567800000));\n\t\tassertConverts(\"1970-01-01T00:00:00Z\", Instant.class, Instant.ofEpochMilli(0));\n\t\tassertConverts(\"2017-03-14\", LocalDate.class, LocalDate.of(2017, 3, 14));\n\t\tassertConverts(\"2017-03-14T12:34:56.789\", LocalDateTime.class,\n\t\t\tLocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000));\n\t\tassertConverts(\"12:34:56.789\", LocalTime.class, LocalTime.of(12, 34, 56, 789_000_000));\n\t\tassertConverts(\"--03-14\", MonthDay.class, MonthDay.of(3, 14));\n\t\tassertConverts(\"2017-03-14T12:34:56.789Z\", OffsetDateTime.class,\n\t\t\tOffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC));\n\t\tassertConverts(\"12:34:56.789Z\", OffsetTime.class, OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC));\n\t\tassertConverts(\"P2M6D\", Period.class, Period.of(0, 2, 6));\n\t\tassertConverts(\"2017\", Year.class, Year.of(2017));\n\t\tassertConverts(\"2017-03\", YearMonth.class, YearMonth.of(2017, 3));\n\t\tassertConverts(\"2017-03-14T12:34:56.789Z\", ZonedDateTime.class,\n\t\t\tZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC));\n\t\tassertConverts(\"Europe/Berlin\", ZoneId.class, ZoneId.of(\"Europe/Berlin\"));\n\t\tassertConverts(\"+02:30\", ZoneOffset.class, ZoneOffset.ofHoursMinutes(2, 30));\n\t}\n\n\t// --- java.util -----------------------------------------------------------\n\n\t@Test\n\tvoid convertsStringToCurrency() {\n\t\tassertConverts(\"JPY\", Currency.class, Currency.getInstance(\"JPY\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToLocale() {\n\t\tassertConverts(\"en\", Locale.class, Locale.ENGLISH);\n\t\tassertConverts(\"en-US\", Locale.class, Locale.US);\n\t}\n\n\t@Test\n\tvoid convertsStringToUUID() {\n\t\tvar uuid = \"d043e930-7b3b-48e3-bdbe-5a3ccfb833db\";\n\t\tassertConverts(uuid, UUID.class, UUID.fromString(uuid));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate void assertConverts(@Nullable String input, Class<?> targetClass, @Nullable Object expectedOutput) {\n\t\tvar result = convert(input, targetClass);\n\n\t\tassertThat(result) //\n\t\t\t\t.describedAs(input + \" --(\" + targetClass.getName() + \")--> \" + expectedOutput) //\n\t\t\t\t.isEqualTo(expectedOutput);\n\t}\n\n\tprivate @Nullable Object convert(@Nullable String input, Class<?> targetClass) {\n\t\treturn convert(input, targetClass, classLoader());\n\t}\n\n\tprivate @Nullable Object convert(@Nullable String input, Class<?> targetClass, ClassLoader classLoader) {\n\t\treturn ConversionSupport.convert(input, targetClass, classLoader);\n\t}\n\n\tprivate static ClassLoader classLoader() {\n\t\tMethod declaringExecutable = ReflectionSupport.findMethod(ConversionSupportTests.class, \"foo\").orElseThrow();\n\t\treturn classLoader(declaringExecutable);\n\t}\n\n\tprivate static ClassLoader classLoader(Method declaringExecutable) {\n\t\treturn ClassLoaderUtils.getClassLoader(declaringExecutable.getDeclaringClass());\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static void foo() {\n\t}\n\n\tprivate static class Enigma {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid foo() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/support/conversion/FallbackStringToObjectConverterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.support.conversion;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethod;\nimport static org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.DeprecationStatus.EXCLUDE_DEPRECATED;\nimport static org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.DeprecationStatus.INCLUDE_DEPRECATED;\nimport static org.junit.platform.commons.util.ReflectionUtils.getDeclaredConstructor;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Method;\nimport java.util.Objects;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.IsFactoryConstructor;\nimport org.junit.platform.commons.support.conversion.FallbackStringToObjectConverter.IsFactoryMethod;\nimport org.junit.platform.commons.util.ReflectionUtils;\n\n/**\n * Unit tests for {@link FallbackStringToObjectConverter}, {@link IsFactoryMethod},\n * and {@link IsFactoryConstructor}.\n *\n * @since 1.11 (originally since JUnit Jupiter 5.1)\n */\nclass FallbackStringToObjectConverterTests {\n\n\tprivate static final IsFactoryMethod isBookFactoryMethod = new IsFactoryMethod(Book.class, String.class,\n\t\tINCLUDE_DEPRECATED);\n\n\tprivate static final FallbackStringToObjectConverter converter = new FallbackStringToObjectConverter();\n\n\t@Test\n\tvoid isNotFactoryMethodForWrongParameterType() {\n\t\tassertThat(isBookFactoryMethod).rejects(bookMethod(\"factory\", Object.class));\n\t\tassertThat(isBookFactoryMethod).rejects(bookMethod(\"factory\", Number.class));\n\t\tassertThat(isBookFactoryMethod).rejects(bookMethod(\"factory\", StringBuilder.class));\n\t\tassertThat(new IsFactoryMethod(Record2.class, String.class, INCLUDE_DEPRECATED)).rejects(record2Method(\"from\"));\n\t\tassertThat(new IsFactoryMethod(Record2.class, String.class, EXCLUDE_DEPRECATED)).rejects(record2Method(\"from\"));\n\t}\n\n\t@Test\n\tvoid isNotFactoryMethodForPrivateMethod() {\n\t\tassertThat(isBookFactoryMethod).rejects(bookMethod(\"privateFactory\"));\n\t}\n\n\t@Test\n\tvoid isNotFactoryMethodForNonStaticMethod() {\n\t\tassertThat(isBookFactoryMethod).rejects(bookMethod(\"nonStaticFactory\"));\n\t}\n\n\t@Test\n\tvoid isFactoryMethodForValidMethodsNoDeprecated() {\n\t\tassertThat(new IsFactoryMethod(Book.class, String.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookMethod(\"factory\", String.class));\n\t\tassertThat(new IsFactoryMethod(Book.class, String.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookMethod(\"factory\", String.class));\n\n\t\tassertThat(new IsFactoryMethod(Book.class, CharSequence.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookMethod(\"factory\", CharSequence.class));\n\t\tassertThat(new IsFactoryMethod(Book.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookMethod(\"factory\", CharSequence.class));\n\n\t\tassertThat(new IsFactoryMethod(Newspaper.class, String.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(newspaperMethod(\"from\"), newspaperMethod(\"of\"));\n\t\tassertThat(new IsFactoryMethod(Newspaper.class, String.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(newspaperMethod(\"from\"), newspaperMethod(\"of\"));\n\n\t\tassertThat(new IsFactoryMethod(Magazine.class, String.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(magazineMethod(\"from\"), magazineMethod(\"of\"));\n\t\tassertThat(new IsFactoryMethod(Magazine.class, String.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(magazineMethod(\"from\"), magazineMethod(\"of\"));\n\n\t\tassertThat(new IsFactoryMethod(Record2.class, CharSequence.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(record2Method(\"from\"));\n\t\tassertThat(new IsFactoryMethod(Record2.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(record2Method(\"from\"));\n\t}\n\n\t@Test\n\tvoid isFactoryMethodForValidMethodsWithDeprecated() {\n\t\tassertThat(new IsFactoryMethod(Book2.class, String.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factory\", String.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, String.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factory\", String.class));\n\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factory\", CharSequence.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factory\", CharSequence.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.rejects(bookWithDeprecatedMethod(\"factory\", StringBuilder.class));\n\n\t\tassertThat(new IsFactoryMethod(Book2.class, String.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factoryDeprecated\", String.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, String.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.rejects(bookWithDeprecatedMethod(\"factoryDeprecated\", String.class));\n\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, INCLUDE_DEPRECATED))//\n\t\t\t\t.accepts(bookWithDeprecatedMethod(\"factoryDeprecated\", CharSequence.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.rejects(bookWithDeprecatedMethod(\"factoryDeprecated\", CharSequence.class));\n\t\tassertThat(new IsFactoryMethod(Book2.class, CharSequence.class, EXCLUDE_DEPRECATED))//\n\t\t\t\t.rejects(bookWithDeprecatedMethod(\"factoryDeprecated\", CharSequence.class));\n\t}\n\n\t@Test\n\tvoid isNotFactoryConstructorForPrivateConstructor() {\n\t\tassertThat(new IsFactoryConstructor(Magazine.class, String.class)).rejects(constructor(Magazine.class));\n\t}\n\n\t@Test\n\tvoid isNotFactoryConstructorForWrongParameterType() {\n\t\tassertThat(new IsFactoryConstructor(Record1.class, String.class))//\n\t\t\t\t.rejects(getDeclaredConstructor(Record1.class));\n\t\tassertThat(new IsFactoryConstructor(Record2.class, String.class))//\n\t\t\t\t.rejects(getDeclaredConstructor(Record2.class));\n\t\tassertThat(new IsFactoryConstructor(Record3.class, String.class))//\n\t\t\t\t.rejects(getDeclaredConstructor(Record3.class));\n\t}\n\n\t@Test\n\tvoid isFactoryConstructorForValidConstructors() {\n\t\tassertThat(new IsFactoryConstructor(Book.class, String.class))//\n\t\t\t\t.accepts(constructor(Book.class));\n\t\tassertThat(new IsFactoryConstructor(Journal.class, String.class))//\n\t\t\t\t.accepts(constructor(Journal.class));\n\t\tassertThat(new IsFactoryConstructor(Newspaper.class, String.class))//\n\t\t\t\t.accepts(constructor(Newspaper.class));\n\t\tassertThat(new IsFactoryConstructor(Record1.class, CharSequence.class))//\n\t\t\t\t.accepts(getDeclaredConstructor(Record1.class));\n\t\tassertThat(new IsFactoryConstructor(Record2.class, CharSequence.class))//\n\t\t\t\t.accepts(getDeclaredConstructor(Record2.class));\n\t}\n\n\t@Test\n\tvoid convertsStringToBookViaStaticFactoryMethod() throws Exception {\n\t\tassertConverts(\"enigma\", Book.class, new Book(\"factory(String): enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToBookWithDeprecatedViaConstructor() throws Exception {\n\t\t// constructor takes precedence over factory method when there are two factory methods, and one is deprecated\n\t\tassertConverts(\"enigma\", Book2.class, new Book2(\"enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToRecord2ViaStaticFactoryMethodAcceptingCharSequence() throws Exception {\n\t\tassertConvertsRecord2(\"enigma\", Record2.from(new StringBuffer(\"enigma\")));\n\t}\n\n\t@Test\n\tvoid convertsStringToJournalViaFactoryConstructor() throws Exception {\n\t\tassertConverts(\"enigma\", Journal.class, new Journal(\"enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsStringToRecord1ViaFactoryConstructorAcceptingCharSequence() throws Exception {\n\t\tassertConvertsRecord1(\"enigma\", new Record1(new StringBuffer(\"enigma\")));\n\t}\n\n\t@Test\n\tvoid convertsStringToNewspaperViaConstructorIgnoringMultipleFactoryMethods() throws Exception {\n\t\tassertConverts(\"enigma\", Newspaper.class, new Newspaper(\"enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsDeprecatedToNewspaper() throws Exception {\n\t\t// when only one method @Deprecated is irrelevant, @Deprecated from(String) > @Deprecated from(CharSequence)\n\t\tassertConverts(\"enigma\", Newspaper1.class, new Newspaper1(\"from(String): enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsToNewspaperPreferNonDeprecatedToDeprecated() throws Exception {\n\t\t// when two String factories: parse(String) > @Deprecated from(String)\n\t\tassertConverts(\"enigma\", Newspaper2.class, new Newspaper2(\"parse(String): enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsToNewspaperPreferOnlyCharSequenceToNonDeprecatedString() throws Exception {\n\t\t// when two String and one CharSequence factories: parse(CharSequence) > parse(String)/@Deprecated from(String)\n\t\tassertConverts(\"enigma\", Newspaper3.class, new Newspaper3(\"parse(CharSequence): enigma\"));\n\t}\n\n\t@Test\n\tvoid convertsToNewspaperPreferOnlyCharSequenceToDeprecatedString() throws Exception {\n\t\t// when two String and two CharSequence factories: parse(CharSequence) > @Deprecated parse(String)/@Deprecated from(String)/@Deprecated from(CharSequence)\n\t\tassertConverts(\"enigma\", Newspaper4.class, new Newspaper4(\"parse(CharSequence): enigma\"));\n\t}\n\n\t@Test\n\t@DisplayName(\"Cannot convert String to Diary because Diary has neither a static factory method nor a factory constructor\")\n\tvoid cannotConvertStringToDiary() {\n\t\tassertThat(converter.canConvertTo(Diary.class)).isFalse();\n\t}\n\n\t@Test\n\t@DisplayName(\"Cannot convert String to Magazine because Magazine has multiple static factory methods\")\n\tvoid cannotConvertStringToMagazine() {\n\t\tassertThat(converter.canConvertTo(Magazine.class)).isFalse();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static Constructor<?> constructor(Class<?> clazz) {\n\t\treturn ReflectionUtils.findConstructors(clazz,\n\t\t\tctr -> ctr.getParameterCount() == 1 && ctr.getParameterTypes()[0] == String.class).getFirst();\n\t}\n\n\tprivate static Method bookMethod(String methodName) {\n\t\treturn bookMethod(methodName, String.class);\n\t}\n\n\tprivate static Method bookMethod(String methodName, Class<?> parameterType) {\n\t\treturn findMethod(Book.class, methodName, parameterType).orElseThrow();\n\t}\n\n\tprivate static Method bookWithDeprecatedMethod(String methodName, Class<?> parameterType) {\n\t\treturn findMethod(Book2.class, methodName, parameterType).orElseThrow();\n\t}\n\n\tprivate static Method newspaperMethod(String methodName) {\n\t\treturn findMethod(Newspaper.class, methodName, String.class).orElseThrow();\n\t}\n\n\tprivate static Method magazineMethod(String methodName) {\n\t\treturn findMethod(Magazine.class, methodName, String.class).orElseThrow();\n\t}\n\n\tprivate static Method record2Method(String methodName) {\n\t\treturn findMethod(Record2.class, methodName, CharSequence.class).orElseThrow();\n\t}\n\n\tprivate static void assertConverts(String input, Class<?> targetType, Object expectedOutput) throws Exception {\n\t\tassertCanConvertTo(targetType);\n\n\t\tvar result = converter.convert(input, targetType);\n\n\t\tassertThat(result) //\n\t\t\t\t.as(input + \" (\" + targetType.getSimpleName() + \") --> \" + expectedOutput) //\n\t\t\t\t.isEqualTo(expectedOutput);\n\t}\n\n\tprivate static void assertConvertsRecord1(String input, Record1 expected) throws Exception {\n\t\tClass<?> targetType = Record1.class;\n\t\tassertCanConvertTo(targetType);\n\n\t\tRecord1 result = (Record1) converter.convert(input, targetType);\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.title.toString()).isEqualTo(expected.title.toString());\n\t}\n\n\tprivate static void assertConvertsRecord2(String input, Record2 expected) throws Exception {\n\t\tClass<?> targetType = Record2.class;\n\t\tassertCanConvertTo(targetType);\n\n\t\tvar result = converter.convert(input, targetType);\n\n\t\tassertThat(result).isEqualTo(expected);\n\t}\n\n\tprivate static void assertCanConvertTo(Class<?> targetType) {\n\t\tassertThat(converter.canConvertTo(targetType)).as(\"canConvertTo(%s)\", targetType.getSimpleName()).isTrue();\n\t}\n\n\tstatic class Book {\n\n\t\tprivate final String title;\n\n\t\tBook(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t// static and non-private\n\t\tstatic Book factory(String title) {\n\t\t\treturn new Book(\"factory(String): \" + title);\n\t\t}\n\n\t\t/**\n\t\t * Static and non-private, but intentionally overloads {@link #factory(String)}\n\t\t * with a {@link CharSequence} argument to ensure that we don't introduce a\n\t\t * regression in 6.0, since the String-based factory method should take\n\t\t * precedence over a CharSequence-based factory method.\n\t\t */\n\t\tstatic Book factory(CharSequence title) {\n\t\t\treturn new Book(\"factory(CharSequence): \" + title);\n\t\t}\n\n\t\t// wrong parameter type\n\t\tstatic Book factory(Object obj) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t// wrong parameter type\n\t\tstatic Book factory(Number number) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t/**\n\t\t * Wrong parameter type, intentionally a subtype of {@link CharSequence}\n\t\t * other than {@link String}.\n\t\t */\n\t\tstatic Book factory(StringBuilder builder) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate static Book privateFactory(String title) {\n\t\t\treturn new Book(title);\n\t\t}\n\n\t\tBook nonStaticFactory(String title) {\n\t\t\treturn new Book(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Book that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Book [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Journal {\n\n\t\tprivate final String title;\n\n\t\tJournal(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t/**\n\t\t * Intentionally overloads {@link #Journal(String)} with a {@link CharSequence}\n\t\t * argument to ensure that we don't introduce a regression in 6.0, since the\n\t\t * String-based constructor should take precedence over a CharSequence-based\n\t\t * constructor.\n\t\t */\n\t\tJournal(CharSequence title) {\n\t\t\tthis(\"Journal(CharSequence): \" + title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Journal that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Journal [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Newspaper {\n\n\t\tprivate final String title;\n\n\t\tNewspaper(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\tstatic Newspaper from(String title) {\n\t\t\treturn new Newspaper(title);\n\t\t}\n\n\t\tstatic Newspaper of(String title) {\n\t\t\treturn new Newspaper(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Newspaper that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Newspaper [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Magazine {\n\n\t\tprivate Magazine(String title) {\n\t\t}\n\n\t\tstatic Magazine from(String title) {\n\t\t\treturn new Magazine(title);\n\t\t}\n\n\t\tstatic Magazine of(String title) {\n\t\t\treturn new Magazine(title);\n\t\t}\n\n\t}\n\n\trecord Record1(CharSequence title) {\n\t}\n\n\trecord Record2(CharSequence title) {\n\n\t\tstatic Record2 from(CharSequence title) {\n\t\t\treturn new Record2(\"Record2(CharSequence): \" + title);\n\t\t}\n\t}\n\n\trecord Record3(StringBuilder title) {\n\n\t\tstatic Record2 from(StringBuilder title) {\n\t\t\treturn new Record2(\"Record2(StringBuilder): \" + title);\n\t\t}\n\t}\n\n\tstatic class Diary {\n\t}\n\n\tstatic class Book2 {\n\n\t\tprivate final String title;\n\n\t\tBook2(String title) {\n\t\t\tthis.title = title;\n\t\t}\n\n\t\tstatic Book2 factory(String title) {\n\t\t\treturn new Book2(\"factory(String): \" + title);\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Book2 factoryDeprecated(String title) {\n\t\t\treturn new Book2(\"factoryDeprecated(String): \" + title);\n\t\t}\n\n\t\t/**\n\t\t * Static and non-private, but intentionally overloads {@link #factory(String)}\n\t\t * with a {@link CharSequence} argument to ensure that we don't introduce a\n\t\t * regression in 6.0, since the String-based factory method should take\n\t\t * precedence over a CharSequence-based factory method.\n\t\t */\n\t\tstatic Book2 factory(CharSequence title) {\n\t\t\treturn new Book2(\"factory(CharSequence): \" + title);\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Book2 factoryDeprecated(CharSequence title) {\n\t\t\treturn new Book2(\"factoryDeprecated(CharSequence): \" + title);\n\t\t}\n\n\t\t// wrong parameter type\n\t\tstatic Book2 factory(Object obj) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t// wrong parameter type\n\t\tstatic Book2 factory(Number number) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t/**\n\t\t * Wrong parameter type, intentionally a subtype of {@link CharSequence}\n\t\t * other than {@link String}.\n\t\t */\n\t\tstatic Book2 factory(StringBuilder builder) {\n\t\t\tthrow new UnsupportedOperationException();\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate static Book2 privateFactory(String title) {\n\t\t\treturn new Book2(title);\n\t\t}\n\n\t\tBook2 nonStaticFactory(String title) {\n\t\t\treturn new Book2(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Book2 that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Book2 [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Newspaper1 {\n\n\t\tprivate final String title;\n\n\t\tprivate Newspaper1(String title) {\n\t\t\t// constructor must be private for factory/deprecated logic to kick in\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t// only String factory, thus being deprecated is irrelevant\n\t\t@Deprecated\n\t\tstatic Newspaper1 from(String title) {\n\t\t\treturn new Newspaper1(\"from(String): \" + title);\n\t\t}\n\n\t\t// only CharSequence factory, thus being deprecated is irrelevant, but String version takes precedence\n\t\t@Deprecated\n\t\tstatic Newspaper1 from(CharSequence title) {\n\t\t\treturn new Newspaper1(\"from(CharSequence): \" + title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Newspaper1 that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Newspaper1 [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Newspaper2 {\n\n\t\tprivate final String title;\n\n\t\tprivate Newspaper2(String title) {\n\t\t\t// constructor must be private for factory/deprecated logic to kick in\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper2 from(String title) {\n\t\t\treturn new Newspaper2(\"from(String): \" + title);\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper2 other(String title) {\n\t\t\treturn new Newspaper2(\"parse(CharSequence): \" + title);\n\t\t}\n\n\t\t// String factory without deprecated has precedence over String factory with deprecated\n\t\tstatic Newspaper2 parse(String title) {\n\t\t\treturn new Newspaper2(\"parse(String): \" + title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Newspaper2 that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Newspaper2 [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Newspaper3 {\n\n\t\tprivate final String title;\n\n\t\tprivate Newspaper3(String title) {\n\t\t\t// constructor must be private for factory/deprecated logic to kick in\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper3 from(String title) {\n\t\t\treturn new Newspaper3(\"from(String): \" + title);\n\t\t}\n\n\t\tstatic Newspaper3 parse(String title) {\n\t\t\treturn new Newspaper3(\"parse(String): \" + title);\n\t\t}\n\n\t\t// CharSequence factory without deprecated alternative has precedence\n\t\t// over String factory with deprecated alternative\n\t\tstatic Newspaper3 parse(CharSequence title) {\n\t\t\treturn new Newspaper3(\"parse(CharSequence): \" + title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Newspaper3 that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Newspaper3 [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n\tstatic class Newspaper4 {\n\n\t\tprivate final String title;\n\n\t\tprivate Newspaper4(String title) {\n\t\t\t// constructor must be private for factory/deprecated logic to kick in\n\t\t\tthis.title = title;\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper4 from(String title) {\n\t\t\treturn new Newspaper4(\"from(String): \" + title);\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper4 parse(String title) {\n\t\t\treturn new Newspaper4(\"parse(String): \" + title);\n\t\t}\n\n\t\t@Deprecated\n\t\tstatic Newspaper4 from(CharSequence title) {\n\t\t\treturn new Newspaper4(\"from(CharSequence): \" + title);\n\t\t}\n\n\t\t// CharSequence factory with deprecated alternative has precedence\n\t\t// over deprecated String factory\n\t\tstatic Newspaper4 parse(CharSequence title) {\n\t\t\treturn new Newspaper4(\"parse(CharSequence): \" + title);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\treturn (this == obj) || (obj instanceof Newspaper4 that && Objects.equals(this.title, that.title));\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn Objects.hash(title);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"Newspaper4 [title=\" + this.title + \"]\";\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/AnnotationUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertIterableEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields;\nimport static org.junit.platform.commons.util.AnnotationUtils.findAnnotatedMethods;\nimport static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;\nimport static org.junit.platform.commons.util.AnnotationUtils.findPublicAnnotatedFields;\nimport static org.junit.platform.commons.util.AnnotationUtils.findRepeatableAnnotations;\nimport static org.junit.platform.commons.util.AnnotationUtils.isAnnotated;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Inherited;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.AnnotatedElement;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.function.Predicate;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.pkg1.ClassLevelDir;\nimport org.junit.platform.commons.util.pkg1.InstanceLevelDir;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField;\nimport org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateBeforeMethod;\nimport org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateTempDirField;\n\n/**\n * Unit tests for {@link AnnotationUtils}.\n *\n * @since 1.0\n */\nclass AnnotationUtilsTests {\n\n\tprivate static final Predicate<Field> isStringField = field -> field.getType() == String.class;\n\n\t@Test\n\tvoid findAnnotationForNullOptional() {\n\t\tassertThat(findAnnotation((Optional<AnnotatedElement>) null, Annotation1.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAnnotationForEmptyOptional() {\n\t\tassertThat(findAnnotation(Optional.empty(), Annotation1.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAnnotationForNullAnnotatedElement() {\n\t\tassertThat(findAnnotation((AnnotatedElement) null, Annotation1.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAnnotationOnClassWithoutAnnotation() {\n\t\tassertThat(findAnnotation(Annotation1Class.class, Annotation2.class)).isNotPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationIndirectlyPresentOnOptionalClass() {\n\t\tOptional<Class<?>> optional = Optional.of(SubInheritedAnnotationClass.class);\n\t\tassertThat(findAnnotation(optional, InheritedAnnotation.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationIndirectlyPresentOnClass() {\n\t\tassertThat(findAnnotation(SubInheritedAnnotationClass.class, InheritedAnnotation.class)).isPresent();\n\t}\n\n\t/**\n\t * Test for https://github.com/junit-team/junit-framework/issues/1133\n\t */\n\t@Test\n\tvoid findInheritedAnnotationMetaPresentOnNonInheritedComposedAnnotationPresentOnSuperclass() {\n\t\tassertThat(findAnnotation(SubNonInheritedCompositionOfInheritedAnnotationClass.class,\n\t\t\tInheritedAnnotation.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationDirectlyPresentOnClass() {\n\t\tassertThat(findAnnotation(Annotation1Class.class, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationMetaPresentOnClass() {\n\t\tassertThat(findAnnotation(ComposedAnnotationClass.class, Annotation1.class)).isPresent();\n\t}\n\n\t/**\n\t * <b>Note:</b> there is no findAnnotationIndirectlyMetaPresentOnMethod\n\t * counterpart because the {@code @Inherited} annotation has no effect if\n\t * the annotation type is used to annotate anything other than a class.\n\t *\n\t * @see Inherited\n\t */\n\t@Test\n\tvoid findAnnotationIndirectlyMetaPresentOnClass() {\n\t\tassertThat(findAnnotation(SubInheritedComposedAnnotationClass.class, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationDirectlyPresentOnImplementedInterface() {\n\t\tassertThat(findAnnotation(TestingTraitClass.class, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationMetaPresentOnImplementedInterface() {\n\t\tassertThat(findAnnotation(ComposedTestingTraitClass.class, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationDirectlyPresentOnMethod() throws Exception {\n\t\tvar method = Annotation2Class.class.getDeclaredMethod(\"method\");\n\t\tassertThat(findAnnotation(method, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationMetaPresentOnMethod() throws Exception {\n\t\tvar method = ComposedAnnotationClass.class.getDeclaredMethod(\"method\");\n\t\tassertThat(findAnnotation(method, Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationMetaPresentOnOptionalMethod() throws Exception {\n\t\tvar method = ComposedAnnotationClass.class.getDeclaredMethod(\"method\");\n\t\tassertThat(findAnnotation(Optional.of(method), Annotation1.class)).isPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationDirectlyPresentOnEnclosingClass() throws Exception {\n\t\tClass<?> clazz = Annotation1Class.InnerClass.class;\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, false)).isNotPresent();\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, true)).isPresent();\n\n\t\tclazz = Annotation1Class.InnerClass.InnerInnerClass.class;\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, false)).isNotPresent();\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, true)).isPresent();\n\n\t\tclazz = Annotation1Class.NestedClass.class;\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, false)).isNotPresent();\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, true)).isNotPresent();\n\t}\n\n\t@Test\n\tvoid findAnnotationMetaPresentOnEnclosingClass() throws Exception {\n\t\tClass<?> clazz = ComposedAnnotationClass.InnerClass.class;\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, false)).isNotPresent();\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, true)).isPresent();\n\n\t\tclazz = ComposedAnnotationClass.InnerClass.InnerInnerClass.class;\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, false)).isNotPresent();\n\t\tassertThat(findAnnotation(clazz, Annotation1.class, true)).isPresent();\n\t}\n\n\t@Test\n\tvoid isAnnotatedForClassWithoutAnnotation() {\n\t\tassertFalse(isAnnotated(Annotation1Class.class, Annotation2.class));\n\t}\n\n\t@Test\n\tvoid isAnnotatedWhenIndirectlyPresentOnClass() {\n\t\tassertTrue(isAnnotated(SubInheritedAnnotationClass.class, InheritedAnnotation.class));\n\t}\n\n\t@Test\n\tvoid isAnnotatedWhenDirectlyPresentOnClass() {\n\t\tassertTrue(isAnnotated(Annotation1Class.class, Annotation1.class));\n\t}\n\n\t@Test\n\tvoid isAnnotatedWhenMetaPresentOnClass() {\n\t\tassertTrue(isAnnotated(ComposedAnnotationClass.class, Annotation1.class));\n\t}\n\n\t@Test\n\tvoid isAnnotatedWhenDirectlyPresentOnMethod() throws Exception {\n\t\tassertTrue(isAnnotated(Annotation2Class.class.getDeclaredMethod(\"method\"), Annotation1.class));\n\t}\n\n\t@Test\n\tvoid isAnnotatedWhenMetaPresentOnMethod() throws Exception {\n\t\tassertTrue(isAnnotated(ComposedAnnotationClass.class.getDeclaredMethod(\"method\"), Annotation1.class));\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsForNotRepeatableAnnotation() {\n\t\tassertPreconditionViolationFor(() -> findRepeatableAnnotations(getClass(), Inherited.class))//\n\t\t\t\t.withMessage(Inherited.class.getName() + \" must be @Repeatable\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsForNullOptionalAnnotatedElement() {\n\t\tassertThat(findRepeatableAnnotations((Optional<AnnotatedElement>) null, Tag.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsForEmptyOptionalAnnotatedElement() {\n\t\tassertThat(findRepeatableAnnotations(Optional.empty(), Tag.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsForNullAnnotatedElement() {\n\t\tassertThat(findRepeatableAnnotations((AnnotatedElement) null, Tag.class)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithSingleTag() {\n\t\tassertTagsFound(SingleTaggedClass.class, \"a\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithSingleComposedTag() {\n\t\tassertTagsFound(SingleComposedTaggedClass.class, \"fast\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithSingleComposedTagOnImplementedInterface() {\n\t\tassertTagsFound(TaggedInterfaceClass.class, \"fast\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithLocalComposedTagAndComposedTagOnImplementedInterface() {\n\t\tassertTagsFound(LocalTagOnTaggedInterfaceClass.class, \"fast\", \"smoke\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithMultipleTags() {\n\t\tassertTagsFound(MultiTaggedClass.class, \"a\", \"b\", \"c\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithMultipleComposedTags() {\n\t\tassertTagsFound(MultiComposedTaggedClass.class, \"fast\", \"smoke\");\n\t\tassertTagsFound(FastAndSmokyTaggedClass.class, \"fast\", \"smoke\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithContainer() {\n\t\tassertTagsFound(ContainerTaggedClass.class, \"a\", \"b\", \"c\", \"d\");\n\t}\n\n\t@Test\n\tvoid findRepeatableAnnotationsWithComposedTagBeforeContainer() {\n\t\tassertTagsFound(ContainerAfterComposedTaggedClass.class, \"fast\", \"a\", \"b\", \"c\");\n\t}\n\n\tprivate void assertTagsFound(Class<?> clazz, String... tags) {\n\t\tassertEquals(List.of(tags), findRepeatableAnnotations(clazz, Tag.class).stream().map(Tag::value).toList(),\n\t\t\t() -> \"Tags found for class \" + clazz.getName());\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithSingleAnnotationOnSuperclass() {\n\t\tassertExtensionsFound(SingleExtensionClass.class, \"a\");\n\t\tassertExtensionsFound(SubSingleExtensionClass.class, \"a\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithMultipleAnnotationsOnSuperclass() {\n\t\tassertExtensionsFound(MultiExtensionClass.class, \"a\", \"b\", \"c\");\n\t\tassertExtensionsFound(SubMultiExtensionClass.class, \"a\", \"b\", \"c\", \"x\", \"y\", \"z\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithContainerAnnotationOnSuperclass() {\n\t\tassertExtensionsFound(ContainerExtensionClass.class, \"a\", \"b\", \"c\");\n\t\tassertExtensionsFound(SubContainerExtensionClass.class, \"a\", \"b\", \"c\", \"x\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithSingleComposedAnnotation() {\n\t\tassertExtensionsFound(SingleComposedExtensionClass.class, \"foo\");\n\t}\n\n\t/**\n\t * @since 1.5\n\t */\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithComposedAnnotationsInNestedContainer() {\n\t\tassertExtensionsFound(MultipleFoos1.class, \"foo\");\n\t\tassertExtensionsFound(MultipleFoos2.class, \"foo\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithSingleComposedAnnotationOnSuperclass() {\n\t\tassertExtensionsFound(SubSingleComposedExtensionClass.class, \"foo\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithMultipleComposedAnnotations() {\n\t\tassertExtensionsFound(MultiComposedExtensionClass.class, \"foo\", \"bar\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithMultipleComposedAnnotationsOnSuperclass() {\n\t\tassertExtensionsFound(SubMultiComposedExtensionClass.class, \"foo\", \"bar\");\n\t}\n\n\t@Test\n\tvoid findInheritedRepeatableAnnotationsWithMultipleComposedAnnotationsOnSuperclassAndLocalContainerAndComposed() {\n\t\tassertExtensionsFound(ContainerPlusSubMultiComposedExtensionClass.class, \"foo\", \"bar\", \"x\", \"y\", \"z\");\n\t}\n\n\tprivate void assertExtensionsFound(Class<?> clazz, String... tags) {\n\t\tassertEquals(List.of(tags),\n\t\t\tfindRepeatableAnnotations(clazz, ExtendWith.class).stream().map(ExtendWith::value).toList(),\n\t\t\t() -> \"Extensions found for class \" + clazz.getName());\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedMethodsForNullClass() {\n\t\tassertPreconditionViolationFor(() -> findAnnotatedMethods(null, Annotation1.class, TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedMethodsForNullAnnotationType() {\n\t\tassertPreconditionViolationFor(() -> findAnnotatedMethods(ClassWithAnnotatedMethods.class, null, TOP_DOWN));\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsForAnnotationThatIsNotPresent() {\n\t\tassertThat(findAnnotatedMethods(ClassWithAnnotatedMethods.class, Fast.class, TOP_DOWN)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsForAnnotationOnMethodsInClassUsingHierarchyDownMode() throws Exception {\n\t\tvar method2 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method2\");\n\t\tvar method3 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method3\");\n\n\t\tvar methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation2.class, TOP_DOWN);\n\n\t\tassertThat(methods).containsOnly(method2, method3);\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsForAnnotationOnMethodsInClassHierarchyUsingHierarchyUpMode() throws Exception {\n\t\tvar method1 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method1\");\n\t\tvar method3 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method3\");\n\t\tvar superMethod = SuperclassWithAnnotatedMethod.class.getDeclaredMethod(\"superMethod\");\n\n\t\tvar methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation1.class, BOTTOM_UP);\n\n\t\tassertEquals(3, methods.size());\n\t\tassertThat(methods.subList(0, 2)).containsOnly(method1, method3);\n\t\tassertEquals(superMethod, methods.get(2));\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsForAnnotationUsedInClassAndSuperclassHierarchyDown() throws Exception {\n\t\tvar method1 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method1\");\n\t\tvar method3 = ClassWithAnnotatedMethods.class.getDeclaredMethod(\"method3\");\n\t\tvar superMethod = SuperclassWithAnnotatedMethod.class.getDeclaredMethod(\"superMethod\");\n\n\t\tvar methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation1.class, TOP_DOWN);\n\n\t\tassertEquals(3, methods.size());\n\t\tassertEquals(superMethod, methods.getFirst());\n\t\tassertThat(methods.subList(1, 3)).containsOnly(method1, method3);\n\t}\n\n\t/*\n\t * see https://github.com/junit-team/junit-framework/issues/3553\n\t */\n\t@Test\n\tvoid findAnnotatedMethodsDoesNotAllowInstanceMethodToHideStaticMethod() throws Exception {\n\t\tfinal String BEFORE = \"before\";\n\t\tClass<?> superclass = SuperclassWithStaticPackagePrivateBeforeMethod.class;\n\t\tMethod beforeAllMethod = superclass.getDeclaredMethod(BEFORE);\n\t\tClass<?> subclass = SubclassWithNonStaticPackagePrivateBeforeMethod.class;\n\t\tMethod beforeEachMethod = subclass.getDeclaredMethod(BEFORE);\n\n\t\t// Prerequisite\n\t\tvar methods = findAnnotatedMethods(superclass, BeforeAll.class, TOP_DOWN);\n\t\tassertThat(methods).containsExactly(beforeAllMethod);\n\n\t\t// Actual use cases for this test\n\t\tmethods = findAnnotatedMethods(subclass, BeforeAll.class, TOP_DOWN);\n\t\tassertThat(methods).containsExactly(beforeAllMethod);\n\t\tmethods = findAnnotatedMethods(subclass, BeforeEach.class, TOP_DOWN);\n\t\tassertThat(methods).containsExactly(beforeEachMethod);\n\t}\n\n\t@Test\n\tvoid findAnnotatedMethodsForAnnotationUsedInInterface() throws Exception {\n\t\tvar interfaceMethod = InterfaceWithAnnotatedDefaultMethod.class.getDeclaredMethod(\"interfaceMethod\");\n\n\t\tvar methods = findAnnotatedMethods(ClassWithAnnotatedMethods.class, Annotation3.class, BOTTOM_UP);\n\n\t\tassertThat(methods).containsExactly(interfaceMethod);\n\t}\n\n\t// === findAnnotatedFields() ===============================================\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedFieldsForNullClass() {\n\t\tassertPreconditionViolationFor(() -> findAnnotatedFields(null, Annotation1.class, isStringField, TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedFieldsForNullAnnotationType() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> findAnnotatedFields(ClassWithAnnotatedFields.class, null, isStringField, TOP_DOWN));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAnnotatedFieldsForNullPredicate() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> findAnnotatedFields(ClassWithAnnotatedFields.class, Annotation1.class, null, TOP_DOWN));\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsForAnnotationThatIsNotPresent() {\n\t\tassertThat(findAnnotatedFields(ClassWithAnnotatedFields.class, Fast.class, isStringField, TOP_DOWN)).isEmpty();\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsForAnnotationOnFieldsInClassUsingHierarchyDownMode() throws Exception {\n\t\tvar field2 = ClassWithAnnotatedFields.class.getDeclaredField(\"field2\");\n\t\tvar field3 = ClassWithAnnotatedFields.class.getDeclaredField(\"field3\");\n\n\t\tvar fields = findAnnotatedFields(ClassWithAnnotatedFields.class, Annotation2.class, isStringField, TOP_DOWN);\n\n\t\tassertThat(fields).containsOnly(field2, field3);\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsForAnnotationOnFieldsInClassHierarchyUsingHierarchyUpMode() throws Exception {\n\t\tvar field1 = ClassWithAnnotatedFields.class.getDeclaredField(\"field1\");\n\t\tvar field3 = ClassWithAnnotatedFields.class.getDeclaredField(\"field3\");\n\t\tvar superField = SuperclassWithAnnotatedField.class.getDeclaredField(\"superField\");\n\n\t\tvar fields = findAnnotatedFields(ClassWithAnnotatedFields.class, Annotation1.class, isStringField, BOTTOM_UP);\n\n\t\tassertEquals(3, fields.size());\n\t\tassertThat(fields.subList(0, 2)).containsOnly(field1, field3);\n\t\tassertEquals(superField, fields.get(2));\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsForAnnotationUsedInClassAndSuperclassHierarchyDown() throws Exception {\n\t\tvar field1 = ClassWithAnnotatedFields.class.getDeclaredField(\"field1\");\n\t\tvar field3 = ClassWithAnnotatedFields.class.getDeclaredField(\"field3\");\n\t\tvar superField = SuperclassWithAnnotatedField.class.getDeclaredField(\"superField\");\n\n\t\tvar fields = findAnnotatedFields(ClassWithAnnotatedFields.class, Annotation1.class, isStringField, TOP_DOWN);\n\n\t\tassertEquals(3, fields.size());\n\t\tassertEquals(superField, fields.getFirst());\n\t\tassertThat(fields.subList(1, 3)).containsOnly(field1, field3);\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsForAnnotationUsedInInterface() throws Exception {\n\t\tvar interfaceField = InterfaceWithAnnotatedField.class.getDeclaredField(\"interfaceField\");\n\n\t\tvar fields = findAnnotatedFields(ClassWithAnnotatedFields.class, Annotation3.class, isStringField, BOTTOM_UP);\n\n\t\tassertThat(fields).containsExactly(interfaceField);\n\t}\n\n\t@Test\n\tvoid findAnnotatedFieldsFindsAllFieldsInTypeHierarchy() {\n\t\tassertThat(findShadowingAnnotatedFields(Annotation1.class))//\n\t\t\t\t.containsExactly(\"super\", \"foo\", \"baz\", \"super-shadow\", \"foo-shadow\", \"baz-shadow\");\n\t\tassertThat(findShadowingAnnotatedFields(Annotation2.class))//\n\t\t\t\t.containsExactly(\"bar\", \"baz\", \"bar-shadow\", \"baz-shadow\");\n\t\tassertThat(findShadowingAnnotatedFields(Annotation3.class))//\n\t\t\t\t.containsExactly(\"interface\", \"interface-shadow\");\n\t}\n\n\tprivate List<String> findShadowingAnnotatedFields(Class<? extends Annotation> annotationType) {\n\t\tvar fields = findAnnotatedFields(ClassWithShadowedAnnotatedFields.class, annotationType, isStringField);\n\t\tvar values = ReflectionUtils.readFieldValues(fields, new ClassWithShadowedAnnotatedFields());\n\t\treturn values.stream().map(String::valueOf).toList();\n\t}\n\n\t/*\n\t * see https://github.com/junit-team/junit-framework/issues/3553\n\t */\n\t@Test\n\tvoid findAnnotatedFieldsDoesNotAllowInstanceFieldToHideStaticField() throws Exception {\n\t\tfinal String TEMP_DIR = \"tempDir\";\n\t\tClass<?> superclass = SuperclassWithStaticPackagePrivateTempDirField.class;\n\t\tField staticField = superclass.getDeclaredField(TEMP_DIR);\n\t\tClass<?> subclass = SubclassWithNonStaticPackagePrivateTempDirField.class;\n\t\tField nonStaticField = subclass.getDeclaredField(TEMP_DIR);\n\n\t\t// Prerequisite\n\t\tvar fields = findAnnotatedFields(superclass, ClassLevelDir.class, field -> true);\n\t\tassertThat(fields).containsExactly(staticField);\n\n\t\t// Actual use cases for this test\n\t\tfields = findAnnotatedFields(subclass, ClassLevelDir.class, field -> true);\n\t\tassertThat(fields).containsExactly(staticField);\n\t\tfields = findAnnotatedFields(subclass, InstanceLevelDir.class, field -> true);\n\t\tassertThat(fields).containsExactly(nonStaticField);\n\t}\n\n\t// === findPublicAnnotatedFields() =========================================\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findPublicAnnotatedFieldsForNullClass() {\n\t\tassertPreconditionViolationFor(() -> findPublicAnnotatedFields(null, String.class, Annotation1.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findPublicAnnotatedFieldsForNullFieldType() {\n\t\tassertPreconditionViolationFor(() -> findPublicAnnotatedFields(getClass(), null, Annotation1.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findPublicAnnotatedFieldsForNullAnnotationType() {\n\t\tassertPreconditionViolationFor(() -> findPublicAnnotatedFields(getClass(), String.class, null));\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForPrivateField() {\n\t\tvar fields = findPublicAnnotatedFields(getClass(), Boolean.class, Annotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertEquals(0, fields.size());\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForDirectlyAnnotatedFieldOfWrongFieldType() {\n\t\tvar fields = findPublicAnnotatedFields(getClass(), BigDecimal.class, Annotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertEquals(0, fields.size());\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForDirectlyAnnotatedField() {\n\t\tvar fields = findPublicAnnotatedFields(getClass(), String.class, Annotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertIterableEquals(List.of(\"directlyAnnotatedField\"), asNames(fields));\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForMetaAnnotatedField() {\n\t\tvar fields = findPublicAnnotatedFields(getClass(), Number.class, Annotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertEquals(1, fields.size());\n\t\tassertIterableEquals(List.of(\"metaAnnotatedField\"), asNames(fields));\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForDirectlyAnnotatedFieldInInterface() {\n\t\tvar fields = findPublicAnnotatedFields(InterfaceWithAnnotatedFields.class, String.class, Annotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertIterableEquals(List.of(\"foo\"), asNames(fields));\n\t}\n\n\t@Test\n\tvoid findPublicAnnotatedFieldsForDirectlyAnnotatedFieldsInClassAndInterface() {\n\t\tvar fields = findPublicAnnotatedFields(ClassWithPublicAnnotatedFieldsFromInterface.class, String.class,\n\t\t\tAnnotation1.class);\n\t\tassertNotNull(fields);\n\t\tassertThat(asNames(fields)).containsExactlyInAnyOrder(\"foo\", \"bar\");\n\t}\n\n\tprivate List<String> asNames(List<Field> fields) {\n\t\treturn fields.stream().map(Field::getName).toList();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface AnnotationWithDefaultValue {\n\n\t\tString value() default \"default\";\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface Annotation1 {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface Annotation2 {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface Annotation3 {\n\t}\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@interface InheritedAnnotation {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Annotation1\n\t@interface ComposedAnnotation {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Annotation1\n\t@Inherited\n\t@interface InheritedComposedAnnotation {\n\t}\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@InheritedAnnotation\n\t// DO NOT make this @Inherited.\n\t@interface NonInheritedCompositionOfInheritedAnnotation {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t// DO NOT make this @Inherited.\n\t@interface Tags {\n\n\t\tTag[] value();\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Repeatable(Tags.class)\n\t// DO NOT make this @Inherited.\n\t@interface Tag {\n\n\t\tString value();\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Tag(\"fast\")\n\t@interface Fast {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Tag(\"smoke\")\n\t@interface Smoke {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Fast\n\t@Smoke\n\t@interface FastAndSmoky {\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@interface Extensions {\n\n\t\tExtendWith[] value();\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@Repeatable(Extensions.class)\n\t@interface ExtendWith {\n\n\t\tString value();\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@ExtendWith(\"foo\")\n\t@Repeatable(FooExtensions.class)\n\t@interface ExtendWithFoo {\n\n\t\tString info() default \"\";\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Inherited\n\t@interface FooExtensions {\n\n\t\tExtendWithFoo[] value();\n\t}\n\n\t@Target({ ElementType.TYPE, ElementType.METHOD })\n\t@Retention(RetentionPolicy.RUNTIME)\n\t// Intentionally NOT @Inherited in order to ensure that the algorithm for\n\t// findRepeatableAnnotations() in fact lives up to the claims in the\n\t// Javadoc regarding searching for repeatable annotations with implicit\n\t// \"inheritance\" if the repeatable annotation is @Inherited but the\n\t// custom composed annotation is not @Inherited.\n\t// @Inherited\n\t@ExtendWith(\"bar\")\n\t@interface ExtendWithBar {\n\t}\n\n\t@AnnotationWithDefaultValue\n\tstatic class AnnotationWithDefaultValueClass {\n\t}\n\n\t@Annotation1\n\tstatic class Annotation1Class {\n\t\tclass InnerClass {\n\t\t\tclass InnerInnerClass {\n\t\t\t}\n\t\t}\n\t\tstatic class NestedClass {\n\t\t}\n\t}\n\n\t@Annotation2\n\tstatic class Annotation2Class {\n\n\t\t@Annotation1\n\t\tvoid method() {\n\t\t}\n\t}\n\n\t@InheritedAnnotation\n\tstatic class InheritedAnnotationClass {\n\t}\n\n\tstatic class SubInheritedAnnotationClass extends InheritedAnnotationClass {\n\t}\n\n\t@ComposedAnnotation\n\tstatic class ComposedAnnotationClass {\n\n\t\t@ComposedAnnotation\n\t\tvoid method() {\n\t\t}\n\n\t\tclass InnerClass {\n\t\t\tclass InnerInnerClass {\n\t\t\t}\n\t\t}\n\t}\n\n\t@InheritedComposedAnnotation\n\tstatic class InheritedComposedAnnotationClass {\n\n\t\t@InheritedComposedAnnotation\n\t\tvoid method() {\n\t\t}\n\t}\n\n\tstatic class SubInheritedComposedAnnotationClass extends InheritedComposedAnnotationClass {\n\t}\n\n\t@NonInheritedCompositionOfInheritedAnnotation\n\tstatic class NonInheritedCompositionOfInheritedAnnotationClass {\n\t}\n\n\tstatic class SubNonInheritedCompositionOfInheritedAnnotationClass\n\t\t\textends NonInheritedCompositionOfInheritedAnnotationClass {\n\t}\n\n\t@Annotation1\n\tinterface TestingTrait {\n\t}\n\n\tstatic class TestingTraitClass implements TestingTrait {\n\t}\n\n\t@ComposedAnnotation\n\tinterface ComposedTestingTrait {\n\t}\n\n\tstatic class ComposedTestingTraitClass implements ComposedTestingTrait {\n\t}\n\n\t@Tag(\"a\")\n\tstatic class SingleTaggedClass {\n\t}\n\n\t@Fast\n\tstatic class SingleComposedTaggedClass {\n\t}\n\n\t@Tag(\"a\")\n\t@Tag(\"b\")\n\t@Tag(\"c\")\n\tstatic class MultiTaggedClass {\n\t}\n\n\t@Fast\n\t@Smoke\n\tstatic class MultiComposedTaggedClass {\n\t}\n\n\t@FastAndSmoky\n\tstatic class FastAndSmokyTaggedClass {\n\t}\n\n\t@Fast\n\tinterface TaggedInterface {\n\t}\n\n\tstatic class TaggedInterfaceClass implements TaggedInterface {\n\t}\n\n\t@Smoke\n\tstatic class LocalTagOnTaggedInterfaceClass implements TaggedInterface {\n\t}\n\n\t@Tags({ @Tag(\"a\"), @Tag(\"b\"), @Tag(\"c\") })\n\t@Tag(\"d\")\n\tstatic class ContainerTaggedClass {\n\t}\n\n\t@Fast\n\t@Tags({ @Tag(\"a\"), @Tag(\"b\"), @Tag(\"c\") })\n\tstatic class ContainerAfterComposedTaggedClass {\n\t}\n\n\t@ExtendWith(\"a\")\n\tstatic class SingleExtensionClass {\n\t}\n\n\tstatic class SubSingleExtensionClass extends SingleExtensionClass {\n\t}\n\n\t@ExtendWith(\"a\")\n\t@ExtendWith(\"b\")\n\t@ExtendWith(\"c\")\n\tstatic class MultiExtensionClass {\n\t}\n\n\t@ExtendWith(\"x\")\n\t@ExtendWith(\"y\")\n\t@ExtendWith(\"b\") // duplicates parent\n\t@ExtendWith(\"z\")\n\t@ExtendWith(\"a\") // duplicates parent\n\tstatic class SubMultiExtensionClass extends MultiExtensionClass {\n\t}\n\n\t@Extensions({ @ExtendWith(\"a\"), @ExtendWith(\"b\"), @ExtendWith(\"c\"), @ExtendWith(\"a\") })\n\tstatic class ContainerExtensionClass {\n\t}\n\n\t@ExtendWith(\"x\")\n\tstatic class SubContainerExtensionClass extends ContainerExtensionClass {\n\t}\n\n\t@ExtendWithFoo\n\tstatic class SingleComposedExtensionClass {\n\t}\n\n\t@ExtendWithFoo(info = \"A\")\n\t@ExtendWithFoo(info = \"B\")\n\tstatic class MultipleFoos1 {\n\t}\n\n\t@FooExtensions({ @ExtendWithFoo(info = \"A\"), @ExtendWithFoo(info = \"B\") })\n\tstatic class MultipleFoos2 {\n\t}\n\n\tstatic class SubSingleComposedExtensionClass extends SingleComposedExtensionClass {\n\t}\n\n\t@ExtendWithFoo\n\t@ExtendWithBar\n\tstatic class MultiComposedExtensionClass {\n\t}\n\n\tstatic class SubMultiComposedExtensionClass extends MultiComposedExtensionClass {\n\t}\n\n\t@ExtendWith(\"x\")\n\t@Extensions({ @ExtendWith(\"y\"), @ExtendWith(\"z\") })\n\t@ExtendWithBar\n\tstatic class ContainerPlusSubMultiComposedExtensionClass extends MultiComposedExtensionClass {\n\t}\n\n\tinterface InterfaceWithAnnotatedDefaultMethod {\n\n\t\t@Annotation3\n\t\tdefault void interfaceMethod() {\n\t\t}\n\t}\n\n\tstatic class SuperclassWithAnnotatedMethod {\n\n\t\t@Annotation1\n\t\tvoid superMethod() {\n\t\t}\n\t}\n\n\tstatic class ClassWithAnnotatedMethods extends SuperclassWithAnnotatedMethod\n\t\t\timplements InterfaceWithAnnotatedDefaultMethod {\n\n\t\t@Annotation1\n\t\tvoid method1() {\n\t\t}\n\n\t\t@Annotation2\n\t\tvoid method2() {\n\t\t}\n\n\t\t@Annotation1\n\t\t@Annotation2\n\t\tvoid method3() {\n\t\t}\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tinterface InterfaceWithAnnotatedField {\n\n\t\t@Annotation3\n\t\tString interfaceField = \"interface\";\n\t}\n\n\tstatic class SuperclassWithAnnotatedField {\n\n\t\t@Annotation1\n\t\tString superField = \"super\";\n\t}\n\n\tstatic class ClassWithAnnotatedFields extends SuperclassWithAnnotatedField implements InterfaceWithAnnotatedField {\n\n\t\t@Annotation1\n\t\tprotected Object field0 = \"?\";\n\n\t\t@Annotation1\n\t\tprotected String field1 = \"foo\";\n\n\t\t@Annotation2\n\t\tprotected String field2 = \"bar\";\n\n\t\t@Annotation1\n\t\t@Annotation2\n\t\tprotected String field3 = \"baz\";\n\n\t}\n\n\tstatic class ClassWithShadowedAnnotatedFields extends ClassWithAnnotatedFields {\n\n\t\t@Annotation3\n\t\tString interfaceField = \"interface-shadow\";\n\n\t\t@Annotation1\n\t\tString superField = \"super-shadow\";\n\n\t\t@Annotation1\n\t\tprotected String field1 = \"foo-shadow\";\n\n\t\t@Annotation2\n\t\tprotected String field2 = \"bar-shadow\";\n\n\t\t@Annotation1\n\t\t@Annotation2\n\t\tprotected String field3 = \"baz-shadow\";\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Annotation1\n\tprivate @Nullable Boolean privateDirectlyAnnotatedField;\n\n\t@Annotation1\n\tpublic @Nullable String directlyAnnotatedField;\n\n\t@ComposedAnnotation\n\tpublic @Nullable Integer metaAnnotatedField;\n\n\tinterface InterfaceWithAnnotatedFields {\n\n\t\t@Annotation1\n\t\tString foo = \"bar\";\n\n\t\t@Annotation1\n\t\tboolean wrongType = false;\n\t}\n\n\tclass ClassWithPublicAnnotatedFieldsFromInterface implements InterfaceWithAnnotatedFields {\n\n\t\t@Annotation1\n\t\tpublic String bar = \"baz\";\n\n\t\t@Annotation1\n\t\tpublic boolean notAString = true;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ClassLoaderUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.mockito.Mockito.mock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.test.TestClassLoader;\n\n/**\n * Unit tests for {@link ClassLoaderUtils}.\n *\n * @since 1.0\n */\nclass ClassLoaderUtilsTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getClassLoaderPreconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> ClassLoaderUtils.getClassLoader(null));\n\t}\n\n\t@Test\n\tvoid getClassLoaderForPrimitive() {\n\t\tassertThat(int.class.getClassLoader()).isNull();\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(int.class);\n\t\tassertThat(classLoader).isSameAs(getClass().getClassLoader());\n\t}\n\n\t@Test\n\tvoid getClassLoaderForWrapperType() {\n\t\tassertThat(Byte.class.getClassLoader()).isNull();\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(Byte.class);\n\t\tassertThat(classLoader).isSameAs(getClass().getClassLoader());\n\t}\n\n\t@Test\n\tvoid getClassLoaderForVoidType() {\n\t\tassertThat(void.class.getClassLoader()).isNull();\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(void.class);\n\t\tassertThat(classLoader).isSameAs(getClass().getClassLoader());\n\t}\n\n\t@Test\n\tvoid getClassLoaderForTestClass() {\n\t\tassertThat(getClass().getClassLoader()).isNotNull();\n\t\tClassLoader classLoader = ClassLoaderUtils.getClassLoader(getClass());\n\t\tassertThat(classLoader).isSameAs(getClass().getClassLoader());\n\t}\n\n\t@Test\n\tvoid getClassLoaderForClassInDifferentClassLoader() throws Exception {\n\t\ttry (var testClassLoader = TestClassLoader.forClasses(getClass())) {\n\t\t\tvar testClass = testClassLoader.loadClass(getClass().getName());\n\t\t\tassertThat(testClass.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\tvar classLoader = ClassLoaderUtils.getClassLoader(testClass);\n\t\t\tassertThat(classLoader).isSameAs(testClassLoader);\n\t\t}\n\t}\n\n\t@Test\n\tvoid getDefaultClassLoaderWithExplicitContextClassLoader() {\n\t\tvar original = Thread.currentThread().getContextClassLoader();\n\t\tvar mock = mock(ClassLoader.class);\n\t\tThread.currentThread().setContextClassLoader(mock);\n\t\ttry {\n\t\t\tassertSame(mock, ClassLoaderUtils.getDefaultClassLoader());\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(original);\n\t\t}\n\t}\n\n\t@Test\n\tvoid getDefaultClassLoaderWithNullContextClassLoader() {\n\t\tvar original = Thread.currentThread().getContextClassLoader();\n\t\tThread.currentThread().setContextClassLoader(null);\n\t\ttry {\n\t\t\tassertSame(ClassLoader.getSystemClassLoader(), ClassLoaderUtils.getDefaultClassLoader());\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(original);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getLocationFromNullFails() {\n\t\tassertPreconditionViolationNotNullFor(\"object\", () -> ClassLoaderUtils.getLocation(null));\n\t}\n\n\t@Test\n\tvoid getLocationFromVariousObjectsArePresent() {\n\t\tassertTrue(ClassLoaderUtils.getLocation(getClass()).isPresent());\n\t\tassertTrue(ClassLoaderUtils.getLocation(this).isPresent());\n\t\tassertTrue(ClassLoaderUtils.getLocation(\"\").isPresent());\n\t\tassertTrue(ClassLoaderUtils.getLocation(0).isPresent());\n\t\tassertTrue(ClassLoaderUtils.getLocation(Thread.State.RUNNABLE).isPresent());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ClassNamePatternFilterUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.util.classes.AExecutionConditionClass;\nimport org.junit.platform.commons.util.classes.ATestExecutionListenerClass;\nimport org.junit.platform.commons.util.classes.AVanillaEmpty;\nimport org.junit.platform.commons.util.classes.BExecutionConditionClass;\nimport org.junit.platform.commons.util.classes.BTestExecutionListenerClass;\nimport org.junit.platform.commons.util.classes.BVanillaEmpty;\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * Unit tests for {@link ClassNamePatternFilterUtils}.\n *\n * @since 1.7\n */\n@TestInstance(Lifecycle.PER_CLASS)\nclass ClassNamePatternFilterUtilsTests {\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AExecutionConditionClass, BExecutionConditionClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverExcludedConditions(String pattern) {\n\t\tList<? extends ExecutionCondition> executionConditions = List.of(new AExecutionConditionClass(),\n\t\t\tnew BExecutionConditionClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AExecutionConditionClass, *BExecutionConditionClass\",\n\t\t\t\"*ExecutionConditionClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysExcludedConditions(String pattern) {\n\t\tList<? extends ExecutionCondition> executionConditions = List.of(new AExecutionConditionClass(),\n\t\t\tnew BExecutionConditionClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"ATestExecutionListenerClass, BTestExecutionListenerClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverExcludedListeners(String pattern) {\n\t\tList<? extends TestExecutionListener> executionConditions = List.of(new ATestExecutionListenerClass(),\n\t\t\tnew BTestExecutionListenerClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*ATestExecutionListenerClass, *BTestExecutionListenerClass\",\n\t\t\t\"*TestExecutionListenerClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysExcludedListeners(String pattern) {\n\t\tList<? extends TestExecutionListener> executionConditions = List.of(new ATestExecutionListenerClass(),\n\t\t\tnew BTestExecutionListenerClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AVanillaEmpty, BVanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverExcludedClass(String pattern) {\n\t\tvar executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AVanillaEmpty, *BVanillaEmpty\",\n\t\t\t\"*VanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysExcludedClass(String pattern) {\n\t\tvar executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AVanillaEmpty, BVanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverExcludedClassName(String pattern) {\n\t\tvar executionConditions = List.of(\"org.junit.platform.commons.util.classes.AVanillaEmpty\",\n\t\t\t\"org.junit.platform.commons.util.classes.BVanillaEmpty\");\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClassNames(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AVanillaEmpty, *BVanillaEmpty\",\n\t\t\t\"*VanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysExcludedClassName(String pattern) {\n\t\tvar executionConditions = List.of(\"org.junit.platform.commons.util.classes.AVanillaEmpty\",\n\t\t\t\"org.junit.platform.commons.util.classes.BVanillaEmpty\");\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.excludeMatchingClassNames(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AExecutionConditionClass, BExecutionConditionClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverIncludedConditions(String pattern) {\n\t\tList<? extends ExecutionCondition> executionConditions = List.of(new AExecutionConditionClass(),\n\t\t\tnew BExecutionConditionClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AExecutionConditionClass, *BExecutionConditionClass\",\n\t\t\t\"*ExecutionConditionClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysIncludedConditions(String pattern) {\n\t\tList<? extends ExecutionCondition> executionConditions = List.of(new AExecutionConditionClass(),\n\t\t\tnew BExecutionConditionClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"ATestExecutionListenerClass, BTestExecutionListenerClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverIncludedListeners(String pattern) {\n\t\tList<? extends TestExecutionListener> executionConditions = List.of(new ATestExecutionListenerClass(),\n\t\t\tnew BTestExecutionListenerClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*ATestExecutionListenerClass, *BTestExecutionListenerClass\",\n\t\t\t\"*TestExecutionListenerClass\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysIncludedListeners(String pattern) {\n\t\tList<? extends TestExecutionListener> executionConditions = List.of(new ATestExecutionListenerClass(),\n\t\t\tnew BTestExecutionListenerClass());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AVanillaEmpty, BVanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverIncludedClass(String pattern) {\n\t\tvar executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AVanillaEmpty, *BVanillaEmpty\",\n\t\t\t\"*VanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysIncludedClass(String pattern) {\n\t\tvar executionConditions = List.of(new AVanillaEmpty(), new BVanillaEmpty());\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClasses(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.jupiter.*\",\n\t\t\t\"org.junit.platform.*.NonExistentClass\",\n\t\t\t\"*.NonExistentClass*\",\n\t\t\t\"*NonExistentClass*\",\n\t\t\t\"AVanillaEmpty, BVanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid neverIncludedClassName(String pattern) {\n\t\tvar executionConditions = List.of(\"org.junit.platform.commons.util.classes.AVanillaEmpty\",\n\t\t\t\"org.junit.platform.commons.util.classes.BVanillaEmpty\");\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) //\n\t\t\t\t.isEmpty();\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AVanillaEmpty, *BVanillaEmpty\",\n\t\t\t\"*VanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid alwaysIncludedClassName(String pattern) {\n\t\tvar executionConditions = List.of(\"org.junit.platform.commons.util.classes.AVanillaEmpty\",\n\t\t\t\"org.junit.platform.commons.util.classes.BVanillaEmpty\");\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n\t//@formatter:off\n\t@ValueSource(strings = {\n\t\t\t\"org.junit.platform.*\",\n\t\t\t\"*.platform.*\",\n\t\t\t\"*\",\n\t\t\t\"*AVanillaEmpty, *BVanillaEmpty\",\n\t\t\t\"*VanillaEmpty\"\n\t})\n\t//@formatter:on\n\t@ParameterizedTest\n\tvoid includeAndExcludeSame(String pattern) {\n\t\tvar executionConditions = List.of(\"org.junit.platform.commons.util.classes.AVanillaEmpty\",\n\t\t\t\"org.junit.platform.commons.util.classes.BVanillaEmpty\");\n\t\tassertThat(executionConditions).filteredOn(ClassNamePatternFilterUtils.includeMatchingClassNames(pattern)) //\n\t\t\t\t.hasSize(2);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ClassUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.commons.util.ClassUtils.nullSafeToString;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClassUtils}.\n *\n * @since 1.0\n */\nclass ClassUtilsTests {\n\n\t@Test\n\tvoid nullSafeToStringWithDefaultMapper() {\n\t\tassertEquals(\"\", nullSafeToString((Class<?>[]) null));\n\t\tassertEquals(\"\", nullSafeToString());\n\t\tassertEquals(\"java.lang.String\", nullSafeToString(String.class));\n\t\tassertEquals(\"java.lang.String, java.lang.Integer\", nullSafeToString(String.class, Integer.class));\n\t\tassertEquals(\"java.lang.String, null, java.lang.Integer\", nullSafeToString(String.class, null, Integer.class));\n\t}\n\n\t@Test\n\tvoid nullSafeToStringWithCustomMapper() {\n\t\tassertEquals(\"\", nullSafeToString(Class::getSimpleName, (Class<?>[]) null));\n\t\tassertEquals(\"\", nullSafeToString(Class::getSimpleName));\n\t\tassertEquals(\"String\", nullSafeToString(Class::getSimpleName, String.class));\n\t\tassertEquals(\"String, Integer\", nullSafeToString(Class::getSimpleName, String.class, Integer.class));\n\t\tassertEquals(\"String, null, Integer\",\n\t\t\tnullSafeToString(Class::getSimpleName, String.class, null, Integer.class));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/CloseablePathTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.commons.util.CloseablePath.JAR_URI_SCHEME;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.only;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\nimport static org.mockito.Mockito.when;\n\nimport java.net.URI;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystemNotFoundException;\nimport java.nio.file.FileSystems;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.test.ConcurrencyTestingUtils;\nimport org.junit.platform.commons.util.CloseablePath.FileSystemProvider;\nimport org.junit.platform.engine.support.hierarchical.OpenTest4JAwareThrowableCollector;\n\nclass CloseablePathTests {\n\n\tURI uri;\n\tURI jarUri;\n\n\tList<CloseablePath> paths = new ArrayList<>();\n\n\t@BeforeEach\n\tvoid createUris() throws Exception {\n\t\turi = getClass().getResource(\"/jartest.jar\").toURI();\n\t\tjarUri = URI.create(JAR_URI_SCHEME + ':' + uri);\n\t}\n\n\t@AfterEach\n\tvoid closeAllPaths() {\n\t\tcloseAll(paths);\n\t}\n\n\t@Test\n\tvoid parsesJarUri() throws Exception {\n\t\tFileSystemProvider fileSystemProvider = mock();\n\n\t\tFileSystem fileSystem = mock();\n\t\twhen(fileSystemProvider.newFileSystem(any())).thenReturn(fileSystem);\n\n\t\tURI jarFileWithEntry = URI.create(\"jar:file:/example.jar!/com/example/Example.class\");\n\t\tCloseablePath.create(jarFileWithEntry, fileSystemProvider).close();\n\n\t\tURI jarFileUri = URI.create(\"jar:file:/example.jar\");\n\t\tverify(fileSystemProvider).newFileSystem(jarFileUri);\n\t\tverifyNoMoreInteractions(fileSystemProvider);\n\t}\n\n\t@Test\n\tvoid parsesRecursiveJarUri() throws Exception {\n\t\tFileSystemProvider fileSystemProvider = mock();\n\n\t\tFileSystem fileSystem = mock();\n\t\twhen(fileSystemProvider.newFileSystem(any())).thenReturn(fileSystem);\n\n\t\tURI jarNestedFileWithEntry = URI.create(\n\t\t\t\"jar:nested:file:/example.jar!/BOOT-INF/classes!/com/example/Example.class\");\n\t\tCloseablePath.create(jarNestedFileWithEntry, fileSystemProvider).close();\n\n\t\tURI jarNestedFile = URI.create(\"jar:nested:file:/example.jar!/BOOT-INF/classes\");\n\t\tverify(fileSystemProvider).newFileSystem(jarNestedFile);\n\t\tverifyNoMoreInteractions(fileSystemProvider);\n\t}\n\n\t@Test\n\tvoid createsAndClosesJarFileSystemOnceWhenCalledConcurrently() throws Exception {\n\t\tvar numThreads = 50;\n\n\t\tFileSystemProvider fileSystemProvider = mock();\n\t\twhen(fileSystemProvider.newFileSystem(any())) //\n\t\t\t\t.thenAnswer(invocation -> FileSystems.newFileSystem((URI) invocation.getArgument(0), Map.of()));\n\n\t\tpaths = ConcurrencyTestingUtils.executeConcurrently(numThreads,\n\t\t\t() -> CloseablePath.create(uri, fileSystemProvider));\n\t\tverify(fileSystemProvider, only()).newFileSystem(jarUri);\n\n\t\t// Close all but the first path\n\t\tcloseAll(paths.subList(1, numThreads));\n\t\tassertDoesNotThrow(() -> FileSystems.getFileSystem(jarUri), \"FileSystem should still be open\");\n\n\t\t// Close last remaining path\n\t\tpaths.getFirst().close();\n\t\tassertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri),\n\t\t\t\"FileSystem should have been closed\");\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid closingIsIdempotent() throws Exception {\n\t\tvar path1 = CloseablePath.create(uri);\n\t\tpaths.add(path1);\n\t\tvar path2 = CloseablePath.create(uri);\n\t\tpaths.add(path2);\n\n\t\tpath1.close();\n\t\tpath1.close();\n\t\tassertDoesNotThrow(() -> FileSystems.getFileSystem(jarUri), \"FileSystem should still be open\");\n\n\t\tpath2.close();\n\t\tassertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri),\n\t\t\t\"FileSystem should have been closed\");\n\t}\n\n\tprivate static void closeAll(List<CloseablePath> paths) {\n\t\tvar throwableCollector = new OpenTest4JAwareThrowableCollector();\n\t\tpaths.forEach(closeablePath -> throwableCollector.execute(closeablePath::close));\n\t\tthrowableCollector.assertEmpty();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/CollectionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Spliterator;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.stream.DoubleStream;\nimport java.util.stream.IntStream;\nimport java.util.stream.LongStream;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.converter.ArgumentConversionException;\nimport org.junit.jupiter.params.converter.ArgumentConverter;\nimport org.junit.jupiter.params.converter.ConvertWith;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * Unit tests for {@link CollectionUtils}.\n *\n * @since 1.0\n */\nclass CollectionUtilsTests {\n\n\t@Nested\n\tclass OnlyElement {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid nullCollection() {\n\t\t\tassertPreconditionViolationNotNullFor(\"collection\", () -> CollectionUtils.getOnlyElement(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid emptyCollection() {\n\t\t\tassertPreconditionViolationFor(() -> CollectionUtils.getOnlyElement(Set.of()))//\n\t\t\t\t\t.withMessage(\"collection must contain exactly one element: []\");\n\t\t}\n\n\t\t@Test\n\t\tvoid singleElementCollection() {\n\t\t\tvar expected = new Object();\n\t\t\tvar actual = CollectionUtils.getOnlyElement(Set.of(expected));\n\t\t\tassertSame(expected, actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid multiElementCollection() {\n\t\t\tassertPreconditionViolationFor(() -> CollectionUtils.getOnlyElement(List.of(\"foo\", \"bar\")))//\n\t\t\t\t\t.withMessage(\"collection must contain exactly one element: [foo, bar]\");\n\t\t}\n\t}\n\n\t@Nested\n\tclass FirstElement {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid nullCollection() {\n\t\t\tassertPreconditionViolationNotNullFor(\"collection\", () -> CollectionUtils.getFirstElement(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid emptyCollection() {\n\t\t\tassertThat(CollectionUtils.getFirstElement(Set.of())).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid singleElementCollection() {\n\t\t\tvar expected = new Object();\n\t\t\tassertThat(CollectionUtils.getFirstElement(Set.of(expected))).containsSame(expected);\n\t\t}\n\n\t\t@Test\n\t\tvoid multiElementCollection() {\n\t\t\tassertThat(CollectionUtils.getFirstElement(List.of(\"foo\", \"bar\"))).contains(\"foo\");\n\t\t}\n\n\t\t@Test\n\t\tvoid collectionWithNullValues() {\n\t\t\tassertThat(CollectionUtils.getFirstElement(Arrays.asList(new Object[1]))).isEmpty();\n\t\t}\n\t}\n\n\t@Nested\n\tclass StreamConversion {\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { //\n\t\t\t\tStream.class, //\n\t\t\t\tDoubleStream.class, //\n\t\t\t\tIntStream.class, //\n\t\t\t\tLongStream.class, //\n\t\t\t\tCollection.class, //\n\t\t\t\tIterable.class, //\n\t\t\t\tIterator.class, //\n\t\t\t\tIteratorProvider.class, //\n\t\t\t\tObject[].class, //\n\t\t\t\tString[].class, //\n\t\t\t\tint[].class, //\n\t\t\t\tdouble[].class, //\n\t\t\t\tchar[].class //\n\t\t})\n\t\tvoid isConvertibleToStreamForSupportedTypes(Class<?> type) {\n\t\t\tassertThat(CollectionUtils.isConvertibleToStream(type)).isTrue();\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@MethodSource(\"objectsConvertibleToStreams\")\n\t\tvoid isConvertibleToStreamForSupportedTypesFromObjects(Object object) {\n\t\t\tassertThat(CollectionUtils.isConvertibleToStream(object.getClass())).isTrue();\n\t\t}\n\n\t\tstatic Stream<Object> objectsConvertibleToStreams() {\n\t\t\treturn Stream.of(//\n\t\t\t\tStream.of(\"cat\", \"dog\"), //\n\t\t\t\tDoubleStream.of(42.3), //\n\t\t\t\tIntStream.of(99), //\n\t\t\t\tLongStream.of(100_000_000), //\n\t\t\t\tSet.of(1, 2, 3), //\n\t\t\t\tArguments.of((Object) new Object[] { 9, 8, 7 }), //\n\t\t\t\tnew int[] { 5, 10, 15 }, //\n\t\t\t\tnew IteratorProvider(1, 2, 3, 4, 5)//\n\t\t\t);\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(classes = { //\n\t\t\t\tvoid.class, //\n\t\t\t\tVoid.class, //\n\t\t\t\tObject.class, //\n\t\t\t\tInteger.class, //\n\t\t\t\tString.class, //\n\t\t\t\tUnusableIteratorProvider.class, //\n\t\t\t\tSpliterator.class, //\n\t\t\t\tint.class, //\n\t\t\t\tboolean.class //\n\t\t})\n\t\tvoid isConvertibleToStreamForUnsupportedTypes(Class<?> type) {\n\t\t\tassertThat(CollectionUtils.isConvertibleToStream(type)).isFalse();\n\t\t}\n\n\t\t@Test\n\t\tvoid isConvertibleToStreamForNull() {\n\t\t\tassertThat(CollectionUtils.isConvertibleToStream(null)).isFalse();\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid toStreamWithNull() {\n\t\t\tassertPreconditionViolationNotNullFor(\"Object\", () -> CollectionUtils.toStream(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid toStreamWithUnsupportedObjectType() {\n\t\t\tassertPreconditionViolationFor(() -> CollectionUtils.toStream(\"unknown\"))//\n\t\t\t\t\t.withMessage(\"Cannot convert instance of java.lang.String into a Stream: unknown\");\n\t\t}\n\n\t\t@Test\n\t\tvoid toStreamWithExistingStream() {\n\t\t\tvar input = Stream.of(\"foo\");\n\n\t\t\tvar result = CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).isSameAs(input);\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithDoubleStream() {\n\t\t\tvar input = DoubleStream.of(42.23);\n\n\t\t\tvar result = (Stream<Double>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(42.23);\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithIntStream() {\n\t\t\tvar input = IntStream.of(23, 42);\n\n\t\t\tvar result = (Stream<Integer>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(23, 42);\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithLongStream() {\n\t\t\tvar input = LongStream.of(23L, 42L);\n\n\t\t\tvar result = (Stream<Long>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(23L, 42L);\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings({ \"unchecked\", \"serial\" })\n\t\tvoid toStreamWithCollection() {\n\t\t\tvar collectionStreamClosed = new AtomicBoolean(false);\n\t\t\tvar input = new ArrayList<>(List.of(\"foo\", \"bar\")) {\n\t\t\t\t@Override\n\t\t\t\tpublic Stream<String> stream() {\n\t\t\t\t\treturn super.stream().onClose(() -> collectionStreamClosed.set(true));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\ttry (var stream = (Stream<String>) CollectionUtils.toStream(input)) {\n\t\t\t\tvar result = stream.toList();\n\t\t\t\tassertThat(result).containsExactly(\"foo\", \"bar\");\n\t\t\t}\n\n\t\t\tassertThat(collectionStreamClosed.get()).describedAs(\"collectionStreamClosed\").isTrue();\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithIterable() {\n\n\t\t\tIterable<String> input = () -> List.of(\"foo\", \"bar\").iterator();\n\n\t\t\tvar result = (Stream<String>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithIterator() {\n\t\t\tvar input = List.of(\"foo\", \"bar\").iterator();\n\n\t\t\tvar result = (Stream<String>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithIteratorProvider() {\n\t\t\tvar input = new IteratorProvider(\"foo\", \"bar\");\n\n\t\t\tvar result = (Stream<String>) CollectionUtils.toStream(input);\n\n\t\t\tassertThat(result).containsExactly(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Test\n\t\tvoid throwWhenIteratorNamedMethodDoesNotReturnAnIterator() {\n\t\t\tvar o = new UnusableIteratorProvider(\"Test\");\n\t\t\tassertPreconditionViolationFor(() -> CollectionUtils.toStream(o))//\n\t\t\t\t\t.withMessage(\"Cannot convert instance of %s into a Stream: %s\".formatted(\n\t\t\t\t\t\tUnusableIteratorProvider.class.getName(), o));\n\t\t}\n\n\t\t@Test\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tvoid toStreamWithArray() {\n\t\t\tvar result = (Stream<String>) CollectionUtils.toStream(new String[] { \"foo\", \"bar\" });\n\n\t\t\tassertThat(result).containsExactly(\"foo\", \"bar\");\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> toStreamWithPrimitiveArrays() {\n\t\t\t//@formatter:off\n\t\t\treturn Stream.of(\n\t\t\t\t\tdynamicTest(\"boolean[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new boolean[] { true, false })),\n\t\t\t\t\tdynamicTest(\"byte[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new byte[] { 0, Byte.MIN_VALUE, Byte.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"char[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new char[] { 0, Character.MIN_VALUE, Character.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"double[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new double[] { 0, Double.MIN_VALUE, Double.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"float[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new float[] { 0, Float.MIN_VALUE, Float.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"int[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new int[] { 0, Integer.MIN_VALUE, Integer.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"long[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new long[] { 0, Long.MIN_VALUE, Long.MAX_VALUE })),\n\t\t\t\t\tdynamicTest(\"short[]\",\n\t\t\t\t\t\t\t() -> toStreamWithPrimitiveArray(new short[] { 0, Short.MIN_VALUE, Short.MAX_VALUE }))\n\t\t\t);\n\t\t\t//@formatter:on\n\t\t}\n\n\t\tprivate void toStreamWithPrimitiveArray(Object primitiveArray) {\n\t\t\tassertTrue(primitiveArray.getClass().isArray());\n\t\t\tassertTrue(primitiveArray.getClass().getComponentType().isPrimitive());\n\t\t\tvar result = CollectionUtils.toStream(primitiveArray).toArray();\n\t\t\tfor (var i = 0; i < result.length; i++) {\n\t\t\t\tassertEquals(Array.get(primitiveArray, i), result[i]);\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nested\n\tclass ReverseOrderIteration {\n\n\t\t@ParameterizedTest\n\t\t@CsvSource(delimiter = '|', nullValues = \"N/A\", textBlock = \"\"\"\n\t\t\t\t        foo,bar,baz | baz,bar,foo\n\t\t\t\t        foo,bar     | bar,foo\n\t\t\t\t        foo         | foo\n\t\t\t\t        N/A         | N/A\n\t\t\t\t\"\"\")\n\t\tvoid iteratesListElementsInReverseOrder(@ConvertWith(CommaSeparator.class) List<String> input,\n\t\t\t\t@ConvertWith(CommaSeparator.class) List<String> expected) {\n\t\t\tvar result = new ArrayList<>();\n\n\t\t\tCollectionUtils.forEachInReverseOrder(input, result::add);\n\n\t\t\tassertEquals(expected, result);\n\t\t}\n\n\t\tprivate static class CommaSeparator implements ArgumentConverter {\n\t\t\t@Override\n\t\t\tpublic Object convert(@Nullable Object source, ParameterContext context)\n\t\t\t\t\tthrows ArgumentConversionException {\n\t\t\t\treturn source == null ? List.of() : List.of(((String) source).split(\",\"));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * An interface that has a method with name 'iterator', returning a java.util/Iterator as a return type\n\t */\n\tprivate record IteratorProvider(Object... elements) {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tIterator<?> iterator() {\n\t\t\treturn Arrays.stream(elements).iterator();\n\t\t}\n\t}\n\n\t/**\n\t * An interface that has a method with name 'iterator', but does not return java.util/Iterator as a return type\n\t */\n\tprivate record UnusableIteratorProvider(Object... elements) {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tObject iterator() {\n\t\t\treturn Arrays.stream(elements).iterator();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/DefaultClasspathScannerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeFalse;\nimport static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.module.ModuleFinder;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.FileSystemNotFoundException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.spi.ToolProvider;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.DisabledInEclipse;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.commons.function.Try;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.support.scanning.ClassFilter;\n\n/**\n * Unit tests for {@link DefaultClasspathScanner}.\n *\n * @since 1.0\n */\n@TrackLogRecords\nclass DefaultClasspathScannerTests {\n\n\tprivate static final ClassFilter allClasses = ClassFilter.of(__ -> true);\n\tprivate static final ResourceFilter allResources = ResourceFilter.of(__ -> true);\n\n\tprivate final List<Class<?>> loadedClasses = new ArrayList<>();\n\n\tprivate final BiFunction<String, ClassLoader, Try<Class<?>>> trackingClassLoader = (name,\n\t\t\tclassLoader) -> ReflectionUtils.tryToLoadClass(name, classLoader).ifSuccess(loadedClasses::add);\n\n\tprivate final DefaultClasspathScanner classpathScanner = new DefaultClasspathScanner(\n\t\tClassLoaderUtils::getDefaultClassLoader, trackingClassLoader);\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWhenMalformedClassnameInternalErrorOccursWithNullDetailedMessage(\n\t\t\tLogRecordListener listener) throws Exception {\n\n\t\tPredicate<Class<?>> malformedClassNameSimulationFilter = clazz -> {\n\t\t\tif (clazz.getSimpleName().equals(ClassForMalformedClassNameSimulation.class.getSimpleName())) {\n\t\t\t\tthrow new InternalError();\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\n\t\tassertClassesScannedWhenExceptionIsThrown(malformedClassNameSimulationFilter);\n\t\tassertDebugMessageLogged(listener, \"Failed to load .+ during classpath scanning.\");\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWhenMalformedClassnameInternalErrorOccurs(LogRecordListener listener)\n\t\t\tthrows Exception {\n\n\t\tPredicate<Class<?>> malformedClassNameSimulationFilter = clazz -> {\n\t\t\tif (clazz.getSimpleName().equals(ClassForMalformedClassNameSimulation.class.getSimpleName())) {\n\t\t\t\tthrow new InternalError(\"Malformed class name\");\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\n\t\tassertClassesScannedWhenExceptionIsThrown(malformedClassNameSimulationFilter);\n\t\tassertDebugMessageLogged(listener, \"The java.lang.Class loaded from path .+ has a malformed class name .+\");\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWhenOtherInternalErrorOccurs(LogRecordListener listener) throws Exception {\n\t\tPredicate<Class<?>> otherInternalErrorSimulationFilter = clazz -> {\n\t\t\tif (clazz.getSimpleName().equals(ClassForOtherInternalErrorSimulation.class.getSimpleName())) {\n\t\t\t\tthrow new InternalError(\"other internal error\");\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\n\t\tassertClassesScannedWhenExceptionIsThrown(otherInternalErrorSimulationFilter);\n\t\tassertDebugMessageLogged(listener, \"Failed to load .+ during classpath scanning.\");\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWhenGenericRuntimeExceptionOccurs(LogRecordListener listener) throws Exception {\n\t\tPredicate<Class<?>> runtimeExceptionSimulationFilter = clazz -> {\n\t\t\tif (clazz.getSimpleName().equals(ClassForGenericRuntimeExceptionSimulation.class.getSimpleName())) {\n\t\t\t\tthrow new RuntimeException(\"a generic exception\");\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\n\t\tassertClassesScannedWhenExceptionIsThrown(runtimeExceptionSimulationFilter);\n\t\tassertDebugMessageLogged(listener, \"Failed to load .+ during classpath scanning.\");\n\t}\n\n\tprivate void assertClassesScannedWhenExceptionIsThrown(Predicate<Class<?>> filter) throws Exception {\n\t\tvar classFilter = ClassFilter.of(filter);\n\t\tvar classes = this.classpathScanner.scanForClassesInClasspathRoot(getTestClasspathRoot(), classFilter);\n\t\tassertThat(classes).hasSizeGreaterThanOrEqualTo(150);\n\t}\n\n\t@Test\n\tvoid scanForResourcesInClasspathRootWhenGenericRuntimeExceptionOccurs(LogRecordListener listener) {\n\t\tPredicate<Resource> runtimeExceptionSimulationFilter = resource -> {\n\t\t\tif (resource.getName().equals(\"org/junit/platform/commons/other-example.resource\")) {\n\t\t\t\tthrow new RuntimeException(\"a generic exception\");\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\n\t\tassertResourcesScannedWhenExceptionIsThrown(runtimeExceptionSimulationFilter);\n\t\tassertDebugMessageLogged(listener, \"Failed to load .+ during classpath scanning.\");\n\t}\n\n\tprivate void assertResourcesScannedWhenExceptionIsThrown(Predicate<Resource> filter) {\n\t\tvar resources = this.classpathScanner.scanForResourcesInClasspathRoot(getTestClasspathResourceRoot(),\n\t\t\tResourceFilter.of(filter));\n\t\tassertThat(resources).hasSizeGreaterThanOrEqualTo(150);\n\t}\n\n\tprivate void assertDebugMessageLogged(LogRecordListener listener, String regex) {\n\t\t// @formatter:off\n\t\tassertThat(listener.stream(DefaultClasspathScanner.class, Level.FINE)\n\t\t\t\t.map(LogRecord::getMessage)\n\t\t\t\t.filter(m -> m.matches(regex))\n\t\t).hasSize(1);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWhenOutOfMemoryErrorOccurs() {\n\t\tPredicate<Class<?>> outOfMemoryErrorSimulationFilter = clazz -> {\n\t\t\tif (clazz.getSimpleName().equals(ClassForOutOfMemoryErrorSimulation.class.getSimpleName())) {\n\t\t\t\tthrow new OutOfMemoryError();\n\t\t\t}\n\t\t\treturn true;\n\t\t};\n\t\tvar classFilter = ClassFilter.of(outOfMemoryErrorSimulationFilter);\n\n\t\tassertThrows(OutOfMemoryError.class,\n\t\t\t() -> this.classpathScanner.scanForClassesInClasspathRoot(getTestClasspathRoot(), classFilter));\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWithinJarFile() throws Exception {\n\t\tscanForClassesInClasspathRootWithinJarFile(\"/jartest.jar\");\n\t}\n\n\t@Test\n\tvoid scanForClassesInClasspathRootWithinJarWithSpacesInPath() throws Exception {\n\t\tscanForClassesInClasspathRootWithinJarFile(\"/folder with spaces/jar test with spaces.jar\");\n\t}\n\n\tprivate void scanForClassesInClasspathRootWithinJarFile(String resourceName) throws Exception {\n\t\tvar jarfile = requireNonNull(getClass().getResource(resourceName));\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarfile }, null)) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar classes = classpathScanner.scanForClassesInClasspathRoot(jarfile.toURI(), allClasses);\n\t\t\tassertThat(classes).extracting(Class::getName) //\n\t\t\t\t\t.containsExactlyInAnyOrder(\"org.junit.platform.jartest.notincluded.NotIncluded\",\n\t\t\t\t\t\t\"org.junit.platform.jartest.included.recursive.RecursivelyIncluded\",\n\t\t\t\t\t\t\"org.junit.platform.jartest.included.Included\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid scanForResourcesInClasspathRootWithinJarFile() throws Exception {\n\t\tscanForResourcesInClasspathRootWithinJarFile(\"/jartest.jar\");\n\t}\n\n\t@Test\n\tvoid scanForResourcesInClasspathRootWithinJarWithSpacesInPath() throws Exception {\n\t\tscanForResourcesInClasspathRootWithinJarFile(\"/folder with spaces/jar test with spaces.jar\");\n\t}\n\n\tprivate void scanForResourcesInClasspathRootWithinJarFile(String resourceName) throws Exception {\n\t\tvar jarfile = requireNonNull(getClass().getResource(resourceName));\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarfile }, null)) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar resources = classpathScanner.scanForResourcesInClasspathRoot(jarfile.toURI(), allResources);\n\t\t\tassertThat(resources).extracting(Resource::getName) //\n\t\t\t\t\t.containsExactlyInAnyOrder(\"org/junit/platform/jartest/notincluded/not-included.resource\",\n\t\t\t\t\t\t\"org/junit/platform/jartest/included/included.resource\",\n\t\t\t\t\t\t\"org/junit/platform/jartest/included/recursive/recursively-included.resource\",\n\t\t\t\t\t\t\"META-INF/MANIFEST.MF\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid scanForResourcesInShadowedClassPathRoot() throws Exception {\n\t\tvar jarFile = requireNonNull(getClass().getResource(\"/jartest.jar\"));\n\t\tvar shadowedJarFile = requireNonNull(getClass().getResource(\"/jartest-shadowed.jar\"));\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarFile, shadowedJarFile }, null)) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar resources = classpathScanner.scanForResourcesInClasspathRoot(shadowedJarFile.toURI(), allResources);\n\t\t\tassertThat(resources).extracting(Resource::getName).containsExactlyInAnyOrder(\n\t\t\t\t\"org/junit/platform/jartest/included/unique.resource\", //\n\t\t\t\t\"org/junit/platform/jartest/included/included.resource\", //\n\t\t\t\t\"org/junit/platform/jartest/included/recursive/recursively-included.resource\", //\n\t\t\t\t\"META-INF/MANIFEST.MF\");\n\n\t\t\tassertThat(resources).extracting(Resource::getUri) //\n\t\t\t\t\t.map(DefaultClasspathScannerTests::jarFileAndEntry) //\n\t\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t// This resource only exists in the shadowed jar file\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/unique.resource\",\n\t\t\t\t\t\t// These resources exist in both the jar and shadowed jar file.\n\t\t\t\t\t\t// They must be discovered in the shadowed jar as we're searching in that classpath root.\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/included.resource\",\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource\",\n\t\t\t\t\t\t\"jartest-shadowed.jar!/META-INF/MANIFEST.MF\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid scanForResourcesInPackageWithDuplicateResources() throws Exception {\n\t\tvar jarFile = requireNonNull(getClass().getResource(\"/jartest.jar\"));\n\t\tvar shadowedJarFile = requireNonNull(getClass().getResource(\"/jartest-shadowed.jar\"));\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarFile, shadowedJarFile }, null)) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar resources = classpathScanner.scanForResourcesInPackage(\"org.junit.platform.jartest.included\",\n\t\t\t\tallResources);\n\n\t\t\tassertThat(resources).extracting(Resource::getUri) //\n\t\t\t\t\t.map(DefaultClasspathScannerTests::jarFileAndEntry) //\n\t\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t// This resource only exists in the shadowed jar file\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/unique.resource\",\n\t\t\t\t\t\t// These resources exist in both the jar and shadowed jar file.\n\t\t\t\t\t\t\"jartest.jar!/org/junit/platform/jartest/included/included.resource\",\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/included.resource\",\n\t\t\t\t\t\t\"jartest.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource\",\n\t\t\t\t\t\t\"jartest-shadowed.jar!/org/junit/platform/jartest/included/recursive/recursively-included.resource\");\n\t\t}\n\t}\n\n\tprivate static String jarFileAndEntry(URI uri) {\n\t\tvar uriString = uri.toString();\n\t\tint lastJarUriSeparator = uriString.lastIndexOf(\"!/\");\n\t\tvar jarUri = uriString.substring(0, lastJarUriSeparator);\n\t\tvar jarEntry = uriString.substring(lastJarUriSeparator + 1);\n\t\tvar fileName = jarUri.substring(jarUri.lastIndexOf(\"/\") + 1);\n\t\treturn fileName + \"!\" + jarEntry;\n\t}\n\n\t@Test\n\tvoid scanForClassesInPackage() {\n\t\tvar classes = classpathScanner.scanForClassesInPackage(\"org.junit.platform.commons\", allClasses);\n\t\tassertThat(classes).hasSizeGreaterThanOrEqualTo(20);\n\t\tassertTrue(classes.contains(NestedClassToBeFound.class));\n\t\tassertTrue(classes.contains(MemberClassToBeFound.class));\n\t}\n\n\t@Test\n\tvoid scanForResourcesInPackage() {\n\t\tvar resources = classpathScanner.scanForResourcesInPackage(\"org.junit.platform.commons\", allResources);\n\t\tassertThat(resources).extracting(Resource::getUri).containsExactlyInAnyOrder(\n\t\t\turiOf(\"/org/junit/platform/commons/example.resource\"),\n\t\t\turiOf(\"/org/junit/platform/commons/other-example.resource\"));\n\t}\n\n\t@Test // #2500\n\t@DisabledInEclipse\n\tvoid scanForClassesInPackageWithinModulesSharingNamePrefix(@TempDir Path temp) throws Exception {\n\t\tvar moduleSourcePath = Path.of(requireNonNull(getClass().getResource(\"/modules-2500/\")).toURI()).toString();\n\t\trun(\"javac\", \"--module\", \"foo,foo.bar\", \"--module-source-path\", moduleSourcePath, \"-d\", temp.toString());\n\n\t\tcheckModules2500(ModuleFinder.of(temp)); // exploded modules\n\n\t\tvar foo = temp.resolve(\"foo.jar\");\n\t\tvar bar = temp.resolve(\"foo.bar.jar\");\n\t\trun(\"jar\", \"--create\", \"--file\", foo.toString(), \"-C\", temp.resolve(\"foo\").toString(), \".\");\n\t\trun(\"jar\", \"--create\", \"--file\", bar.toString(), \"-C\", temp.resolve(\"foo.bar\").toString(), \".\");\n\n\t\tcheckModules2500(ModuleFinder.of(foo, bar)); // jarred modules\n\n\t\tSystem.gc(); // required on Windows in order to release JAR file handles\n\t}\n\n\tprivate static void run(String tool, String... args) {\n\t\tToolProvider.findFirst(tool).orElseThrow().run(System.out, System.err, args);\n\t}\n\n\tprivate void checkModules2500(ModuleFinder finder) {\n\t\tvar root = \"foo.bar\";\n\t\tvar before = ModuleFinder.of();\n\t\tvar boot = ModuleLayer.boot();\n\t\tvar configuration = boot.configuration().resolve(before, finder, Set.of(root));\n\t\tvar parent = ClassLoader.getPlatformClassLoader();\n\t\tvar layer = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(boot), parent).layer();\n\n\t\tvar classpathScanner = new DefaultClasspathScanner(() -> layer.findLoader(root),\n\t\t\tReflectionUtils::tryToLoadClass);\n\t\t{\n\t\t\tvar classes = classpathScanner.scanForClassesInPackage(\"foo\", allClasses);\n\t\t\tvar classNames = classes.stream().map(Class::getName).toList();\n\t\t\tassertThat(classNames).hasSize(2).contains(\"foo.Foo\", \"foo.bar.FooBar\");\n\t\t}\n\t\t{\n\t\t\tvar classes = classpathScanner.scanForClassesInPackage(\"foo.bar\", allClasses);\n\t\t\tvar classNames = classes.stream().map(Class::getName).toList();\n\t\t\tassertThat(classNames).hasSize(1).contains(\"foo.bar.FooBar\");\n\t\t}\n\t}\n\n\t@Test\n\tvoid findAllClassesInPackageWithinJarFileConcurrently() throws Exception {\n\t\tvar jarFile = getClass().getResource(\"/jartest.jar\");\n\t\tvar jarUri = URI.create(\"jar:\" + jarFile);\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarFile })) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar results = executeConcurrently(10,\n\t\t\t\t() -> classpathScanner.scanForClassesInPackage(\"org.junit.platform.jartest.included\", allClasses));\n\n\t\t\tassertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri),\n\t\t\t\t\"FileSystem should be closed\");\n\n\t\t\tresults.forEach(classes -> assertThat(classes) //\n\t\t\t\t\t.hasSize(2) //\n\t\t\t\t\t.extracting(Class::getSimpleName) //\n\t\t\t\t\t.containsExactlyInAnyOrder(\"Included\", \"RecursivelyIncluded\"));\n\t\t}\n\t}\n\n\t@Test\n\tvoid findAllResourcesInPackageWithinJarFileConcurrently() throws Exception {\n\t\tvar jarFile = getClass().getResource(\"/jartest.jar\");\n\t\tvar jarUri = URI.create(\"jar:\" + jarFile);\n\n\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarFile })) {\n\t\t\tvar classpathScanner = new DefaultClasspathScanner(() -> classLoader, ReflectionUtils::tryToLoadClass);\n\n\t\t\tvar results = executeConcurrently(10,\n\t\t\t\t() -> classpathScanner.scanForResourcesInPackage(\"org.junit.platform.jartest.included\", allResources));\n\n\t\t\tassertThrows(FileSystemNotFoundException.class, () -> FileSystems.getFileSystem(jarUri),\n\t\t\t\t\"FileSystem should be closed\");\n\n\t\t\t// @formatter:off\n\t\t\tresults.forEach(resources -> assertThat(resources)\n\t\t\t\t\t.hasSize(2)\n\t\t\t\t\t.extracting(Resource::getName).containsExactlyInAnyOrder(\n\t\t\t\t\t\t\t\"org/junit/platform/jartest/included/included.resource\",\n\t\t\t\t\t\t\t\"org/junit/platform/jartest/included/recursive/recursively-included.resource\"\n\t\t\t\t\t));\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@Test\n\tvoid scanForClassesInDefaultPackage() {\n\t\tvar classFilter = ClassFilter.of(this::inDefaultPackage);\n\t\tvar classes = classpathScanner.scanForClassesInPackage(\"\", classFilter);\n\n\t\tassertThat(classes).as(\"number of classes found in default package\").isNotEmpty();\n\t\tassertTrue(classes.stream().allMatch(this::inDefaultPackage));\n\t\tassertTrue(classes.stream().anyMatch(clazz -> \"DefaultPackageTestCase\".equals(clazz.getName())));\n\t}\n\n\t@Test\n\tvoid scanForResourcesInDefaultPackage() {\n\t\tvar resourceFilter = ResourceFilter.of(this::inDefaultPackage);\n\t\tvar resources = classpathScanner.scanForResourcesInPackage(\"\", resourceFilter);\n\n\t\tassertThat(resources).as(\"number of resources found in default package\").isNotEmpty();\n\t\tassertTrue(resources.stream().allMatch(this::inDefaultPackage));\n\t\tassertTrue(resources.stream().anyMatch(resource -> \"default-package.resource\".equals(resource.getName())));\n\t}\n\n\t@Test\n\tvoid scanForClassesInPackageWithFilter() {\n\t\tvar thisClassOnly = ClassFilter.of(clazz -> clazz == DefaultClasspathScannerTests.class);\n\t\tvar classes = classpathScanner.scanForClassesInPackage(\"org.junit.platform.commons\", thisClassOnly);\n\t\tassertSame(DefaultClasspathScannerTests.class, classes.getFirst());\n\t}\n\n\t@Test\n\tvoid scanForResourcesInPackageWithFilter() {\n\t\tvar thisResourceOnly = ResourceFilter.of(\n\t\t\tresource -> \"org/junit/platform/commons/example.resource\".equals(resource.getName()));\n\t\tvar resources = classpathScanner.scanForResourcesInPackage(\"org.junit.platform.commons\", thisResourceOnly);\n\t\tassertThat(resources).extracting(Resource::getName).containsExactly(\n\t\t\t\"org/junit/platform/commons/example.resource\");\n\t}\n\n\t@Test\n\tvoid resourcesCanBeRead() throws Exception {\n\t\tvar thisResourceOnly = ResourceFilter.of(\n\t\t\tresource -> \"org/junit/platform/commons/example.resource\".equals(resource.getName()));\n\t\tvar resources = classpathScanner.scanForResourcesInPackage(\"org.junit.platform.commons\", thisResourceOnly);\n\t\tResource resource = resources.getFirst();\n\n\t\tassertThat(resource.getName()).isEqualTo(\"org/junit/platform/commons/example.resource\");\n\t\tassertThat(resource.getUri()).isEqualTo(uriOf(\"/org/junit/platform/commons/example.resource\"));\n\t\ttry (InputStream is = resource.getInputStream()) {\n\t\t\tString contents = new String(is.readAllBytes(), StandardCharsets.UTF_8);\n\t\t\tassertThat(contents).isEqualTo(\"This file was unintentionally left blank.\\n\");\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid scanForClassesInPackageForNullBasePackage() {\n\t\tassertPreconditionViolationFor(() -> classpathScanner.scanForClassesInPackage(null, allClasses));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid scanForResourcesInPackageForNullBasePackage() {\n\t\tassertPreconditionViolationFor(() -> classpathScanner.scanForResourcesInPackage(null, allResources));\n\t}\n\n\t@Test\n\tvoid scanForClassesInPackageForWhitespaceBasePackage() {\n\t\tassertPreconditionViolationFor(() -> classpathScanner.scanForClassesInPackage(\"    \", allClasses));\n\t}\n\n\t@Test\n\tvoid scanForResourcesInPackageForWhitespaceBasePackage() {\n\t\tassertPreconditionViolationFor(() -> classpathScanner.scanForResourcesInPackage(\"    \", allResources));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid scanForClassesInPackageForNullClassFilter() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> classpathScanner.scanForClassesInPackage(\"org.junit.platform.commons\", null));\n\t}\n\n\t@Test\n\tvoid scanForClassesInPackageWhenIOExceptionOccurs() {\n\t\tvar scanner = new DefaultClasspathScanner(ThrowingClassLoader::new, ReflectionUtils::tryToLoadClass);\n\t\tvar classes = scanner.scanForClassesInPackage(\"org.junit.platform.commons\", allClasses);\n\t\tassertThat(classes).isEmpty();\n\t}\n\n\t@Test\n\tvoid scanForResourcesInPackageWhenIOExceptionOccurs() {\n\t\tvar scanner = new DefaultClasspathScanner(ThrowingClassLoader::new, ReflectionUtils::tryToLoadClass);\n\t\tvar classes = scanner.scanForResourcesInPackage(\"org.junit.platform.commons\", allResources);\n\t\tassertThat(classes).isEmpty();\n\t}\n\n\t@Test\n\tvoid scanForClassesInPackageOnlyLoadsClassesThatAreIncludedByTheClassNameFilter() {\n\t\tPredicate<String> classNameFilter = name -> DefaultClasspathScannerTests.class.getName().equals(name);\n\t\tvar classFilter = ClassFilter.of(classNameFilter, type -> true);\n\n\t\tclasspathScanner.scanForClassesInPackage(\"org.junit.platform.commons\", classFilter);\n\n\t\tassertThat(loadedClasses).containsExactly(DefaultClasspathScannerTests.class);\n\t}\n\n\t@Test\n\tvoid findAllClassesInClasspathRoot() throws Exception {\n\t\tvar thisClassOnly = ClassFilter.of(clazz -> clazz == DefaultClasspathScannerTests.class);\n\t\tvar root = getTestClasspathRoot();\n\t\tvar classes = classpathScanner.scanForClassesInClasspathRoot(root, thisClassOnly);\n\t\tassertSame(DefaultClasspathScannerTests.class, classes.getFirst());\n\t}\n\n\t@Test\n\tvoid findAllClassesInDefaultPackageInClasspathRoot() throws Exception {\n\t\tvar classFilter = ClassFilter.of(this::inDefaultPackage);\n\t\tvar classes = classpathScanner.scanForClassesInClasspathRoot(getTestClasspathRoot(), classFilter);\n\n\t\tassertEquals(1, classes.size(), \"number of classes found in default package\");\n\t\tvar testClass = classes.getFirst();\n\t\tassertTrue(inDefaultPackage(testClass));\n\t\tassertEquals(\"DefaultPackageTestCase\", testClass.getName());\n\t}\n\n\t@Test\n\tvoid doesNotLoopInfinitelyWithCircularSymlinks(@TempDir Path tempDir) throws Exception {\n\n\t\t// Abort if running on Microsoft Windows since we are testing symbolic links\n\t\tassumeFalse(System.getProperty(\"os.name\").toLowerCase().contains(\"win\"));\n\n\t\tvar directory = Files.createDirectory(tempDir.resolve(\"directory\"));\n\t\tvar symlink1 = Files.createSymbolicLink(tempDir.resolve(\"symlink1\"), directory);\n\t\tFiles.createSymbolicLink(directory.resolve(\"symlink2\"), symlink1);\n\n\t\tvar classes = classpathScanner.scanForClassesInClasspathRoot(symlink1.toUri(), allClasses);\n\n\t\tassertThat(classes).isEmpty();\n\t}\n\n\tprivate boolean inDefaultPackage(Class<?> clazz) {\n\t\t// OpenJDK returns NULL for the default package.\n\t\tvar pkg = clazz.getPackage();\n\t\treturn pkg == null || clazz.getPackage().getName().isEmpty();\n\t}\n\n\tprivate boolean inDefaultPackage(Resource resource) {\n\t\treturn !resource.getName().contains(\"/\");\n\t}\n\n\t@Test\n\tvoid findAllClassesInClasspathRootWithFilter() throws Exception {\n\t\tvar root = getTestClasspathRoot();\n\t\tvar classes = classpathScanner.scanForClassesInClasspathRoot(root, allClasses);\n\n\t\tassertThat(classes).hasSizeGreaterThanOrEqualTo(20);\n\t\tassertTrue(classes.contains(DefaultClasspathScannerTests.class));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllClassesInClasspathRootForNullRoot() {\n\t\tassertPreconditionViolationFor(() -> classpathScanner.scanForClassesInClasspathRoot(null, allClasses));\n\t}\n\n\t@Test\n\tvoid findAllClassesInClasspathRootForNonExistingRoot() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> classpathScanner.scanForClassesInClasspathRoot(Path.of(\"does_not_exist\").toUri(), allClasses));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid findAllClassesInClasspathRootForNullClassFilter() {\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> classpathScanner.scanForClassesInClasspathRoot(getTestClasspathRoot(), null));\n\t}\n\n\t@Test\n\tvoid onlyLoadsClassesInClasspathRootThatAreIncludedByTheClassNameFilter() throws Exception {\n\t\tvar classFilter = ClassFilter.of(name -> DefaultClasspathScannerTests.class.getName().equals(name),\n\t\t\ttype -> true);\n\t\tvar root = getTestClasspathRoot();\n\n\t\tclasspathScanner.scanForClassesInClasspathRoot(root, classFilter);\n\n\t\tassertThat(loadedClasses).containsExactly(DefaultClasspathScannerTests.class);\n\t}\n\n\tprivate static URI uriOf(String name) {\n\t\tvar resource = DefaultClasspathScannerTests.class.getResource(name);\n\t\ttry {\n\t\t\treturn requireNonNull(resource).toURI();\n\t\t}\n\t\tcatch (URISyntaxException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tprivate URI getTestClasspathRoot() throws Exception {\n\t\tvar location = getClass().getProtectionDomain().getCodeSource().getLocation();\n\t\treturn location.toURI();\n\t}\n\n\tprivate URI getTestClasspathResourceRoot() {\n\t\t// Gradle puts classes and resources in different roots.\n\t\tvar defaultPackageResource = \"/default-package.resource\";\n\t\tvar resourceUri = requireNonNull(getClass().getResource(defaultPackageResource)).toString();\n\t\treturn URI.create(resourceUri.substring(0, resourceUri.length() - defaultPackageResource.length()));\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tclass MemberClassToBeFound {\n\t}\n\n\tstatic class NestedClassToBeFound {\n\t}\n\n\tstatic class ClassForMalformedClassNameSimulation {\n\t}\n\n\tstatic class ClassForOtherInternalErrorSimulation {\n\t}\n\n\tstatic class ClassForGenericRuntimeExceptionSimulation {\n\t}\n\n\tstatic class ClassForOutOfMemoryErrorSimulation {\n\t}\n\n\tprivate static class ThrowingClassLoader extends ClassLoader {\n\n\t\t@Override\n\t\tpublic Enumeration<URL> getResources(String name) throws IOException {\n\t\t\tthrow new IOException(\"Demo I/O error\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ExceptionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.ExceptionUtils.findNestedThrowables;\nimport static org.junit.platform.commons.util.ExceptionUtils.pruneStackTrace;\nimport static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Unit tests for {@link ExceptionUtils}.\n *\n * @since 1.0\n */\n@SuppressWarnings(\"ThrowableNotThrown\")\nclass ExceptionUtilsTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid throwAsUncheckedExceptionWithNullException() {\n\t\tassertPreconditionViolationFor(() -> throwAsUncheckedException(null));\n\t}\n\n\t@Test\n\tvoid throwAsUncheckedExceptionWithCheckedException() {\n\t\tassertThrows(IOException.class, () -> throwAsUncheckedException(new IOException()));\n\t}\n\n\t@Test\n\tvoid throwAsUncheckedExceptionWithUncheckedException() {\n\t\tassertThrows(RuntimeException.class, () -> throwAsUncheckedException(new NumberFormatException()));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid readStackTraceForNullThrowable() {\n\t\tassertPreconditionViolationFor(() -> readStackTrace(null));\n\t}\n\n\t@Test\n\tvoid readStackTraceForLocalJUnitException() {\n\t\ttry {\n\t\t\tthrow new JUnitException(\"expected\");\n\t\t}\n\t\tcatch (JUnitException e) {\n\t\t\t// @formatter:off\n\t\t\tassertThat(readStackTrace(e))\n\t\t\t\t.startsWith(JUnitException.class.getName() + \": expected\")\n\t\t\t\t.contains(\"at \" + ExceptionUtilsTests.class.getName());\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"org.junit.\", \"jdk.internal.reflect.\", \"sun.reflect.\" })\n\tvoid pruneStackTraceOfCallsFromSpecificPackage(String shouldBePruned) {\n\t\tJUnitException exception = new JUnitException(\"expected\");\n\n\t\tpruneStackTrace(exception, List.of());\n\n\t\tassertThat(exception.getStackTrace()) //\n\t\t\t\t.noneMatch(element -> element.toString().contains(shouldBePruned));\n\t}\n\n\t@Test\n\tvoid pruneStackTraceOfAllLauncherCalls() {\n\t\tJUnitException exception = new JUnitException(\"expected\");\n\n\t\tpruneStackTrace(exception, List.of());\n\n\t\tassertThat(exception.getStackTrace()) //\n\t\t\t\t.noneMatch(element -> element.toString().contains(\"org.junit.platform.launcher.\"));\n\t}\n\n\t@Test\n\tvoid pruneStackTraceOfEverythingPriorToFirstLauncherCall() {\n\t\tJUnitException exception = new JUnitException(\"expected\");\n\t\tStackTraceElement[] stackTrace = exception.getStackTrace();\n\t\tstackTrace[stackTrace.length - 1] = new StackTraceElement(\"org.example.Class\", \"method\", \"file\", 123);\n\t\texception.setStackTrace(stackTrace);\n\n\t\tpruneStackTrace(exception, List.of());\n\n\t\tassertThat(exception.getStackTrace()) //\n\t\t\t\t.noneMatch(element -> element.toString().contains(\"org.example.Class.method(file:123)\"));\n\t}\n\n\t@Test\n\tvoid pruneStackTraceRetainsStackFramesFromJUnitStart() {\n\t\t// Non-test class frames from org.junit are filtered.\n\t\tvar testClassName = \"com.example.project.HelloTest\";\n\t\tvar testFileName = \"HelloTest.java\";\n\n\t\tvar exception = new JUnitException(\"expected\");\n\t\tvar stackTrace = exception.getStackTrace();\n\t\tvar extendedStacktrace = Arrays.copyOfRange(stackTrace, 0, stackTrace.length + 2);\n\t\textendedStacktrace[0] = new StackTraceElement(testClassName, \"stringLength\", testFileName, 10);\n\t\textendedStacktrace[stackTrace.length] = new StackTraceElement(\"org.junit.start.JUnit\", \"run\", \"JUnit.java\", 3);\n\t\textendedStacktrace[stackTrace.length + 1] = new StackTraceElement(testClassName, \"main\", testFileName, 5);\n\t\texception.setStackTrace(extendedStacktrace);\n\n\t\tpruneStackTrace(exception, List.of(testClassName));\n\n\t\tassertThat(exception.getStackTrace()) //\n\t\t\t\t.extracting(StackTraceElement::toString) //\n\t\t\t\t.containsExactly( //\n\t\t\t\t\t\"com.example.project.HelloTest.stringLength(HelloTest.java:10)\", //\n\t\t\t\t\t\"org.junit.start.JUnit.run(JUnit.java:3)\", //\n\t\t\t\t\t\"com.example.project.HelloTest.main(HelloTest.java:5)\");\n\t}\n\n\t@Test\n\tvoid findSuppressedExceptionsAndCausesOfThrowable() {\n\t\tThrowable t1 = new Throwable(\"#1\");\n\t\tThrowable t2 = new Throwable(\"#2\");\n\t\tThrowable t3 = new Throwable(\"#3\");\n\t\tThrowable t4 = new Throwable(\"#4\");\n\t\tThrowable t5 = new Throwable(\"#5\");\n\t\tt1.initCause(t2);\n\t\tt2.initCause(t3);\n\t\tt1.addSuppressed(t4);\n\t\tt2.addSuppressed(t5);\n\n\t\tassertThat(findNestedThrowables(t1)) //\n\t\t\t\t.extracting(Throwable::getMessage) //\n\t\t\t\t.containsExactlyInAnyOrder(\"#1\", \"#2\", \"#3\", \"#4\", \"#5\");\n\t}\n\n\t@Test\n\tvoid avoidCyclesWhileSearchingForNestedThrowables() {\n\t\tThrowable t1 = new Throwable();\n\t\tThrowable t2 = new Throwable(t1);\n\t\tThrowable t3 = new Throwable(t1);\n\t\tt1.initCause(t2);\n\t\tt1.addSuppressed(t3);\n\t\tt2.addSuppressed(t3);\n\n\t\tassertThat(findNestedThrowables(t1)).hasSize(3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/FunctionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.function.Predicate.isEqual;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link FunctionUtils}.\n *\n * @since 1.0\n */\nclass FunctionUtilsTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid whereWithNullFunction() {\n\t\tassertPreconditionViolationNotNullFor(\"function\", () -> FunctionUtils.where(null, o -> true));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid whereWithNullPredicate() {\n\t\tassertPreconditionViolationNotNullFor(\"predicate\", () -> FunctionUtils.where(o -> o, null));\n\t}\n\n\t@Test\n\tvoid whereWithChecksPredicateAgainstResultOfFunction() {\n\t\tvar combinedPredicate = FunctionUtils.where(String::length, isEqual(3));\n\t\tassertFalse(combinedPredicate.test(\"fo\"));\n\t\tassertTrue(combinedPredicate.test(\"foo\"));\n\t\tassertFalse(combinedPredicate.test(\"fooo\"));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/LruCacheTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.6\n */\nclass LruCacheTests {\n\n\t@Test\n\tvoid evictsEldestEntryWhenMaxSizeIsReached() {\n\t\tvar cache = new LruCache<Integer, Integer>(1);\n\n\t\tcache.put(0, 0);\n\t\tcache.put(1, 1);\n\n\t\tassertThat(cache) //\n\t\t\t\t.doesNotContain(entry(0, 0)) //\n\t\t\t\t.hasSize(1);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/PackageUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotBlankFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\n\nimport java.util.List;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.function.Executable;\nimport org.opentest4j.ValueWrapper;\n\n/**\n * Unit tests for {@link PackageUtils}.\n *\n * @since 1.0\n */\nclass PackageUtilsTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getAttributeWithNullType() {\n\t\tassertPreconditionViolationNotNullFor(\"type\", () -> PackageUtils.getAttribute(null, p -> \"any\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getAttributeWithNullFunction() {\n\t\tassertPreconditionViolationNotNullFor(\"function\",\n\t\t\t() -> PackageUtils.getAttribute(getClass(), (Function<Package, String>) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getAttributeWithFunctionReturningNullIsEmpty() {\n\t\tassertFalse(PackageUtils.getAttribute(ValueWrapper.class, p -> null).isPresent());\n\t}\n\n\t@Test\n\tvoid getAttributeFromDefaultPackageMemberIsEmpty() throws Exception {\n\t\tvar classInDefaultPackage = ReflectionUtils.tryToLoadClass(\"DefaultPackageTestCase\").getNonNull();\n\t\tassertFalse(PackageUtils.getAttribute(classInDefaultPackage, Package::getSpecificationTitle).isPresent());\n\t}\n\n\t@TestFactory\n\tList<DynamicTest> attributesFromValueWrapperClassArePresent() {\n\t\treturn List.of( //\n\t\t\tdynamicTest(\"getName\", isPresent(Package::getName)),\n\t\t\tdynamicTest(\"getImplementationTitle\", isPresent(Package::getImplementationTitle)),\n\t\t\tdynamicTest(\"getImplementationVendor\", isPresent(Package::getImplementationVendor)),\n\t\t\tdynamicTest(\"getImplementationVersion\", isPresent(Package::getImplementationVersion)),\n\t\t\tdynamicTest(\"getSpecificationTitle\", isPresent(Package::getSpecificationTitle)),\n\t\t\tdynamicTest(\"getSpecificationVendor\", isPresent(Package::getSpecificationVendor)),\n\t\t\tdynamicTest(\"getSpecificationVersion\", isPresent(Package::getSpecificationVersion)) //\n\t\t);\n\t}\n\n\tprivate Executable isPresent(Function<Package, String> function) {\n\t\treturn () -> assertTrue(PackageUtils.getAttribute(ValueWrapper.class, function).isPresent());\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getAttributeWithNullTypeAndName() {\n\t\tassertPreconditionViolationNotNullFor(\"type\", () -> PackageUtils.getAttribute(null, \"foo\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getAttributeWithNullName() {\n\t\tassertPreconditionViolationNotBlankFor(\"name\", () -> PackageUtils.getAttribute(getClass(), (String) null));\n\t}\n\n\t@Test\n\tvoid getAttributeWithEmptyName() {\n\t\tassertPreconditionViolationNotBlankFor(\"name\", () -> PackageUtils.getAttribute(getClass(), \"\"));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/PreconditionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.util.Collections.singletonList;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.Preconditions.condition;\nimport static org.junit.platform.commons.util.Preconditions.containsNoBlankElements;\nimport static org.junit.platform.commons.util.Preconditions.containsNoNullElements;\nimport static org.junit.platform.commons.util.Preconditions.notBlank;\nimport static org.junit.platform.commons.util.Preconditions.notEmpty;\nimport static org.junit.platform.commons.util.Preconditions.notNull;\n\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link Preconditions}.\n *\n * @since 1.0\n */\nclass PreconditionsTests {\n\n\t@Test\n\tvoid notNullPassesForNonNullObject() {\n\t\tvar object = new Object();\n\t\tvar nonNullObject = notNull(object, \"message\");\n\t\tassertSame(object, nonNullObject);\n\t}\n\n\t@Test\n\tvoid notNullThrowsForNullObject() {\n\t\tvar message = \"argument is null\";\n\n\t\tassertPreconditionViolationFor(() -> notNull(null, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notNullThrowsForNullObjectAndMessageSupplier() {\n\t\tvar message = \"argument is null\";\n\t\tObject object = null;\n\n\t\tassertPreconditionViolationFor(() -> notNull(object, () -> message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notEmptyPassesForNonEmptyArray() {\n\t\tvar array = new String[] { \"a\", \"b\", \"c\" };\n\t\tvar nonEmptyArray = notEmpty(array, () -> \"should not fail\");\n\t\tassertSame(array, nonEmptyArray);\n\t}\n\n\t@Test\n\tvoid notEmptyPassesForNonEmptyCollection() {\n\t\tCollection<String> collection = List.of(\"a\", \"b\", \"c\");\n\t\tvar nonEmptyCollection = notEmpty(collection, () -> \"should not fail\");\n\t\tassertSame(collection, nonEmptyCollection);\n\t}\n\n\t@Test\n\tvoid notEmptyPassesForArrayWithNullElements() {\n\t\tnotEmpty(new String[] { null }, \"message\");\n\t}\n\n\t@Test\n\tvoid notEmptyPassesForCollectionWithNullElements() {\n\t\tnotEmpty(singletonListOfNull(), \"message\");\n\t}\n\n\t@Test\n\tvoid notEmptyThrowsForNullArray() {\n\t\tvar message = \"array is empty\";\n\n\t\tassertPreconditionViolationFor(() -> notEmpty((Object[]) null, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notEmptyThrowsForNullCollection() {\n\t\tvar message = \"collection is empty\";\n\n\t\tassertPreconditionViolationFor(() -> notEmpty((Collection<?>) null, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notEmptyThrowsForEmptyArray() {\n\t\tvar message = \"array is empty\";\n\n\t\tassertPreconditionViolationFor(() -> notEmpty(new Object[0], message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notEmptyThrowsForEmptyCollection() {\n\t\tvar message = \"collection is empty\";\n\n\t\tassertPreconditionViolationFor(() -> notEmpty(List.of(), message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid containsNoBlankElementsPassesForCollectionThatIsNullOrEmpty() {\n\t\tcontainsNoBlankElements((List<String>) null, \"collection is null\");\n\t\tcontainsNoBlankElements(List.of(), \"collection is empty\");\n\n\t\tcontainsNoBlankElements((List<String>) null, () -> \"collection is null\");\n\t\tcontainsNoBlankElements(List.of(), () -> \"collection is empty\");\n\t}\n\n\t@Test\n\tvoid containsNoBlankElementsPassesForCollectionContainingNonBlankElements() {\n\t\tvar input = List.of(\"a\", \"b\", \"c\");\n\t\tvar output = containsNoBlankElements(input, \"message\");\n\t\tassertSame(input, output);\n\t}\n\n\t@Test\n\tvoid containsNoBlankElementsThrowsForCollectionContainingNullElements() {\n\t\tvar message = \"collection contains blank elements\";\n\n\t\tassertPreconditionViolationFor(() -> containsNoBlankElements(singletonList(\"\"), message)) //\n\t\t\t\t.withMessage(message);\n\t\tassertPreconditionViolationFor(() -> containsNoBlankElements(singletonList((String) null), message)) //\n\t\t\t\t.withMessage(message);\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsPassesForArrayThatIsNullOrEmpty() {\n\t\tcontainsNoNullElements((Object[]) null, \"array is null\");\n\t\tcontainsNoNullElements((Object[]) null, () -> \"array is null\");\n\n\t\tcontainsNoNullElements(new Object[0], \"array is empty\");\n\t\tcontainsNoNullElements(new Object[0], () -> \"array is empty\");\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsPassesForCollectionThatIsNullOrEmpty() {\n\t\tcontainsNoNullElements((List<?>) null, \"collection is null\");\n\t\tcontainsNoNullElements(List.of(), \"collection is empty\");\n\n\t\tcontainsNoNullElements((List<?>) null, () -> \"collection is null\");\n\t\tcontainsNoNullElements(List.of(), () -> \"collection is empty\");\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsPassesForArrayContainingNonNullElements() {\n\t\tvar input = new String[] { \"a\", \"b\", \"c\" };\n\t\tvar output = containsNoNullElements(input, \"message\");\n\t\tassertSame(input, output);\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsPassesForCollectionContainingNonNullElements() {\n\t\tvar input = List.of(\"a\", \"b\", \"c\");\n\t\tvar output = containsNoNullElements(input, \"message\");\n\t\tassertSame(input, output);\n\n\t\toutput = containsNoNullElements(input, () -> \"message\");\n\t\tassertSame(input, output);\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsThrowsForArrayContainingNullElements() {\n\t\tvar message = \"array contains null elements\";\n\t\tObject[] array = { new Object(), null, new Object() };\n\n\t\tassertPreconditionViolationFor(() -> containsNoNullElements(array, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid containsNoNullElementsThrowsForCollectionContainingNullElements() {\n\t\tvar message = \"collection contains null elements\";\n\n\t\tassertPreconditionViolationFor(() -> containsNoNullElements(singletonListOfNull(), message)).withMessage(\n\t\t\tmessage);\n\t}\n\n\t@Test\n\tvoid notBlankPassesForNonBlankString() {\n\t\tvar string = \"abc\";\n\t\tvar nonBlankString = notBlank(string, \"message\");\n\t\tassertSame(string, nonBlankString);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForNullString() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(null, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForNullStringWithMessageSupplier() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(null, () -> message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForEmptyString() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(\"\", message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForEmptyStringWithMessageSupplier() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(\"\", () -> message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForBlankString() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(\"          \", message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid notBlankThrowsForBlankStringWithMessageSupplier() {\n\t\tvar message = \"string shouldn't be blank\";\n\n\t\tassertPreconditionViolationFor(() -> notBlank(\"          \", () -> message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid conditionPassesForTruePredicate() {\n\t\tcondition(true, \"error message\");\n\t}\n\n\t@Test\n\tvoid conditionPassesForTruePredicateWithMessageSupplier() {\n\t\tcondition(true, () -> \"error message\");\n\t}\n\n\t@Test\n\tvoid conditionThrowsForFalsePredicate() {\n\t\tvar message = \"condition does not hold\";\n\n\t\tassertPreconditionViolationFor(() -> condition(false, message)).withMessage(message);\n\t}\n\n\t@Test\n\tvoid conditionThrowsForFalsePredicateWithMessageSupplier() {\n\t\tvar message = \"condition does not hold\";\n\n\t\tassertPreconditionViolationFor(() -> condition(false, () -> message)).withMessage(message);\n\t}\n\n\tprivate static List<@Nullable Object> singletonListOfNull() {\n\t\treturn singletonList(null);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static java.time.Duration.ofMillis;\nimport static java.util.Arrays.stream;\nimport static java.util.stream.Collectors.joining;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrBlankFor;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP;\nimport static org.junit.platform.commons.util.ReflectionUtils.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.util.ReflectionUtils.findFields;\nimport static org.junit.platform.commons.util.ReflectionUtils.findMethod;\nimport static org.junit.platform.commons.util.ReflectionUtils.findMethods;\nimport static org.junit.platform.commons.util.ReflectionUtils.invokeMethod;\nimport static org.junit.platform.commons.util.ReflectionUtils.isWideningConversion;\nimport static org.junit.platform.commons.util.ReflectionUtils.readFieldValues;\nimport static org.junit.platform.commons.util.ReflectionUtils.tryToReadFieldValue;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Predicate;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Assertions;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.util.ClearSystemProperty;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.commons.util.ReflectionUtils.CycleErrorHandling;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested1;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested2;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.ClassWithNestedClasses.Nested3;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.Interface45.Nested5;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.InterfaceWithNestedClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.NestedClassTests.InterfaceWithNestedClass.Nested4;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerClass.RecursiveInnerInnerClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.InnerSiblingClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.RecursiveInnerClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.StaticNestedClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClass.StaticNestedSiblingClass;\nimport org.junit.platform.commons.util.ReflectionUtilsTests.OuterClassImplementingInterface.InnerClassImplementingInterface;\nimport org.junit.platform.commons.util.classes.CustomType;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField;\nimport org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateBeforeMethod;\nimport org.junit.platform.commons.util.pkg1.subpkg.SubclassWithNonStaticPackagePrivateTempDirField;\n\n/**\n * Unit tests for {@link ReflectionUtils}.\n *\n * @since 1.0\n */\nclass ReflectionUtilsTests {\n\n\tprivate static final Predicate<Method> isFooMethod = method -> method.getName().equals(\"foo\");\n\tprivate static final Predicate<Method> methodContains1 = method -> method.getName().contains(\"1\");\n\tprivate static final Predicate<Method> methodContains2 = method -> method.getName().contains(\"2\");\n\tprivate static final Predicate<Method> methodContains4 = method -> method.getName().contains(\"4\");\n\tprivate static final Predicate<Method> methodContains5 = method -> method.getName().contains(\"5\");\n\n\t@Nested\n\tclass MiscellaneousTests {\n\n\t\t@Test\n\t\tvoid returnsPrimitiveVoid() throws Exception {\n\t\t\tClass<?> clazz = ClassWithVoidAndNonVoidMethods.class;\n\t\t\tassertTrue(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod(\"voidMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod(\"methodReturningVoidReference\")));\n\t\t\tassertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod(\"methodReturningObject\")));\n\t\t\tassertFalse(ReflectionUtils.returnsPrimitiveVoid(clazz.getDeclaredMethod(\"methodReturningPrimitive\")));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid getAllAssignmentCompatibleClassesWithNullClass() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getAllAssignmentCompatibleClasses(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid getAllAssignmentCompatibleClasses() {\n\t\t\tvar superclasses = ReflectionUtils.getAllAssignmentCompatibleClasses(B.class);\n\t\t\tassertThat(superclasses).containsExactly(B.class, InterfaceC.class, InterfaceA.class, InterfaceB.class,\n\t\t\t\tA.class, InterfaceD.class, Object.class);\n\t\t\tassertTrue(superclasses.stream().allMatch(clazz -> clazz.isAssignableFrom(B.class)));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid newInstance() {\n\t\t\t// @formatter:off\n\t\t\tassertThat(ReflectionUtils.newInstance(C.class, \"one\", \"two\")).isNotNull();\n\t\t\tassertThat(ReflectionUtils.newInstance(C.class)).isNotNull();\n\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.newInstance(C.class, \"one\", null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.newInstance(C.class, null, \"two\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.newInstance(C.class, null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.newInstance(C.class, ((Object[]) null)));\n\n\t\t\tvar exception = assertThrows(RuntimeException.class, () -> ReflectionUtils.newInstance(Exploder.class));\n\t\t\tassertThat(exception).hasMessage(\"boom\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid wideningConversion() {\n\t\t\t// byte\n\t\t\tassertTrue(isWideningConversion(byte.class, short.class));\n\t\t\tassertTrue(isWideningConversion(byte.class, int.class));\n\t\t\tassertTrue(isWideningConversion(byte.class, long.class));\n\t\t\tassertTrue(isWideningConversion(byte.class, float.class));\n\t\t\tassertTrue(isWideningConversion(byte.class, double.class));\n\t\t\t// Byte\n\t\t\tassertTrue(isWideningConversion(Byte.class, short.class));\n\t\t\tassertTrue(isWideningConversion(Byte.class, int.class));\n\t\t\tassertTrue(isWideningConversion(Byte.class, long.class));\n\t\t\tassertTrue(isWideningConversion(Byte.class, float.class));\n\t\t\tassertTrue(isWideningConversion(Byte.class, double.class));\n\n\t\t\t// short\n\t\t\tassertTrue(isWideningConversion(short.class, int.class));\n\t\t\tassertTrue(isWideningConversion(short.class, long.class));\n\t\t\tassertTrue(isWideningConversion(short.class, float.class));\n\t\t\tassertTrue(isWideningConversion(short.class, double.class));\n\t\t\t// Short\n\t\t\tassertTrue(isWideningConversion(Short.class, int.class));\n\t\t\tassertTrue(isWideningConversion(Short.class, long.class));\n\t\t\tassertTrue(isWideningConversion(Short.class, float.class));\n\t\t\tassertTrue(isWideningConversion(Short.class, double.class));\n\n\t\t\t// char\n\t\t\tassertTrue(isWideningConversion(char.class, int.class));\n\t\t\tassertTrue(isWideningConversion(char.class, long.class));\n\t\t\tassertTrue(isWideningConversion(char.class, float.class));\n\t\t\tassertTrue(isWideningConversion(char.class, double.class));\n\t\t\t// Character\n\t\t\tassertTrue(isWideningConversion(Character.class, int.class));\n\t\t\tassertTrue(isWideningConversion(Character.class, long.class));\n\t\t\tassertTrue(isWideningConversion(Character.class, float.class));\n\t\t\tassertTrue(isWideningConversion(Character.class, double.class));\n\n\t\t\t// int\n\t\t\tassertTrue(isWideningConversion(int.class, long.class));\n\t\t\tassertTrue(isWideningConversion(int.class, float.class));\n\t\t\tassertTrue(isWideningConversion(int.class, double.class));\n\t\t\t// Integer\n\t\t\tassertTrue(isWideningConversion(Integer.class, long.class));\n\t\t\tassertTrue(isWideningConversion(Integer.class, float.class));\n\t\t\tassertTrue(isWideningConversion(Integer.class, double.class));\n\n\t\t\t// long\n\t\t\tassertTrue(isWideningConversion(long.class, float.class));\n\t\t\tassertTrue(isWideningConversion(long.class, double.class));\n\t\t\t// Long\n\t\t\tassertTrue(isWideningConversion(Long.class, float.class));\n\t\t\tassertTrue(isWideningConversion(Long.class, double.class));\n\n\t\t\t// float\n\t\t\tassertTrue(isWideningConversion(float.class, double.class));\n\t\t\t// Float\n\t\t\tassertTrue(isWideningConversion(Float.class, double.class));\n\n\t\t\t// double and Double --> nothing to test\n\n\t\t\t// Unsupported\n\t\t\tassertFalse(isWideningConversion(int.class, byte.class)); // narrowing\n\t\t\tassertFalse(isWideningConversion(float.class, int.class)); // narrowing\n\t\t\tassertFalse(isWideningConversion(int.class, int.class)); // direct match\n\t\t\tassertFalse(isWideningConversion(String.class, int.class)); // neither a primitive nor a wrapper\n\t\t}\n\n\t\t@Test\n\t\tvoid getWrapperType() {\n\t\t\tassertEquals(Boolean.class, ReflectionUtils.getWrapperType(boolean.class));\n\t\t\tassertEquals(Byte.class, ReflectionUtils.getWrapperType(byte.class));\n\t\t\tassertEquals(Character.class, ReflectionUtils.getWrapperType(char.class));\n\t\t\tassertEquals(Short.class, ReflectionUtils.getWrapperType(short.class));\n\t\t\tassertEquals(Integer.class, ReflectionUtils.getWrapperType(int.class));\n\t\t\tassertEquals(Long.class, ReflectionUtils.getWrapperType(long.class));\n\t\t\tassertEquals(Float.class, ReflectionUtils.getWrapperType(float.class));\n\t\t\tassertEquals(Double.class, ReflectionUtils.getWrapperType(double.class));\n\t\t\tassertNull(ReflectionUtils.getWrapperType(Object.class));\n\t\t}\n\n\t\t@Test\n\t\t@ClearSystemProperty(key = \"java.class.path\")\n\t\tvoid getAllClasspathRootDirectories(@TempDir Path tempDirectory) throws Exception {\n\t\t\tvar root1 = tempDirectory.resolve(\"root1\").toAbsolutePath();\n\t\t\tvar root2 = tempDirectory.resolve(\"root2\").toAbsolutePath();\n\t\t\tvar testClassPath = root1 + File.pathSeparator + root2;\n\n\t\t\tSystem.setProperty(\"java.class.path\", testClassPath);\n\t\t\tcreateDirectories(root1, root2);\n\t\t\tassertThat(ReflectionUtils.getAllClasspathRootDirectories()).containsOnly(root1, root2);\n\t\t}\n\n\t\tprivate static void createDirectories(Path... paths) throws IOException {\n\t\t\tfor (var path : paths) {\n\t\t\t\tFiles.createDirectory(path);\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid getDeclaredConstructorPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getDeclaredConstructor(null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getDeclaredConstructor(ClassWithTwoConstructors.class));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid getDeclaredConstructor() {\n\t\t\tConstructor<?> constructor = ReflectionUtils.getDeclaredConstructor(getClass());\n\t\t\tassertNotNull(constructor);\n\t\t\tassertEquals(getClass(), constructor.getDeclaringClass());\n\n\t\t\tconstructor = ReflectionUtils.getDeclaredConstructor(ClassWithOneCustomConstructor.class);\n\t\t\tassertNotNull(constructor);\n\t\t\tassertEquals(ClassWithOneCustomConstructor.class, constructor.getDeclaringClass());\n\t\t\tassertEquals(String.class, constructor.getParameterTypes()[0]);\n\t\t}\n\n\t\t@Test\n\t\tvoid isGeneric() {\n\t\t\tfor (var method : Generic.class.getMethods()) {\n\t\t\t\tassertTrue(ReflectionUtils.isGeneric(method));\n\t\t\t}\n\t\t\tfor (var method : NonGenericClass.class.getMethods()) {\n\t\t\t\tassertFalse(ReflectionUtils.isGeneric(method));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3684\">GitHub - Issue #3684</a>\n\t\t */\n\t\t@Test\n\t\tvoid getInterfaceMethodIfPossible() throws Exception {\n\t\t\t// \"anonymous\" because it's implemented as an anonymous class.\n\t\t\tInputStream anonymousInputStream = InputStream.nullInputStream();\n\t\t\tClass<?> targetType = anonymousInputStream.getClass();\n\t\t\tassertThat(targetType.isAnonymousClass()).isTrue();\n\n\t\t\tMethod method = targetType.getMethod(\"close\");\n\t\t\tassertThat(method).isNotNull();\n\t\t\tassertThat(method.getDeclaringClass()).isEqualTo(targetType);\n\n\t\t\tMethod interfaceMethod = ReflectionUtils.getInterfaceMethodIfPossible(method, targetType);\n\t\t\tassertThat(interfaceMethod).isNotNull().isNotEqualTo(method);\n\t\t\t// InputStream implements Closeable directly, so we find the `close` method\n\t\t\t// in Closeable instead of AutoCloseable.\n\t\t\tassertThat(interfaceMethod.getDeclaringClass()).isEqualTo(Closeable.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid isRecordObject() {\n\t\t\tassertTrue(ReflectionUtils.isRecordObject(new SomeRecord(1)));\n\t\t\tassertFalse(ReflectionUtils.isRecordObject(new ClassWithOneCustomConstructor(\"\")));\n\t\t\tassertFalse(ReflectionUtils.isRecordObject(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid isRecordClass() {\n\t\t\tassertTrue(ReflectionUtils.isRecordClass(SomeRecord.class));\n\t\t\tassertFalse(ReflectionUtils.isRecordClass(ClassWithOneCustomConstructor.class));\n\t\t\tassertFalse(ReflectionUtils.isRecordClass(Object.class));\n\t\t}\n\n\t\trecord SomeRecord(int n) {\n\t\t}\n\n\t\tstatic class ClassWithVoidAndNonVoidMethods {\n\n\t\t\tvoid voidMethod() {\n\t\t\t}\n\n\t\t\tVoid methodReturningVoidReference() {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tString methodReturningObject() {\n\t\t\t\treturn \"\";\n\t\t\t}\n\n\t\t\tint methodReturningPrimitive() {\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t}\n\n\t\tstatic class Exploder {\n\n\t\t\tExploder() {\n\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t}\n\n\t\t}\n\n\t\tinterface InterfaceA {\n\t\t}\n\n\t\tinterface InterfaceB {\n\t\t}\n\n\t\tinterface InterfaceC extends InterfaceA, InterfaceB {\n\t\t}\n\n\t\tinterface InterfaceD {\n\t\t}\n\n\t\tstatic class A implements InterfaceA, InterfaceD {\n\t\t}\n\n\t\tstatic class B extends A implements InterfaceC {\n\t\t}\n\n\t\tstatic class C {\n\n\t\t\tC() {\n\t\t\t}\n\n\t\t\tC(String a, String b) {\n\t\t\t}\n\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate static class ClassWithOneCustomConstructor {\n\n\t\t\tClassWithOneCustomConstructor(String str) {\n\t\t\t}\n\t\t}\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tprivate static class ClassWithTwoConstructors {\n\n\t\t\tClassWithTwoConstructors() {\n\t\t\t}\n\n\t\t\tClassWithTwoConstructors(String str) {\n\t\t\t}\n\t\t}\n\n\t\tpublic class NonGenericClass {\n\n\t\t\tpublic void publicMethod() {\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ModifierTests {\n\n\t\t@Test\n\t\tvoid isPublic() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isPublic(PublicClass.class));\n\t\t\tassertTrue(ReflectionUtils.isPublic(PublicClass.class.getMethod(\"publicMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isPublic(PrivateClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPublic(PrivateClass.class.getDeclaredMethod(\"privateMethod\")));\n\t\t\tassertFalse(ReflectionUtils.isPublic(ProtectedClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPublic(ProtectedClass.class.getDeclaredMethod(\"protectedMethod\")));\n\t\t\tassertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPublic(PackageVisibleClass.class.getDeclaredMethod(\"packageVisibleMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isPrivate() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isPrivate(PrivateClass.class));\n\t\t\tassertTrue(ReflectionUtils.isPrivate(PrivateClass.class.getDeclaredMethod(\"privateMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isPrivate(PublicClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPrivate(PublicClass.class.getMethod(\"publicMethod\")));\n\t\t\tassertFalse(ReflectionUtils.isPrivate(ProtectedClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPrivate(ProtectedClass.class.getDeclaredMethod(\"protectedMethod\")));\n\t\t\tassertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class));\n\t\t\tassertFalse(ReflectionUtils.isPrivate(PackageVisibleClass.class.getDeclaredMethod(\"packageVisibleMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isNotPrivate() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isNotPrivate(PublicClass.class));\n\t\t\tassertTrue(ReflectionUtils.isNotPrivate(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\t\t\tassertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class));\n\t\t\tassertTrue(ReflectionUtils.isNotPrivate(ProtectedClass.class.getDeclaredMethod(\"protectedMethod\")));\n\t\t\tassertTrue(ReflectionUtils.isNotPrivate(PackageVisibleClass.class));\n\t\t\tassertTrue(\n\t\t\t\tReflectionUtils.isNotPrivate(PackageVisibleClass.class.getDeclaredMethod(\"packageVisibleMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isNotPrivate(PrivateClass.class.getDeclaredMethod(\"privateMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAbstract() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isAbstract(AbstractClass.class));\n\t\t\tassertTrue(ReflectionUtils.isAbstract(AbstractClass.class.getDeclaredMethod(\"abstractMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isAbstract(PublicClass.class));\n\t\t\tassertFalse(ReflectionUtils.isAbstract(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isStatic() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isStatic(StaticClass.class));\n\t\t\tassertTrue(ReflectionUtils.isStatic(StaticClass.class.getDeclaredMethod(\"staticMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isStatic(PublicClass.class));\n\t\t\tassertFalse(ReflectionUtils.isStatic(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isNotStatic() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isNotStatic(PublicClass.class));\n\t\t\tassertTrue(ReflectionUtils.isNotStatic(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isNotStatic(StaticClass.class));\n\t\t\tassertFalse(ReflectionUtils.isNotStatic(StaticClass.class.getDeclaredMethod(\"staticMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isFinal() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isFinal(FinalClass.class));\n\t\t\tassertTrue(ReflectionUtils.isFinal(FinalClass.class.getDeclaredMethod(\"finalMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isFinal(PublicClass.class));\n\t\t\tassertFalse(ReflectionUtils.isFinal(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid isNotFinal() throws Exception {\n\t\t\tassertTrue(ReflectionUtils.isNotFinal(PublicClass.class));\n\t\t\tassertTrue(ReflectionUtils.isNotFinal(PublicClass.class.getDeclaredMethod(\"publicMethod\")));\n\n\t\t\tassertFalse(ReflectionUtils.isNotFinal(FinalClass.class));\n\t\t\tassertFalse(ReflectionUtils.isNotFinal(FinalClass.class.getDeclaredMethod(\"finalMethod\")));\n\t\t}\n\n\t\t// Intentionally non-static\n\t\tpublic class PublicClass {\n\n\t\t\tpublic void publicMethod() {\n\t\t\t}\n\n\t\t\tpublic void method(String str, Integer num) {\n\t\t\t}\n\n\t\t\tpublic void method(String[] strings, Integer[] nums) {\n\t\t\t}\n\n\t\t\tpublic void method(boolean b, char c) {\n\t\t\t}\n\n\t\t\tpublic void method(char[] characters, int[] nums) {\n\t\t\t}\n\t\t}\n\n\t\tprivate class PrivateClass {\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tprivate void privateMethod() {\n\t\t\t}\n\t\t}\n\n\t\tprotected class ProtectedClass {\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tprotected void protectedMethod() {\n\t\t\t}\n\t\t}\n\n\t\tclass PackageVisibleClass {\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tvoid packageVisibleMethod() {\n\t\t\t}\n\t\t}\n\n\t\tfinal class FinalClass {\n\n\t\t\t@SuppressWarnings({ \"unused\", \"FinalMethodInFinalClass\", \"RedundantModifier\" })\n\t\t\tfinal void finalMethod() {\n\t\t\t}\n\t\t}\n\n\t\tabstract static class AbstractClass {\n\n\t\t\tabstract void abstractMethod();\n\t\t}\n\n\t\tstatic class StaticClass {\n\n\t\t\tstatic void staticMethod() {\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass IsClassAssignableToClassTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid isAssignableToForNullSourceType() {\n\t\t\tassertPreconditionViolationNotNullFor(\"source type\",\n\t\t\t\t() -> ReflectionUtils.isAssignableTo(null, getClass()));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAssignableToForPrimitiveSourceType() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isAssignableTo(int.class, Integer.class))//\n\t\t\t\t\t.withMessage(\"source type must not be a primitive type\");\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid isAssignableToForNullTargetType() {\n\t\t\tassertPreconditionViolationNotNullFor(\"target type\",\n\t\t\t\t() -> ReflectionUtils.isAssignableTo(getClass(), null));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAssignableTo() {\n\t\t\t// Reference Types\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String.class, Object.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String.class, CharSequence.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String.class, String.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Integer.class, Number.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Integer.class, Integer.class));\n\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Object.class, String.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(CharSequence.class, String.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Number.class, Integer.class));\n\n\t\t\t// Arrays\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(int[].class, int[].class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(double[].class, double[].class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(double[].class, Object.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String[].class, Object.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String[].class, Object[].class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(String[].class, String[].class));\n\n\t\t\t// Wrappers to Primitives\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Integer.class, int.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Boolean.class, boolean.class));\n\n\t\t\t// Void to void\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Void.class, void.class));\n\n\t\t\t// Widening Conversions from Wrappers to Primitives\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Integer.class, long.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Float.class, double.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Byte.class, double.class));\n\n\t\t\t// Widening Conversions from Wrappers to Wrappers (not supported by Java)\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Integer.class, Long.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Float.class, Double.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Byte.class, Double.class));\n\n\t\t\t// Narrowing Conversions\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Integer.class, char.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Long.class, byte.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Long.class, int.class));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass IsObjectAssignableToClassTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid isAssignableToForNullClass() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isAssignableTo(new Object(), null));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAssignableTo() {\n\t\t\t// Reference Types\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(\"string\", String.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(\"string\", CharSequence.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(\"string\", Object.class));\n\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(new Object(), String.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(Integer.valueOf(\"1\"), StringBuilder.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(new StringBuilder(), String.class));\n\n\t\t\t// Arrays\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(new int[0], int[].class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(new double[0], Object.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(new String[0], String[].class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(new String[0], Object.class));\n\n\t\t\t// Primitive Types\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(1, int.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Long.valueOf(\"1\"), long.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(Boolean.TRUE, boolean.class));\n\n\t\t\t// Widening Conversions to Primitives\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(1, long.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo(1f, double.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo((byte) 1, double.class));\n\n\t\t\t// Widening Conversions to Wrappers (not supported by Java)\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(1, Long.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(1f, Double.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo((byte) 1, Double.class));\n\n\t\t\t// Narrowing Conversions\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(1, char.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(1L, byte.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo(1L, int.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAssignableToForNullObject() {\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo((Object) null, Object.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo((Object) null, String.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo((Object) null, Long.class));\n\t\t\tassertTrue(ReflectionUtils.isAssignableTo((Object) null, Character[].class));\n\t\t}\n\n\t\t@Test\n\t\tvoid isAssignableToForNullObjectAndPrimitive() {\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo((Object) null, byte.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo((Object) null, int.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo((Object) null, long.class));\n\t\t\tassertFalse(ReflectionUtils.isAssignableTo((Object) null, boolean.class));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass MethodInvocationTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid invokeMethodPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> invokeMethod(null, new Object()));\n\t\t\tassertPreconditionViolationFor(() -> invokeMethod(Object.class.getMethod(\"hashCode\"), null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid invokePublicMethod() throws Exception {\n\t\t\tvar tracker = new InvocationTracker();\n\t\t\tinvokeMethod(InvocationTracker.class.getDeclaredMethod(\"publicMethod\"), tracker);\n\t\t\tassertTrue(tracker.publicMethodInvoked);\n\t\t}\n\n\t\t@Test\n\t\tvoid invokePrivateMethod() throws Exception {\n\t\t\tvar tracker = new InvocationTracker();\n\t\t\tinvokeMethod(InvocationTracker.class.getDeclaredMethod(\"privateMethod\"), tracker);\n\t\t\tassertTrue(tracker.privateMethodInvoked);\n\t\t}\n\n\t\t@Test\n\t\tvoid invokePublicStaticMethod() throws Exception {\n\t\t\tinvokeMethod(InvocationTracker.class.getDeclaredMethod(\"publicStaticMethod\"), null);\n\t\t\tassertTrue(InvocationTracker.publicStaticMethodInvoked);\n\t\t}\n\n\t\t@Test\n\t\tvoid invokePrivateStaticMethod() throws Exception {\n\t\t\tinvokeMethod(InvocationTracker.class.getDeclaredMethod(\"privateStaticMethod\"), null);\n\t\t\tassertTrue(InvocationTracker.privateStaticMethodInvoked);\n\t\t}\n\n\t\tstatic class InvocationTracker {\n\n\t\t\tstatic boolean publicStaticMethodInvoked;\n\t\t\tstatic boolean privateStaticMethodInvoked;\n\n\t\t\tboolean publicMethodInvoked;\n\t\t\tboolean privateMethodInvoked;\n\n\t\t\tpublic static void publicStaticMethod() {\n\t\t\t\tpublicStaticMethodInvoked = true;\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tprivate static void privateStaticMethod() {\n\t\t\t\tprivateStaticMethodInvoked = true;\n\t\t\t}\n\n\t\t\tpublic void publicMethod() {\n\t\t\t\tpublicMethodInvoked = true;\n\t\t\t}\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tprivate void privateMethod() {\n\t\t\t\tprivateMethodInvoked = true;\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ResourceLoadingTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid tryToGetResourcePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetResources(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetResources(\"   \"));\n\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetResources(null));\n\t\t\tassertPreconditionViolationFor(\n\t\t\t\t() -> ReflectionUtils.tryToGetResources(\"org/junit/platform/commons/example.resource\", null));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToGetResource() {\n\t\t\tvar tryToGetResource = ReflectionUtils.tryToGetResources(\"org/junit/platform/commons/example.resource\");\n\t\t\tvar resource = assertDoesNotThrow(tryToGetResource::get);\n\t\t\tassertAll( //\n\t\t\t\t() -> assertThat(resource).hasSize(1), //\n\t\t\t\t() -> assertThat(resource).extracting(Resource::getName) //\n\t\t\t\t\t\t.containsExactly(\"org/junit/platform/commons/example.resource\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToGetResourceWithPrefixedSlash() {\n\t\t\tvar tryToGetResource = ReflectionUtils.tryToGetResources(\"/org/junit/platform/commons/example.resource\");\n\t\t\tvar resource = assertDoesNotThrow(tryToGetResource::get);\n\t\t\tassertAll( //\n\t\t\t\t() -> assertThat(resource).hasSize(1), //\n\t\t\t\t() -> assertThat(resource).extracting(Resource::getName) //\n\t\t\t\t\t\t.containsExactly(\"org/junit/platform/commons/example.resource\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToGetResourceWhenResourceNotFound() {\n\t\t\tvar tryToGetResource = ReflectionUtils.tryToGetResources(\"org/junit/platform/commons/no-such.resource\");\n\t\t\tvar resource = assertDoesNotThrow(tryToGetResource::get);\n\t\t\tassertThat(resource).isEmpty();\n\t\t}\n\t}\n\n\t@Nested\n\tclass ClassLoadingTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid tryToLoadClassPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToLoadClass(null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToLoadClass(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToLoadClass(\"   \"));\n\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToLoadClass(null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToLoadClass(getClass().getName(), null));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassWhenClassNotFoundException() {\n\t\t\tassertThrows(ClassNotFoundException.class,\n\t\t\t\t() -> ReflectionUtils.tryToLoadClass(\"foo.bar.EnigmaClassThatDoesNotExist\").get());\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassFailsWithinReasonableTimeForInsanelyLargeAndInvalidMultidimensionalPrimitiveArrayName() {\n\t\t\t// Create a class name of the form int[][][]...[][][]X\n\t\t\tString className = IntStream.rangeClosed(1, 20_000)//\n\t\t\t\t\t.mapToObj(i -> \"[]\")//\n\t\t\t\t\t.collect(joining(\"\", \"int\", \"X\"));\n\n\t\t\t// The following should ideally fail in less than 50ms. So we just make\n\t\t\t// sure it fails in less than 500ms in order to (hopefully) allow the\n\t\t\t// test to pass on CI servers with limited resources.\n\t\t\tassertTimeoutPreemptively(ofMillis(500), () -> assertThrows(ClassNotFoundException.class,\n\t\t\t\t() -> ReflectionUtils.tryToLoadClass(className).get()));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClass() {\n\t\t\tassertTryToLoadClass(getClass().getName(), getClass());\n\t\t\tassertTryToLoadClass(Integer.class.getName(), Integer.class);\n\t\t\tassertTryToLoadClass(Void.class.getName(), Void.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassTrimsClassName() {\n\t\t\tassertTryToLoadClass(\"  \" + Integer.class.getName() + \"\\t\", Integer.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForVoidPseudoPrimitiveType() {\n\t\t\tassertTryToLoadClass(\"void\", void.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForPrimitiveType() {\n\t\t\tassertTryToLoadClass(\"boolean\", boolean.class);\n\t\t\tassertTryToLoadClass(\"char\", char.class);\n\t\t\tassertTryToLoadClass(\"byte\", byte.class);\n\t\t\tassertTryToLoadClass(\"short\", short.class);\n\t\t\tassertTryToLoadClass(\"int\", int.class);\n\t\t\tassertTryToLoadClass(\"long\", long.class);\n\t\t\tassertTryToLoadClass(\"float\", float.class);\n\t\t\tassertTryToLoadClass(\"double\", double.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForBinaryPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"[Z\", boolean[].class);\n\t\t\tassertTryToLoadClass(\"[C\", char[].class);\n\t\t\tassertTryToLoadClass(\"[B\", byte[].class);\n\t\t\tassertTryToLoadClass(\"[S\", short[].class);\n\t\t\tassertTryToLoadClass(\"[I\", int[].class);\n\t\t\tassertTryToLoadClass(\"[J\", long[].class);\n\t\t\tassertTryToLoadClass(\"[F\", float[].class);\n\t\t\tassertTryToLoadClass(\"[D\", double[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForCanonicalPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"boolean[]\", boolean[].class);\n\t\t\tassertTryToLoadClass(\"char[]\", char[].class);\n\t\t\tassertTryToLoadClass(\"byte[]\", byte[].class);\n\t\t\tassertTryToLoadClass(\"short[]\", short[].class);\n\t\t\tassertTryToLoadClass(\"int[]\", int[].class);\n\t\t\tassertTryToLoadClass(\"long[]\", long[].class);\n\t\t\tassertTryToLoadClass(\"float[]\", float[].class);\n\t\t\tassertTryToLoadClass(\"double[]\", double[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForBinaryObjectArrayName() {\n\t\t\tassertTryToLoadClass(String[].class.getName(), String[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForCanonicalObjectArrayName() {\n\t\t\tassertTryToLoadClass(\"java.lang.String[]\", String[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForBinaryTwoDimensionalPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"[[Z\", boolean[][].class);\n\t\t\tassertTryToLoadClass(\"[[C\", char[][].class);\n\t\t\tassertTryToLoadClass(\"[[B\", byte[][].class);\n\t\t\tassertTryToLoadClass(\"[[S\", short[][].class);\n\t\t\tassertTryToLoadClass(\"[[I\", int[][].class);\n\t\t\tassertTryToLoadClass(\"[[J\", long[][].class);\n\t\t\tassertTryToLoadClass(\"[[F\", float[][].class);\n\t\t\tassertTryToLoadClass(\"[[D\", double[][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForCanonicalTwoDimensionalPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"boolean[][]\", boolean[][].class);\n\t\t\tassertTryToLoadClass(\"char[][]\", char[][].class);\n\t\t\tassertTryToLoadClass(\"byte[][]\", byte[][].class);\n\t\t\tassertTryToLoadClass(\"short[][]\", short[][].class);\n\t\t\tassertTryToLoadClass(\"int[][]\", int[][].class);\n\t\t\tassertTryToLoadClass(\"long[][]\", long[][].class);\n\t\t\tassertTryToLoadClass(\"float[][]\", float[][].class);\n\t\t\tassertTryToLoadClass(\"double[][]\", double[][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForBinaryMultidimensionalPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"[[[[[Z\", boolean[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[C\", char[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[B\", byte[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[S\", short[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[I\", int[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[J\", long[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[F\", float[][][][][].class);\n\t\t\tassertTryToLoadClass(\"[[[[[D\", double[][][][][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForCanonicalMultidimensionalPrimitiveArrayName() {\n\t\t\tassertTryToLoadClass(\"boolean[][][][][]\", boolean[][][][][].class);\n\t\t\tassertTryToLoadClass(\"char[][][][][]\", char[][][][][].class);\n\t\t\tassertTryToLoadClass(\"byte[][][][][]\", byte[][][][][].class);\n\t\t\tassertTryToLoadClass(\"short[][][][][]\", short[][][][][].class);\n\t\t\tassertTryToLoadClass(\"int[][][][][]\", int[][][][][].class);\n\t\t\tassertTryToLoadClass(\"long[][][][][]\", long[][][][][].class);\n\t\t\tassertTryToLoadClass(\"float[][][][][]\", float[][][][][].class);\n\t\t\tassertTryToLoadClass(\"double[][][][][]\", double[][][][][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForBinaryMultidimensionalObjectArrayName() {\n\t\t\tassertTryToLoadClass(String[][][][][].class.getName(), String[][][][][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToLoadClassForCanonicalMultidimensionalObjectArrayName() {\n\t\t\tassertTryToLoadClass(\"java.lang.String[][][][][]\", String[][][][][].class);\n\t\t}\n\n\t\tprivate static void assertTryToLoadClass(String name, Class<?> type) {\n\t\t\ttry {\n\t\t\t\tassertThat(ReflectionUtils.tryToLoadClass(name).get()).as(name).isEqualTo(type);\n\t\t\t}\n\t\t\tcatch (Exception ex) {\n\t\t\t\tExceptionUtils.throwAsUncheckedException(ex);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass FullyQualifiedMethodNameTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid getFullyQualifiedMethodNamePreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getFullyQualifiedMethodName(null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getFullyQualifiedMethodName(null, \"testMethod\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.getFullyQualifiedMethodName(Object.class, null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid getFullyQualifiedMethodNameForMethodWithoutParameters() {\n\t\t\tassertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, \"toString\"))//\n\t\t\t\t\t.isEqualTo(\"java.lang.Object#toString()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid getFullyQualifiedMethodNameForMethodWithNullParameters() {\n\t\t\tassertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, \"toString\", (Class<?>[]) null))//\n\t\t\t\t\t.isEqualTo(\"java.lang.Object#toString()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid getFullyQualifiedMethodNameForMethodWithSingleParameter() {\n\t\t\tassertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, \"equals\", Object.class))//\n\t\t\t\t\t.isEqualTo(\"java.lang.Object#equals(java.lang.Object)\");\n\t\t}\n\n\t\t@Test\n\t\tvoid getFullyQualifiedMethodNameForMethodWithMultipleParameters() {\n\t\t\t// @formatter:off\n\t\t\tassertThat(ReflectionUtils.getFullyQualifiedMethodName(Object.class, \"testMethod\", int.class, Object.class))//\n\t\t\t\t\t.isEqualTo(\"java.lang.Object#testMethod(int, java.lang.Object)\");\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid parseFullyQualifiedMethodNamePreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"   \"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"java.lang.Object#\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"#equals\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"#\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"java.lang.Object\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"equals\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"()\"));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.parseFullyQualifiedMethodName(\"(int, java.lang.Object)\"));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFullyQualifiedMethodNameForMethodWithoutParameters() {\n\t\t\tassertThat(ReflectionUtils.parseFullyQualifiedMethodName(\"com.example.Test#method()\"))//\n\t\t\t\t\t.containsExactly(\"com.example.Test\", \"method\", \"\");\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFullyQualifiedMethodNameForMethodWithSingleParameter() {\n\t\t\tassertThat(ReflectionUtils.parseFullyQualifiedMethodName(\"com.example.Test#method(java.lang.Object)\"))//\n\t\t\t\t\t.containsExactly(\"com.example.Test\", \"method\", \"java.lang.Object\");\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFullyQualifiedMethodNameForMethodWithMultipleParameters() {\n\t\t\tassertThat(ReflectionUtils.parseFullyQualifiedMethodName(\"com.example.Test#method(int, java.lang.Object)\"))//\n\t\t\t\t\t.containsExactly(\"com.example.Test\", \"method\", \"int, java.lang.Object\");\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass NestedClassTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid findNestedClassesPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.findNestedClasses(null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.findNestedClasses(null, clazz -> true));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.findNestedClasses(getClass(), null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid isNestedClassPresentPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isNestedClassPresent(null, null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isNestedClassPresent(null, clazz -> true, CycleErrorHandling.THROW_EXCEPTION));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isNestedClassPresent(getClass(), null, CycleErrorHandling.ABORT_VISIT));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isNestedClassPresent(getClass(), clazz -> true, null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid findNestedClasses() {\n\t\t\t// @formatter:off\n\t\t\tassertThat(findNestedClasses(Object.class)).isEmpty();\n\t\t\tassertThat(isNestedClassPresent(Object.class)).isFalse();\n\n\t\t\tassertThat(findNestedClasses(ClassWithNestedClasses.class))\n\t\t\t\t\t.containsOnly(Nested1.class, Nested2.class, Nested3.class);\n\t\t\tassertThat(isNestedClassPresent(ClassWithNestedClasses.class))\n\t\t\t\t\t.isTrue();\n\n\t\t\tassertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, clazz -> clazz.getName().contains(\"1\")))\n\t\t\t\t\t.containsExactly(Nested1.class);\n\t\t\tassertThat(ReflectionUtils.isNestedClassPresent(ClassWithNestedClasses.class, clazz -> clazz.getName().contains(\"1\"), CycleErrorHandling.THROW_EXCEPTION))\n\t\t\t\t\t.isTrue();\n\n\t\t\tassertThat(ReflectionUtils.findNestedClasses(ClassWithNestedClasses.class, ReflectionUtils::isStatic))\n\t\t\t\t\t.containsExactly(Nested3.class);\n\t\t\tassertThat(ReflectionUtils.isNestedClassPresent(ClassWithNestedClasses.class, ReflectionUtils::isStatic, CycleErrorHandling.THROW_EXCEPTION))\n\t\t\t\t\t.isTrue();\n\n\t\t\tassertThat(findNestedClasses(ClassExtendingClassWithNestedClasses.class))\n\t\t\t\t\t.containsOnly(Nested1.class, Nested2.class, Nested3.class, Nested4.class, Nested5.class);\n\t\t\tassertThat(isNestedClassPresent(ClassExtendingClassWithNestedClasses.class))\n\t\t\t\t\t.isTrue();\n\n\t\t\tassertThat(findNestedClasses(ClassWithNestedClasses.Nested1.class)).isEmpty();\n\t\t\tassertThat(isNestedClassPresent(ClassWithNestedClasses.Nested1.class)).isFalse();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t/**\n\t\t * @since 1.6\n\t\t */\n\t\t@Test\n\t\tvoid findNestedClassesWithSeeminglyRecursiveHierarchies() {\n\t\t\tassertThat(findNestedClasses(AbstractOuterClass.class))//\n\t\t\t\t\t.containsExactly(AbstractOuterClass.InnerClass.class);\n\t\t\tassertThat(isNestedClassPresent(AbstractOuterClass.class))//\n\t\t\t\t\t.isTrue();\n\n\t\t\t// OuterClass contains recursive hierarchies, but the non-matching\n\t\t\t// predicate should prevent cycle detection.\n\t\t\t// See https://github.com/junit-team/junit-framework/issues/2249\n\t\t\tassertThat(ReflectionUtils.findNestedClasses(OuterClass.class, clazz -> false)).isEmpty();\n\t\t\tassertThat(ReflectionUtils.isNestedClassPresent(OuterClass.class, clazz -> false,\n\t\t\t\tCycleErrorHandling.THROW_EXCEPTION)).isFalse();\n\n\t\t\t// RecursiveInnerInnerClass is part of a recursive hierarchy, but the non-matching\n\t\t\t// predicate should prevent cycle detection.\n\t\t\tassertThat(ReflectionUtils.findNestedClasses(RecursiveInnerInnerClass.class, clazz -> false)).isEmpty();\n\t\t\tassertThat(ReflectionUtils.isNestedClassPresent(RecursiveInnerInnerClass.class, clazz -> false,\n\t\t\t\tCycleErrorHandling.THROW_EXCEPTION)).isFalse();\n\n\t\t\t// Sibling types don't actually result in cycles.\n\t\t\tassertThat(findNestedClasses(StaticNestedSiblingClass.class))//\n\t\t\t\t\t.containsExactly(AbstractOuterClass.InnerClass.class);\n\t\t\tassertThat(isNestedClassPresent(StaticNestedSiblingClass.class))//\n\t\t\t\t\t.isTrue();\n\t\t\tassertThat(findNestedClasses(InnerSiblingClass.class))//\n\t\t\t\t\t.containsExactly(AbstractOuterClass.InnerClass.class);\n\t\t\tassertThat(isNestedClassPresent(InnerSiblingClass.class))//\n\t\t\t\t\t.isTrue();\n\n\t\t\t// Interfaces with static nested classes\n\t\t\tassertThat(findNestedClasses(OuterClassImplementingInterface.class))//\n\t\t\t\t\t.containsExactly(InnerClassImplementingInterface.class, Nested4.class);\n\t\t\tassertThat(isNestedClassPresent(OuterClassImplementingInterface.class))//\n\t\t\t\t\t.isTrue();\n\t\t\tassertThat(findNestedClasses(InnerClassImplementingInterface.class))//\n\t\t\t\t\t.containsExactly(Nested4.class);\n\t\t\tassertThat(isNestedClassPresent(InnerClassImplementingInterface.class))//\n\t\t\t\t\t.isTrue();\n\t\t}\n\n\t\t/**\n\t\t * @since 1.6\n\t\t */\n\t\t@Test\n\t\tvoid findNestedClassesWithRecursiveHierarchies() {\n\t\t\tRunnable runnable1 = () -> assertNestedCycle(OuterClass.class, InnerClass.class, OuterClass.class);\n\t\t\tRunnable runnable2 = () -> assertNestedCycle(StaticNestedClass.class, InnerClass.class, OuterClass.class);\n\t\t\tRunnable runnable3 = () -> assertNestedCycle(RecursiveInnerClass.class, OuterClass.class);\n\t\t\tRunnable runnable4 = () -> assertNestedCycle(RecursiveInnerInnerClass.class, OuterClass.class);\n\t\t\tRunnable runnable5 = () -> assertNestedCycle(InnerClass.class, RecursiveInnerInnerClass.class,\n\t\t\t\tOuterClass.class);\n\t\t\tStream.of(runnable1, runnable1, runnable1, runnable2, runnable2, runnable2, runnable3, runnable3, runnable3,\n\t\t\t\trunnable4, runnable4, runnable4, runnable5, runnable5, runnable5).parallel().forEach(Runnable::run);\n\t\t}\n\n\t\t@Test\n\t\tvoid findNestedClassesWithMultipleNestedClasses() {\n\t\t\tvar nestedClasses = findNestedClasses(OuterClassWithMultipleNestedClasses.class);\n\n\t\t\tassertThat(nestedClasses) //\n\t\t\t\t\t.map(Class::getSimpleName) //\n\t\t\t\t\t.containsExactly(\"Beta\", \"Zeta\", \"Eta\", \"Alpha\", \"Delta\", \"Gamma\", \"Theta\", \"Epsilon\");\n\t\t}\n\n\t\tprivate static List<Class<?>> findNestedClasses(Class<?> clazz) {\n\t\t\treturn ReflectionUtils.findNestedClasses(clazz, c -> true);\n\t\t}\n\n\t\tprivate static boolean isNestedClassPresent(Class<?> clazz) {\n\t\t\treturn ReflectionUtils.isNestedClassPresent(clazz, c -> true, CycleErrorHandling.THROW_EXCEPTION);\n\t\t}\n\n\t\tprivate void assertNestedCycle(Class<?> from, Class<?> to) {\n\t\t\tassertNestedCycle(from, from, to);\n\t\t}\n\n\t\tprivate void assertNestedCycle(Class<?> start, Class<?> from, Class<?> to) {\n\t\t\tassertThatExceptionOfType(JUnitException.class)//\n\t\t\t\t\t.as(\"expected cycle from %s to %s\", from.getSimpleName(), to.getSimpleName())//\n\t\t\t\t\t.isThrownBy(() -> findNestedClasses(start))//\n\t\t\t\t\t.withMessageMatching(\"Detected cycle in inner class hierarchy between .+%s and .+%s\".formatted(\n\t\t\t\t\t\tfrom.getSimpleName(), to.getSimpleName()));\n\t\t}\n\n\t\t/**\n\t\t * @since 1.3\n\t\t */\n\t\t@Test\n\t\tvoid findNestedClassesWithInvalidNestedClassFile(@TrackLogRecords LogRecordListener listener) throws Exception {\n\t\t\tvar jarUrl = getClass().getResource(\"/gh-1436-invalid-nested-class-file.jar\");\n\n\t\t\ttry (var classLoader = new URLClassLoader(new URL[] { jarUrl })) {\n\t\t\t\tvar fqcn = \"tests.NestedInterfaceGroovyTests\";\n\t\t\t\tvar classWithInvalidNestedClassFile = classLoader.loadClass(fqcn);\n\n\t\t\t\tassertEquals(fqcn, classWithInvalidNestedClassFile.getName());\n\t\t\t\tvar noClassDefFoundError = assertThrows(NoClassDefFoundError.class,\n\t\t\t\t\tclassWithInvalidNestedClassFile::getDeclaredClasses);\n\t\t\t\tassertThat(noClassDefFoundError) //\n\t\t\t\t\t\t.hasMessageMatching(\"tests[./]NestedInterfaceGroovyTests\\\\$NestedInterface\\\\$1\");\n\n\t\t\t\tassertThat(findNestedClasses(classWithInvalidNestedClassFile)).isEmpty();\n\t\t\t\t// @formatter:off\n\t\t\t\tvar logMessage = listener.stream(ReflectionUtils.class, Level.FINE)\n\t\t\t\t\t\t.findFirst()\n\t\t\t\t\t\t.map(LogRecord::getMessage)\n\t\t\t\t\t\t.orElse(\"didn't find log record\");\n\t\t\t\t// @formatter:on\n\t\t\t\tassertThat(logMessage).isEqualTo(\"Failed to retrieve declared classes for \" + fqcn);\n\t\t\t}\n\t\t}\n\n\t\tstatic class ClassWithNestedClasses {\n\n\t\t\tclass Nested1 {\n\t\t\t}\n\n\t\t\tclass Nested2 {\n\t\t\t}\n\n\t\t\tstatic class Nested3 {\n\t\t\t}\n\t\t}\n\n\t\tinterface InterfaceWithNestedClass {\n\n\t\t\tclass Nested4 {\n\t\t\t}\n\t\t}\n\n\t\tinterface Interface45 extends InterfaceWithNestedClass {\n\n\t\t\tclass Nested5 {\n\t\t\t}\n\t\t}\n\n\t\tstatic class ClassExtendingClassWithNestedClasses extends ClassWithNestedClasses implements Interface45 {\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass MethodUtilitiesTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid tryToGetMethodPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetMethod(null, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetMethod(String.class, null));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.tryToGetMethod(null, \"hashCode\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToGetMethod() throws Exception {\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(Object.class, \"hashCode\").get())//\n\t\t\t\t\t.isEqualTo(Object.class.getMethod(\"hashCode\"));\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(String.class, \"charAt\", int.class).get())//\n\t\t\t\t\t.isEqualTo(String.class.getMethod(\"charAt\", int.class));\n\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(Path.class, \"subpath\", int.class, int.class).get())//\n\t\t\t\t\t.isEqualTo(Path.class.getMethod(\"subpath\", int.class, int.class));\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(String.class, \"chars\").get())//\n\t\t\t\t\t.isEqualTo(String.class.getMethod(\"chars\"));\n\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(String.class, \"noSuchMethod\").toOptional()).isEmpty();\n\t\t\tassertThat(ReflectionUtils.tryToGetMethod(Object.class, \"clone\", int.class).toOptional()).isEmpty();\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid isMethodPresentPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isMethodPresent(null, m -> true));\n\t\t\tassertPreconditionViolationFor(() -> ReflectionUtils.isMethodPresent(getClass(), null));\n\t\t}\n\n\t\t@Test\n\t\tvoid isMethodPresent() {\n\t\t\tPredicate<Method> isMethod1 = method -> (method.getName().equals(\"method1\")\n\t\t\t\t\t&& method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == String.class);\n\n\t\t\tassertThat(ReflectionUtils.isMethodPresent(MethodShadowingChild.class, isMethod1)).isTrue();\n\n\t\t\tassertThat(ReflectionUtils.isMethodPresent(getClass(), isMethod1)).isFalse();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass FindMethodTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid findMethodByParameterTypesPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> findMethod(null, null));\n\t\t\tassertPreconditionViolationFor(() -> findMethod(null, \"method\"));\n\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\", () -> findMethod(String.class, null));\n\n\t\t\tassertPreconditionViolationNotNullOrBlankFor(\"Method name\", () -> findMethod(String.class, \"   \"));\n\n\t\t\tassertPreconditionViolationNotNullFor(\"Parameter types array\",\n\t\t\t\t\t() -> findMethod(Files.class, \"copy\", (Class<?>[]) null));\n\n\t\t\tassertPreconditionViolationNotNullFor(\"Individual parameter types\",\n\t\t\t\t\t() -> findMethod(Files.class, \"copy\", (Class<?>) null));\n\n\t\t\tassertPreconditionViolationNotNullFor(\"Individual parameter types\",\n\t\t\t\t\t() -> findMethod(Files.class, \"copy\", new Class<?>[] { Path.class, null }));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterTypes() throws Exception {\n\t\t\tassertThat(findMethod(Object.class, \"noSuchMethod\")).isEmpty();\n\t\t\tassertThat(findMethod(String.class, \"noSuchMethod\")).isEmpty();\n\n\t\t\tassertThat(findMethod(String.class, \"chars\")).contains(String.class.getMethod(\"chars\"));\n\t\t\tassertThat(findMethod(Files.class, \"copy\", Path.class, OutputStream.class))//\n\t\t\t\t\t.contains(Files.class.getMethod(\"copy\", Path.class, OutputStream.class));\n\n\t\t\tassertThat(findMethod(MethodShadowingChild.class, \"method1\", String.class))//\n\t\t\t\t\t.contains(MethodShadowingChild.class.getMethod(\"method1\", String.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterTypesInGenericInterface() {\n\t\t\tClass<?> ifc = InterfaceWithGenericDefaultMethod.class;\n\t\t\tvar method = findMethod(ifc, \"foo\", Number.class);\n\t\t\tassertThat(method).isNotEmpty();\n\t\t\tassertThat(method.get().getName()).isEqualTo(\"foo\");\n\t\t}\n\n\t\t/**\n\t\t * @see #findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod()\n\t\t */\n\t\t@Test\n\t\tvoid findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass() {\n\t\t\tClass<?> clazz = InterfaceWithGenericDefaultMethodImpl.class;\n\t\t\tvar method = findMethod(clazz, \"foo\", Long.class);\n\t\t\tassertThat(method).isNotEmpty();\n\t\t\tassertThat(method.get().getName()).isEqualTo(\"foo\");\n\n\t\t\t// One might expect or desire that the signature for the generic foo(N)\n\t\t\t// default method would be \"foo(java.lang.Long)\" when looked up via the\n\t\t\t// concrete parameterized class, but it apparently is only _visible_ as\n\t\t\t// \"foo(java.lang.Number)\" via reflection. Hence the following assertion\n\t\t\t// checks for java.lang.Number instead of java.lang.Long.\n\t\t\tassertThat(method.get().getParameterTypes()[0]).isEqualTo(Number.class);\n\t\t}\n\n\t\t/**\n\t\t * This test is identical to\n\t\t * {@link #findMethodByParameterTypesInGenericInterfaceViaParameterizedSubclass()},\n\t\t * except that this test attempts to find the overloaded\n\t\t * {@link InterfaceWithGenericDefaultMethodImpl#foo(Double)} method instead of\n\t\t * the {@link InterfaceWithGenericDefaultMethod#foo(Number)} default method.\n\t\t */\n\t\t@Test\n\t\tvoid findMethodByParameterTypesWithOverloadedMethodNextToGenericDefaultMethod() {\n\t\t\tClass<?> clazz = InterfaceWithGenericDefaultMethodImpl.class;\n\t\t\tClass<?> parameterType = Double.class;\n\t\t\tvar method = findMethod(clazz, \"foo\", parameterType);\n\t\t\tassertThat(method).isNotEmpty();\n\t\t\tassertThat(method.get().getName()).isEqualTo(\"foo\");\n\t\t\tassertThat(method.get().getParameterTypes()[0]).isEqualTo(parameterType);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithPrimitiveArrayParameter() throws Exception {\n\t\t\tassertFindMethodByParameterNames(\"methodWithPrimitiveArray\", int[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithTwoDimensionalPrimitiveArrayParameter() throws Exception {\n\t\t\tassertFindMethodByParameterNames(\"methodWithTwoDimensionalPrimitiveArray\", int[][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithMultidimensionalPrimitiveArrayParameter() throws Exception {\n\t\t\tassertFindMethodByParameterNames(\"methodWithMultidimensionalPrimitiveArray\", int[][][][][].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithObjectArrayParameter() throws Exception {\n\t\t\tassertFindMethodByParameterNames(\"methodWithObjectArray\", String[].class);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithMultidimensionalObjectArrayParameter() throws Exception {\n\t\t\tassertFindMethodByParameterNames(\"methodWithMultidimensionalObjectArray\", Double[][][][][].class);\n\t\t}\n\n\t\t/**\n\t\t * @since 5.10\n\t\t */\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithWithCustomTypeFromDifferentClassLoader() throws Exception {\n\t\t\tvar methodName = \"customMethod\";\n\t\t\tvar customTypeName = CustomType.class.getName();\n\t\t\tvar nestedTypeName = CustomType.NestedType.class.getName();\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClassNamePrefix(customTypeName)) {\n\t\t\t\tvar customType = testClassLoader.loadClass(customTypeName);\n\t\t\t\tassertThat(customType.getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\t\tvar optional = findMethod(customType, methodName, nestedTypeName);\n\t\t\t\tassertThat(optional).get().satisfies(method -> {\n\t\t\t\t\tassertThat(method.getName()).isEqualTo(methodName);\n\n\t\t\t\t\tvar declaringClass = method.getDeclaringClass();\n\t\t\t\t\tassertThat(declaringClass.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\t\tassertThat(declaringClass.getName()).isEqualTo(customTypeName);\n\t\t\t\t\tassertThat(declaringClass).isNotEqualTo(CustomType.class);\n\n\t\t\t\t\tvar parameterTypes = method.getParameterTypes();\n\t\t\t\t\tassertThat(parameterTypes).extracting(Class::getName).containsExactly(nestedTypeName);\n\t\t\t\t\tClass<?> parameterType = parameterTypes[0];\n\t\t\t\t\tassertThat(parameterType).isNotEqualTo(CustomType.NestedType.class);\n\t\t\t\t\tassertThat(parameterType.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodByParameterNamesWithParameterizedMapParameter() throws Exception {\n\t\t\tvar methodName = \"methodWithParameterizedMap\";\n\n\t\t\t// standard, supported use case\n\t\t\tassertFindMethodByParameterNames(methodName, Map.class);\n\n\t\t\t// generic type info in parameter list\n\t\t\tvar clazz = getClass();\n\t\t\tvar method = clazz.getDeclaredMethod(methodName, Map.class);\n\t\t\tvar genericParameterTypeName = method.getGenericParameterTypes()[0].getTypeName();\n\t\t\tvar exception = assertThrows(JUnitException.class,\n\t\t\t\t() -> findMethod(clazz, methodName, genericParameterTypeName));\n\n\t\t\tassertThat(exception).hasMessageStartingWith(\n\t\t\t\t\"Failed to load parameter type [java.util.Map<java.lang.String\");\n\t\t}\n\n\t\tprivate void assertFindMethodByParameterNames(String methodName, Class<?> parameterType)\n\t\t\t\tthrows NoSuchMethodException {\n\n\t\t\tvar clazz = getClass();\n\t\t\tvar method = clazz.getDeclaredMethod(methodName, parameterType);\n\t\t\tvar optional = findMethod(clazz, methodName, parameterType.getName());\n\t\t\tassertThat(optional).contains(method);\n\t\t}\n\n\t\tvoid methodWithPrimitiveArray(int[] nums) {\n\t\t}\n\n\t\tvoid methodWithTwoDimensionalPrimitiveArray(int[][] grid) {\n\t\t}\n\n\t\tvoid methodWithMultidimensionalPrimitiveArray(int[][][][][] grid) {\n\t\t}\n\n\t\tvoid methodWithObjectArray(String[] info) {\n\t\t}\n\n\t\tvoid methodWithTwoDimensionalObjectArray(String[][] info) {\n\t\t}\n\n\t\tvoid methodWithMultidimensionalObjectArray(Double[][][][][] data) {\n\t\t}\n\n\t\tvoid methodWithParameterizedMap(Map<String, String> map) {\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass FindMethodsTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid findMethodsPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> findMethods(null, null));\n\t\t\tassertPreconditionViolationFor(() -> findMethods(null, clazz -> true));\n\t\t\tassertPreconditionViolationFor(() -> findMethods(String.class, null));\n\n\t\t\tassertPreconditionViolationFor(() -> findMethods(null, null, null));\n\t\t\tassertPreconditionViolationFor(() -> findMethods(null, clazz -> true, BOTTOM_UP));\n\t\t\tassertPreconditionViolationFor(() -> findMethods(String.class, null, BOTTOM_UP));\n\t\t\tassertPreconditionViolationFor(() -> findMethods(String.class, clazz -> true, null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsInInterface() {\n\t\t\tassertOneFooMethodIn(InterfaceWithOneDeclaredMethod.class);\n\t\t\tassertOneFooMethodIn(InterfaceWithDefaultMethod.class);\n\t\t\tassertOneFooMethodIn(InterfaceWithDefaultMethodImpl.class);\n\t\t\tassertOneFooMethodIn(InterfaceWithStaticMethod.class);\n\t\t\tassertOneFooMethodIn(InterfaceWithStaticMethodImpl.class);\n\t\t}\n\n\t\tprivate static void assertOneFooMethodIn(Class<?> clazz) {\n\t\t\tassertThat(findMethods(clazz, isFooMethod)).hasSize(1);\n\t\t}\n\n\t\t/**\n\t\t * @since 1.9.1\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/2993\">GitHub - Issue #2993</a>\n\t\t */\n\t\t@Test\n\t\tvoid findMethodsFindsDistinctMethodsDeclaredInMultipleInterfaces() {\n\t\t\tPredicate<Method> isStringsMethod = method -> method.getName().equals(\"strings\");\n\t\t\tassertThat(findMethods(DoubleInheritedInterfaceMethodTestCase.class, isStringsMethod)).hasSize(1);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsInObject() {\n\t\t\tvar methods = findMethods(Object.class, method -> true);\n\t\t\tassertNotNull(methods);\n\t\t\tassertTrue(methods.size() > 10);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsInVoid() {\n\t\t\tassertThat(findMethods(void.class, method -> true)).isEmpty();\n\t\t\tassertThat(findMethods(Void.class, method -> true)).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsInPrimitive() {\n\t\t\tassertThat(findMethods(int.class, method -> true)).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsInArrays() {\n\t\t\tassertThat(findMethods(int[].class, method -> true)).isEmpty();\n\t\t\tassertThat(findMethods(Integer[].class, method -> true)).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsIgnoresSyntheticMethods() {\n\t\t\tassertTrue(stream(ClassWithSyntheticMethod.class.getDeclaredMethods()).anyMatch(Method::isSynthetic),\n\t\t\t\t\"ClassWithSyntheticMethod must actually contain at least one synthetic method.\");\n\n\t\t\tvar methods = findMethods(ClassWithSyntheticMethod.class, method -> true);\n\t\t\tassertThat(methods).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsUsingHierarchyUpMode() throws Exception {\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().contains(\"method\"), BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(ChildClass.class.getMethod(\"method4\"), ParentClass.class.getMethod(\"method3\"),\n\t\t\t\t\t\tGrandparentInterface.class.getMethod(\"method2\"), GrandparentClass.class.getMethod(\"method1\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().contains(\"other\"), BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(ChildClass.class.getMethod(\"otherMethod3\"),\n\t\t\t\t\t\tParentClass.class.getMethod(\"otherMethod2\"), GrandparentClass.class.getMethod(\"otherMethod1\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().equals(\"method2\"), BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(ParentClass.class.getMethod(\"method2\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().equals(\"wrongName\"), BOTTOM_UP))//\n\t\t\t\t\t.isEmpty();\n\n\t\t\tassertThat(findMethods(ParentClass.class, method -> method.getName().contains(\"method\"), BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(ParentClass.class.getMethod(\"method3\"),\n\t\t\t\t\t\tGrandparentInterface.class.getMethod(\"method2\"), GrandparentClass.class.getMethod(\"method1\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsUsingHierarchyDownMode() throws Exception {\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().contains(\"method\"), TOP_DOWN))//\n\t\t\t\t\t.containsExactly(GrandparentClass.class.getMethod(\"method1\"),\n\t\t\t\t\t\tGrandparentInterface.class.getMethod(\"method2\"), ParentClass.class.getMethod(\"method3\"),\n\t\t\t\t\t\tChildClass.class.getMethod(\"method4\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().contains(\"other\"), TOP_DOWN))//\n\t\t\t\t\t.containsExactly(GrandparentClass.class.getMethod(\"otherMethod1\"),\n\t\t\t\t\t\tParentClass.class.getMethod(\"otherMethod2\"), ChildClass.class.getMethod(\"otherMethod3\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().equals(\"method2\"), TOP_DOWN))//\n\t\t\t\t\t.containsExactly(ParentClass.class.getMethod(\"method2\"));\n\n\t\t\tassertThat(findMethods(ChildClass.class, method -> method.getName().equals(\"wrongName\"), TOP_DOWN))//\n\t\t\t\t\t.isEmpty();\n\n\t\t\tassertThat(findMethods(ParentClass.class, method -> method.getName().contains(\"method\"), TOP_DOWN))//\n\t\t\t\t\t.containsExactly(GrandparentClass.class.getMethod(\"method1\"),\n\t\t\t\t\t\tGrandparentInterface.class.getMethod(\"method2\"), ParentClass.class.getMethod(\"method3\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsWithShadowingUsingHierarchyUpMode() throws Exception {\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains1, BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(MethodShadowingChild.class.getMethod(\"method1\", String.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains2, BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(MethodShadowingParent.class.getMethod(\"method2\", int.class, int.class, int.class),\n\t\t\t\t\t\tMethodShadowingInterface.class.getMethod(\"method2\", int.class, int.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains4, BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(MethodShadowingChild.class.getMethod(\"method4\", boolean.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains5, BOTTOM_UP))//\n\t\t\t\t\t.containsExactly(MethodShadowingChild.class.getMethod(\"method5\", Long.class),\n\t\t\t\t\t\tMethodShadowingParent.class.getMethod(\"method5\", String.class));\n\n\t\t\tvar methods = findMethods(MethodShadowingChild.class, method -> true, BOTTOM_UP);\n\t\t\tassertEquals(6, methods.size());\n\t\t\tassertThat(methods.subList(0, 3)).containsOnly(\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method4\", boolean.class),\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method1\", String.class),\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method5\", Long.class));\n\t\t\tassertThat(methods.subList(3, 5)).containsOnly(\n\t\t\t\tMethodShadowingParent.class.getMethod(\"method2\", int.class, int.class, int.class),\n\t\t\t\tMethodShadowingParent.class.getMethod(\"method5\", String.class));\n\t\t\tassertEquals(MethodShadowingInterface.class.getMethod(\"method2\", int.class, int.class), methods.get(5));\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsWithShadowingUsingHierarchyDownMode() throws Exception {\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains1, TOP_DOWN))//\n\t\t\t\t\t.containsExactly(MethodShadowingChild.class.getMethod(\"method1\", String.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains2, TOP_DOWN))//\n\t\t\t\t\t.containsExactly(MethodShadowingInterface.class.getMethod(\"method2\", int.class, int.class),\n\t\t\t\t\t\tMethodShadowingParent.class.getMethod(\"method2\", int.class, int.class, int.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains4, TOP_DOWN))//\n\t\t\t\t\t.containsExactly(MethodShadowingChild.class.getMethod(\"method4\", boolean.class));\n\n\t\t\tassertThat(findMethods(MethodShadowingChild.class, methodContains5, TOP_DOWN))//\n\t\t\t\t\t.containsExactly(MethodShadowingParent.class.getMethod(\"method5\", String.class),\n\t\t\t\t\t\tMethodShadowingChild.class.getMethod(\"method5\", Long.class));\n\n\t\t\tvar methods = findMethods(MethodShadowingChild.class, method -> true, TOP_DOWN);\n\t\t\tassertEquals(6, methods.size());\n\t\t\tassertEquals(MethodShadowingInterface.class.getMethod(\"method2\", int.class, int.class), methods.getFirst());\n\t\t\tassertThat(methods.subList(1, 3)).containsOnly(\n\t\t\t\tMethodShadowingParent.class.getMethod(\"method2\", int.class, int.class, int.class),\n\t\t\t\tMethodShadowingParent.class.getMethod(\"method5\", String.class));\n\t\t\tassertThat(methods.subList(3, 6)).containsOnly(\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method4\", boolean.class),\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method1\", String.class),\n\t\t\t\tMethodShadowingChild.class.getMethod(\"method5\", Long.class));\n\t\t}\n\n\t\t/**\n\t\t * In non-legacy mode, \"static hiding\" does not occur.\n\t\t */\n\t\t@Test\n\t\tvoid findMethodsWithoutStaticHidingUsingHierarchyUpMode() throws Exception {\n\t\t\tClass<?> ifc = StaticMethodHidingInterface.class;\n\t\t\tClass<?> parent = StaticMethodHidingParent.class;\n\t\t\tClass<?> child = StaticMethodHidingChild.class;\n\n\t\t\tvar ifcMethod1 = ifc.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar ifcMethod2 = ifc.getDeclaredMethod(\"method2\", int.class, int.class);\n\t\t\tvar childMethod1 = child.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar childMethod4 = child.getDeclaredMethod(\"method4\", boolean.class);\n\t\t\tvar childMethod5 = child.getDeclaredMethod(\"method5\", Long.class);\n\t\t\tvar parentMethod1 = parent.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar parentMethod2 = parent.getDeclaredMethod(\"method2\", int.class, int.class, int.class);\n\t\t\tvar parentMethod4 = parent.getDeclaredMethod(\"method4\", boolean.class);\n\t\t\tvar parentMethod5 = parent.getDeclaredMethod(\"method5\", String.class);\n\n\t\t\tassertThat(findMethods(child, methodContains1, BOTTOM_UP)).containsExactly(childMethod1, parentMethod1,\n\t\t\t\tifcMethod1);\n\t\t\tassertThat(findMethods(child, methodContains2, BOTTOM_UP)).containsExactly(parentMethod2, ifcMethod2);\n\t\t\tassertThat(findMethods(child, methodContains4, BOTTOM_UP)).containsExactly(childMethod4, parentMethod4);\n\t\t\tassertThat(findMethods(child, methodContains5, BOTTOM_UP)).containsExactly(childMethod5, parentMethod5);\n\n\t\t\tvar methods = findMethods(child, method -> true, BOTTOM_UP);\n\t\t\tassertEquals(9, methods.size());\n\t\t\tassertThat(methods.subList(0, 3)).containsOnly(childMethod1, childMethod4, childMethod5);\n\t\t\tassertThat(methods.subList(3, 7)).containsOnly(parentMethod1, parentMethod2, parentMethod4, parentMethod5);\n\t\t\tassertThat(methods.subList(7, 9)).containsOnly(ifcMethod1, ifcMethod2);\n\t\t}\n\n\t\t/**\n\t\t * In non-legacy mode, \"static hiding\" does not occur.\n\t\t */\n\t\t@Test\n\t\tvoid findMethodsWithoutStaticHidingUsingHierarchyDownMode() throws Exception {\n\t\t\tClass<?> ifc = StaticMethodHidingInterface.class;\n\t\t\tClass<?> parent = StaticMethodHidingParent.class;\n\t\t\tClass<?> child = StaticMethodHidingChild.class;\n\n\t\t\tvar ifcMethod1 = ifc.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar ifcMethod2 = ifc.getDeclaredMethod(\"method2\", int.class, int.class);\n\t\t\tvar childMethod1 = child.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar childMethod4 = child.getDeclaredMethod(\"method4\", boolean.class);\n\t\t\tvar childMethod5 = child.getDeclaredMethod(\"method5\", Long.class);\n\t\t\tvar parentMethod1 = parent.getDeclaredMethod(\"method1\", String.class);\n\t\t\tvar parentMethod2 = parent.getDeclaredMethod(\"method2\", int.class, int.class, int.class);\n\t\t\tvar parentMethod4 = parent.getDeclaredMethod(\"method4\", boolean.class);\n\t\t\tvar parentMethod5 = parent.getDeclaredMethod(\"method5\", String.class);\n\n\t\t\tassertThat(findMethods(child, methodContains1, TOP_DOWN)).containsExactly(ifcMethod1, parentMethod1,\n\t\t\t\tchildMethod1);\n\t\t\tassertThat(findMethods(child, methodContains2, TOP_DOWN)).containsExactly(ifcMethod2, parentMethod2);\n\t\t\tassertThat(findMethods(child, methodContains4, TOP_DOWN)).containsExactly(parentMethod4, childMethod4);\n\t\t\tassertThat(findMethods(child, methodContains5, TOP_DOWN)).containsExactly(parentMethod5, childMethod5);\n\n\t\t\tvar methods = findMethods(child, method -> true, TOP_DOWN);\n\t\t\tassertEquals(9, methods.size());\n\t\t\tassertThat(methods.subList(0, 2)).containsOnly(ifcMethod1, ifcMethod2);\n\t\t\tassertThat(methods.subList(2, 6)).containsOnly(parentMethod1, parentMethod2, parentMethod4, parentMethod5);\n\t\t\tassertThat(methods.subList(6, 9)).containsOnly(childMethod1, childMethod4, childMethod5);\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsDoesNotReturnOverriddenMethods() {\n\t\t\tPredicate<Method> isSpecial = method -> method.isAnnotationPresent(Special.class);\n\n\t\t\t// Search for all @Special methods.\n\t\t\tvar methods = findMethods(SuperclassWithInstanceMethods.class, isSpecial);\n\n\t\t\tassertThat(signaturesOf(methods))//\n\t\t\t\t\t.containsExactlyInAnyOrder(\"specialFoo()\", \"specialFoo(int)\", \"specialFoo(char)\", \"specialBar()\",\n\t\t\t\t\t\t\"specialBaz()\");\n\n\t\t\t// Search for all @Special methods.\n\t\t\tmethods = findMethods(SubclassWithOverriddenInstanceMethods.class, isSpecial);\n\n\t\t\tassertThat(signaturesOf(methods))//\n\t\t\t\t\t.containsExactlyInAnyOrder(\"foo()\", \"specialFoo()\", \"specialFoo(int)\", \"specialBar()\");\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsReturnsAllOverloadedMethodsInGenericTypeHierarchy() {\n\t\t\tClass<?> clazz = InterfaceWithGenericDefaultMethodImpl.class;\n\n\t\t\t// Search for all foo(*) methods.\n\t\t\tvar methods = findMethods(clazz, isFooMethod);\n\n\t\t\t// One might expect or desire that the signature for the generic foo(N)\n\t\t\t// default method would be \"foo(java.lang.Long)\" when looked up via the\n\t\t\t// concrete parameterized class, but it apparently is only _visible_ as\n\t\t\t// \"foo(java.lang.Number)\" via reflection.\n\t\t\tassertThat(signaturesOf(methods)).containsExactly(\"foo(java.lang.Number)\", \"foo(java.lang.Double)\");\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsDoesNotReturnOverriddenDefaultMethods() {\n\t\t\tClass<?> clazz = InterfaceWithOverriddenGenericDefaultMethodImpl.class;\n\n\t\t\t// Search for all foo(*) methods.\n\t\t\tvar methods = findMethods(clazz, isFooMethod);\n\t\t\tvar signatures = signaturesOf(methods);\n\n\t\t\t// Although the subsequent assertion covers this case as well, this\n\t\t\t// assertion is in place to provide a more informative failure message.\n\t\t\tassertThat(signatures).as(\"overridden default method should not be in results\").doesNotContain(\n\t\t\t\t\"foo(java.lang.Number)\");\n\t\t\tassertThat(signatures).containsExactly(\"foo(java.lang.Long)\", \"foo(java.lang.Double)\");\n\t\t}\n\n\t\tprivate static List<String> signaturesOf(List<Method> methods) {\n\t\t\t// @formatter:off\n\t\t\treturn methods.stream()\n\t\t\t\t\t.map(m -> \"%s(%s)\".formatted(m.getName(), ClassUtils.nullSafeToString(m.getParameterTypes())))\n\t\t\t\t\t.toList();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid findMethodsIgnoresBridgeMethods() throws Exception {\n\t\t\tassertFalse(Modifier.isPublic(PublicChildClass.class.getSuperclass().getModifiers()));\n\t\t\tassertTrue(Modifier.isPublic(PublicChildClass.class.getModifiers()));\n\t\t\tassertTrue(PublicChildClass.class.getDeclaredMethod(\"method1\").isBridge());\n\t\t\tassertTrue(PublicChildClass.class.getDeclaredMethod(\"method3\").isBridge());\n\n\t\t\tvar methods = findMethods(PublicChildClass.class, method -> true);\n\t\t\tvar signatures = signaturesOf(methods);\n\t\t\tassertThat(signatures).containsOnly(\"method1()\", \"method2()\", \"method3()\", \"otherMethod1()\",\n\t\t\t\t\"otherMethod2()\");\n\t\t\tassertEquals(0, methods.stream().filter(Method::isBridge).count());\n\t\t}\n\n\t\t/**\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">GitHub - Issue #3553</a>\n\t\t */\n\t\t@Test\n\t\tvoid findMethodsDoesNotAllowInstanceMethodToHideStaticMethod() throws Exception {\n\t\t\tfinal String BEFORE = \"before\";\n\t\t\tClass<?> superclass = SuperclassWithStaticPackagePrivateBeforeMethod.class;\n\t\t\tMethod staticMethod = superclass.getDeclaredMethod(BEFORE);\n\t\t\tClass<?> subclass = SubclassWithNonStaticPackagePrivateBeforeMethod.class;\n\t\t\tMethod nonStaticMethod = subclass.getDeclaredMethod(BEFORE);\n\n\t\t\t// Prerequisite\n\t\t\tvar methods = findMethods(superclass, ReflectionUtils::isStatic);\n\t\t\tassertThat(methods).containsExactly(staticMethod);\n\n\t\t\t// Actual use cases for this test\n\t\t\tmethods = findMethods(subclass, ReflectionUtils::isStatic);\n\t\t\tassertThat(methods).containsExactly(staticMethod);\n\t\t\tmethods = findMethods(subclass, ReflectionUtils::isNotStatic);\n\t\t\tassertThat(methods).containsExactly(nonStaticMethod);\n\t\t}\n\n\t\tinterface StringsInterface1 {\n\t\t\tstatic Stream<String> strings() {\n\t\t\t\treturn Stream.of(\"abc\", \"def\");\n\t\t\t}\n\t\t}\n\n\t\tinterface StringsInterface2 extends StringsInterface1 {\n\t\t}\n\n\t\t/**\n\t\t * Inherits strings() from interfaces StringsInterface1 and StringsInterface2.\n\t\t */\n\t\tstatic class DoubleInheritedInterfaceMethodTestCase implements StringsInterface1, StringsInterface2 {\n\t\t}\n\n\t\t@Target(ElementType.METHOD)\n\t\t@Retention(RetentionPolicy.RUNTIME)\n\t\t@interface Special {\n\t\t}\n\n\t\tstatic class SuperclassWithInstanceMethods {\n\n\t\t\tvoid foo() {\n\t\t\t}\n\n\t\t\tvoid bar() {\n\t\t\t}\n\n\t\t\tvoid baz() {\n\t\t\t}\n\n\t\t\t@Special\n\t\t\tvoid specialFoo() {\n\t\t\t}\n\n\t\t\t@Special\n\t\t\tvoid specialFoo(int i) {\n\t\t\t}\n\n\t\t\t@Special\n\t\t\tvoid specialFoo(char ch) {\n\t\t\t}\n\n\t\t\t@Special\n\t\t\tint specialBar() {\n\t\t\t\treturn 99;\n\t\t\t}\n\n\t\t\t@Special\n\t\t\tString specialBaz() {\n\t\t\t\treturn \"42\";\n\t\t\t}\n\t\t}\n\n\t\tstatic class SubclassWithOverriddenInstanceMethods extends SuperclassWithInstanceMethods {\n\n\t\t\t// foo() is now special.\n\t\t\t@Special\n\t\t\t@Override\n\t\t\tvoid foo() {\n\t\t\t}\n\n\t\t\t// No longer special.\n\t\t\t// Simulates overriding a @Test method without redeclaring @Test.\n\t\t\t@Override\n\t\t\tvoid specialFoo(char ch) {\n\t\t\t}\n\n\t\t\t// No longer special.\n\t\t\t// Simulates overriding a @TestFactory method without redeclaring @TestFactory.\n\t\t\t@Override\n\t\t\tString specialBaz() {\n\t\t\t\treturn super.specialBaz();\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ReadFieldTests {\n\n\t\t@Test\n\t\tvoid tryToReadFieldValueOfNonexistentStaticField() {\n\t\t\tassertThrows(NoSuchFieldException.class,\n\t\t\t\t() -> tryToReadFieldValue(MyClass.class, \"doesNotExist\", null).get());\n\t\t\tassertThrows(NoSuchFieldException.class,\n\t\t\t\t() -> tryToReadFieldValue(MySubClass.class, \"staticField\", null).get());\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToReadFieldValueOfNonexistentInstanceField() {\n\t\t\tassertThrows(NoSuchFieldException.class,\n\t\t\t\t() -> tryToReadFieldValue(MyClass.class, \"doesNotExist\", new MyClass(42)).get());\n\t\t\tassertThrows(NoSuchFieldException.class,\n\t\t\t\t() -> tryToReadFieldValue(MyClass.class, \"doesNotExist\", new MySubClass(42)).get());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid tryToReadFieldValueOfExistingStaticField() throws Exception {\n\t\t\tassertThat(tryToReadFieldValue(MyClass.class, \"staticField\", null).get()).isEqualTo(42);\n\n\t\t\tvar field = MyClass.class.getDeclaredField(\"staticField\");\n\t\t\tassertThat(tryToReadFieldValue(field).get()).isEqualTo(42);\n\t\t\tassertThat(tryToReadFieldValue(field, null).get()).isEqualTo(42);\n\t\t}\n\n\t\t@Test\n\t\tvoid tryToReadFieldValueOfExistingInstanceField() throws Exception {\n\t\t\tvar instance = new MyClass(42);\n\t\t\tassertThat(tryToReadFieldValue(MyClass.class, \"instanceField\", instance).get()).isEqualTo(42);\n\n\t\t\tvar field = MyClass.class.getDeclaredField(\"instanceField\");\n\t\t\tassertThat(tryToReadFieldValue(field, instance).getNonNull()).isEqualTo(42);\n\n\t\t\tassertPreconditionViolationFor(() -> tryToReadFieldValue(field, null).get())//\n\t\t\t\t\t.withMessageStartingWith(\"Cannot read non-static field\")//\n\t\t\t\t\t.withMessageEndingWith(\"on a null instance.\");\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass FindAndReadFieldsTests {\n\n\t\t/**\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">GitHub - Issue #3553</a>\n\t\t */\n\t\t@Test\n\t\tvoid findFieldsDoesNotAllowInstanceFieldToHideStaticField() throws Exception {\n\t\t\tfinal String TEMP_DIR = \"tempDir\";\n\t\t\tClass<?> superclass = SuperclassWithStaticPackagePrivateTempDirField.class;\n\t\t\tField staticField = superclass.getDeclaredField(TEMP_DIR);\n\t\t\tClass<?> subclass = SubclassWithNonStaticPackagePrivateTempDirField.class;\n\t\t\tField nonStaticField = subclass.getDeclaredField(TEMP_DIR);\n\n\t\t\t// Prerequisite\n\t\t\tvar fields = findFields(superclass, ReflectionUtils::isStatic, TOP_DOWN);\n\t\t\tassertThat(fields).containsExactly(staticField);\n\n\t\t\t// Actual use cases for this test\n\t\t\tfields = findFields(subclass, ReflectionUtils::isStatic, TOP_DOWN);\n\t\t\tassertThat(fields).containsExactly(staticField);\n\t\t\tfields = findFields(subclass, ReflectionUtils::isNotStatic, TOP_DOWN);\n\t\t\tassertThat(fields).containsExactly(nonStaticField);\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid readFieldValuesPreconditions() {\n\t\t\tList<Field> fields = new ArrayList<>();\n\t\t\tassertPreconditionViolationFor(() -> readFieldValues(null, new Object()));\n\t\t\tassertPreconditionViolationFor(() -> readFieldValues(fields, null, null));\n\t\t\tassertPreconditionViolationFor(() -> readFieldValues(fields, new Object(), null));\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromInstance() {\n\t\t\tvar fields = findFields(ClassWithFields.class, f -> true, TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, new ClassWithFields());\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(\"enigma\", 3.14, \"text\", 2.5, null, 42, \"constant\",\n\t\t\t\t99);\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromClass() {\n\t\t\tvar fields = findFields(ClassWithFields.class, ReflectionUtils::isStatic, TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, null);\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(2.5, \"constant\", 99);\n\t\t}\n\n\t\t/**\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3646\">GitHub - Issue #3646</a>\n\t\t * @since 1.11\n\t\t */\n\t\t@Test\n\t\tvoid readFieldValuesFromInterfacesAndClassesInTypeHierarchy() {\n\t\t\tvar fields = findFields(InterfaceWithField.class, ReflectionUtils::isStatic, TOP_DOWN);\n\t\t\tvar values = readFieldValues(fields, null);\n\t\t\tAssertions.<Object> assertThat(values).containsOnly(\"ifc\");\n\n\t\t\tfields = findFields(SuperclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN);\n\t\t\tvalues = readFieldValues(fields, null);\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(\"ifc\", \"super\");\n\n\t\t\tfields = findFields(SubclassWithFieldAndFieldFromInterface.class, ReflectionUtils::isStatic, TOP_DOWN);\n\t\t\tvalues = readFieldValues(fields, null);\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(\"ifc\", \"super\", \"sub\");\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromInstanceWithTypeFilterForString() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(String.class), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, new ClassWithFields(), isA(String.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(\"enigma\", \"text\", null, \"constant\");\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromClassWithTypeFilterForString() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(String.class).and(ReflectionUtils::isStatic), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, null, isA(String.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(\"constant\");\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromInstanceWithTypeFilterForInteger() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(int.class), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, new ClassWithFields(), isA(int.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(42);\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromClassWithTypeFilterForInteger() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(Integer.class).and(ReflectionUtils::isStatic), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, null, isA(Integer.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(99);\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromInstanceWithTypeFilterForDouble() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(double.class), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, new ClassWithFields(), isA(double.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(3.14);\n\t\t}\n\n\t\t@Test\n\t\tvoid readFieldValuesFromClassWithTypeFilterForDouble() {\n\t\t\tvar fields = findFields(ClassWithFields.class, isA(Double.class).and(ReflectionUtils::isStatic), TOP_DOWN);\n\n\t\t\tvar values = readFieldValues(fields, null, isA(Double.class));\n\n\t\t\tAssertions.<Object> assertThat(values).containsExactly(2.5);\n\t\t}\n\n\t\tprivate static Predicate<Field> isA(Class<?> type) {\n\t\t\treturn f -> f.getType().isAssignableFrom(type);\n\t\t}\n\n\t\tpublic static class ClassWithFields {\n\n\t\t\tpublic static final String CONST = \"constant\";\n\n\t\t\tpublic static final Integer CONST_INTEGER = 99;\n\n\t\t\tpublic static final Double CONST_DOUBLE = 2.5;\n\n\t\t\tpublic final String stringField = \"text\";\n\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tprivate final String privateStringField = \"enigma\";\n\n\t\t\t@Nullable\n\t\t\tfinal String nullStringField = null;\n\n\t\t\tpublic final int integerField = 42;\n\n\t\t\tpublic final double doubleField = 3.14;\n\n\t\t}\n\n\t\tinterface InterfaceWithField {\n\n\t\t\tString interfacePath = \"ifc\";\n\t\t}\n\n\t\tstatic class SuperclassWithFieldAndFieldFromInterface implements InterfaceWithField {\n\n\t\t\tstatic final String superPath = \"super\";\n\t\t}\n\n\t\tstatic class SubclassWithFieldAndFieldFromInterface extends SuperclassWithFieldAndFieldFromInterface\n\t\t\t\timplements InterfaceWithField {\n\n\t\t\tstatic final String subPath = \"sub\";\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tinterface Generic<X, Y, Z extends X> {\n\n\t\tX foo();\n\n\t\tY foo(X x, Y y);\n\n\t\tZ foo(Z[][] zees);\n\n\t\t<T> int foo(T t);\n\n\t\t<T> T foo(int i);\n\t}\n\n\tclass ClassWithSyntheticMethod {\n\n\t\t// The following lambda expression results in a synthetic method in the\n\t\t// compiled byte code.\n\t\tComparable<Number> synthetic = number -> 0;\n\t}\n\n\tinterface InterfaceWithOneDeclaredMethod {\n\n\t\tvoid foo();\n\t}\n\n\tinterface InterfaceWithDefaultMethod {\n\n\t\tdefault void foo() {\n\t\t}\n\t}\n\n\tstatic class InterfaceWithDefaultMethodImpl implements InterfaceWithDefaultMethod {\n\t}\n\n\tinterface InterfaceWithGenericDefaultMethod<N extends Number> {\n\n\t\tdefault void foo(N number) {\n\t\t}\n\t}\n\n\tstatic class InterfaceWithGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod<Long> {\n\n\t\tvoid foo(Double number) {\n\t\t}\n\t}\n\n\tstatic class InterfaceWithOverriddenGenericDefaultMethodImpl implements InterfaceWithGenericDefaultMethod<Long> {\n\n\t\t@Override\n\t\tpublic void foo(Long number) {\n\t\t}\n\n\t\tvoid foo(Double number) {\n\t\t}\n\t}\n\n\tinterface InterfaceWithStaticMethod {\n\n\t\tstatic void foo() {\n\t\t}\n\t}\n\n\tstatic class InterfaceWithStaticMethodImpl implements InterfaceWithStaticMethod {\n\t}\n\n\tstatic class MyClass {\n\n\t\tstatic final int staticField = 42;\n\n\t\tfinal int instanceField;\n\n\t\tMyClass(int value) {\n\t\t\tthis.instanceField = value;\n\t\t}\n\t}\n\n\tstatic class MySubClass extends MyClass {\n\n\t\tMySubClass(int value) {\n\t\t\tsuper(value);\n\t\t}\n\t}\n\n\tstatic class GrandparentClass {\n\n\t\tpublic void method1() {\n\t\t}\n\n\t\tpublic void otherMethod1() {\n\t\t}\n\t}\n\n\tinterface GrandparentInterface {\n\n\t\tdefault void method2() {\n\t\t}\n\t}\n\n\tstatic class ParentClass extends GrandparentClass implements GrandparentInterface {\n\n\t\tpublic void method3() {\n\t\t}\n\n\t\tpublic void otherMethod2() {\n\t\t}\n\t}\n\n\tstatic class ChildClass extends ParentClass {\n\n\t\tpublic void method4() {\n\t\t}\n\n\t\tpublic void otherMethod3() {\n\t\t}\n\t}\n\n\t// \"public\" modifier is necessary here, so that the compiler creates a bridge method.\n\tpublic static class PublicChildClass extends ParentClass {\n\n\t\t@Override\n\t\tpublic void otherMethod1() {\n\t\t}\n\n\t\t@Override\n\t\tpublic void otherMethod2() {\n\t\t}\n\t}\n\n\tinterface MethodShadowingInterface {\n\n\t\tdefault void method1(String string) {\n\t\t}\n\n\t\tdefault void method2(int i, int j) {\n\t\t}\n\t}\n\n\tstatic class MethodShadowingParent implements MethodShadowingInterface {\n\n\t\t@Override\n\t\tpublic void method1(String string) {\n\t\t}\n\n\t\tpublic void method2(int i, int j, int k) {\n\t\t}\n\n\t\tpublic void method4(boolean flag) {\n\t\t}\n\n\t\tpublic void method5(String string) {\n\t\t}\n\t}\n\n\tstatic class MethodShadowingChild extends MethodShadowingParent {\n\n\t\t@Override\n\t\tpublic void method1(String string) {\n\t\t}\n\n\t\t@Override\n\t\tpublic void method4(boolean flag) {\n\t\t}\n\n\t\tpublic void method5(Long i) {\n\t\t}\n\t}\n\n\tinterface StaticMethodHidingInterface {\n\n\t\tstatic void method1(String string) {\n\t\t}\n\n\t\tstatic void method2(int i, int j) {\n\t\t}\n\t}\n\n\tstatic class StaticMethodHidingParent implements StaticMethodHidingInterface {\n\n\t\tstatic void method1(String string) {\n\t\t}\n\n\t\tstatic void method2(int i, int j, int k) {\n\t\t}\n\n\t\tstatic void method4(boolean flag) {\n\t\t}\n\n\t\tstatic void method5(String string) {\n\t\t}\n\t}\n\n\tstatic class StaticMethodHidingChild extends StaticMethodHidingParent {\n\n\t\tstatic void method1(String string) {\n\t\t}\n\n\t\tstatic void method4(boolean flag) {\n\t\t}\n\n\t\tstatic void method5(Long i) {\n\t\t}\n\t}\n\n\tabstract static class AbstractOuterClass {\n\n\t\tclass InnerClass {\n\t\t}\n\t}\n\n\tstatic class OuterClass extends AbstractOuterClass {\n\n\t\t// sibling of OuterClass due to common super type\n\t\tstatic class StaticNestedSiblingClass extends AbstractOuterClass {\n\t\t}\n\n\t\t// sibling of OuterClass due to common super type\n\t\tclass InnerSiblingClass extends AbstractOuterClass {\n\t\t}\n\n\t\tstatic class StaticNestedClass extends OuterClass {\n\t\t}\n\n\t\tclass RecursiveInnerClass extends OuterClass {\n\t\t}\n\n\t\tclass InnerClass {\n\t\t\tclass RecursiveInnerInnerClass extends OuterClass {\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class OuterClassImplementingInterface implements InterfaceWithNestedClass {\n\n\t\tclass InnerClassImplementingInterface implements InterfaceWithNestedClass {\n\t\t}\n\t}\n\n\tstatic class OuterClassWithMultipleNestedClasses {\n\t\tclass Alpha {\n\t\t}\n\t\tclass Beta {\n\t\t}\n\t\tclass Gamma {\n\t\t}\n\t\tclass Delta {\n\t\t}\n\t\tclass Epsilon {\n\t\t}\n\t\tclass Zeta {\n\t\t}\n\t\tclass Eta {\n\t\t}\n\t\tclass Theta {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ReflectionUtilsWithGenericTypeHierarchiesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.commons.util.ReflectionUtils.findMethod;\n\nimport java.lang.reflect.Method;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n@SuppressWarnings(\"TypeParameterExplicitlyExtendsObject\")\nclass ReflectionUtilsWithGenericTypeHierarchiesTests {\n\t@Test\n\t@Disabled(\"Describes a new case that does not yet yield the expected result.\")\n\tvoid findsMethodsIndependentlyFromOrderOfImplementationsOfInterfaces() {\n\n\t\tclass AB implements InterfaceDouble, InterfaceGenericNumber<Number> {\n\t\t}\n\n\t\tclass BA implements InterfaceGenericNumber<Number>, InterfaceDouble {\n\t\t}\n\n\t\tvar methodAB = findMethod(AB.class, \"foo\", Double.class).orElseThrow();\n\t\tvar methodBA = findMethod(BA.class, \"foo\", Double.class).orElseThrow();\n\n\t\tassertEquals(methodAB, methodBA);\n\t}\n\n\t@Test\n\tvoid findMoreSpecificMethodFromAbstractImplementationOverDefaultInterfaceMethod() {\n\t\tclass A implements InterfaceGenericNumber<Long> {\n\t\t\t@Override\n\t\t\tpublic void foo(Long parameter) {\n\t\t\t}\n\t\t}\n\n\t\tvar foo = findMethod(A.class, \"foo\", Long.class).orElseThrow();\n\n\t\tassertEquals(A.class, foo.getDeclaringClass());\n\t}\n\n\t@Test\n\t@Disabled(\"Describes a new case that does not yet yield the expected result.\")\n\tvoid findMoreSpecificMethodFromOverriddenImplementationOfGenericInterfaceMethod() {\n\t\tclass A implements InterfaceGenericNumber<Number> {\n\t\t\t@Override\n\t\t\tpublic void foo(Number parameter) {\n\t\t\t}\n\t\t}\n\n\t\tvar foo = findMethod(A.class, \"foo\", Long.class).orElseThrow();\n\n\t\tassertEquals(A.class, foo.getDeclaringClass());\n\t}\n\n\t@Test\n\t@Disabled(\"Describes a new case that does not yet yield the expected result.\")\n\tvoid findMoreSpecificMethodFromImplementationOverDefaultInterfaceMethodAndGenericClassExtension() {\n\n\t\tclass AParent {\n\t\t\t@SuppressWarnings(\"unused\")\n\t\t\tpublic void foo(Number parameter) {\n\t\t\t}\n\t\t}\n\n\t\tclass A extends AParent implements InterfaceGenericNumber<Number> {\n\t\t\t@Override\n\t\t\tpublic void foo(Number parameter) {\n\t\t\t}\n\t\t}\n\n\t\tvar foo = findMethod(A.class, \"foo\", Long.class).orElseThrow();\n\n\t\tassertEquals(A.class, foo.getDeclaringClass());\n\t}\n\n\t@Test\n\t@Disabled(\"Expected behaviour is not clear yet.\")\n\tvoid unclearPrecedenceOfImplementationsInParentClassAndInterfaceDefault() {\n\n\t\tclass AParent {\n\t\t\tpublic void foo(@SuppressWarnings(\"unused\") Number parameter) {\n\t\t\t}\n\t\t}\n\n\t\tclass A extends AParent implements InterfaceGenericNumber<Number> {\n\t\t}\n\n\t\tvar foo = findMethod(A.class, \"foo\", Long.class).orElseThrow();\n\n\t\t// ????????\n\t\tassertEquals(A.class, foo.getDeclaringClass());\n\t\tassertEquals(AParent.class, foo.getDeclaringClass());\n\t\tassertEquals(InterfaceGenericNumber.class, foo.getDeclaringClass());\n\t}\n\n\t@Test\n\t@Disabled(\"Describes cases where current implementation returns unexpected value\")\n\tpublic void findMethodWithMostSpecificParameterTypeInHierarchy() {\n\t\t// Searched Parameter Type is more specific\n\t\tassertSpecificFooMethodFound(ClassImplementingInterfaceWithInvertedHierarchy.class,\n\t\t\tInterfaceWithGenericNumberParameter.class, Double.class);\n\t\tassertSpecificFooMethodFound(ClassImplementingGenericInterfaceWithMoreSpecificMethod.class,\n\t\t\tClassImplementingGenericInterfaceWithMoreSpecificMethod.class, Double.class);\n\t\tassertSpecificFooMethodFound(ClassImplementingGenericAndMoreSpecificInterface.class,\n\t\t\tInterfaceWithGenericNumberParameter.class, Double.class);\n\t\tassertSpecificFooMethodFound(ClassOverridingDefaultMethodAndImplementingMoreSpecificInterface.class,\n\t\t\tClassOverridingDefaultMethodAndImplementingMoreSpecificInterface.class, Double.class);\n\n\t\t// Exact Type Match\n\t\tassertSpecificFooMethodFound(ClassImplementingGenericInterfaceWithMoreSpecificMethod.class,\n\t\t\tClassImplementingGenericInterfaceWithMoreSpecificMethod.class, Number.class);\n\t}\n\n\tprivate void assertSpecificFooMethodFound(Class<?> classToSearchIn, Class<?> classWithMostSpecificMethod,\n\t\t\tClass<?> parameterType) {\n\t\tvar foo = findMethod(classToSearchIn, \"foo\", parameterType).orElseThrow();\n\t\tassertDeclaringClass(foo, classWithMostSpecificMethod);\n\t}\n\n\tprivate void assertDeclaringClass(Method method, Class<?> expectedClass) {\n\t\tassertEquals(expectedClass, method.getDeclaringClass());\n\t}\n\n\tinterface InterfaceDouble {\n\t\tdefault void foo(@SuppressWarnings(\"unused\") Double parameter) {\n\t\t}\n\t}\n\n\tinterface InterfaceGenericNumber<T extends Number> {\n\t\tdefault void foo(@SuppressWarnings(\"unused\") T parameter) {\n\t\t}\n\t}\n\n\tpublic interface InterfaceWithGenericObjectParameter {\n\t\tdefault <T extends Object> void foo(@SuppressWarnings(\"unused\") T a) {\n\t\t}\n\t}\n\n\tpublic interface InterfaceWithGenericNumberParameter {\n\t\tdefault <T extends Number> void foo(@SuppressWarnings(\"unused\") T a) {\n\t\t}\n\t}\n\n\tpublic interface InterfaceExtendingNumberInterfaceWithGenericObjectMethod\n\t\t\textends InterfaceWithGenericNumberParameter {\n\t\tdefault <T extends Object> void foo(@SuppressWarnings(\"unused\") T a) {\n\t\t}\n\t}\n\n\tpublic static class ClassImplementingGenericInterfaceWithMoreSpecificMethod\n\t\t\timplements InterfaceWithGenericObjectParameter {\n\t\tpublic void foo(@SuppressWarnings(\"unused\") Number a) {\n\t\t}\n\t}\n\n\tpublic static class ClassImplementingGenericAndMoreSpecificInterface\n\t\t\timplements InterfaceWithGenericObjectParameter, InterfaceWithGenericNumberParameter {\n\t}\n\n\tpublic static class ClassOverridingDefaultMethodAndImplementingMoreSpecificInterface\n\t\t\timplements InterfaceWithGenericObjectParameter, InterfaceWithGenericNumberParameter {\n\n\t\t@Override\n\t\tpublic <T> void foo(@SuppressWarnings(\"unused\") T a) {\n\t\t}\n\t}\n\n\tpublic static class ClassImplementingInterfaceWithInvertedHierarchy\n\t\t\timplements InterfaceExtendingNumberInterfaceWithGenericObjectMethod {\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/RuntimeUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link RuntimeUtils}.\n *\n * @since 1.6\n */\nclass RuntimeUtilsTests {\n\n\t@Test\n\tvoid jmxIsAvailableAndInputArgumentsAreReturned() {\n\t\tvar optionalArguments = RuntimeUtils.getInputArguments();\n\t\tassertTrue(optionalArguments.isPresent(), \"JMX not available or something else happened...\");\n\t\tvar arguments = optionalArguments.get();\n\t\tassertNotNull(arguments);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/SerializationUtils.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\n\npublic class SerializationUtils {\n\n\tpublic static Object deserialize(byte[] bytes) throws Exception {\n\t\ttry (var in = new ObjectInputStream(new ByteArrayInputStream(bytes))) {\n\t\t\treturn in.readObject();\n\t\t}\n\t}\n\n\tpublic static byte[] serialize(Object object) throws Exception {\n\t\ttry (var byteArrayOutputStream = new ByteArrayOutputStream();\n\t\t\t\tvar objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {\n\t\t\tobjectOutputStream.writeObject(object);\n\t\t\tobjectOutputStream.flush();\n\t\t\treturn byteArrayOutputStream.toByteArray();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/StringUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.StringUtils.containsIsoControlCharacter;\nimport static org.junit.platform.commons.util.StringUtils.containsWhitespace;\nimport static org.junit.platform.commons.util.StringUtils.doesNotContainIsoControlCharacter;\nimport static org.junit.platform.commons.util.StringUtils.doesNotContainWhitespace;\nimport static org.junit.platform.commons.util.StringUtils.isBlank;\nimport static org.junit.platform.commons.util.StringUtils.isNotBlank;\nimport static org.junit.platform.commons.util.StringUtils.nullSafeToString;\nimport static org.junit.platform.commons.util.StringUtils.replaceIsoControlCharacters;\nimport static org.junit.platform.commons.util.StringUtils.replaceWhitespaceCharacters;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link StringUtils}.\n *\n * @since 1.0\n */\nclass StringUtilsTests {\n\n\t@Test\n\tvoid blankness() {\n\t\t// @formatter:off\n\t\tassertAll(\"Blankness\",\n\t\t\t() -> assertTrue(isBlank(null)),\n\t\t\t() -> assertTrue(isBlank(\"\")),\n\t\t\t() -> assertTrue(isBlank(\" \\t\\n\\r\")),\n\t\t\t() -> assertTrue(isNotBlank(\".\"))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid whitespace() {\n\t\t// @formatter:off\n\t\tassertAll(\"Whitespace\",\n\t\t\t() -> shouldContainWhitespace(\"   \"),\n\t\t\t() -> shouldContainWhitespace(\"\\u005Ct\"), // horizontal tab\n\t\t\t() -> shouldContainWhitespace(\"\\t\"),\n\t\t\t() -> shouldContainWhitespace(\"\\u005Cn\"), // line feed\n\t\t\t() -> shouldContainWhitespace(\"\\n\"),\n\t\t\t() -> shouldContainWhitespace(\"\\u005Cf\"), // form feed\n\t\t\t() -> shouldContainWhitespace(\"\\f\"),\n\t\t\t() -> shouldContainWhitespace(\"\\u005Cr\"), // carriage return\n\t\t\t() -> shouldContainWhitespace(\"\\r\"),\n\t\t\t() -> shouldContainWhitespace(\"hello world\"),\n\t\t\t() -> shouldNotContainWhitespace(null),\n\t\t\t() -> shouldNotContainWhitespace(\"\"),\n\t\t\t() -> shouldNotContainWhitespace(\"hello-world\"),\n\t\t\t() -> shouldNotContainWhitespace(\"0123456789\"),\n\t\t\t() -> shouldNotContainWhitespace(\"$-_=+!@.,\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid controlCharacters() {\n\t\t// @formatter:off\n\t\tassertAll(\"ISO Control Characters\",\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\u005Ct\"), // horizontal tab\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\t\"),\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\u005Cn\"), // line feed\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\n\"),\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\u005Cf\"), // form feed\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\f\"),\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\u005Cr\"), // carriage return\n\t\t\t() -> shouldContainIsoControlCharacter(\"\\r\"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(null),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"\"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"hello-world\"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"0123456789\"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"$-_=+!@.,\"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"   \"),\n\t\t\t() -> shouldNotContainIsoControlCharacter(\"hello world\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid replaceControlCharacters() {\n\t\tassertNull(replaceIsoControlCharacters(null, \"\"));\n\t\tassertEquals(\"\", replaceIsoControlCharacters(\"\", \".\"));\n\t\tassertEquals(\"\", replaceIsoControlCharacters(\"\\t\\n\\r\", \"\"));\n\t\tassertEquals(\"...\", replaceIsoControlCharacters(\"\\t\\n\\r\", \".\"));\n\t\tassertEquals(\"...\", replaceIsoControlCharacters(\"\\u005Ct\\u005Cn\\u005Cr\", \".\"));\n\t\tassertEquals(\"abc\", replaceIsoControlCharacters(\"abc\", \"?\"));\n\t\tassertEquals(\"...\", replaceIsoControlCharacters(\"...\", \"?\"));\n\n\t\tassertPreconditionViolationFor(() -> replaceIsoControlCharacters(\"\", null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid replaceWhitespaces() {\n\t\tassertNull(replaceWhitespaceCharacters(null, \"\"));\n\t\tassertEquals(\"\", replaceWhitespaceCharacters(\"\", \".\"));\n\t\tassertEquals(\"\", replaceWhitespaceCharacters(\"\\t\\n\\r\", \"\"));\n\t\tassertEquals(\"...\", replaceWhitespaceCharacters(\"\\t\\n\\r\", \".\"));\n\t\tassertEquals(\"...\", replaceWhitespaceCharacters(\"\\u005Ct\\u005Cn\\u005Cr\", \".\"));\n\t\tassertEquals(\"abc\", replaceWhitespaceCharacters(\"abc\", \"?\"));\n\t\tassertEquals(\"...\", replaceWhitespaceCharacters(\"...\", \"?\"));\n\t\tassertEquals(\" \", replaceWhitespaceCharacters(\" \", \" \"));\n\t\tassertEquals(\" \", replaceWhitespaceCharacters(\"\\u000B\", \" \"));\n\t\tassertEquals(\" \", replaceWhitespaceCharacters(\"\\f\", \" \"));\n\n\t\tassertPreconditionViolationFor(() -> replaceWhitespaceCharacters(\"\", null));\n\t}\n\n\t@Test\n\tvoid nullSafeToStringChecks() {\n\t\tassertEquals(\"null\", nullSafeToString(null));\n\t\tassertEquals(\"\", nullSafeToString(\"\"));\n\t\tassertEquals(\"\\t\", nullSafeToString(\"\\t\"));\n\t\tassertEquals(\"foo\", nullSafeToString(\"foo\"));\n\t\tassertEquals(\"3.14\", nullSafeToString(Double.valueOf(\"3.14\")));\n\t\tassertEquals(\"[1, 2, 3]\", nullSafeToString(new int[] { 1, 2, 3 }));\n\t\tassertEquals(\"[a, b, c]\", nullSafeToString(new char[] { 'a', 'b', 'c' }));\n\t\tassertEquals(\"[foo, bar]\", nullSafeToString(new String[] { \"foo\", \"bar\" }));\n\t\tassertEquals(\"[34, 42]\", nullSafeToString(new Integer[] { 34, 42 }));\n\t\tassertEquals(\"[[2, 4], [3, 9]]\", nullSafeToString(new Integer[][] { { 2, 4 }, { 3, 9 } }));\n\t}\n\n\t@Test\n\tvoid nullSafeToStringForObjectWhoseToStringImplementationReturnsNull() {\n\t\tassertEquals(\"null\", nullSafeToString(new ToStringReturnsNull()));\n\t}\n\n\t@Test\n\tvoid nullSafeToStringForObjectWhoseToStringImplementationThrowsAnException() {\n\t\tassertThat(nullSafeToString(new ToStringThrowsException()))//\n\t\t\t\t.startsWith(ToStringThrowsException.class.getName() + \"@\");\n\t}\n\n\tprivate void shouldContainWhitespace(String str) {\n\t\tassertTrue(containsWhitespace(str), () -> \"'%s' should contain whitespace\".formatted(str));\n\t\tassertFalse(doesNotContainWhitespace(str), () -> \"'%s' should contain whitespace\".formatted(str));\n\t}\n\n\tprivate void shouldNotContainWhitespace(String str) {\n\t\tassertTrue(doesNotContainWhitespace(str), () -> \"'%s' should not contain whitespace\".formatted(str));\n\t\tassertFalse(containsWhitespace(str), () -> \"'%s' should not contain whitespace\".formatted(str));\n\t}\n\n\tprivate void shouldContainIsoControlCharacter(String str) {\n\t\tassertTrue(containsIsoControlCharacter(str), () -> \"'%s' should contain ISO control character\".formatted(str));\n\t\tassertFalse(doesNotContainIsoControlCharacter(str),\n\t\t\t() -> \"'%s' should contain ISO control character\".formatted(str));\n\t}\n\n\tprivate void shouldNotContainIsoControlCharacter(String str) {\n\t\tassertTrue(doesNotContainIsoControlCharacter(str),\n\t\t\t() -> \"'%s' should not contain ISO control character\".formatted(str));\n\t\tassertFalse(containsIsoControlCharacter(str),\n\t\t\t() -> \"'%s' should not contain ISO control character\".formatted(str));\n\t}\n\n\t@NullUnmarked\n\tprivate static class ToStringReturnsNull {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate static class ToStringThrowsException {\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\tthrow new RuntimeException(\"Boom!\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/ToStringBuilderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.jspecify.annotations.NullUnmarked;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ToStringBuilder}.\n *\n * @since 1.0\n */\nclass ToStringBuilderTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid withNullObject() {\n\t\tassertPreconditionViolationFor(() -> new ToStringBuilder((Object) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid withNullClass() {\n\t\tassertPreconditionViolationFor(() -> new ToStringBuilder((Class<?>) null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid appendWithIllegalName() {\n\t\tvar builder = new ToStringBuilder(\"\");\n\n\t\tassertPreconditionViolationFor(() -> builder.append(null, \"\"));\n\t\tassertPreconditionViolationFor(() -> builder.append(\"\", \"\"));\n\t\tassertPreconditionViolationFor(() -> builder.append(\"    \", \"\"));\n\t}\n\n\t@Test\n\tvoid withZeroFields() {\n\t\tassertEquals(\"RoleModel []\", new ToStringBuilder(new RoleModel()).toString());\n\t\tassertEquals(\"RoleModel []\", new ToStringBuilder(RoleModel.class).toString());\n\t}\n\n\t@Test\n\tvoid withOneField() {\n\t\tassertEquals(\"RoleModel [name = 'Dilbert']\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"name\", \"Dilbert\").toString());\n\t}\n\n\t@Test\n\tvoid withNullField() {\n\t\tassertEquals(\"RoleModel [name = null]\", new ToStringBuilder(new RoleModel()).append(\"name\", null).toString());\n\t}\n\n\t@Test\n\tvoid withTwoFields() {\n\t\tassertEquals(\"RoleModel [name = 'Dilbert', age = 42]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"name\", \"Dilbert\").append(\"age\", 42).toString());\n\t}\n\n\t@Test\n\tvoid withIntegerArrayField() {\n\t\tassertEquals(\"RoleModel [magic numbers = [1, 42, 99]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"magic numbers\", new Integer[] { 1, 42, 99 }).toString());\n\t}\n\n\t@Test\n\tvoid withIntArrayField() {\n\t\tassertEquals(\"RoleModel [magic numbers = [1, 42, 23]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"magic numbers\", new int[] { 1, 42, 23 }).toString());\n\t}\n\n\t@Test\n\tvoid withCharArrayField() {\n\t\tassertEquals(\"RoleModel [magic characters = [a, b]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"magic characters\", new char[] { 'a', 'b' }).toString());\n\t}\n\n\t@Test\n\tvoid withPrimitiveBooleanArrayField() {\n\t\tassertEquals(\"RoleModel [booleans = [true, false, true]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"booleans\", new boolean[] { true, false, true }).toString());\n\t}\n\n\t@Test\n\tvoid withShortArrayField() {\n\t\tassertEquals(\"RoleModel [values = [23, 42]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"values\", new short[] { 23, 42 }).toString());\n\t}\n\n\t@Test\n\tvoid withByteArrayField() {\n\t\tassertEquals(\"RoleModel [values = [23, 42]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"values\", new byte[] { 23, 42 }).toString());\n\t}\n\n\t@Test\n\tvoid withPrimitiveLongArrayField() {\n\t\tassertEquals(\"RoleModel [values = [23, 42]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"values\", new long[] { 23, 42 }).toString());\n\t}\n\n\t@Test\n\tvoid withPrimitiveFloatArrayField() {\n\t\tassertEquals(\"RoleModel [values = [23.45, 17.13]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"values\", new float[] { 23.45f, 17.13f }).toString());\n\t}\n\n\t@Test\n\tvoid withPrimitiveDoubleArrayField() {\n\t\tassertEquals(\"RoleModel [values = [23.45, 17.13]]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"values\", new double[] { 23.45d, 17.13d }).toString());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"serial\")\n\tvoid withMapField() {\n\t\t// @formatter:off\n\t\tMap<String,Object> map = new LinkedHashMap<>() {{\n\t\t\tput(\"foo\", 42);\n\t\t\tput(\"bar\", \"enigma\");\n\t\t}};\n\t\t// @formatter:on\n\t\tassertEquals(\"RoleModel [mystery map = {foo=42, bar=enigma}]\",\n\t\t\tnew ToStringBuilder(new RoleModel()).append(\"mystery map\", map).toString());\n\t}\n\n\t@Test\n\tvoid withDemoImplementation() {\n\t\tvar roleModel = new RoleModel(\"Dilbert\", 42);\n\t\tassertEquals(\"RoleModel [name = 'Dilbert', age = 42]\", roleModel.toString());\n\t}\n\n\t@NullUnmarked\n\tstatic class RoleModel {\n\n\t\tString name;\n\t\tint age;\n\n\t\tRoleModel() {\n\t\t}\n\n\t\tRoleModel(String name, int age) {\n\t\t\tthis.name = name;\n\t\t\tthis.age = age;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\t// @formatter:off\n\t\t\treturn new ToStringBuilder(this)\n\t\t\t\t.append(\"name\", this.name)\n\t\t\t\t.append(\"age\", this.age)\n\t\t\t\t.toString();\n\t\t\t// @formatter:on\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/AExecutionConditionClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.7\n */\npublic class AExecutionConditionClass implements ExecutionCondition {\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\treturn ConditionEvaluationResult.enabled(\"for Class Name Filter Tests\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/ATestExecutionListenerClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * @since 5.7\n */\npublic class ATestExecutionListenerClass implements TestExecutionListener {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/AVanillaEmpty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\n/**\n * @since 5.7\n */\npublic class AVanillaEmpty {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/BExecutionConditionClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\nimport org.junit.jupiter.api.extension.ConditionEvaluationResult;\nimport org.junit.jupiter.api.extension.ExecutionCondition;\nimport org.junit.jupiter.api.extension.ExtensionContext;\n\n/**\n * @since 5.7\n */\npublic class BExecutionConditionClass implements ExecutionCondition {\n\n\t@Override\n\tpublic ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {\n\t\treturn ConditionEvaluationResult.enabled(\"for Class Name Filter Tests\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/BTestExecutionListenerClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * @since 5.7\n */\npublic class BTestExecutionListenerClass implements TestExecutionListener {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/BVanillaEmpty.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\n/**\n * @since 5.7\n */\npublic class BVanillaEmpty {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/classes/CustomType.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.classes;\n\npublic class CustomType {\n\n\tvoid customMethod(NestedType arg) {\n\t}\n\n\tpublic static class NestedType {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/ClassLevelDir.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Mimics {@code @TempDir}.\n */\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ClassLevelDir {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/InstanceLevelDir.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Mimics {@code @TempDir}.\n */\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface InstanceLevelDir {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateBeforeMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1;\n\nimport org.junit.jupiter.api.BeforeAll;\n\n/**\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">GitHub - Issue #3553</a>\n */\npublic class SuperclassWithStaticPackagePrivateBeforeMethod {\n\n\t@BeforeAll\n\tstatic void before() {\n\t\t// no-op\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/SuperclassWithStaticPackagePrivateTempDirField.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1;\n\nimport java.nio.file.Path;\n\n/**\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">GitHub - Issue #3553</a>\n */\npublic class SuperclassWithStaticPackagePrivateTempDirField {\n\n\t@ClassLevelDir\n\tstatic Path tempDir;\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateBeforeMethod.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1.subpkg;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateBeforeMethod;\n\n/**\n * See <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">GitHub -Issue #3553</a>\n */\npublic class SubclassWithNonStaticPackagePrivateBeforeMethod extends SuperclassWithStaticPackagePrivateBeforeMethod {\n\n\t@BeforeEach\n\tvoid before() {\n\t\t// no-op\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/commons/util/pkg1/subpkg/SubclassWithNonStaticPackagePrivateTempDirField.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util.pkg1.subpkg;\n\nimport java.nio.file.Path;\n\nimport org.junit.platform.commons.util.pkg1.InstanceLevelDir;\nimport org.junit.platform.commons.util.pkg1.SuperclassWithStaticPackagePrivateTempDirField;\n\n/**\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3553\">Github - Issue #3553</a>\n */\npublic class SubclassWithNonStaticPackagePrivateTempDirField extends SuperclassWithStaticPackagePrivateTempDirField {\n\n\t@InstanceLevelDir\n\tPath tempDir;\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/ConsoleDetailsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.support.HierarchyTraversalMode.TOP_DOWN;\nimport static org.junit.platform.commons.support.ReflectionSupport.findMethods;\nimport static org.junit.platform.commons.util.ReflectionUtils.getFullyQualifiedMethodName;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.EnumMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.console.options.Details;\nimport org.junit.platform.console.output.Theme;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * @since 1.0\n */\nclass ConsoleDetailsTests {\n\n\t@TestFactory\n\t@DisplayName(\"Basic tests and annotations usage\")\n\tList<DynamicNode> basic() {\n\t\treturn scanContainerClassAndCreateDynamicTests(BasicTestCase.class);\n\t}\n\n\t@TestFactory\n\t@DisplayName(\"Skipped and disabled tests\")\n\tList<DynamicNode> skipped() {\n\t\treturn scanContainerClassAndCreateDynamicTests(SkipTestCase.class);\n\t}\n\n\t@TestFactory\n\t@DisplayName(\"Failed tests\")\n\tList<DynamicNode> failed() {\n\t\treturn scanContainerClassAndCreateDynamicTests(FailTestCase.class);\n\t}\n\n\t@TestFactory\n\t@DisplayName(\"Tests publishing report entries\")\n\tList<DynamicNode> reports() {\n\t\treturn scanContainerClassAndCreateDynamicTests(ReportTestCase.class);\n\t}\n\n\tprivate List<DynamicNode> scanContainerClassAndCreateDynamicTests(Class<?> containerClass) {\n\t\tvar containerName = containerClass.getSimpleName().replace(\"TestCase\", \"\");\n\t\t// String containerName = containerClass.getSimpleName();\n\t\tList<DynamicNode> nodes = new ArrayList<>();\n\t\tMap<Details, List<DynamicTest>> map = new EnumMap<>(Details.class);\n\t\tfor (var method : findMethods(containerClass, m -> m.isAnnotationPresent(Test.class), TOP_DOWN)) {\n\t\t\tvar methodName = method.getName();\n\t\t\tvar types = method.getParameterTypes();\n\t\t\tfor (var details : Details.values()) {\n\t\t\t\tvar tests = map.computeIfAbsent(details, key -> new ArrayList<>());\n\t\t\t\tfor (var theme : Theme.values()) {\n\t\t\t\t\tvar caption = containerName + \"-\" + methodName + \"-\" + details + \"-\" + theme;\n\t\t\t\t\tString[] args = { //\n\t\t\t\t\t\t\t\"execute\", //\n\t\t\t\t\t\t\t\"--include-engine\", \"junit-jupiter\", //\n\t\t\t\t\t\t\t\"--details\", details.name(), //\n\t\t\t\t\t\t\t\"--details-theme\", theme.name(), //\n\t\t\t\t\t\t\t\"--disable-ansi-colors\", //\n\t\t\t\t\t\t\t\"--disable-banner\", //\n\t\t\t\t\t\t\t\"--include-classname\", containerClass.getCanonicalName(), //\n\t\t\t\t\t\t\t\"--select-method\", getFullyQualifiedMethodName(containerClass, methodName, types) //\n\t\t\t\t\t};\n\t\t\t\t\tvar displayName = methodName + \"() \" + theme.name();\n\t\t\t\t\tvar dirName = \"console/details/\" + containerName.toLowerCase();\n\t\t\t\t\tvar outName = caption + \".out.txt\";\n\t\t\t\t\tvar runner = new Runner(dirName, outName, args);\n\t\t\t\t\tvar source = toUri(dirName, outName).orElse(null);\n\t\t\t\t\ttests.add(dynamicTest(displayName, source, runner));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tvar source = new File(\"src/test/resources/console/details\").toURI();\n\t\tmap.forEach((details, tests) -> nodes.add(dynamicContainer(details.name(), source, tests.stream())));\n\t\treturn nodes;\n\t}\n\n\t@DisplayName(\"Basic\")\n\tstatic class BasicTestCase {\n\n\t\t@Test\n\t\tvoid empty() {\n\t\t}\n\n\t\t@Test\n\t\t@DisplayName(\".oO fancy display name Oo.\")\n\t\tvoid changeDisplayName() {\n\t\t}\n\n\t}\n\n\t@DisplayName(\"Skip\")\n\tstatic class SkipTestCase {\n\n\t\t@Test\n\t\t@Disabled(\"single line skip reason\")\n\t\tvoid skipWithSingleLineReason() {\n\t\t}\n\n\t\t@Test\n\t\t@Disabled(\"multi\\nline\\nfail\\nmessage\")\n\t\tvoid skipWithMultiLineMessage() {\n\t\t}\n\n\t}\n\n\t@DisplayName(\"Fail\")\n\tstatic class FailTestCase {\n\n\t\t@Test\n\t\tvoid failWithSingleLineMessage() {\n\t\t\tfail(\"single line fail message\");\n\t\t}\n\n\t\t@Test\n\t\tvoid failWithMultiLineMessage() {\n\t\t\tfail(\"multi\\nline\\nfail\\nmessage\");\n\t\t}\n\n\t}\n\n\t@DisplayName(\"Report\")\n\tstatic class ReportTestCase {\n\n\t\t@Test\n\t\tvoid reportSingleMessage(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"foo\");\n\t\t}\n\n\t\t@Test\n\t\tvoid reportMultipleMessages(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"foo\");\n\t\t\treporter.publishEntry(\"bar\");\n\t\t}\n\n\t\t@Test\n\t\tvoid reportSingleEntryWithSingleMapping(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"foo\", \"bar\");\n\t\t}\n\n\t\t@Test\n\t\tvoid reportMultiEntriesWithSingleMapping(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"foo\", \"bar\");\n\t\t\treporter.publishEntry(\"far\", \"boo\");\n\t\t}\n\n\t\t@Test\n\t\tvoid reportMultiEntriesWithMultiMappings(TestReporter reporter) {\n\t\t\tMap<String, String> values = new LinkedHashMap<>();\n\t\t\tvalues.put(\"user name\", \"dk38\");\n\t\t\tvalues.put(\"award year\", \"1974\");\n\t\t\treporter.publishEntry(values);\n\t\t\treporter.publishEntry(\"single\", \"mapping\");\n\t\t\tMap<String, String> more = new LinkedHashMap<>();\n\t\t\tmore.put(\"user name\", \"st77\");\n\t\t\tmore.put(\"award year\", \"1977\");\n\t\t\tmore.put(\"last seen\", \"2001\");\n\t\t\treporter.publishEntry(more);\n\t\t}\n\n\t}\n\n\tprivate record Runner(String dirName, String outName, String... args) implements Executable {\n\n\t\t@Override\n\t\tpublic void execute() throws Throwable {\n\t\t\tvar wrapper = new ConsoleLauncherWrapper();\n\t\t\tvar result = wrapper.execute(Optional.empty(), args);\n\n\t\t\tvar optionalUri = toUri(dirName, outName);\n\t\t\tif (optionalUri.isEmpty()) {\n\t\t\t\tif (Boolean.getBoolean(\"org.junit.platform.console.ConsoleDetailsTests.writeResultOut\")) {\n\t\t\t\t\t// do not use Files.createTempDirectory(prefix) as we want one folder for one container\n\t\t\t\t\tvar temp = Path.of(System.getProperty(\"java.io.tmpdir\"), dirName.replace('/', '-'));\n\t\t\t\t\tFiles.createDirectories(temp);\n\t\t\t\t\tvar path = Files.writeString(temp.resolve(outName), result.out);\n\t\t\t\t\tthrow new TestAbortedException(\n\t\t\t\t\t\t\"resource `%s` not found\\nwrote console stdout to: %s/%s\".formatted(dirName, outName, path));\n\t\t\t\t}\n\t\t\t\tfail(\"could not load resource named `\" + dirName + \"/\" + outName + \"`\");\n\t\t\t}\n\n\t\t\tvar path = Path.of(optionalUri.get());\n\t\t\tassumeTrue(Files.exists(path), \"path does not exist: \" + path);\n\t\t\tassumeTrue(Files.isReadable(path), \"can not read: \" + path);\n\n\t\t\tvar expectedLines = Files.readAllLines(path, UTF_8);\n\t\t\tvar actualLines = List.of(result.out.split(\"\\\\R\"));\n\n\t\t\tassertLinesMatch(expectedLines, actualLines);\n\t\t}\n\t}\n\n\tstatic Optional<URI> toUri(String dirName, String outName) {\n\t\tvar resourceName = dirName + \"/\" + outName;\n\t\tvar url = ConsoleDetailsTests.class.getClassLoader().getResource(resourceName);\n\t\tif (url == null) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t\ttry {\n\t\t\treturn Optional.of(url.toURI());\n\t\t}\n\t\tcatch (URISyntaxException e) {\n\t\t\treturn Optional.empty();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertArrayEquals;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.FieldSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.console.command.StdStreamTestCase;\nimport org.junit.platform.console.subpackage.FailingTestCase;\n\n/**\n * @since 1.0\n */\nclass ConsoleLauncherIntegrationTests {\n\n\t@Test\n\tvoid executeWithoutSubcommandFailsAndPrintsHelpInformation() {\n\t\tvar result = new ConsoleLauncherWrapper().execute(-1);\n\t\tassertAll(\"empty args array results in display of help information and an exception stacktrace\", //\n\t\t\t() -> assertThat(result.err).contains(\"help information\"), //\n\t\t\t() -> assertThat(result.err).contains(\"Missing required subcommand\") //\n\t\t);\n\t}\n\n\t@Test\n\tvoid executeWithoutExcludeClassnameOptionDoesNotExcludeClassesAndMustIncludeAllClassesMatchingTheStandardClassnamePattern() {\n\t\tString[] args = { \"execute\", \"-e\", \"junit-jupiter\", \"-p\", \"org.junit.platform.console.subpackage\" };\n\t\tassertEquals(9, new ConsoleLauncherWrapper().execute(args).getTestsFoundCount());\n\t}\n\n\t@Test\n\tvoid executeWithExcludeClassnameOptionExcludesClasses() {\n\t\tString[] args = { \"execute\", \"-e\", \"junit-jupiter\", \"-p\", \"org.junit.platform.console.subpackage\",\n\t\t\t\t\"--exclude-classname\", \"^org\\\\.junit\\\\.platform\\\\.console\\\\.subpackage\\\\..*\" };\n\t\tvar result = new ConsoleLauncherWrapper().execute(args);\n\t\tassertAll(\"all subpackage test classes are excluded by the class name filter\", //\n\t\t\t() -> assertArrayEquals(args, result.args), //\n\t\t\t() -> assertEquals(0, result.code), //\n\t\t\t() -> assertEquals(0, result.getTestsFoundCount()) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid executeWithExcludeMethodNameOptionExcludesMethods() {\n\t\tvar line = \"execute -e junit-jupiter -p org.junit.platform.console.subpackage --exclude-methodname\"\n\t\t\t\t+ \" ^org\\\\.junit\\\\.platform\\\\.console\\\\.subpackage\\\\..+#test\";\n\t\tvar args = line.split(\" \");\n\t\tvar result = new ConsoleLauncherWrapper().execute(args);\n\t\tassertAll(\"all subpackage test methods are excluded by the method name filter\", //\n\t\t\t() -> assertArrayEquals(args, result.args), //\n\t\t\t() -> assertEquals(0, result.code), //\n\t\t\t() -> assertEquals(0, result.getTestsFoundCount()) //\n\t\t);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { //\n\t\t\t\"execute -e junit-jupiter -o java.base\", //\n\t\t\t\"execute -e junit-jupiter --select-module java.base\" //\n\t})\n\tvoid executeSelectingModuleNames(String line) {\n\t\tvar args = line.split(\" \");\n\t\tassertEquals(0, new ConsoleLauncherWrapper().execute(args).getTestsFoundCount());\n\t}\n\n\t@Test\n\tvoid executeScanModules() {\n\t\tString[] args = { \"execute\", \"-e\", \"junit-jupiter\", \"--scan-modules\" };\n\t\tassertEquals(0, new ConsoleLauncherWrapper().execute(args).getTestsFoundCount());\n\t}\n\n\t@ParameterizedTest\n\t@FieldSource(\"redirectStreamArguments\")\n\tvoid executeWithRedirectedStdStream(String redirectedStream, int outputFileSize, @TempDir Path tempDir)\n\t\t\tthrows Exception {\n\n\t\tvar outputFile = tempDir.resolve(\"output.txt\");\n\t\tvar line = \"execute -e junit-jupiter --select-class %s %s %s\".formatted(StdStreamTestCase.class.getName(),\n\t\t\tredirectedStream, outputFile);\n\t\tvar args = line.split(\" \");\n\t\tnew ConsoleLauncherWrapper().execute(args);\n\n\t\tassertTrue(Files.exists(outputFile), \"File does not exist.\");\n\t\tassertEquals(outputFileSize, Files.size(outputFile), \"Invalid file size.\");\n\t}\n\n\tstatic List<Arguments> redirectStreamArguments = List.of(\n\t\targuments(\"--redirect-stdout\", StdStreamTestCase.getStdoutOutputFileSize()),\n\t\targuments(\"--redirect-stderr\", StdStreamTestCase.getStderrOutputFileSize()));\n\n\t@Test\n\tvoid executeWithRedirectedStdStreamsToSameFile(@TempDir Path tempDir) throws Exception {\n\t\tvar outputFile = tempDir.resolve(\"output.txt\");\n\t\tvar line = \"execute -e junit-jupiter --select-class %s --redirect-stdout %s --redirect-stderr %s\".formatted(\n\t\t\tStdStreamTestCase.class.getName(), outputFile, outputFile);\n\t\tvar args = line.split(\" \");\n\t\tnew ConsoleLauncherWrapper().execute(args);\n\n\t\tassertTrue(Files.exists(outputFile), \"File does not exist.\");\n\t\tassertEquals(StdStreamTestCase.getStdoutOutputFileSize() + StdStreamTestCase.getStderrOutputFileSize(),\n\t\t\tFiles.size(outputFile), \"Invalid file size.\");\n\t}\n\n\t@Test\n\tvoid stopsAfterFirstFailingTest() {\n\t\tvar result = new ConsoleLauncherWrapper().execute(1, \"execute\", \"-e\", \"junit-jupiter\", \"--select-class\",\n\t\t\tFailingTestCase.class.getName(), \"--fail-fast\", \"--disable-ansi-colors\");\n\n\t\tassertThat(result.getTestsStartedCount()).isEqualTo(1);\n\t\tassertThat(result.getTestsFailedCount()).isEqualTo(1);\n\t\tassertThat(result.getTestsSkippedCount()).isEqualTo(1);\n\n\t\tassertThat(result.out).endsWith(\"%nTest execution was cancelled due to --fail-fast mode.%n%n\".formatted());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.EmptySource;\nimport org.junit.jupiter.params.provider.MethodSource;\n\n/**\n * @since 1.0\n */\nclass ConsoleLauncherTests {\n\n\tprivate final StringWriter stringWriter = new StringWriter();\n\tprivate final PrintWriter printSink = new PrintWriter(stringWriter);\n\n\t@ParameterizedTest(name = \"cmd={0}\")\n\t@EmptySource\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid displayHelp(String command) {\n\t\tvar exitCode = ConsoleLauncher.run(printSink, printSink, command, \"--help\").getExitCode();\n\n\t\tassertEquals(0, exitCode);\n\t\tassertThat(output()).contains(\"--help\");\n\t}\n\n\t@ParameterizedTest(name = \"cmd={0}\")\n\t@EmptySource\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid displayVersion(String command) {\n\t\tvar exitCode = ConsoleLauncher.run(printSink, printSink, command, \"--version\").getExitCode();\n\n\t\tassertEquals(0, exitCode);\n\t\tassertThat(output()).contains(\"JUnit Platform Console Launcher\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid displayBanner(String command) {\n\t\tConsoleLauncher.run(printSink, printSink, command);\n\n\t\tassertThat(output()).contains(\"Thanks for using JUnit!\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid disableBanner(String command, int expectedExitCode) {\n\t\tvar exitCode = ConsoleLauncher.run(printSink, printSink, command, \"--disable-banner\").getExitCode();\n\n\t\tassertEquals(expectedExitCode, exitCode);\n\t\tassertThat(output()).doesNotContain(\"Thanks for using JUnit!\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid executeWithUnknownCommandLineOption(String command) {\n\t\tvar exitCode = ConsoleLauncher.run(printSink, printSink, command, \"--all\").getExitCode();\n\n\t\tassertEquals(-1, exitCode);\n\t\tassertThat(output()).contains(\"Unknown option: '--all'\").contains(\"Usage:\");\n\t}\n\n\tprivate String output() {\n\t\treturn stringWriter.toString();\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"commandsWithEmptyOptionExitCodes\")\n\tvoid executeWithoutCommandLineOptions(String command, int expectedExitCode) {\n\t\tvar actualExitCode = ConsoleLauncher.run(printSink, printSink, command).getExitCode();\n\n\t\tassertEquals(expectedExitCode, actualExitCode);\n\t}\n\n\tstatic Stream<Arguments> commandsWithEmptyOptionExitCodes() {\n\t\treturn Stream.of( //\n\t\t\targuments(\"execute\", -1), //\n\t\t\targuments(\"discover\", -1), //\n\t\t\targuments(\"engines\", 0) //\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapper.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.Optional;\n\nimport org.junit.platform.console.command.CommandFacade;\nimport org.junit.platform.console.command.ConsoleTestExecutor;\n\n/**\n * @since 1.0\n */\nclass ConsoleLauncherWrapper {\n\n\tprivate final StringWriter out = new StringWriter();\n\tprivate final StringWriter err = new StringWriter();\n\tprivate final ConsoleTestExecutor.Factory consoleTestExecutorFactory;\n\n\tConsoleLauncherWrapper() {\n\t\tthis(ConsoleTestExecutor::new);\n\t}\n\n\tprivate ConsoleLauncherWrapper(ConsoleTestExecutor.Factory consoleTestExecutorFactory) {\n\t\tthis.consoleTestExecutorFactory = consoleTestExecutorFactory;\n\t}\n\n\tpublic ConsoleLauncherWrapperResult execute(String... args) {\n\t\treturn execute(0, args);\n\t}\n\n\tpublic ConsoleLauncherWrapperResult execute(int expectedExitCode, String... args) {\n\t\treturn execute(Optional.of(expectedExitCode), args);\n\t}\n\n\tpublic ConsoleLauncherWrapperResult execute(Optional<Integer> expectedCode, String... args) {\n\t\tvar outWriter = new PrintWriter(out, false);\n\t\tvar errWriter = new PrintWriter(err, false);\n\t\tvar result = new CommandFacade(consoleTestExecutorFactory).run(args, outWriter, errWriter);\n\t\tvar code = result.getExitCode();\n\t\tvar outText = out.toString();\n\t\tvar errText = err.toString();\n\t\tif (expectedCode.isPresent()) {\n\t\t\tint expectedValue = expectedCode.get();\n\t\t\tassertEquals(expectedValue, code, \"ConsoleLauncher execute code mismatch!\");\n\t\t\tif (expectedValue != 0 && expectedValue != 1) {\n\t\t\t\tassertThat(errText).isNotBlank();\n\t\t\t}\n\t\t}\n\t\treturn new ConsoleLauncherWrapperResult(args, outText, errText, result);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/ConsoleLauncherWrapperResult.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console;\n\nimport static java.util.Objects.requireNonNull;\n\nimport java.io.PrintWriter;\nimport java.util.List;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.console.command.CommandResult;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\n\n/**\n * @since 1.0\n */\nclass ConsoleLauncherWrapperResult implements TestExecutionSummary {\n\n\tfinal String[] args;\n\tfinal String out;\n\tfinal String err;\n\tfinal int code;\n\n\tprivate final @Nullable TestExecutionSummary summary;\n\n\tConsoleLauncherWrapperResult(String[] args, String out, String err, CommandResult<?> result) {\n\t\tthis.args = args;\n\t\tthis.out = out;\n\t\tthis.err = err;\n\t\tthis.code = result.getExitCode();\n\t\tthis.summary = (TestExecutionSummary) result.getValue() //\n\t\t\t\t.filter(TestExecutionSummary.class::isInstance) //\n\t\t\t\t.orElse(null);\n\t}\n\n\tprivate void checkTestExecutionSummaryState() {\n\t\tif (summary == null) {\n\t\t\tthrow new IllegalStateException(\"TestExecutionSummary not assigned. Exit code is: \" + code);\n\t\t}\n\t}\n\n\t@Override\n\tpublic long getTimeStarted() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTimeStarted();\n\t}\n\n\t@Override\n\tpublic long getTimeFinished() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTimeFinished();\n\t}\n\n\t@Override\n\tpublic long getTotalFailureCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTotalFailureCount();\n\t}\n\n\t@Override\n\tpublic long getContainersFoundCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersFoundCount();\n\t}\n\n\t@Override\n\tpublic long getContainersStartedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersStartedCount();\n\t}\n\n\t@Override\n\tpublic long getContainersSkippedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersSkippedCount();\n\t}\n\n\t@Override\n\tpublic long getContainersAbortedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersAbortedCount();\n\t}\n\n\t@Override\n\tpublic long getContainersSucceededCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersSucceededCount();\n\t}\n\n\t@Override\n\tpublic long getContainersFailedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getContainersFailedCount();\n\t}\n\n\t@Override\n\tpublic long getTestsFoundCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsFoundCount();\n\t}\n\n\t@Override\n\tpublic long getTestsStartedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsStartedCount();\n\t}\n\n\t@Override\n\tpublic long getTestsSkippedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsSkippedCount();\n\t}\n\n\t@Override\n\tpublic long getTestsAbortedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsAbortedCount();\n\t}\n\n\t@Override\n\tpublic long getTestsSucceededCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsSucceededCount();\n\t}\n\n\t@Override\n\tpublic long getTestsFailedCount() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getTestsFailedCount();\n\t}\n\n\t@Override\n\tpublic void printTo(PrintWriter writer) {\n\t\tcheckTestExecutionSummaryState();\n\t\trequiredSummary().printTo(writer);\n\t}\n\n\t@Override\n\tpublic void printFailuresTo(PrintWriter writer) {\n\t\tcheckTestExecutionSummaryState();\n\t\trequiredSummary().printFailuresTo(writer);\n\t}\n\n\t@Override\n\tpublic void printFailuresTo(PrintWriter writer, int maxStackTraceLines) {\n\t\tcheckTestExecutionSummaryState();\n\t\trequiredSummary().printFailuresTo(writer, maxStackTraceLines);\n\t}\n\n\t@Override\n\tpublic List<Failure> getFailures() {\n\t\tcheckTestExecutionSummaryState();\n\t\treturn requiredSummary().getFailures();\n\t}\n\n\tprivate TestExecutionSummary requiredSummary() {\n\t\treturn requireNonNull(summary);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/CommandLineOptionsParsingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.STANDARD_INCLUDE_PATTERN;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnOs;\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.console.options.Details;\nimport org.junit.platform.console.options.TestConsoleOutputOptions;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.console.output.Theme;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.FilePosition;\n\n/**\n * @since 1.10\n */\nclass CommandLineOptionsParsingTests {\n\n\t@Test\n\tvoid parseNoArguments() {\n\t\tString[] noArguments = {};\n\t\tResult options = parse(noArguments);\n\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertFalse(options.output.isAnsiColorOutputDisabled()),\n\t\t\t() -> assertNull(options.output.getStdoutPath()),\n\t\t\t() -> assertNull(options.output.getStderrPath()),\n\t\t\t() -> assertEquals(TestConsoleOutputOptions.DEFAULT_DETAILS, options.output.getDetails()),\n\t\t\t() -> assertFalse(options.discovery.isScanClasspath()),\n\t\t\t() -> assertEquals(List.of(STANDARD_INCLUDE_PATTERN), options.discovery.getIncludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getExcludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getIncludedPackages()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getExcludedPackages()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getIncludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getExcludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getIncludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getExcludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedUris()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedDirectories()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedModules()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedPackages()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedMethods()),\n\t\t\t() -> assertEquals(List.of(), options.discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertEquals(Map.of(), options.discovery.getConfigurationParameters())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseSwitches() {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertTrue(parse(\"--disable-ansi-colors\").output.isAnsiColorOutputDisabled(), \"disable ansi\"),\n\t\t\t() -> assertTrue(parse(\"--scan-class-path\").discovery.isScanClasspath(), \"scan class path\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidDetails(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(Details.VERBOSE, type.parseArgLine(\"--details verbose\").output.getDetails()),\n\t\t\t() -> assertEquals(Details.TREE, type.parseArgLine(\"--details tree\").output.getDetails()),\n\t\t\t() -> assertEquals(Details.FLAT, type.parseArgLine(\"--details flat\").output.getDetails()),\n\t\t\t() -> assertEquals(Details.NONE, type.parseArgLine(\"--details NONE\").output.getDetails()),\n\t\t\t() -> assertEquals(Details.NONE, type.parseArgLine(\"--details none\").output.getDetails()),\n\t\t\t() -> assertEquals(Details.NONE, type.parseArgLine(\"--details None\").output.getDetails())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidDetails() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--details\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidDetailsTheme(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(Theme.ASCII, type.parseArgLine(\"--details-theme ascii\").output.getTheme()),\n\t\t\t() -> assertEquals(Theme.ASCII, type.parseArgLine(\"--details-theme ASCII\").output.getTheme()),\n\t\t\t() -> assertEquals(Theme.UNICODE, type.parseArgLine(\"--details-theme unicode\").output.getTheme()),\n\t\t\t() -> assertEquals(Theme.UNICODE, type.parseArgLine(\"--details-theme UNICODE\").output.getTheme()),\n\t\t\t() -> assertEquals(Theme.UNICODE, type.parseArgLine(\"--details-theme uniCode\").output.getTheme())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidDetailsTheme() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--details-theme\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIncludeClassNamePatterns(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\".*Test\"), type.parseArgLine(\"-n .*Test\").discovery.getIncludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".*Test\", \".*Tests\"), type.parseArgLine(\"--include-classname .*Test --include-classname .*Tests\").discovery.getIncludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".*Test\"), type.parseArgLine(\"--include-classname=.*Test\").discovery.getIncludedClassNamePatterns())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidExcludeClassNamePatterns(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\".*Test\"), type.parseArgLine(\"-N .*Test\").discovery.getExcludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".*Test\", \".*Tests\"), type.parseArgLine(\"--exclude-classname .*Test --exclude-classname .*Tests\").discovery.getExcludedClassNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".*Test\"), type.parseArgLine(\"--exclude-classname=.*Test\").discovery.getExcludedClassNamePatterns())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid usesDefaultClassNamePatternWithoutExplicitArgument() throws Exception {\n\t\tassertEquals(List.of(STANDARD_INCLUDE_PATTERN),\n\t\t\tArgsType.args.parseArgLine(\"\").discovery.getIncludedClassNamePatterns());\n\t}\n\n\t@Test\n\tvoid parseInvalidIncludeClassNamePatterns() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-n\", \"--include-classname\");\n\t}\n\n\t@Test\n\tvoid parseInvalidExcludeClassNamePatterns() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-N\", \"--exclude-classname\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIncludedPackages(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"org.junit.included\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-package org.junit.included\").discovery.getIncludedPackages()),\n\t\t\t() -> assertEquals(List.of(\"org.junit.included\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-package=org.junit.included\").discovery.getIncludedPackages()),\n\t\t\t() -> assertEquals(List.of(\"org.junit.included1\", \"org.junit.included2\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-package org.junit.included1 --include-package org.junit.included2\").discovery.getIncludedPackages())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidExcludedPackages(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"org.junit.excluded\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-package org.junit.excluded\").discovery.getExcludedPackages()),\n\t\t\t() -> assertEquals(List.of(\"org.junit.excluded\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-package=org.junit.excluded\").discovery.getExcludedPackages()),\n\t\t\t() -> assertEquals(List.of(\"org.junit.excluded1\", \"org.junit.excluded2\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-package org.junit.excluded1 --exclude-package org.junit.excluded2\").discovery.getExcludedPackages())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIncludeMethodNamePatterns(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\".+#method.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-methodname .+#method.*\").discovery.getIncludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".+#methodA.*\", \".+#methodB.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-methodname .+#methodA.* --include-methodname .+#methodB.*\").discovery.getIncludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".+#method.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--include-methodname=.+#method.*\").discovery.getIncludedMethodNamePatterns())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidExcludeMethodNamePatterns(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\".+#method.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-methodname .+#method.*\").discovery.getExcludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".+#methodA.*\", \".+#methodB.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-methodname .+#methodA.* --exclude-methodname .+#methodB.*\").discovery.getExcludedMethodNamePatterns()),\n\t\t\t() -> assertEquals(List.of(\".+#method.*\"),\n\t\t\t\t\ttype.parseArgLine(\"--exclude-methodname=.+#method.*\").discovery.getExcludedMethodNamePatterns())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidIncludeMethodNamePatterns() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--include-methodname\");\n\t}\n\n\t@Test\n\tvoid parseInvalidExcludeMethodNamePatterns() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--exclude-methodname\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIncludedTags(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"-t fast\").discovery.getIncludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"--include-tag fast\").discovery.getIncludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"--include-tag=fast\").discovery.getIncludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\", \"slow\"), type.parseArgLine(\"-t fast -t slow\").discovery.getIncludedTagExpressions())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidIncludedTags() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-t\", \"--include-tag\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidExcludedTags(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"-T fast\").discovery.getExcludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"--exclude-tag fast\").discovery.getExcludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\"), type.parseArgLine(\"--exclude-tag=fast\").discovery.getExcludedTagExpressions()),\n\t\t\t() -> assertEquals(List.of(\"fast\", \"slow\"), type.parseArgLine(\"-T fast -T slow\").discovery.getExcludedTagExpressions())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidExcludedTags() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-T\", \"--exclude-tag\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIncludedEngines(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"junit-jupiter\"), type.parseArgLine(\"-e junit-jupiter\").discovery.getIncludedEngines()),\n\t\t\t() -> assertEquals(List.of(\"junit-vintage\"), type.parseArgLine(\"--include-engine junit-vintage\").discovery.getIncludedEngines()),\n\t\t\t() -> assertEquals(List.of(), type.parseArgLine(\"\").discovery.getIncludedEngines())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidIncludedEngines() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-e\", \"--include-engine\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidExcludedEngines(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(\"junit-jupiter\"), type.parseArgLine(\"-E junit-jupiter\").discovery.getExcludedEngines()),\n\t\t\t() -> assertEquals(List.of(\"junit-vintage\"), type.parseArgLine(\"--exclude-engine junit-vintage\").discovery.getExcludedEngines()),\n\t\t\t() -> assertEquals(List.of(), type.parseArgLine(\"\").discovery.getExcludedEngines())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidExcludedEngines() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-E\", \"--exclude-engine\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidAdditionalClasspathEntries(ArgsType type) {\n\t\tvar dir = Path.of(\".\");\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"-cp .\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--classpath .\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--classpath=.\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--class-path .\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--class-path=.\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir, Path.of(\"lib/some.jar\")), type.parseArgLine(\"-cp . -cp lib/some.jar\").discovery.getAdditionalClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir, Path.of(\"lib/some.jar\")), type.parseArgLine(\"-cp .\" + File.pathSeparator + \"lib/some.jar\").discovery.getAdditionalClasspathEntries())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\t@EnabledOnOs(OS.WINDOWS)\n\tvoid parseValidAndAbsoluteAdditionalClasspathEntries() throws Exception {\n\t\tArgsType type = ArgsType.args;\n\t\tassertEquals(List.of(Path.of(\"C:\\\\a.jar\")),\n\t\t\ttype.parseArgLine(\"-cp C:\\\\a.jar\").discovery.getAdditionalClasspathEntries());\n\t\tassertEquals(List.of(Path.of(\"C:\\\\foo.jar\"), Path.of(\"D:\\\\bar.jar\")),\n\t\t\ttype.parseArgLine(\"-cp C:\\\\foo.jar;D:\\\\bar.jar\").discovery.getAdditionalClasspathEntries());\n\t}\n\n\t@Test\n\tvoid parseInvalidAdditionalClasspathEntries() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-cp\", \"--classpath\", \"--class-path\");\n\t}\n\n\t@Test\n\tvoid parseInvalidXmlReportsDirs() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--reports-dir\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidUriSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectUri(\"file:///foo.txt\")), type.parseArgLine(\"-u file:///foo.txt\").discovery.getSelectedUris()),\n\t\t\t() -> assertEquals(List.of(selectUri(\"file:///foo.txt\")), type.parseArgLine(\"--select-uri file:///foo.txt\").discovery.getSelectedUris()),\n\t\t\t() -> assertEquals(List.of(selectUri(\"file:///foo.txt\")), type.parseArgLine(\"--select-uri=file:///foo.txt\").discovery.getSelectedUris()),\n\t\t\t() -> assertEquals(List.of(selectUri(\"file:///foo.txt\"), selectUri(\"https://example\")), type.parseArgLine(\"-u file:///foo.txt -u https://example\").discovery.getSelectedUris()),\n\t\t\t() -> assertEquals(List.of(selectUri(\"file:///foo.txt\"), selectUri(\"https://example\")), type.parseArgLine(\"-u file:///foo.txt https://example\").discovery.getSelectedUris())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidUriSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-u\", \"--select-uri\", \"-u unknown-scheme:\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidFileSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\")), type.parseArgLine(\"-f foo.txt\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\")), type.parseArgLine(\"--select-file foo.txt\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\")), type.parseArgLine(\"--select-file=foo.txt\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\"), selectFile(\"bar.csv\")), type.parseArgLine(\"-f foo.txt -f bar.csv\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\"), selectFile(\"bar.csv\")), type.parseArgLine(\"-f foo.txt bar.csv\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\", FilePosition.from(5))), type.parseArgLine(\"-f foo.txt?line=5\").discovery.getSelectedFiles()),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\", FilePosition.from(12, 34))), type.parseArgLine(\"-f foo.txt?line=12&column=34\").discovery.getSelectedFiles())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidFileSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-f\", \"--select-file\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidDirectorySelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\")), type.parseArgLine(\"-d foo/bar\").discovery.getSelectedDirectories()),\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\")), type.parseArgLine(\"--select-directory foo/bar\").discovery.getSelectedDirectories()),\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\")), type.parseArgLine(\"--select-directory=foo/bar\").discovery.getSelectedDirectories()),\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\"), selectDirectory(\"bar/qux\")), type.parseArgLine(\"-d foo/bar -d bar/qux\").discovery.getSelectedDirectories()),\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\"), selectDirectory(\"bar/qux\")), type.parseArgLine(\"-d foo/bar bar/qux\").discovery.getSelectedDirectories())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidDirectorySelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-d\", \"--select-directory\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidModuleSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\")), type.parseArgLine(\"-o com.acme.foo\").discovery.getSelectedModules()),\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\")), type.parseArgLine(\"--select-module com.acme.foo\").discovery.getSelectedModules()),\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\")), type.parseArgLine(\"--select-module=com.acme.foo\").discovery.getSelectedModules()),\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\"), selectModule(\"com.example.bar\")), type.parseArgLine(\"-o com.acme.foo -o com.example.bar\").discovery.getSelectedModules()),\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\"), selectModule(\"com.example.bar\")), type.parseArgLine(\"-o com.acme.foo com.example.bar\").discovery.getSelectedModules())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidModuleSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-o\", \"--select-module\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidPackageSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\")), type.parseArgLine(\"-p com.acme.foo\").discovery.getSelectedPackages()),\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\")), type.parseArgLine(\"--select-package com.acme.foo\").discovery.getSelectedPackages()),\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\")), type.parseArgLine(\"--select-package=com.acme.foo\").discovery.getSelectedPackages()),\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\"), selectPackage(\"com.example.bar\")), type.parseArgLine(\"-p com.acme.foo -p com.example.bar\").discovery.getSelectedPackages()),\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\"), selectPackage(\"com.example.bar\")), type.parseArgLine(\"-p com.acme.foo com.example.bar\").discovery.getSelectedPackages())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidPackageSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-p\", \"--select-package\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidClassSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\")), type.parseArgLine(\"-c com.acme.Foo\").discovery.getSelectedClasses()),\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\")), type.parseArgLine(\"--select-class com.acme.Foo\").discovery.getSelectedClasses()),\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\")), type.parseArgLine(\"--select-class=com.acme.Foo\").discovery.getSelectedClasses()),\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\"), selectClass(\"com.example.Bar\")), type.parseArgLine(\"-c com.acme.Foo -c com.example.Bar\").discovery.getSelectedClasses()),\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\"), selectClass(\"com.example.Bar\")), type.parseArgLine(\"-c com.acme.Foo com.example.Bar\").discovery.getSelectedClasses())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidClassSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-c\", \"--select-class\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidMethodSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\")), type.parseArgLine(\"-m com.acme.Foo#m()\").discovery.getSelectedMethods()),\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\")), type.parseArgLine(\"--select-method com.acme.Foo#m()\").discovery.getSelectedMethods()),\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\")), type.parseArgLine(\"--select-method=com.acme.Foo#m()\").discovery.getSelectedMethods()),\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\"), selectMethod(\"com.example.Bar#method(java.lang.Object)\")),\n\t\t\t\t\ttype.parseArgLine(\"-m com.acme.Foo#m() -m com.example.Bar#method(java.lang.Object)\").discovery.getSelectedMethods()),\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\"), selectMethod(\"com.example.Bar#method(java.lang.Object)\")),\n\t\t\t\t\ttype.parseArgLine(\"-m com.acme.Foo#m() com.example.Bar#method(java.lang.Object)\").discovery.getSelectedMethods())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidMethodSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-m\", \"--select-method\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidClasspathResourceSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\")), type.parseArgLine(\"-r /foo.csv\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\")), type.parseArgLine(\"--select-resource /foo.csv\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\")), type.parseArgLine(\"--select-resource=/foo.csv\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\"), selectClasspathResource(\"bar.json\")), type.parseArgLine(\"-r /foo.csv -r bar.json\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\"), selectClasspathResource(\"bar.json\")), type.parseArgLine(\"-r /foo.csv bar.json\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\", FilePosition.from(5))), type.parseArgLine(\"-r /foo.csv?line=5\").discovery.getSelectedClasspathResources()),\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\", FilePosition.from(12, 34))), type.parseArgLine(\"-r /foo.csv?line=12&column=34\").discovery.getSelectedClasspathResources())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidClasspathResourceSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-r\", \"--select-resource\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidIterationSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectIteration(selectClasspathResource(\"/foo.csv\"), 0)), type.parseArgLine(\"-i resource:/foo.csv[0]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectMethod(\"com.acme.Foo#m()\"), 1, 2)), type.parseArgLine(\"-i method:com.acme.Foo#m()[1..2]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectClass(\"com.acme.Foo\"), 0, 2)), type.parseArgLine(\"--select-iteration class:com.acme.Foo[0,2]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectPackage(\"com.acme.foo\"), 3)), type.parseArgLine(\"--select-iteration=package:com.acme.foo[3]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectModule(\"com.acme.foo\"), 0, 1, 2, 4, 5, 6)), type.parseArgLine(\"--select-iteration module:com.acme.foo[0..2,4..6]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectDirectory(\"foo/bar\"), 1, 5)), type.parseArgLine(\"--select-iteration=directory:foo/bar[1,5]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectFile(\"foo.txt\"), 6), selectIteration(selectUri(\"file:///foo.txt\"), 7)), type.parseArgLine(\"-i file:foo.txt[6] -i uri:file:///foo.txt[7]\").discovery.getSelectedIterations()),\n\t\t\t() -> assertEquals(List.of(selectIteration(selectFile(\"foo.txt\"), 6), selectIteration(selectUri(\"file:///foo.txt\"), 7)), type.parseArgLine(\"-i file:foo.txt[6] uri:file:///foo.txt[7]\").discovery.getSelectedIterations())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidIterationSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"-i\", \"--select-iteration\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidUniqueIdSelectors(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\")), type.parseArgLine(\"--uid [engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\").discovery.getSelectedUniqueIds()),\n\t\t\t() -> assertEquals(List.of(selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\")), type.parseArgLine(\"--select-unique-id [engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\").discovery.getSelectedUniqueIds()),\n\t\t\t() -> assertEquals(List.of(selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass1]\"), selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass2]\")), type.parseArgLine(\"--uid [engine:junit-jupiter]/[class:MyClass1] --uid [engine:junit-jupiter]/[class:MyClass2]\").discovery.getSelectedUniqueIds()),\n\t\t\t() -> assertEquals(List.of(selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass1]\"), selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass2]\")), type.parseArgLine(\"--uid [engine:junit-jupiter]/[class:MyClass1] [engine:junit-jupiter]/[class:MyClass2]\").discovery.getSelectedUniqueIds())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidUniqueIdSelectors() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--uid\", \"--select-unique-id\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseClasspathScanningEntries(ArgsType type) {\n\t\tvar dir = Path.of(\".\");\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertTrue(type.parseArgLine(\"--scan-class-path\").discovery.isScanClasspath()),\n\t\t\t() -> assertEquals(List.of(), type.parseArgLine(\"--scan-class-path\").discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertTrue(type.parseArgLine(\"--scan-classpath\").discovery.isScanClasspath()),\n\t\t\t() -> assertEquals(List.of(), type.parseArgLine(\"--scan-classpath\").discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertTrue(type.parseArgLine(\"--scan-class-path .\").discovery.isScanClasspath()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--scan-class-path .\").discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir), type.parseArgLine(\"--scan-class-path=.\").discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir, Path.of(\"src/test/java\")), type.parseArgLine(\"--scan-class-path . --scan-class-path src/test/java\").discovery.getSelectedClasspathEntries()),\n\t\t\t() -> assertEquals(List.of(dir, Path.of(\"src/test/java\")), type.parseArgLine(\"--scan-class-path .\" + File.pathSeparator + \"src/test/java\").discovery.getSelectedClasspathEntries())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidConfigurationParameters(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertThat(type.parseArgLine(\"--config foo=bar\").discovery.getConfigurationParameters())\n\t\t\t\t\t.containsOnly(entry(\"foo\", \"bar\")),\n\t\t\t() -> assertThat(type.parseArgLine(\"--config=foo=bar\").discovery.getConfigurationParameters())\n\t\t\t\t\t.containsOnly(entry(\"foo\", \"bar\")),\n\t\t\t() -> assertThat(type.parseArgLine(\"--config foo=bar --config baz=qux\").discovery.getConfigurationParameters())\n\t\t\t\t\t.containsExactly(entry(\"foo\", \"bar\"), entry(\"baz\", \"qux\"))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidConfigurationParametersResource(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertThat(type.parseArgLine(\"--config-resource foo.properties\").discovery.getConfigurationParametersResources())\n\t\t\t\t\t.containsOnly(\"foo.properties\"),\n\t\t\t() -> assertThat(type.parseArgLine(\"--config-resource foo.properties --config-resource bar.properties\").discovery.getConfigurationParametersResources())\n\t\t\t\t\t.containsExactly(\"foo.properties\", \"bar.properties\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidConfigurationParameters() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--config\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidStdoutRedirectionFile(ArgsType type) {\n\t\tvar file = Path.of(\"foo.txt\");\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertNull(type.parseArgLine(\"\").output.getStdoutPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stdout=foo.txt\").output.getStdoutPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stdout foo.txt\").output.getStdoutPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stdout bar.txt --redirect-stdout foo.txt\").output.getStdoutPath())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidStderrRedirectionFile(ArgsType type) {\n\t\tvar file = Path.of(\"foo.txt\");\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertNull(type.parseArgLine(\"\").output.getStderrPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stderr=foo.txt\").output.getStderrPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stderr foo.txt\").output.getStderrPath()),\n\t\t\t() -> assertEquals(file, type.parseArgLine(\"--redirect-stderr bar.txt --redirect-stderr foo.txt\").output.getStderrPath())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseInvalidStdoutRedirectionFile() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--redirect-stdout\");\n\t}\n\n\t@Test\n\tvoid parseInvalidStderrRedirectionFile() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--redirect-stderr\");\n\t}\n\n\t@Test\n\tvoid parseInvalidConfigurationParametersResource() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--config-resource\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseInvalidConfigurationParametersWithDuplicateKey(ArgsType type) {\n\t\tException e = assertThrows(Exception.class, () -> type.parseArgLine(\"--config foo=bar --config foo=baz\"));\n\n\t\tassertThat(e.getMessage()).isEqualTo(\"Duplicate key 'foo' for values 'bar' and 'baz'.\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid parseValidSelectorIdentifier(ArgsType type) {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertEquals(List.of(selectClasspathResource(\"/foo.csv\")), parseIdentifiers(type,\"--select resource:/foo.csv\")),\n\t\t\t() -> assertEquals(List.of(selectMethod(\"com.acme.Foo#m()\")), parseIdentifiers(type,\"--select method:com.acme.Foo#m()\")),\n\t\t\t() -> assertEquals(List.of(selectClass(\"com.acme.Foo\")), parseIdentifiers(type,\"--select class:com.acme.Foo\")),\n\t\t\t() -> assertEquals(List.of(selectPackage(\"com.acme.foo\")), parseIdentifiers(type,\"--select package:com.acme.foo\")),\n\t\t\t() -> assertEquals(List.of(selectModule(\"com.acme.foo\")), parseIdentifiers(type,\"--select module:com.acme.foo\")),\n\t\t\t() -> assertEquals(List.of(selectDirectory(\"foo/bar\")), parseIdentifiers(type,\"--select directory:foo/bar\")),\n\t\t\t() -> assertEquals(List.of(selectFile(\"foo.txt\"), selectUri(\"file:///foo.txt\")), parseIdentifiers(type,\"--select file:foo.txt --select uri:file:///foo.txt\")),\n\t\t\t() -> assertEquals(List.of(selectUniqueId(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\")), parseIdentifiers(type,\"--select uid:[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\"))\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate static List<? extends DiscoverySelector> parseIdentifiers(ArgsType type, String argLine)\n\t\t\tthrows IOException {\n\t\treturn DiscoverySelectors.parseAll(type.parseArgLine(argLine).discovery.getSelectorIdentifiers()).toList();\n\t}\n\n\t@Test\n\tvoid parseInvalidSelectorIdentifier() {\n\t\tassertOptionWithMissingRequiredArgumentThrowsException(\"--select\");\n\t}\n\n\tprivate void assertOptionWithMissingRequiredArgumentThrowsException(String... options) {\n\t\tassertAll(\n\t\t\tStream.of(options).map(opt -> () -> assertThrows(Exception.class, () -> ArgsType.args.parseArgLine(opt))));\n\t}\n\n\tenum ArgsType {\n\t\targs {\n\t\t\t@Override\n\t\t\tResult parseArgLine(String argLine) {\n\t\t\t\treturn parse(split(argLine));\n\t\t\t}\n\t\t},\n\t\tatFile {\n\t\t\t@Override\n\t\t\tResult parseArgLine(String argLine) throws IOException {\n\t\t\t\tvar atFile = Files.createTempFile(\"junit-launcher-args\", \".txt\");\n\t\t\t\ttry {\n\t\t\t\t\tFiles.write(atFile, List.of(split(argLine)));\n\t\t\t\t\treturn parse(\"@\" + atFile);\n\t\t\t\t}\n\t\t\t\tfinally {\n\t\t\t\t\tFiles.deleteIfExists(atFile);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tabstract Result parseArgLine(String argLine) throws IOException;\n\n\t\tprivate static String[] split(String argLine) {\n\t\t\treturn argLine.isEmpty() ? new String[0] : argLine.split(\"\\\\s+\");\n\t\t}\n\t}\n\n\tprivate static Result parse(String... args) {\n\t\tExecuteTestsCommand command = new ExecuteTestsCommand((__, ___) -> mock());\n\t\tcommand.parseArgs(args);\n\t\treturn new Result(command.toTestDiscoveryOptions(), command.toTestConsoleOutputOptions());\n\t}\n\n\trecord Result(TestDiscoveryOptions discovery, TestConsoleOutputOptions output) {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/ConsoleTestExecutorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertNotSame;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.commons.util.ClassLoaderUtils.getDefaultClassLoader;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.console.options.Details;\nimport org.junit.platform.console.options.TestConsoleOutputOptions;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\n\n/**\n * @since 1.0\n */\nclass ConsoleTestExecutorTests {\n\n\tprivate static final Runnable FAILING_BLOCK = () -> fail(\"should fail\");\n\tprivate static final Runnable SUCCEEDING_TEST = () -> {\n\t};\n\n\tprivate final StringWriter stringWriter = new StringWriter();\n\tprivate final TestDiscoveryOptions discoveryOptions = new TestDiscoveryOptions();\n\tprivate final TestConsoleOutputOptions outputOptions = new TestConsoleOutputOptions();\n\tprivate final DemoHierarchicalTestEngine dummyTestEngine = new DemoHierarchicalTestEngine();\n\n\t{\n\t\tdiscoveryOptions.setScanClasspath(true);\n\t}\n\n\t@Test\n\tvoid printsSummary() {\n\t\tdummyTestEngine.addTest(\"succeedingTest\", SUCCEEDING_TEST);\n\t\tdummyTestEngine.addTest(\"failingTest\", FAILING_BLOCK);\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).contains(\"Test run finished after\", \"2 tests found\", \"0 tests skipped\",\n\t\t\t\"2 tests started\", \"0 tests aborted\", \"1 tests successful\", \"1 tests failed\");\n\t}\n\n\t@Test\n\tvoid printsDetailsIfTheyAreNotHidden() {\n\t\toutputOptions.setDetails(Details.FLAT);\n\n\t\tdummyTestEngine.addTest(\"failingTest\", FAILING_BLOCK);\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).contains(\"Test execution started.\");\n\t}\n\n\t@Test\n\tvoid printsNoDetailsIfTheyAreHidden() {\n\t\toutputOptions.setDetails(Details.NONE);\n\n\t\tdummyTestEngine.addTest(\"failingTest\", FAILING_BLOCK);\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).doesNotContain(\"Test execution started.\");\n\t}\n\n\t@Test\n\tvoid printsFailuresEvenIfDetailsAreHidden() {\n\t\toutputOptions.setDetails(Details.NONE);\n\n\t\tdummyTestEngine.addTest(\"failingTest\", FAILING_BLOCK);\n\t\tdummyTestEngine.addContainer(\"failingContainer\", FAILING_BLOCK);\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).contains(\"Failures (2)\", \"failingTest\", \"failingContainer\");\n\t}\n\n\t@Test\n\tvoid usesCustomClassLoaderIfAdditionalClassPathEntriesArePresent() {\n\t\tdiscoveryOptions.setAdditionalClasspathEntries(List.of(Path.of(\".\")));\n\n\t\tvar oldClassLoader = getDefaultClassLoader();\n\t\tdummyTestEngine.addTest(\"failingTest\",\n\t\t\t() -> assertSame(oldClassLoader, getDefaultClassLoader(), \"should fail\"));\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).contains(\"failingTest\", \"should fail\", \"1 tests failed\");\n\t}\n\n\t@Test\n\tvoid usesSameClassLoaderIfNoAdditionalClassPathEntriesArePresent() {\n\t\tdiscoveryOptions.setAdditionalClasspathEntries(List.of());\n\n\t\tvar oldClassLoader = getDefaultClassLoader();\n\t\tdummyTestEngine.addTest(\"failingTest\",\n\t\t\t() -> assertNotSame(oldClassLoader, getDefaultClassLoader(), \"should fail\"));\n\n\t\tvar task = new ConsoleTestExecutor(discoveryOptions, outputOptions, () -> createLauncher(dummyTestEngine));\n\t\ttask.execute(new PrintWriter(stringWriter), Optional.empty(), false);\n\n\t\tassertThat(stringWriter.toString()).contains(\"failingTest\", \"should fail\", \"1 tests failed\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/ConsoleUtilsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.nio.charset.Charset;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.console.options.ConsoleUtils;\n\nclass ConsoleUtilsTests {\n\n\t@Test\n\tvoid consoleCharsetReportedByConsoleUtilsIsEitherNativeCharsetOrDefaultCharset() {\n\t\tvar console = System.console();\n\t\tvar expected = console != null ? console.charset() : Charset.defaultCharset();\n\t\tassertEquals(expected, ConsoleUtils.charset());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/CustomContextClassLoaderExecutorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.Optional;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.0\n */\nclass CustomContextClassLoaderExecutorTests {\n\n\t@Test\n\tvoid invokeWithoutCustomClassLoaderDoesNotSetClassLoader() {\n\t\tvar originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\tvar executor = new CustomContextClassLoaderExecutor(Optional.empty());\n\n\t\tint result = executor.invoke(() -> {\n\t\t\tassertSame(originalClassLoader, Thread.currentThread().getContextClassLoader());\n\t\t\treturn 42;\n\t\t});\n\n\t\tassertEquals(42, result);\n\t\tassertSame(originalClassLoader, Thread.currentThread().getContextClassLoader());\n\t}\n\n\t@Test\n\tvoid invokeWithCustomClassLoaderSetsCustomAndResetsToOriginal() {\n\t\tvar originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\tClassLoader customClassLoader = URLClassLoader.newInstance(new URL[0]);\n\t\tvar executor = new CustomContextClassLoaderExecutor(Optional.of(customClassLoader));\n\n\t\tint result = executor.invoke(() -> {\n\t\t\tassertSame(customClassLoader, Thread.currentThread().getContextClassLoader());\n\t\t\treturn 23;\n\t\t});\n\n\t\tassertEquals(23, result);\n\t\tassertSame(originalClassLoader, Thread.currentThread().getContextClassLoader());\n\t}\n\n\t@Test\n\tvoid invokeWithCustomClassLoaderAndEnsureItIsClosedAfterUsage() {\n\t\tvar closed = new AtomicBoolean(false);\n\t\tClassLoader localClassLoader = new URLClassLoader(new URL[0]) {\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\tclosed.set(true);\n\t\t\t\tsuper.close();\n\t\t\t}\n\t\t};\n\t\tvar executor = new CustomContextClassLoaderExecutor(Optional.of(localClassLoader));\n\n\t\tint result = executor.invoke(() -> 4711);\n\n\t\tassertEquals(4711, result);\n\t\tassertTrue(closed.get());\n\t}\n\n\t@Test\n\tvoid invokeWithCustomClassLoaderAndKeepItOpenAfterUsage() {\n\t\tvar closed = new AtomicBoolean(false);\n\t\tClassLoader localClassLoader = new URLClassLoader(new URL[0]) {\n\t\t\t@Override\n\t\t\tpublic void close() throws IOException {\n\t\t\t\tclosed.set(true);\n\t\t\t\tsuper.close();\n\t\t\t}\n\t\t};\n\t\tvar executor = new CustomContextClassLoaderExecutor(Optional.of(localClassLoader),\n\t\t\tCustomClassLoaderCloseStrategy.KEEP_OPEN);\n\n\t\tint result = executor.invoke(() -> 4711);\n\n\t\tassertEquals(4711, result);\n\t\tassertFalse(closed.get());\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/DiscoveryRequestCreatorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static java.util.stream.Collectors.toMap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.discovery.ClassNameFilter.STANDARD_INCLUDE_PATTERN;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.logging.LogRecord;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.console.options.TestDiscoveryOptions;\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.ClasspathRootSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.IterationSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.PackageNameFilter;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * @since 1.0\n */\nclass DiscoveryRequestCreatorTests {\n\n\tprivate final TestDiscoveryOptions options = new TestDiscoveryOptions();\n\n\t@Test\n\tvoid convertsScanClasspathOptionWithoutExplicitRootDirectories() {\n\t\toptions.setScanClasspath(true);\n\n\t\tvar request = convert();\n\n\t\tvar classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class);\n\t\t// @formatter:off\n\t\tassertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot)\n\t\t\t\t.hasAtLeastOneElementOfType(URI.class)\n\t\t\t\t.doesNotContainNull();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid convertsScanClasspathOptionWithExplicitRootDirectories() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setSelectedClasspathEntries(List.of(Path.of(\".\"), Path.of(\"..\")));\n\n\t\tvar request = convert();\n\n\t\tvar classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class);\n\t\t// @formatter:off\n\t\tassertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot)\n\t\t\t\t.containsExactly(new File(\".\").toURI(), new File(\"..\").toURI());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid convertsScanClasspathOptionWithAdditionalClasspathEntries() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setAdditionalClasspathEntries(List.of(Path.of(\".\"), Path.of(\"..\")));\n\n\t\tvar request = convert();\n\n\t\tvar classpathRootSelectors = request.getSelectorsByType(ClasspathRootSelector.class);\n\t\t// @formatter:off\n\t\tassertThat(classpathRootSelectors).extracting(ClasspathRootSelector::getClasspathRoot)\n\t\t\t.contains(new File(\".\").toURI(), new File(\"..\").toURI());\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid doesNotSupportScanClasspathAndExplicitSelectors() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setSelectedClasses(List.of(selectClass(\"SomeTest\")));\n\n\t\tassertPreconditionViolationFor(this::convert).withMessageContaining(\"not supported\");\n\t}\n\n\t@Test\n\tvoid convertsDefaultIncludeClassNamePatternOption() {\n\t\toptions.setScanClasspath(true);\n\n\t\tvar request = convert();\n\n\t\tvar filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertThat(filters).hasSize(1);\n\t\tassertExcludes(filters.getFirst(), STANDARD_INCLUDE_PATTERN);\n\t}\n\n\t@Test\n\tvoid convertsExplicitIncludeClassNamePatternOption() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setIncludedClassNamePatterns(List.of(\"Foo.*Bar\", \"Bar.*Foo\"));\n\n\t\tvar request = convert();\n\n\t\tvar filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertThat(filters).hasSize(1);\n\t\tassertIncludes(filters.getFirst(), \"Foo.*Bar\");\n\t\tassertIncludes(filters.getFirst(), \"Bar.*Foo\");\n\t}\n\n\t@Test\n\tvoid includeSelectedClassesAndMethodsRegardlessOfClassNamePatterns() {\n\t\toptions.setSelectedClasses(List.of(selectClass(\"SomeTest\")));\n\t\toptions.setSelectedMethods(List.of(selectMethod(\"com.acme.Foo#m()\")));\n\t\toptions.setSelectedIterations(List.of(selectIteration(selectMethod(\"com.acme.Bar#n()\"), 0)));\n\t\toptions.setIncludedClassNamePatterns(List.of(\"Foo.*Bar\"));\n\n\t\tvar request = convert();\n\n\t\tvar filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertThat(filters).hasSize(1);\n\t\tassertIncludes(filters.getFirst(), \"SomeTest\");\n\t\tassertIncludes(filters.getFirst(), \"com.acme.Foo\");\n\t\tassertIncludes(filters.getFirst(), \"com.acme.Bar\");\n\t\tassertIncludes(filters.getFirst(), \"Foo.*Bar\");\n\t}\n\n\t@Test\n\tvoid convertsExcludeClassNamePatternOption() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setExcludedClassNamePatterns(List.of(\"Foo.*Bar\", \"Bar.*Foo\"));\n\n\t\tvar request = convert();\n\n\t\tvar filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertThat(filters).hasSize(2);\n\t\tassertExcludes(filters.get(1), \"Foo.*Bar\");\n\t\tassertExcludes(filters.get(1), \"Bar.*Foo\");\n\t}\n\n\t@Test\n\tvoid convertsPackageOptions() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setIncludedPackages(List.of(\"org.junit.included1\", \"org.junit.included2\", \"org.junit.included3\"));\n\t\toptions.setExcludedPackages(List.of(\"org.junit.excluded1\"));\n\n\t\tvar request = convert();\n\t\tvar packageNameFilters = request.getFiltersByType(PackageNameFilter.class);\n\n\t\tassertThat(packageNameFilters).hasSize(2);\n\t\tassertIncludes(packageNameFilters.getFirst(), \"org.junit.included1\");\n\t\tassertIncludes(packageNameFilters.get(0), \"org.junit.included2\");\n\t\tassertIncludes(packageNameFilters.get(0), \"org.junit.included3\");\n\t\tassertExcludes(packageNameFilters.get(1), \"org.junit.excluded1\");\n\t}\n\n\t@Test\n\tvoid convertsMethodNamePatternOptions() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setIncludedMethodNamePatterns(List.of(\".+#foo.*Bar\", \".+#toString\", \".+#method.*\"));\n\t\toptions.setExcludedMethodNamePatterns(List.of(\".+#bar.*Foo\"));\n\t\tvar request = convert();\n\t\tvar methodNameFilters = request.getPostDiscoveryFilters();\n\t\tassertThat(methodNameFilters).hasSize(2);\n\t\tassertThat(methodNameFilters.get(0).toString()).contains(\".+#foo.*Bar\", \".+#toString\", \".+#method.*\");\n\t\tassertThat(methodNameFilters.get(1).toString()).contains(\".+#bar.*Foo\");\n\t}\n\n\t@Test\n\tvoid convertsTagOptions() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setIncludedTagExpressions(List.of(\"fast\", \"medium\", \"slow\"));\n\t\toptions.setExcludedTagExpressions(List.of(\"slow\"));\n\n\t\tvar request = convert();\n\t\tvar postDiscoveryFilters = request.getPostDiscoveryFilters();\n\n\t\tassertThat(postDiscoveryFilters).hasSize(2);\n\t\tassertThat(postDiscoveryFilters.get(0).toString()).contains(\"TagFilter\");\n\t\tassertThat(postDiscoveryFilters.get(1).toString()).contains(\"TagFilter\");\n\t}\n\n\t@Test\n\tvoid convertsEngineOptions() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setIncludedEngines(List.of(\"engine1\", \"engine2\", \"engine3\"));\n\t\toptions.setExcludedEngines(List.of(\"engine2\"));\n\n\t\tvar request = convert();\n\t\tvar engineFilters = request.getEngineFilters();\n\n\t\tassertThat(engineFilters).hasSize(2);\n\t\tassertThat(engineFilters.get(0).toString()).contains(\"includes\", \"[engine1, engine2, engine3]\");\n\t\tassertThat(engineFilters.get(1).toString()).contains(\"excludes\", \"[engine2]\");\n\t}\n\n\t@Test\n\tvoid propagatesUniqueIdSelectors() {\n\t\toptions.setSelectedUniqueId(List.of(selectUniqueId(\"[engine:a]/[1:1]\"), selectUniqueId(\"[engine:b]/[2:2]\")));\n\n\t\tvar request = convert();\n\t\tvar uriSelectors = request.getSelectorsByType(UniqueIdSelector.class);\n\n\t\tassertThat(uriSelectors) //\n\t\t\t\t.extracting(UniqueIdSelector::getUniqueId) //\n\t\t\t\t.containsExactly( //\n\t\t\t\t\tUniqueId.parse(\"[engine:a]/[1:1]\"), //, //\n\t\t\t\t\tUniqueId.parse(\"[engine:b]/[2:2]\") //\n\t\t\t\t);\n\t}\n\n\t@Test\n\tvoid propagatesUriSelectors() {\n\t\toptions.setSelectedUris(List.of(selectUri(\"a\"), selectUri(\"b\")));\n\n\t\tvar request = convert();\n\t\tvar uriSelectors = request.getSelectorsByType(UriSelector.class);\n\n\t\tassertThat(uriSelectors).extracting(UriSelector::getUri).containsExactly(URI.create(\"a\"), URI.create(\"b\"));\n\t}\n\n\t@Test\n\tvoid propagatesFileSelectors() {\n\t\toptions.setSelectedFiles(List.of(selectFile(\"foo.txt\"), selectFile(\"bar.csv\")));\n\n\t\tvar request = convert();\n\t\tvar fileSelectors = request.getSelectorsByType(FileSelector.class);\n\n\t\tassertThat(fileSelectors).extracting(FileSelector::getRawPath).containsExactly(\"foo.txt\", \"bar.csv\");\n\t}\n\n\t@Test\n\tvoid propagatesDirectorySelectors() {\n\t\toptions.setSelectedDirectories(List.of(selectDirectory(\"foo/bar\"), selectDirectory(\"bar/qux\")));\n\n\t\tvar request = convert();\n\t\tvar directorySelectors = request.getSelectorsByType(DirectorySelector.class);\n\n\t\tassertThat(directorySelectors).extracting(DirectorySelector::getRawPath).containsExactly(\"foo/bar\", \"bar/qux\");\n\t}\n\n\t@Test\n\tvoid propagatesPackageSelectors() {\n\t\toptions.setSelectedPackages(List.of(selectPackage(\"com.acme.foo\"), selectPackage(\"com.example.bar\")));\n\n\t\tvar request = convert();\n\t\tvar packageSelectors = request.getSelectorsByType(PackageSelector.class);\n\n\t\tassertThat(packageSelectors).extracting(PackageSelector::getPackageName).containsExactly(\"com.acme.foo\",\n\t\t\t\"com.example.bar\");\n\t}\n\n\t@Test\n\tvoid propagatesClassSelectors() {\n\t\toptions.setSelectedClasses(List.of(selectClass(\"com.acme.Foo\"), selectClass(\"com.example.Bar\")));\n\n\t\tvar request = convert();\n\t\tvar classSelectors = request.getSelectorsByType(ClassSelector.class);\n\n\t\tassertThat(classSelectors).extracting(ClassSelector::getClassName).containsExactly(\"com.acme.Foo\",\n\t\t\t\"com.example.Bar\");\n\t}\n\n\t@Test\n\tvoid propagatesMethodSelectors() {\n\t\toptions.setSelectedMethods(\n\t\t\tList.of(selectMethod(\"com.acme.Foo#m()\"), selectMethod(\"com.example.Bar#method(java.lang.Object)\")));\n\n\t\tvar request = convert();\n\t\tvar methodSelectors = request.getSelectorsByType(MethodSelector.class);\n\n\t\tassertThat(methodSelectors).hasSize(2);\n\t\tassertThat(methodSelectors.getFirst().getClassName()).isEqualTo(\"com.acme.Foo\");\n\t\tassertThat(methodSelectors.get(0).getMethodName()).isEqualTo(\"m\");\n\t\tassertThat(methodSelectors.get(0).getParameterTypeNames()).isEmpty();\n\t\tassertThat(methodSelectors.get(1).getClassName()).isEqualTo(\"com.example.Bar\");\n\t\tassertThat(methodSelectors.get(1).getMethodName()).isEqualTo(\"method\");\n\t\tassertThat(methodSelectors.get(1).getParameterTypeNames()).isEqualTo(\"java.lang.Object\");\n\t}\n\n\t@Test\n\tvoid propagatesClasspathResourceSelectors() {\n\t\toptions.setSelectedClasspathResources(\n\t\t\tList.of(selectClasspathResource(\"foo.csv\"), selectClasspathResource(\"com/example/bar.json\")));\n\n\t\tvar request = convert();\n\t\tvar classpathResourceSelectors = request.getSelectorsByType(ClasspathResourceSelector.class);\n\n\t\tassertThat(classpathResourceSelectors).extracting(\n\t\t\tClasspathResourceSelector::getClasspathResourceName).containsExactly(\"foo.csv\", \"com/example/bar.json\");\n\t}\n\n\t@Test\n\tvoid propagatesIterationSelectors() {\n\t\tvar methodSelector = selectMethod(\"com.acme.Foo#m()\");\n\t\tvar classSelector = selectClass(\"com.example.Bar\");\n\t\toptions.setSelectedIterations(List.of(selectIteration(methodSelector, 1), selectIteration(classSelector, 2)));\n\n\t\tvar request = convert();\n\t\tvar iterationSelectors = request.getSelectorsByType(IterationSelector.class);\n\n\t\tassertThat(iterationSelectors).hasSize(2);\n\t\tassertThat(iterationSelectors.get(0).getParentSelector()).isEqualTo(methodSelector);\n\t\tassertThat(iterationSelectors.get(0).getIterationIndices()).containsExactly(1);\n\t\tassertThat(iterationSelectors.get(1).getParentSelector()).isEqualTo(classSelector);\n\t\tassertThat(iterationSelectors.get(1).getIterationIndices()).containsExactly(2);\n\t}\n\n\t@Test\n\tvoid propagatesSelectorIdentifiers() {\n\t\tvar methodSelector = selectMethod(\"com.acme.Foo#m()\");\n\t\tvar classSelector = selectClass(\"com.example.Bar\");\n\t\toptions.setSelectorIdentifiers(\n\t\t\tList.of(methodSelector.toIdentifier().orElseThrow(), classSelector.toIdentifier().orElseThrow()));\n\n\t\tvar request = convert();\n\n\t\tassertThat(request.getSelectorsByType(MethodSelector.class)).containsExactly(methodSelector);\n\t\tassertThat(request.getSelectorsByType(ClassSelector.class)).containsExactly(classSelector);\n\t}\n\n\t@Test\n\tvoid convertsConfigurationParameters() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setConfigurationParameters(mapOf(entry(\"foo\", \"bar\"), entry(\"baz\", \"true\")));\n\n\t\tvar request = convert();\n\t\tvar configurationParameters = request.getConfigurationParameters();\n\n\t\tassertThat(configurationParameters.get(\"foo\")).contains(\"bar\");\n\t\tassertThat(configurationParameters.getBoolean(\"baz\")).contains(true);\n\t}\n\n\t@Test\n\tvoid convertsConfigurationParametersResources() {\n\t\toptions.setScanClasspath(true);\n\t\toptions.setConfigurationParameters(mapOf(entry(\"foo\", \"bar\"), entry(\"com.example.prop.first\", \"baz\")));\n\t\toptions.setConfigurationParametersResources(List.of(\"config-test.properties\"));\n\n\t\tvar request = convert();\n\t\tvar configurationParameters = request.getConfigurationParameters();\n\n\t\tassertThat(configurationParameters.get(\"foo\")).contains(\"bar\");\n\t\tassertThat(configurationParameters.get(\"com.example.prop.first\")).contains(\"baz\");\n\t\tassertThat(configurationParameters.get(\"com.example.prop.second\")).contains(\"second value\");\n\t}\n\n\t@Test\n\tvoid logsInvalidSearchPathRoots(@TrackLogRecords LogRecordListener listener) {\n\t\tvar opts = new TestDiscoveryOptions();\n\t\topts.setScanClasspath(true);\n\t\tvar missingPath = Path.of(\"/does/not/exist\");\n\t\topts.setSelectedClasspathEntries(List.of(missingPath));\n\n\t\tDiscoveryRequestCreator.toDiscoveryRequestBuilder(opts);\n\n\t\tassertThat(listener.stream(DiscoveryRequestCreator.class)) //\n\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t.filteredOn(message -> message.contains(missingPath.toString())) //\n\t\t\t\t.hasSize(1);\n\t}\n\n\t@Test\n\tvoid logsInvalidAdditionalClasspathRoots(@TrackLogRecords LogRecordListener listener) {\n\t\tvar opts = new TestDiscoveryOptions();\n\t\topts.setScanClasspath(true);\n\t\tvar missingPath = Path.of(\"/also/does/not/exist\");\n\t\topts.setAdditionalClasspathEntries(List.of(missingPath));\n\n\t\tDiscoveryRequestCreator.toDiscoveryRequestBuilder(opts);\n\n\t\tassertThat(listener.stream(DiscoveryRequestCreator.class)) //\n\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t.filteredOn(message -> message.contains(missingPath.toString())) //\n\t\t\t\t.hasSize(1);\n\t}\n\n\tprivate LauncherDiscoveryRequest convert() {\n\t\treturn DiscoveryRequestCreator.toDiscoveryRequestBuilder(options).build();\n\t}\n\n\tprivate void assertIncludes(Filter<String> filter, String included) {\n\t\tassertThat(filter.apply(included).included()).isTrue();\n\t}\n\n\tprivate void assertExcludes(Filter<String> filter, String excluded) {\n\t\tassertThat(filter.apply(excluded).excluded()).isTrue();\n\t}\n\n\t@SafeVarargs\n\t@SuppressWarnings(\"varargs\")\n\tprivate static <K, V> Map<K, V> mapOf(Entry<K, V>... entries) {\n\t\treturn Stream.of(entries).collect(toMap(Entry::getKey, Entry::getValue));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/ExecuteTestsCommandTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.anyBoolean;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.launcher.listeners.TestExecutionSummary;\n\n/**\n * @since 1.10\n */\nclass ExecuteTestsCommandTests {\n\n\tprivate final TestExecutionSummary summary = mock();\n\tprivate final ConsoleTestExecutor consoleTestExecutor = mock();\n\tprivate final ExecuteTestsCommand command = new ExecuteTestsCommand((__, ___) -> consoleTestExecutor);\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(consoleTestExecutor.execute(any(), any(), anyBoolean())).thenReturn(summary);\n\t}\n\n\t@Test\n\tvoid hasStatusCode0ForNoTotalFailures() {\n\t\twhen(summary.getTotalFailureCount()).thenReturn(0L);\n\n\t\tcommand.execute();\n\n\t\tassertThat(command.getExitCode()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid hasStatusCode1ForForAnyFailure() {\n\t\twhen(summary.getTotalFailureCount()).thenReturn(1L);\n\n\t\tcommand.execute();\n\n\t\tassertThat(command.getExitCode()).isEqualTo(1);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@Test\n\tvoid hasStatusCode2ForNoTestsAndHasOptionFailIfNoTestsFound() {\n\t\twhen(summary.getTestsFoundCount()).thenReturn(0L);\n\n\t\tcommand.execute(\"--fail-if-no-tests\");\n\n\t\tassertThat(command.getExitCode()).isEqualTo(2);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@Test\n\tvoid hasStatusCode0ForTestsAndHasOptionFailIfNoTestsFound() {\n\t\twhen(summary.getTestsFoundCount()).thenReturn(1L);\n\t\twhen(summary.getTotalFailureCount()).thenReturn(0L);\n\n\t\tcommand.execute(\"--fail-if-no-tests\");\n\n\t\tassertThat(command.getExitCode()).isEqualTo(0);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@Test\n\tvoid hasStatusCode0ForNoTestsAndNotFailIfNoTestsFound() {\n\t\twhen(summary.getTestsFoundCount()).thenReturn(0L);\n\n\t\tcommand.execute();\n\n\t\tassertThat(command.getExitCode()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid parseValidXmlReportsDirs() {\n\t\tvar dir = Path.of(\"build\", \"test-results\");\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertEquals(Optional.empty(), parseArgs().getReportsDir()),\n\t\t\t\t() -> assertEquals(Optional.of(dir), parseArgs(\"--reports-dir\", \"build/test-results\").getReportsDir()),\n\t\t\t\t() -> assertEquals(Optional.of(dir), parseArgs(\"--reports-dir=build/test-results\").getReportsDir())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid parseValidFailFast() {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t\t() -> assertFalse(parseArgs().isFailFast()),\n\t\t\t\t() -> assertTrue(parseArgs(\"--fail-fast\").isFailFast())\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate ExecuteTestsCommand parseArgs(String... args) {\n\t\tcommand.parseArgs(args);\n\t\treturn command;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/StdStreamTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport org.junit.jupiter.api.Test;\n\npublic class StdStreamTestCase {\n\n\tprivate static final String STDOUT_DATA = \"Writing to STDOUT...\";\n\tprivate static final String STDERR_DATA = \"Writing to STDERR...\";\n\n\tpublic static int getStdoutOutputFileSize() {\n\t\treturn STDOUT_DATA.length();\n\t}\n\n\tpublic static int getStderrOutputFileSize() {\n\t\treturn STDERR_DATA.length();\n\t}\n\n\t@Test\n\tvoid printTest() {\n\t\tSystem.out.print(STDOUT_DATA);\n\t\tSystem.err.print(STDERR_DATA);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/command/ThemeTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.command;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.nio.charset.StandardCharsets;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.console.output.Theme;\n\nclass ThemeTests {\n\n\t@Test\n\tvoid givenUtf8ShouldReturnUnicode() {\n\t\tassertEquals(Theme.UNICODE, Theme.valueOf(StandardCharsets.UTF_8));\n\t}\n\n\t@Test\n\tvoid givenAnythingElseShouldReturnAscii() {\n\t\tassertAll(\"All character sets that are not UTF-8 should return Theme.ASCII\", () -> {\n\t\t\tassertEquals(Theme.ASCII, Theme.valueOf(StandardCharsets.ISO_8859_1));\n\t\t\tassertEquals(Theme.ASCII, Theme.valueOf(StandardCharsets.US_ASCII));\n\t\t\tassertEquals(Theme.ASCII, Theme.valueOf(StandardCharsets.UTF_16));\n\t\t});\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/ColorPaletteTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.PrintWriter;\nimport java.io.StringReader;\nimport java.io.StringWriter;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * Unit tests for {@link ColorPalette}.\n *\n * @since 1.9\n */\nclass ColorPaletteTests {\n\n\t@Nested\n\tclass LoadFromPropertiesTests {\n\n\t\t@Test\n\t\tvoid singleOverride() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tSUCCESSFUL = 35;1\n\t\t\t\t\t\"\"\";\n\t\t\tColorPalette colorPalette = new ColorPalette(new StringReader(properties));\n\n\t\t\tString actual = colorPalette.paint(Style.SUCCESSFUL, \"text\");\n\n\t\t\tassertEquals(\"\\u001B[35;1mtext\\u001B[0m\", actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid keysAreCaseInsensitive() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tsuCcESSfuL = 35;1\n\t\t\t\t\t\"\"\";\n\t\t\tColorPalette colorPalette = new ColorPalette(new StringReader(properties));\n\n\t\t\tString actual = colorPalette.paint(Style.SUCCESSFUL, \"text\");\n\n\t\t\tassertEquals(\"\\u001B[35;1mtext\\u001B[0m\", actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid junkKeysAreIgnored() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tSUCCESSFUL = 35;1\n\t\t\t\t\tJUNK = 1;31;40\n\t\t\t\t\t\"\"\";\n\t\t\tColorPalette colorPalette = new ColorPalette(new StringReader(properties));\n\n\t\t\tString actual = colorPalette.paint(Style.SUCCESSFUL, \"text\");\n\n\t\t\tassertEquals(\"\\u001B[35;1mtext\\u001B[0m\", actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid multipleOverrides() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tSUCCESSFUL = 35;1\n\t\t\t\t\tFAILED = 33;4\n\t\t\t\t\t\"\"\";\n\t\t\tColorPalette colorPalette = new ColorPalette(new StringReader(properties));\n\n\t\t\tString successful = colorPalette.paint(Style.SUCCESSFUL, \"text\");\n\t\t\tString failed = colorPalette.paint(Style.FAILED, \"text\");\n\n\t\t\tassertEquals(\"\\u001B[35;1mtext\\u001B[0m\", successful);\n\t\t\tassertEquals(\"\\u001B[33;4mtext\\u001B[0m\", failed);\n\t\t}\n\n\t\t@Test\n\t\tvoid unspecifiedStylesAreDefault() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tSUCCESSFUL = 35;1\n\t\t\t\t\t\"\"\";\n\t\t\tColorPalette colorPalette = new ColorPalette(new StringReader(properties));\n\n\t\t\tString actual = colorPalette.paint(Style.FAILED, \"text\");\n\n\t\t\tassertEquals(\"\\u001B[31mtext\\u001B[0m\", actual);\n\t\t}\n\n\t\t@Test\n\t\tvoid cannotOverrideNone() {\n\t\t\tString properties = \"\"\"\n\t\t\t\t\tNONE = 35;1\n\t\t\t\t\t\"\"\";\n\t\t\tStringReader reader = new StringReader(properties);\n\n\t\t\tassertThrows(IllegalArgumentException.class, () -> new ColorPalette(reader));\n\t\t}\n\t}\n\n\t@Nested\n\tclass DemonstratePalettesTests {\n\n\t\tprivate static final String ESC = \"\\u001B[\";\n\t\tprivate static final String RESET = ESC + \"0m\";\n\t\tprivate static final String NEW_LINE = System.lineSeparator();\n\n\t\t@Test\n\t\tvoid verbose_default() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new VerboseTreePrintingListener(out, ColorPalette.DEFAULT, 16,\n\t\t\t\tTheme.ASCII);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{green}[OK] SUCCESSFUL{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{red}[X] FAILED{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{yellow}[A] ABORTED{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{magenta}[S] SKIPPED\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid verbose_single_color() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new VerboseTreePrintingListener(out, ColorPalette.SINGLE_COLOR, 16,\n\t\t\t\tTheme.ASCII);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{bold}[OK] SUCCESSFUL{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{reverse}[X] FAILED{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{underline}[A] ABORTED{NL}{reset}\"),\n\t\t\t\twithAnsi(\"{strikethrough}[S] SKIPPED\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid simple_default() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new TreePrintingListener(out, ColorPalette.DEFAULT, Theme.ASCII);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{blue}My Test{reset} {green}[OK]{reset}\"),\n\t\t\t\twithAnsi(\"{red}My Test{reset} {red}[X]{reset}\"),\n\t\t\t\twithAnsi(\"{yellow}My Test{reset} {yellow}[A]{reset}\"),\n\t\t\t\twithAnsi(\"{magenta}My Test{reset} {magenta}[S]{reset}\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid simple_single_color() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new TreePrintingListener(out, ColorPalette.SINGLE_COLOR, Theme.ASCII);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{bold}[OK]{reset}\"),\n\t\t\t\twithAnsi(\"{reverse}[X]{reset}\"),\n\t\t\t\twithAnsi(\"{underline}[A]{reset}\"),\n\t\t\t\twithAnsi(\"{strikethrough}[S]{reset}\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid flat_default() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new FlatPrintingListener(out, ColorPalette.DEFAULT);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{green}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{red}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{yellow}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{magenta}Skipped:     My Test ([engine:demo-engine]){reset}\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid flat_single_color() {\n\t\t\tStringWriter stringWriter = new StringWriter();\n\t\t\tPrintWriter out = new PrintWriter(stringWriter, true);\n\t\t\tTestExecutionListener listener = new FlatPrintingListener(out, ColorPalette.SINGLE_COLOR);\n\n\t\t\tdemoTestRun(listener);\n\n\t\t\tString output = stringWriter.toString();\n\t\t\t// @formatter:off\n\t\t\tassertThat(output).contains(\n\t\t\t\twithAnsi(\"{bold}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{reverse}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{underline}Finished:    My Test ([engine:demo-engine]){reset}\"),\n\t\t\t\twithAnsi(\"{strikethrough}Skipped:     My Test ([engine:demo-engine]){reset}\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate static String withAnsi(String template) {\n\t\t\t// @formatter:off\n\t\t\treturn template\n\t\t\t\t\t.replace(\"{blue}\", ESC + \"34m\")\n\t\t\t\t\t.replace(\"{green}\", ESC + \"32m\")\n\t\t\t\t\t.replace(\"{red}\", ESC + \"31m\")\n\t\t\t\t\t.replace(\"{yellow}\", ESC + \"33m\")\n\t\t\t\t\t.replace(\"{magenta}\", ESC + \"35m\")\n\t\t\t\t\t.replace(\"{bold}\", ESC + \"1m\")\n\t\t\t\t\t.replace(\"{reverse}\", ESC + \"7m\")\n\t\t\t\t\t.replace(\"{underline}\", ESC + \"4m\")\n\t\t\t\t\t.replace(\"{strikethrough}\", ESC + \"9m\")\n\t\t\t\t\t.replace(\"{reset}\", RESET)\n\t\t\t\t\t.replace(\"{NL}\", NEW_LINE);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tprivate void demoTestRun(TestExecutionListener listener) {\n\t\t\tTestDescriptor testDescriptor = new TestDescriptorStub(UniqueId.forEngine(\"demo-engine\"), \"My Test\");\n\t\t\tTestPlan testPlan = TestPlan.from(true, List.of(testDescriptor), mock(), dummyOutputDirectoryCreator());\n\t\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\t\tlistener.executionStarted(TestIdentifier.from(testDescriptor));\n\t\t\tlistener.executionFinished(TestIdentifier.from(testDescriptor), TestExecutionResult.successful());\n\t\t\tlistener.dynamicTestRegistered(TestIdentifier.from(testDescriptor));\n\t\t\tlistener.executionStarted(TestIdentifier.from(testDescriptor));\n\t\t\tlistener.executionFinished(TestIdentifier.from(testDescriptor),\n\t\t\t\tTestExecutionResult.failed(new Exception()));\n\t\t\tlistener.executionStarted(TestIdentifier.from(testDescriptor));\n\t\t\tlistener.executionFinished(TestIdentifier.from(testDescriptor),\n\t\t\t\tTestExecutionResult.aborted(new Exception()));\n\t\t\tlistener.reportingEntryPublished(TestIdentifier.from(testDescriptor), ReportEntry.from(\"Key\", \"Value\"));\n\t\t\tlistener.executionSkipped(TestIdentifier.from(testDescriptor), \"to demonstrate skipping\");\n\t\t\tlistener.testPlanExecutionFinished(testPlan);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/FlatPrintingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.console.output.FlatPrintingListener.INDENTATION;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.Path;\n\nimport org.assertj.core.util.Maps;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * @since 1.0\n */\nclass FlatPrintingListenerTests {\n\n\tprivate static final String EOL = System.lineSeparator();\n\n\t@Test\n\tvoid executionSkipped() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).executionSkipped(newTestIdentifier(), \"Test\" + EOL + \"disabled\");\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertEquals(3, lines.length);\n\t\tassertAll(\"lines in the output\", //\n\t\t\t() -> assertEquals(\"Skipped:     demo-test ([engine:demo-engine])\", lines[0]), //\n\t\t\t() -> assertEquals(INDENTATION + \"=> Reason: Test\", lines[1]), //\n\t\t\t() -> assertEquals(INDENTATION + \"disabled\", lines[2]));\n\t}\n\n\t@Test\n\tvoid reportingEntryPublished() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).reportingEntryPublished(newTestIdentifier(), ReportEntry.from(\"foo\", \"bar\"));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertEquals(2, lines.length);\n\t\tassertAll(\"lines in the output\", //\n\t\t\t() -> assertEquals(\"Reported:    demo-test ([engine:demo-engine])\", lines[0]), //\n\t\t\t() -> assertTrue(lines[1].startsWith(INDENTATION + \"=> Reported values: ReportEntry [timestamp =\")), //\n\t\t\t() -> assertTrue(lines[1].endsWith(\", foo = 'bar']\")));\n\t}\n\n\t@Test\n\tvoid fileEntryPublished() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).fileEntryPublished(newTestIdentifier(),\n\t\t\tFileEntry.from(Path.of(\"test.txt\"), \"text/plain\"));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertEquals(2, lines.length);\n\t\tassertAll(\"lines in the output\", //\n\t\t\t() -> assertEquals(\"Reported:    demo-test ([engine:demo-engine])\", lines[0]), //\n\t\t\t() -> assertTrue(lines[1].startsWith(INDENTATION + \"=> Reported file: FileEntry [timestamp =\")), //\n\t\t\t() -> assertTrue(lines[1].endsWith(\", path = test.txt, mediaType = 'text/plain']\")));\n\t}\n\n\t@Test\n\tvoid executionFinishedWithFailure() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).executionFinished(newTestIdentifier(), failed(new AssertionError(\"Boom!\")));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertAll(\"lines in the output\", //\n\t\t\t() -> assertEquals(\"Finished:    demo-test ([engine:demo-engine])\", lines[0]), //\n\t\t\t() -> assertEquals(INDENTATION + \"=> Exception: java.lang.AssertionError: Boom!\", lines[1]));\n\t}\n\n\t@Nested\n\tclass ColorPaletteTests {\n\n\t\t@Nested\n\t\tclass DefaultColorPaletteTests {\n\n\t\t\t@Test\n\t\t\tvoid executionSkipped() {\n\t\t\t\tvar stringWriter = new StringWriter();\n\t\t\t\tnew FlatPrintingListener(new PrintWriter(stringWriter), ColorPalette.DEFAULT).executionSkipped(\n\t\t\t\t\tnewTestIdentifier(), \"Test\" + EOL + \"disabled\");\n\t\t\t\tvar lines = lines(stringWriter);\n\n\t\t\t\tassertEquals(3, lines.length);\n\t\t\t\tassertAll(\"lines in the output\", //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[35mSkipped:     demo-test ([engine:demo-engine])\\u001B[0m\", lines[0]), //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[35m\" + INDENTATION + \"=> Reason: Test\", lines[1]), //\n\t\t\t\t\t() -> assertEquals(INDENTATION + \"disabled\\u001B[0m\", lines[2]));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid reportingEntryPublished() {\n\t\t\t\tvar stringWriter = new StringWriter();\n\t\t\t\tnew FlatPrintingListener(new PrintWriter(stringWriter), ColorPalette.DEFAULT).reportingEntryPublished(\n\t\t\t\t\tnewTestIdentifier(), ReportEntry.from(\"foo\", \"bar\"));\n\t\t\t\tvar lines = lines(stringWriter);\n\n\t\t\t\tassertEquals(2, lines.length);\n\t\t\t\tassertAll(\"lines in the output\", //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[37mReported:    demo-test ([engine:demo-engine])\\u001B[0m\", lines[0]), //\n\t\t\t\t\t() -> assertTrue(lines[1].startsWith(\n\t\t\t\t\t\t\"\\u001B[37m\" + INDENTATION + \"=> Reported values: ReportEntry [timestamp =\")), //\n\t\t\t\t\t() -> assertTrue(lines[1].endsWith(\", foo = 'bar']\\u001B[0m\")));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid executionFinishedWithFailure() {\n\t\t\t\tvar stringWriter = new StringWriter();\n\t\t\t\tnew FlatPrintingListener(new PrintWriter(stringWriter), ColorPalette.DEFAULT).executionFinished(\n\t\t\t\t\tnewTestIdentifier(), failed(new AssertionError(\"Boom!\")));\n\t\t\t\tvar lines = lines(stringWriter);\n\n\t\t\t\tassertAll(\"lines in the output\", //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[31mFinished:    demo-test ([engine:demo-engine])\\u001B[0m\", lines[0]), //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[31m\" + INDENTATION + \"=> Exception: java.lang.AssertionError: Boom!\",\n\t\t\t\t\t\tlines[1]),\n\t\t\t\t\t() -> assertTrue(lines[lines.length - 1].endsWith(\"\\u001B[0m\")));\n\t\t\t}\n\n\t\t}\n\n\t\t@Nested\n\t\tclass ColorPaletteOverrideTests {\n\n\t\t\t@Test\n\t\t\tvoid overridingSkipped() {\n\t\t\t\tvar stringWriter = new StringWriter();\n\t\t\t\tColorPalette colorPalette = new ColorPalette(Maps.newHashMap(Style.SKIPPED, \"36;7\"));\n\t\t\t\tnew FlatPrintingListener(new PrintWriter(stringWriter), colorPalette).executionSkipped(\n\t\t\t\t\tnewTestIdentifier(), \"Test\" + EOL + \"disabled\");\n\t\t\t\tvar lines = lines(stringWriter);\n\n\t\t\t\tassertEquals(3, lines.length);\n\t\t\t\tassertAll(\"lines in the output\", //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[36;7mSkipped:     demo-test ([engine:demo-engine])\\u001B[0m\", lines[0]), //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[36;7m\" + INDENTATION + \"=> Reason: Test\", lines[1]), //\n\t\t\t\t\t() -> assertEquals(INDENTATION + \"disabled\\u001B[0m\", lines[2]));\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid overridingUnusedStyle() {\n\t\t\t\tvar stringWriter = new StringWriter();\n\t\t\t\tColorPalette colorPalette = new ColorPalette(Maps.newHashMap(Style.FAILED, \"36;7\"));\n\t\t\t\tnew FlatPrintingListener(new PrintWriter(stringWriter), colorPalette).executionSkipped(\n\t\t\t\t\tnewTestIdentifier(), \"Test\" + EOL + \"disabled\");\n\t\t\t\tvar lines = lines(stringWriter);\n\n\t\t\t\tassertEquals(3, lines.length);\n\t\t\t\tassertAll(\"lines in the output\", //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[35mSkipped:     demo-test ([engine:demo-engine])\\u001B[0m\", lines[0]), //\n\t\t\t\t\t() -> assertEquals(\"\\u001B[35m\" + INDENTATION + \"=> Reason: Test\", lines[1]), //\n\t\t\t\t\t() -> assertEquals(INDENTATION + \"disabled\\u001B[0m\", lines[2]));\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tprivate FlatPrintingListener listener(StringWriter stringWriter) {\n\t\treturn new FlatPrintingListener(new PrintWriter(stringWriter), ColorPalette.NONE);\n\t}\n\n\tprivate static TestIdentifier newTestIdentifier() {\n\t\tvar testDescriptor = new TestDescriptorStub(UniqueId.forEngine(\"demo-engine\"), \"demo-test\");\n\t\treturn TestIdentifier.from(testDescriptor);\n\t}\n\n\tprivate String[] lines(StringWriter stringWriter) {\n\t\treturn stringWriter.toString().split(EOL);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/TestFeedPrintingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.opentest4j.TestAbortedException;\n\nclass TestFeedPrintingListenerTests {\n\n\tTestPlan testPlan;\n\tTestIdentifier testIdentifier;\n\n\tStringWriter stringWriter = new StringWriter();\n\tTestFeedPrintingListener listener = new TestFeedPrintingListener(new PrintWriter(stringWriter), ColorPalette.NONE);\n\n\t@BeforeEach\n\tvoid prepareListener() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"demo-engine\"), \"Demo Engine\");\n\t\tvar testDescriptor = new TestDescriptorStub(engineDescriptor.getUniqueId().append(\"type\", \"test\"),\n\t\t\t\"%c ool test\");\n\t\tengineDescriptor.addChild(testDescriptor);\n\n\t\ttestPlan = TestPlan.from(true, Set.of(engineDescriptor), mock(), dummyOutputDirectoryCreator());\n\t\ttestIdentifier = testPlan.getTestIdentifier(testDescriptor.getUniqueId());\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t}\n\n\t@Test\n\tpublic void testExecutionSkipped() {\n\t\tlistener.executionSkipped(testIdentifier, \"Test disabled\");\n\t\tassertLinesMatch( //\n\t\t\t\"\"\"\n\t\t\t\t\tDemo Engine > %c ool test :: SKIPPED\n\t\t\t\t\t\\tReason: Test disabled\n\t\t\t\t\t\"\"\".lines(), //\n\t\t\tactualLines() //\n\t\t);\n\t}\n\n\t@Test\n\tpublic void testExecutionFailed() {\n\t\tlistener.executionFinished(testIdentifier, TestExecutionResult.failed(new AssertionError(\"Boom!\")));\n\t\tassertLinesMatch( //\n\t\t\t\"\"\"\n\t\t\t\t\tDemo Engine > %c ool test :: FAILED\n\t\t\t\t\t\\tjava.lang.AssertionError: Boom!\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\".lines(), //\n\t\t\tactualLines() //\n\t\t);\n\t}\n\n\t@Test\n\tpublic void testExecutionAborted() {\n\t\tlistener.executionFinished(testIdentifier, TestExecutionResult.aborted(new TestAbortedException(\"Boom!\")));\n\t\tassertLinesMatch( //\n\t\t\t\"\"\"\n\t\t\t\t\tDemo Engine > %c ool test :: ABORTED\n\t\t\t\t\t\\torg.opentest4j.TestAbortedException: Boom!\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\".lines(), //\n\t\t\tactualLines() //\n\t\t);\n\t}\n\n\t@Test\n\tpublic void testExecutionSucceeded() {\n\t\tlistener.executionFinished(testIdentifier, TestExecutionResult.successful());\n\t\tassertLinesMatch(Stream.of(\"Demo Engine > %c ool test :: SUCCESSFUL\"), actualLines());\n\t}\n\n\t@Test\n\tpublic void testExecutionFailedOfContainer() {\n\t\tvar engineIdentifier = getOnlyElement(testPlan.getRoots());\n\t\tlistener.executionFinished(engineIdentifier, TestExecutionResult.failed(new AssertionError(\"Boom!\")));\n\t\tassertLinesMatch( //\n\t\t\t\"\"\"\n\t\t\t\t\tDemo Engine :: FAILED\n\t\t\t\t\t\\tjava.lang.AssertionError: Boom!\n\t\t\t\t\t>>>>\n\t\t\t\t\t\"\"\".lines(), //\n\t\t\tactualLines() //\n\t\t);\n\t}\n\n\tprivate Stream<String> actualLines() {\n\t\treturn stringWriter.toString().lines();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/TreeNodeTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.CyclicBarrier;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.reporting.ReportEntry;\n\nclass TreeNodeTests {\n\n\tprivate static final int NUM_THREADS = 2;\n\tprivate static final int ITEMS_PER_THREAD = 1000;\n\n\t@Test\n\tvoid caption() {\n\t\tassertEquals(\"\", TreeNode.createCaption(\"\"));\n\t\tassertEquals(\"[@@]\", TreeNode.createCaption(\"[@@]\"));\n\t\tassertEquals(\"[@ @]\", TreeNode.createCaption(\"[@ @]\"));\n\t\tassertEquals(\"[@ @]\", TreeNode.createCaption(\"[@\\u000B@]\"));\n\t\tassertEquals(\"[@ @]\", TreeNode.createCaption(\"[@\\t@]\"));\n\t\tassertEquals(\"[@  @]\", TreeNode.createCaption(\"[@\\t\\n@]\"));\n\t\tassertEquals(\"[@   @]\", TreeNode.createCaption(\"[@\\t\\n\\r@]\"));\n\t\tassertEquals(\"[@    @]\", TreeNode.createCaption(\"[@\\t\\n\\r\\f@]\"));\n\t\tassertEquals(\"@\".repeat(80) + \"...\", TreeNode.createCaption(\"@\".repeat(1000) + \"!\"));\n\t}\n\n\t@Test\n\tvoid childrenCanBeAddedConcurrently() throws Exception {\n\t\tvar treeNode = new TreeNode(\"root\");\n\n\t\trunConcurrently(() -> {\n\t\t\tfor (long i = 0; i < ITEMS_PER_THREAD; i++) {\n\t\t\t\ttreeNode.addChild(new TreeNode(String.valueOf(i)));\n\t\t\t}\n\t\t});\n\n\t\tassertThat(treeNode.children).hasSize(NUM_THREADS * ITEMS_PER_THREAD);\n\t}\n\n\t@Test\n\tvoid reportEntriesCanBeAddedConcurrently() throws Exception {\n\t\tvar treeNode = new TreeNode(\"root\");\n\n\t\trunConcurrently(() -> {\n\t\t\tfor (long i = 0; i < ITEMS_PER_THREAD; i++) {\n\t\t\t\ttreeNode.addReportEntry(ReportEntry.from(\"index\", String.valueOf(i)));\n\t\t\t}\n\t\t});\n\n\t\tassertThat(treeNode.reports).hasSize(NUM_THREADS * ITEMS_PER_THREAD);\n\t}\n\n\t@SuppressWarnings(\"resource\")\n\tprivate void runConcurrently(Runnable action) throws InterruptedException {\n\t\tExecutorService executor = new ThreadPoolExecutor(NUM_THREADS, NUM_THREADS, 10, SECONDS,\n\t\t\tnew ArrayBlockingQueue<>(NUM_THREADS));\n\t\ttry {\n\t\t\tvar barrier = new CyclicBarrier(NUM_THREADS);\n\t\t\tfor (long i = 0; i < NUM_THREADS; i++) {\n\t\t\t\texecutor.submit(() -> {\n\t\t\t\t\tawait(barrier);\n\t\t\t\t\taction.run();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tfinally {\n\t\t\texecutor.shutdown();\n\t\t\tvar terminated = executor.awaitTermination(10, SECONDS);\n\t\t\tassertTrue(terminated, \"Executor was not terminated\");\n\t\t}\n\t}\n\n\tprivate void await(CyclicBarrier barrier) {\n\t\ttry {\n\t\t\tbarrier.await();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/TreePrinterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.junit.jupiter.api.Assertions.assertIterableEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.platform.engine.TestExecutionResult.aborted;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\n\nclass TreePrinterTests {\n\n\tprivate final Charset charset = StandardCharsets.UTF_8;\n\tprivate final ByteArrayOutputStream stream = new ByteArrayOutputStream(1000);\n\tprivate final PrintWriter out = new PrintWriter(new OutputStreamWriter(stream, charset));\n\n\tprivate List<String> actual() {\n\t\tout.flush();\n\t\treturn List.of(stream.toString(charset).split(\"\\\\R\"));\n\t}\n\n\t@Test\n\tvoid emptyTree() {\n\t\tnew TreePrinter(out, Theme.UNICODE, ColorPalette.NONE).print(new TreeNode(\"<root>\"));\n\t\tassertIterableEquals(List.of(\"╷\"), actual());\n\t}\n\n\t@Test\n\tvoid emptyEngines() {\n\t\tvar root = new TreeNode(\"<root>\");\n\t\troot.addChild(new TreeNode(identifier(\"e-0\", \"engine zero\"), \"none\"));\n\t\troot.addChild(new TreeNode(identifier(\"e-1\", \"engine one\")).setResult(successful()));\n\t\troot.addChild(new TreeNode(identifier(\"e-2\", \"engine two\")).setResult(failed(null)));\n\t\troot.addChild(new TreeNode(identifier(\"e-3\", \"engine three\")).setResult(aborted(null)));\n\t\tnew TreePrinter(out, Theme.UNICODE, ColorPalette.NONE).print(root);\n\t\tassertIterableEquals( //\n\t\t\tList.of( //\n\t\t\t\t\"╷\", //\n\t\t\t\t\"├─ engine zero ↷ none\", //\n\t\t\t\t\"├─ engine one ✔\", //\n\t\t\t\t\"├─ engine two ✘\", //\n\t\t\t\t\"└─ engine three ■\"), //\n\t\t\tactual());\n\t}\n\n\t@Test\n\t// https://github.com/junit-team/junit-framework/issues/786\n\tvoid printNodeHandlesNullMessageThrowableGracefully() {\n\t\tvar result = TestExecutionResult.failed(new NullPointerException());\n\t\tvar node = new TreeNode(identifier(\"NPE\", \"test()\")).setResult(result);\n\t\tnew TreePrinter(out, Theme.ASCII, ColorPalette.NONE).print(node);\n\t\tassertLinesMatch(List.of(\".\", \"+-- test() [X] java.lang.NullPointerException\"), actual());\n\t}\n\n\t@Test\n\t// https://github.com/junit-team/junit-framework/issues/1531\n\tvoid reportsAreTabbedCorrectly() {\n\t\tvar root = new TreeNode(\"<root>\");\n\t\tvar e1 = new TreeNode(identifier(\"e-1\", \"engine one\")).setResult(successful());\n\t\te1.addReportEntry(ReportEntry.from(\"key\", \"e-1\"));\n\t\troot.addChild(e1);\n\n\t\tvar c1 = new TreeNode(identifier(\"c-1\", \"class one\")).setResult(successful());\n\t\tc1.addReportEntry(ReportEntry.from(\"key\", \"c-1\"));\n\t\te1.addChild(c1);\n\n\t\tvar m1 = new TreeNode(identifier(\"m-1\", \"method one\")).setResult(successful());\n\t\tm1.addReportEntry(ReportEntry.from(\"key\", \"m-1\"));\n\t\tc1.addChild(m1);\n\n\t\tvar m2 = new TreeNode(identifier(\"m-2\", \"method two\")).setResult(successful());\n\t\tm2.addFileEntry(FileEntry.from(Path.of(\"test.txt\"), \"text/plain\"));\n\t\tc1.addChild(m2);\n\n\t\tnew TreePrinter(out, Theme.UNICODE, ColorPalette.NONE).print(root);\n\t\tassertLinesMatch(List.of( //\n\t\t\t\"╷\", //\n\t\t\t\"└─ engine one ✔\", //\n\t\t\t\"   │  ....-..-..T..:...* key = `e-1`\", //\n\t\t\t\"   └─ class one ✔\", //\n\t\t\t\"      │  ....-..-..T..:...* key = `c-1`\", //\n\t\t\t\"      ├─ method one ✔\", //\n\t\t\t\"      │     ....-..-..T..:...* key = `m-1`\", //\n\t\t\t\"      └─ method two ✔\", //\n\t\t\t\"            ....-..-..T..:...* file:.*\" //\n\t\t), //\n\t\t\tactual());\n\t}\n\n\tprivate static TestIdentifier identifier(String id, String displayName) {\n\t\tvar descriptor = new TestDescriptorStub(UniqueId.forEngine(id), displayName);\n\t\treturn TestIdentifier.from(descriptor);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/output/VerboseTreePrintingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.output;\n\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\n\n/**\n * @since 1.3.2\n */\nclass VerboseTreePrintingListenerTests {\n\n\tprivate static final String EOL = System.lineSeparator();\n\n\t@Test\n\tvoid executionSkipped() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).executionSkipped(newTestIdentifier(), \"Test\" + EOL + \"disabled\");\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertLinesMatch(List.of( //\n\t\t\t\"+-- %c ool test\", //\n\t\t\t\"     tags: []\", //\n\t\t\t\" uniqueId: [engine:demo-engine]\", //\n\t\t\t\"   parent: []\", //\n\t\t\t\"   reason: Test\", //\n\t\t\t\"             disabled\", //\n\t\t\t\"   status: [S] SKIPPED\"), List.of(lines));\n\t}\n\n\t@Test\n\tvoid reportingEntryPublished() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).reportingEntryPublished(newTestIdentifier(), ReportEntry.from(\"foo\", \"bar\"));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertLinesMatch(List.of(\"  reports: ReportEntry \\\\[timestamp = .+, foo = 'bar'\\\\]\"), List.of(lines));\n\t}\n\n\t@Test\n\tvoid fileEntryPublished() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).fileEntryPublished(newTestIdentifier(),\n\t\t\tFileEntry.from(Path.of(\"test.txt\"), \"text/plain\"));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertLinesMatch(\n\t\t\tList.of(\"  reports: FileEntry \\\\[timestamp = .+, path = test.txt, mediaType = 'text/plain'\\\\]\"),\n\t\t\tList.of(lines));\n\t}\n\n\t@Test\n\tvoid executionFinishedWithFailure() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).executionFinished(newTestIdentifier(), failed(new AssertionError(\"Boom!\")));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertLinesMatch(List.of(\"   caught: java.lang.AssertionError: Boom!\", //\n\t\t\t\">> STACKTRACE >>\", //\n\t\t\t\" duration: \\\\d+ ms\", //\n\t\t\t\"   status: [X] FAILED\"), List.of(lines));\n\t}\n\n\t@Test\n\tvoid failureMessageWithFormatSpecifier() {\n\t\tvar stringWriter = new StringWriter();\n\t\tlistener(stringWriter).executionFinished(newTestIdentifier(), failed(new AssertionError(\"%crash\")));\n\t\tvar lines = lines(stringWriter);\n\n\t\tassertLinesMatch(List.of(\"   caught: java.lang.AssertionError: %crash\", //\n\t\t\t\">> STACKTRACE >>\", //\n\t\t\t\" duration: \\\\d+ ms\", //\n\t\t\t\"   status: [X] FAILED\"), List.of(lines));\n\t}\n\n\tprivate VerboseTreePrintingListener listener(StringWriter stringWriter) {\n\t\treturn new VerboseTreePrintingListener(new PrintWriter(stringWriter), ColorPalette.NONE, 16, Theme.ASCII);\n\t}\n\n\tprivate static TestIdentifier newTestIdentifier() {\n\t\tvar testDescriptor = new TestDescriptorStub(UniqueId.forEngine(\"demo-engine\"), \"%c ool test\");\n\t\treturn TestIdentifier.from(testDescriptor);\n\t}\n\n\tprivate String[] lines(StringWriter stringWriter) {\n\t\treturn stringWriter.toString().split(EOL);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Test in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass ContainerForInnerTest {\n\n\t@Nested\n\tclass NestedTest {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForInnerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Tests in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass ContainerForInnerTests {\n\n\t@Nested\n\tclass NestedTests {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Test in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass ContainerForStaticNestedTest {\n\n\tstatic class NestedTest {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/ContainerForStaticNestedTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Tests in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass ContainerForStaticNestedTests {\n\n\tstatic class NestedTests {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/FailingTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Test;\n\npublic class FailingTestCase {\n\n\t@Test\n\tvoid first() {\n\t\tfail();\n\t}\n\n\t@Test\n\tvoid second() {\n\t\tfail();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/SecondTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Test in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass SecondTest {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/Test.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named Test in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass Test {\n\n\t@org.junit.jupiter.api.Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/Test1.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named Test* in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass Test1 {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/Tests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named Tests in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass Tests {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/console/subpackage/ThirdTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.console.subpackage;\n\nimport org.junit.jupiter.api.Test;\n\n// Even though our \"example test classes\" should be named *TestCase\n// according to team policy, this class must be named *Tests in order\n// for ConsoleLauncherIntegrationTests to pass, but that's not a\n// problem since the test method here can never fail.\nclass ThirdTests {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/CompositeTestDescriptorVisitorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor.Visitor;\nimport org.mockito.InOrder;\n\nclass CompositeTestDescriptorVisitorTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid checksPreconditions() {\n\t\tassertPreconditionViolationFor(Visitor::composite);\n\t\tassertPreconditionViolationFor(() -> Visitor.composite((Visitor[]) null));\n\t\tassertPreconditionViolationFor(() -> Visitor.composite((Visitor) null));\n\t}\n\n\t@Test\n\tvoid optimizesForSingleVisitor() {\n\t\tVisitor visitor = mock();\n\n\t\tassertSame(visitor, Visitor.composite(visitor));\n\t}\n\n\t@Test\n\tvoid callsAllVisitorsInOrder() {\n\t\tVisitor visitor1 = mock(\"visitor1\");\n\t\tVisitor visitor2 = mock(\"visitor2\");\n\t\tTestDescriptor testDescriptor = mock();\n\n\t\tvar composite = Visitor.composite(visitor1, visitor2);\n\t\tcomposite.visit(testDescriptor);\n\n\t\tInOrder inOrder = inOrder(visitor1, visitor2);\n\t\tinOrder.verify(visitor1).visit(testDescriptor);\n\t\tinOrder.verify(visitor2).visit(testDescriptor);\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/DiscoveryIssueTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Optional;\nimport java.util.function.UnaryOperator;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\n\nclass DiscoveryIssueTests {\n\n\t@Test\n\tvoid create() {\n\t\tvar issue = DiscoveryIssue.create(Severity.ERROR, \"message\");\n\n\t\tassertThat(issue.severity()).isEqualTo(Severity.ERROR);\n\t\tassertThat(issue.message()).isEqualTo(\"message\");\n\t\tassertThat(issue.source()).isEmpty();\n\t\tassertThat(issue.cause()).isEmpty();\n\t}\n\n\t@Test\n\tvoid builder() {\n\t\tvar source = mock(TestSource.class);\n\t\tvar cause = new RuntimeException(\"boom\");\n\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, \"message\") //\n\t\t\t\t.source(source) //\n\t\t\t\t.cause(cause) //\n\t\t\t\t.build();\n\n\t\tassertThat(issue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(issue.message()).isEqualTo(\"message\");\n\t\tassertThat(issue.source()).containsSame(source);\n\t\tassertThat(issue.cause()).containsSame(cause);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tassertEqualsAndHashCode( //\n\t\t\tDiscoveryIssue.create(Severity.ERROR, \"message\"), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\").build(), //\n\t\t\tDiscoveryIssue.create(Severity.WARNING, \"message\") //\n\t\t);\n\t\tassertEqualsAndHashCode( //\n\t\t\tDiscoveryIssue.create(Severity.ERROR, \"message\"), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\").build(), //\n\t\t\tDiscoveryIssue.create(Severity.ERROR, \"anotherMessage\") //\n\t\t);\n\t\tassertEqualsAndHashCode( //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\") //\n\t\t\t\t\t.source(ClassSource.from(DiscoveryIssue.class)).build(), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\") //\n\t\t\t\t\t.source(Optional.of(ClassSource.from(DiscoveryIssue.class))).build(), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\") //\n\t\t\t\t\t.source(ClassSource.from(DefaultDiscoveryIssue.class)).build() //\n\t\t);\n\t\tvar cause = new RuntimeException(\"boom\");\n\t\tassertEqualsAndHashCode( //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\").cause(cause).build(), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\").cause(Optional.of(cause)).build(), //\n\t\t\tDiscoveryIssue.builder(Severity.ERROR, \"message\").cause(new RuntimeException(\"boom\")).build() //\n\t\t);\n\t}\n\n\t@Test\n\tvoid stringRepresentationWithoutAttributes() {\n\t\tvar issue = DiscoveryIssue.create(Severity.WARNING, \"message\");\n\n\t\tassertThat(issue.toString()) //\n\t\t\t\t.isEqualTo(\"DiscoveryIssue [severity = WARNING, message = 'message']\");\n\t}\n\n\t@Test\n\tvoid stringRepresentationWithOptionalAttributes() {\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, \"message\") //\n\t\t\t\t.source(ClassSource.from(DiscoveryIssue.class)) //\n\t\t\t\t.cause(new RuntimeException(\"boom\")) //\n\t\t\t\t.build();\n\n\t\tassertThat(issue.toString()) //\n\t\t\t\t.isEqualTo(\n\t\t\t\t\t\"DiscoveryIssue [severity = WARNING, message = 'message', source = ClassSource [className = 'org.junit.platform.engine.DiscoveryIssue', filePosition = null], cause = java.lang.RuntimeException: boom]\");\n\t}\n\n\t@Test\n\tvoid withNewMessage() {\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, \"message\") //\n\t\t\t\t.source(ClassSource.from(DiscoveryIssue.class)) //\n\t\t\t\t.cause(new RuntimeException(\"boom\")) //\n\t\t\t\t.build();\n\n\t\tvar newIssue = issue.withMessage(__ -> \"new message\");\n\n\t\tassertThat(newIssue.severity()).isEqualTo(Severity.WARNING);\n\t\tassertThat(newIssue.message()).isEqualTo(\"new message\");\n\t\tassertThat(newIssue.source()).containsSame(issue.source().orElseThrow());\n\t\tassertThat(newIssue.cause()).containsSame(issue.cause().orElseThrow());\n\t}\n\n\t@Test\n\tvoid withSameMessage() {\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, \"message\") //\n\t\t\t\t.source(ClassSource.from(DiscoveryIssue.class)) //\n\t\t\t\t.cause(new RuntimeException(\"boom\")) //\n\t\t\t\t.build();\n\n\t\tvar newIssue = issue.withMessage(UnaryOperator.identity());\n\n\t\tassertThat(newIssue).isSameAs(issue);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/FilterCompositionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.FilterResult.included;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.launcher.FilterStub;\n\n/**\n * Unit tests for {@link Filter#composeFilters}.\n *\n * {@link Filter#composeFilters} will delegate to {@linkplain CompositeFilter} under the hood.\n *\n * @since 1.0\n */\nclass FilterCompositionTests {\n\n\t@Test\n\tvoid composingNoFiltersCreatesFilterThatIncludesEverything() {\n\t\tvar composedFilter = Filter.composeFilters();\n\n\t\tassertTrue(composedFilter.apply(String.class).included());\n\t\tassertTrue(composedFilter.toPredicate().test(String.class));\n\t\tassertTrue(composedFilter.apply(Object.class).included());\n\t\tassertTrue(composedFilter.toPredicate().test(Object.class));\n\t}\n\n\t@Test\n\tvoid composingSingleFilterWillReturnTheOriginalOne() {\n\t\tFilter<?> singleFilter = ClassNameFilter.includeClassNamePatterns(\".*ring.*\");\n\t\tvar composed = Filter.composeFilters(singleFilter);\n\t\tassertSame(singleFilter, composed);\n\t}\n\n\t@Test\n\tvoid composingMultipleFiltersIsAConjunctionOfFilters() {\n\t\tFilter<String> firstFilter = ClassNameFilter.includeClassNamePatterns(\".*ring.*\");\n\t\tFilter<String> secondFilter = ClassNameFilter.includeClassNamePatterns(\".*Join.*\");\n\n\t\tvar composed = Filter.composeFilters(firstFilter, secondFilter);\n\n\t\tassertFalse(composed.apply(\"java.lang.String\").included());\n\t\tassertFalse(composed.toPredicate().test(\"java.lang.String\"));\n\t\tassertTrue(composed.apply(\"java.util.StringJoiner\").included());\n\t\tassertTrue(composed.toPredicate().test(\"java.util.StringJoiner\"));\n\t}\n\n\t@Test\n\tvoid aFilterComposedOfMultipleFiltersHasReadableDescription() {\n\t\tFilter<Object> firstFilter = new FilterStub<>(o -> excluded(\"wrong\"), () -> \"1st\");\n\t\tFilter<Object> secondFilter = new FilterStub<>(o -> included(\"right\"), () -> \"2nd\");\n\n\t\tvar composed = Filter.composeFilters(firstFilter, secondFilter);\n\n\t\tassertFalse(composed.apply(String.class).included());\n\t\tassertEquals(\"(1st) and (2nd)\", composed.toString());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/TestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.fakes.TestDescriptorStub;\n\n/**\n * @since 1.0\n */\nclass TestDescriptorTests {\n\n\t@Test\n\tvoid isRootWithoutParent() {\n\t\tTestDescriptor root = new TestDescriptorStub(UniqueId.root(\"root\", \"id\"), \"id\");\n\n\t\tassertTrue(root.isRoot());\n\t}\n\n\t@Test\n\tvoid isRootWithParent() {\n\t\tTestDescriptor child = new TestDescriptorStub(UniqueId.root(\"child\", \"child\"), \"child\");\n\t\tchild.setParent(new TestDescriptorStub(UniqueId.root(\"root\", \"root\"), \"root\"));\n\n\t\tassertFalse(child.isRoot());\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/TestTagTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link TestTag}.\n *\n * @since 1.0\n */\n@NullMarked\nclass TestTagTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid validSyntax() {\n\t\t// @formatter:off\n\t\tassertAll(\"Valid Tag Syntax\",\n\t\t\t() -> yep(\"fast\"),\n\t\t\t() -> yep(\"super_fast\"),\n\t\t\t() -> yep(\"unit-test\"),\n\t\t\t() -> yep(\"integration.test\"),\n\t\t\t() -> yep(\"org.example.CustomTagClass\"),\n\t\t\t() -> yep(\"  surrounded-by-whitespace\\t\\n\"),\n\t\t\t() -> nope(null),\n\t\t\t() -> nope(\"\"),\n\t\t\t() -> nope(\"     \"),\n\t\t\t() -> nope(\"\\t\"),\n\t\t\t() -> nope(\"\\f\"),\n\t\t\t() -> nope(\"\\r\"),\n\t\t\t() -> nope(\"\\n\"),\n\t\t\t() -> nope(\"custom tag\"), // internal space\n\t\t\t() -> nope(\",\"),          // comma\n\t\t\t() -> nope(\"(\"),          // opening parenthesis\n\t\t\t() -> nope(\")\"),          // closing parenthesis\n\t\t\t() -> nope(\"&\"),          // boolean AND\n\t\t\t() -> nope(\"|\"),          // boolean OR\n\t\t\t() -> nope(\"!\")           // boolean NOT\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid factory() {\n\t\tassertEquals(\"foo\", TestTag.create(\"foo\").getName());\n\t\tassertEquals(\"foo.tag\", TestTag.create(\"foo.tag\").getName());\n\t\tassertEquals(\"foo-tag\", TestTag.create(\"foo-tag\").getName());\n\t\tassertEquals(\"foo-tag\", TestTag.create(\"    foo-tag    \").getName());\n\t\tassertEquals(\"foo-tag\", TestTag.create(\"\\t  foo-tag  \\n\").getName());\n\t}\n\n\t@SuppressWarnings({ \"DataFlowIssue\", \"UnnecessaryUnicodeEscape\" })\n\t@Test\n\tvoid factoryPreconditions() {\n\t\tassertSyntaxViolation(null);\n\t\tassertSyntaxViolation(\"\");\n\t\tassertSyntaxViolation(\"   \");\n\t\tassertSyntaxViolation(\"X\\tX\");\n\t\tassertSyntaxViolation(\"X\\nX\");\n\t\tassertSyntaxViolation(\"XXX\\u005CtXXX\");\n\t}\n\n\t@Test\n\tvoid tagEqualsOtherTagWithSameName() {\n\t\tassertEqualsAndHashCode(TestTag.create(\"fast\"), TestTag.create(\"fast\"), TestTag.create(\"slow\"));\n\t}\n\n\t@Test\n\tvoid toStringPrintsName() {\n\t\tassertEquals(\"fast\", TestTag.create(\"fast\").toString());\n\t}\n\n\tprivate static void yep(String tag) {\n\t\tassertTrue(TestTag.isValid(tag), () -> \"'%s' should be a valid tag\".formatted(tag));\n\t}\n\n\tprivate static void nope(String tag) {\n\t\tassertFalse(TestTag.isValid(tag), () -> \"'%s' should not be a valid tag\".formatted(tag));\n\t}\n\n\tprivate void assertSyntaxViolation(String tag) {\n\t\tassertPreconditionViolationFor(() -> TestTag.create(tag))//\n\t\t\t\t.withMessageStartingWith(\"Tag name\")//\n\t\t\t\t.withMessageEndingWith(\"must be syntactically valid\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/UniqueIdFormatTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.UniqueId.Segment;\n\n/**\n * @since 1.0\n */\nclass UniqueIdFormatTests {\n\n\t@Nested\n\tclass Formatting {\n\n\t\tprivate final UniqueId engineId = UniqueId.root(\"engine\", \"junit-jupiter\");\n\n\t\tprivate final UniqueIdFormat format = UniqueIdFormat.getDefault();\n\n\t\t@Test\n\t\tvoid engineIdOnly() {\n\t\t\tassertEquals(\"[engine:junit-jupiter]\", engineId.toString());\n\t\t\tassertEquals(format.format(engineId), engineId.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid withTwoSegments() {\n\t\t\tvar classId = engineId.append(\"class\", \"org.junit.MyClass\");\n\t\t\tassertEquals(\"[engine:junit-jupiter]/[class:org.junit.MyClass]\", classId.toString());\n\t\t\tassertEquals(format.format(classId), classId.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid withManySegments() {\n\t\t\tvar uniqueId = engineId.append(\"t1\", \"v1\").append(\"t2\", \"v2\").append(\"t3\", \"v3\");\n\t\t\tassertEquals(\"[engine:junit-jupiter]/[t1:v1]/[t2:v2]/[t3:v3]\", uniqueId.toString());\n\t\t\tassertEquals(format.format(uniqueId), uniqueId.toString());\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ParsingWithDefaultFormat implements ParsingTestTrait {\n\n\t\tprivate final UniqueIdFormat format = UniqueIdFormat.getDefault();\n\n\t\t@Override\n\t\tpublic UniqueIdFormat getFormat() {\n\t\t\treturn this.format;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getEngineUid() {\n\t\t\treturn \"[engine:junit-jupiter]\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDefaultEngineUid() {\n\t\t\treturn getEngineUid();\n\t\t}\n\n\t\t@Override\n\t\tpublic String getMethodUid() {\n\t\t\treturn \"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDefaultMethodUid() {\n\t\t\treturn getMethodUid();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ParsingWithCustomFormat implements ParsingTestTrait {\n\n\t\tprivate final UniqueIdFormat format = new UniqueIdFormat('{', '=', '}', ',');\n\n\t\t@Override\n\t\tpublic UniqueIdFormat getFormat() {\n\t\t\treturn this.format;\n\t\t}\n\n\t\t@Override\n\t\tpublic String getEngineUid() {\n\t\t\treturn \"{engine=junit-jupiter}\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDefaultEngineUid() {\n\t\t\treturn \"[engine:junit-jupiter]\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getMethodUid() {\n\t\t\treturn \"{engine=junit-jupiter},{class=MyClass},{method=myMethod}\";\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDefaultMethodUid() {\n\t\t\treturn \"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\";\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static void assertSegment(Segment segment, String expectedType, String expectedValue) {\n\t\tassertEquals(expectedType, segment.getType(), \"segment type\");\n\t\tassertEquals(expectedValue, segment.getValue(), \"segment value\");\n\t}\n\n\tinterface ParsingTestTrait {\n\n\t\tUniqueIdFormat getFormat();\n\n\t\tString getEngineUid();\n\n\t\tString getDefaultEngineUid();\n\n\t\tString getMethodUid();\n\n\t\tString getDefaultMethodUid();\n\n\t\t@Test\n\t\tdefault void parseMalformedUid() {\n\t\t\tThrowable throwable = assertThrows(JUnitException.class, () -> getFormat().parse(\"malformed UID\"));\n\t\t\tassertThat(throwable).hasMessageContaining(\"malformed UID\");\n\t\t}\n\n\t\t@Test\n\t\tdefault void parseEngineUid() {\n\t\t\tvar parsedId = getFormat().parse(getEngineUid());\n\t\t\tassertSegment(parsedId.getSegments().getFirst(), \"engine\", \"junit-jupiter\");\n\t\t\tassertEquals(getEngineUid(), getFormat().format(parsedId));\n\t\t\tassertEquals(getDefaultEngineUid(), parsedId.toString());\n\t\t}\n\n\t\t@Test\n\t\tdefault void parseMethodUid() {\n\t\t\tvar parsedId = getFormat().parse(getMethodUid());\n\t\t\tassertSegment(parsedId.getSegments().get(0), \"engine\", \"junit-jupiter\");\n\t\t\tassertSegment(parsedId.getSegments().get(1), \"class\", \"MyClass\");\n\t\t\tassertSegment(parsedId.getSegments().get(2), \"method\", \"myMethod\");\n\t\t\tassertEquals(getMethodUid(), getFormat().format(parsedId));\n\t\t\tassertEquals(getDefaultMethodUid(), parsedId.toString());\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/UniqueIdTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.util.Base64;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.UniqueId.Segment;\n\n/**\n * Unit tests for {@link UniqueId}.\n *\n * @since 1.0\n * @see org.junit.jupiter.engine.execution.UniqueIdParsingForArrayParameterIntegrationTests\n */\nclass UniqueIdTests {\n\n\tprivate static final String ENGINE_ID = \"junit-jupiter\";\n\tprivate static final String SUITE_ENGINE_ID = \"junit-platform-suite\";\n\n\t@Nested\n\tclass Creation {\n\n\t\t@Test\n\t\tvoid uniqueIdCanBeCreatedFromEngineId() {\n\t\t\tvar uniqueId = UniqueId.forEngine(ENGINE_ID);\n\n\t\t\tassertEquals(\"[engine:junit-jupiter]\", uniqueId.toString());\n\t\t\tassertSegment(uniqueId.getSegments().getFirst(), \"engine\", \"junit-jupiter\");\n\t\t}\n\n\t\t@Test\n\t\tvoid engineIdCanBeAppended() {\n\t\t\tvar suiteEngineId = UniqueId.forEngine(SUITE_ENGINE_ID);\n\t\t\tvar uniqueId = suiteEngineId.appendEngine(ENGINE_ID);\n\t\t\tassertSegment(uniqueId.getSegments().get(1), \"engine\", \"junit-jupiter\");\n\t\t}\n\n\t\t@Test\n\t\tvoid retrievingOptionalEngineId() {\n\t\t\tvar uniqueIdWithEngine = UniqueId.forEngine(ENGINE_ID);\n\t\t\tassertThat(uniqueIdWithEngine.getEngineId()).contains(\"junit-jupiter\");\n\n\t\t\tvar uniqueIdWithoutEngine = UniqueId.root(\"root\", \"aValue\");\n\t\t\tassertEquals(Optional.empty(), uniqueIdWithoutEngine.getEngineId());\n\t\t}\n\n\t\t@Test\n\t\tvoid uniqueIdCanBeCreatedFromTypeAndValue() {\n\t\t\tvar uniqueId = UniqueId.root(\"aType\", \"aValue\");\n\n\t\t\tassertEquals(\"[aType:aValue]\", uniqueId.toString());\n\t\t\tassertSegment(uniqueId.getSegments().getFirst(), \"aType\", \"aValue\");\n\t\t}\n\n\t\t@Test\n\t\tvoid rootSegmentCanBeRetrieved() {\n\t\t\tvar uniqueId = UniqueId.root(\"aType\", \"aValue\");\n\n\t\t\tassertThat(uniqueId.getRoot()).contains(new Segment(\"aType\", \"aValue\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid appendingOneSegment() {\n\t\t\tvar engineId = UniqueId.root(\"engine\", ENGINE_ID);\n\t\t\tvar classId = engineId.append(\"class\", \"org.junit.MyClass\");\n\n\t\t\tassertThat(classId.getSegments()).hasSize(2);\n\t\t\tassertSegment(classId.getSegments().get(0), \"engine\", ENGINE_ID);\n\t\t\tassertSegment(classId.getSegments().get(1), \"class\", \"org.junit.MyClass\");\n\t\t}\n\n\t\t@Test\n\t\tvoid appendingSegmentLeavesOriginalUnchanged() {\n\t\t\tvar uniqueId = UniqueId.root(\"engine\", ENGINE_ID);\n\t\t\tuniqueId.append(\"class\", \"org.junit.MyClass\");\n\n\t\t\tassertThat(uniqueId.getSegments()).hasSize(1);\n\t\t\tassertSegment(uniqueId.getSegments().getFirst(), \"engine\", ENGINE_ID);\n\t\t}\n\n\t\t@Test\n\t\tvoid appendingSeveralSegments() {\n\t\t\tvar engineId = UniqueId.root(\"engine\", ENGINE_ID);\n\t\t\tvar uniqueId = engineId.append(\"t1\", \"v1\").append(\"t2\", \"v2\").append(\"t3\", \"v3\");\n\n\t\t\tassertThat(uniqueId.getSegments()).hasSize(4);\n\t\t\tassertSegment(uniqueId.getSegments().get(0), \"engine\", ENGINE_ID);\n\t\t\tassertSegment(uniqueId.getSegments().get(1), \"t1\", \"v1\");\n\t\t\tassertSegment(uniqueId.getSegments().get(2), \"t2\", \"v2\");\n\t\t\tassertSegment(uniqueId.getSegments().get(3), \"t3\", \"v3\");\n\t\t}\n\n\t\t@Test\n\t\tvoid appendingSegmentInstance() {\n\t\t\tvar uniqueId = UniqueId.forEngine(ENGINE_ID).append(\"t1\", \"v1\");\n\n\t\t\tuniqueId = uniqueId.append(new Segment(\"t2\", \"v2\"));\n\n\t\t\tassertThat(uniqueId.getSegments()).hasSize(3);\n\t\t\tassertSegment(uniqueId.getSegments().get(0), \"engine\", ENGINE_ID);\n\t\t\tassertSegment(uniqueId.getSegments().get(1), \"t1\", \"v1\");\n\t\t\tassertSegment(uniqueId.getSegments().get(2), \"t2\", \"v2\");\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid appendingNullIsNotAllowed() {\n\t\t\tvar uniqueId = UniqueId.forEngine(ENGINE_ID);\n\n\t\t\tassertPreconditionViolationFor(() -> uniqueId.append(null));\n\t\t\tassertPreconditionViolationFor(() -> uniqueId.append(null, \"foo\"));\n\t\t\tassertPreconditionViolationFor(() -> uniqueId.append(\"foo\", null));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass ParsingAndFormatting {\n\n\t\t@Test\n\t\tvoid ensureDefaultUniqueIdFormatIsUsedForParsing() {\n\t\t\tvar uniqueIdString = \"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\";\n\t\t\tvar parsedDirectly = UniqueId.parse(uniqueIdString);\n\t\t\tvar parsedViaFormat = UniqueIdFormat.getDefault().parse(uniqueIdString);\n\t\t\tassertEquals(parsedViaFormat, parsedDirectly);\n\t\t}\n\n\t\t@Test\n\t\tvoid ensureDefaultUniqueIdFormatIsUsedForFormatting() {\n\t\t\tvar parsedDirectly = UniqueId.parse(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\");\n\t\t\tassertEquals(\"[engine:junit-jupiter]/[class:MyClass]/[method:myMethod]\", parsedDirectly.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid ensureDefaultUniqueIdFormatDecodingEncodesSegmentParts() {\n\t\t\tvar segment = UniqueId.parse(\"[%5B+%25+%5D):(%3A+%2B+%2F]\").getSegments().getFirst();\n\t\t\tassertEquals(\"[ % ])\", segment.getType());\n\t\t\tassertEquals(\"(: + /\", segment.getValue());\n\t\t}\n\n\t\t@Test\n\t\tvoid ensureDefaultUniqueIdFormatCanHandleAllCharacters() {\n\t\t\tfor (char c = 0; c < Character.MAX_VALUE; c++) {\n\t\t\t\tvar value = \"foo \" + c + \" bar\";\n\t\t\t\tvar uniqueId = UniqueId.parse(UniqueId.root(\"type\", value).toString());\n\t\t\t\tvar segment = uniqueId.getSegments().getFirst();\n\t\t\t\tassertEquals(value, segment.getValue());\n\t\t\t}\n\t\t}\n\n\t\t@ParameterizedTest\n\t\t@ValueSource(strings = { \"[a:b]\", \"[a:b]/[a:b]\", \"[a$b:b()]\", \"[a:b(%5BI)]\", \"[%5B%5D:%3A%2F]\" })\n\t\tvoid ensureDefaultToStringAndParsingIsIdempotent(String expected) {\n\t\t\tassertEquals(expected, UniqueId.parse(expected).toString());\n\t\t}\n\t}\n\n\t@Nested\n\tclass EqualsContract {\n\n\t\t@Test\n\t\tvoid sameEnginesAreEqual() {\n\t\t\tvar id1 = UniqueId.root(\"engine\", \"junit-jupiter\");\n\t\t\tvar id2 = UniqueId.root(\"engine\", \"junit-jupiter\");\n\n\t\t\tassertEquals(id2, id1);\n\t\t\tassertEquals(id1, id2);\n\t\t\tassertEquals(id1.hashCode(), id2.hashCode());\n\t\t}\n\n\t\t@Test\n\t\tvoid differentEnginesAreNotEqual() {\n\t\t\tvar id1 = UniqueId.root(\"engine\", \"junit-vintage\");\n\t\t\tvar id2 = UniqueId.root(\"engine\", \"junit-jupiter\");\n\n\t\t\tassertNotEquals(id2, id1);\n\t\t\tassertNotEquals(id1, id2);\n\t\t}\n\n\t\t@Test\n\t\tvoid uniqueIdWithSameSegmentsAreEqual() {\n\t\t\tvar id1 = UniqueId.root(\"engine\", \"junit-jupiter\").append(\"t1\", \"v1\").append(\"t2\", \"v2\");\n\t\t\tvar id2 = UniqueId.root(\"engine\", \"junit-jupiter\").append(\"t1\", \"v1\").append(\"t2\", \"v2\");\n\n\t\t\tassertEquals(id2, id1);\n\t\t\tassertEquals(id1, id2);\n\t\t\tassertEquals(id1.hashCode(), id2.hashCode());\n\t\t}\n\n\t\t@Test\n\t\tvoid differentOrderOfSegmentsAreNotEqual() {\n\t\t\tvar id1 = UniqueId.root(\"engine\", \"junit-jupiter\").append(\"t2\", \"v2\").append(\"t1\", \"v1\");\n\t\t\tvar id2 = UniqueId.root(\"engine\", \"junit-jupiter\").append(\"t1\", \"v1\").append(\"t2\", \"v2\");\n\n\t\t\tassertNotEquals(id2, id1);\n\t\t\tassertNotEquals(id1, id2);\n\t\t}\n\n\t\t@Test\n\t\tvoid additionalSegmentMakesItNotEqual() {\n\t\t\tvar id1 = UniqueId.root(\"engine\", \"junit-jupiter\").append(\"t1\", \"v1\");\n\t\t\tvar id2 = id1.append(\"t2\", \"v2\");\n\n\t\t\tassertNotEquals(id2, id1);\n\t\t\tassertNotEquals(id1, id2);\n\t\t}\n\t}\n\n\t@Nested\n\tclass Prefixing {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid nullIsNotAPrefix() {\n\t\t\tvar id = UniqueId.forEngine(ENGINE_ID);\n\n\t\t\tassertPreconditionViolationFor(() -> id.hasPrefix(null));\n\t\t}\n\n\t\t@Test\n\t\tvoid uniqueIdIsPrefixForItself() {\n\t\t\tvar id = UniqueId.forEngine(ENGINE_ID).append(\"t1\", \"v1\").append(\"t2\", \"v2\");\n\n\t\t\tassertTrue(id.hasPrefix(id));\n\t\t}\n\n\t\t@Test\n\t\tvoid uniqueIdIsPrefixForUniqueIdWithAdditionalSegments() {\n\t\t\tvar id1 = UniqueId.forEngine(ENGINE_ID);\n\t\t\tvar id2 = id1.append(\"t1\", \"v1\");\n\t\t\tvar id3 = id2.append(\"t2\", \"v2\");\n\n\t\t\tassertFalse(id1.hasPrefix(id2));\n\t\t\tassertFalse(id1.hasPrefix(id3));\n\t\t\tassertTrue(id2.hasPrefix(id1));\n\t\t\tassertFalse(id2.hasPrefix(id3));\n\t\t\tassertTrue(id3.hasPrefix(id1));\n\t\t\tassertTrue(id3.hasPrefix(id2));\n\t\t}\n\n\t\t@Test\n\t\tvoid completelyUnrelatedUniqueIdsAreNotPrefixesForEachOther() {\n\t\t\tvar id1 = UniqueId.forEngine(\"foo\");\n\t\t\tvar id2 = UniqueId.forEngine(\"bar\");\n\n\t\t\tassertFalse(id1.hasPrefix(id2));\n\t\t\tassertFalse(id2.hasPrefix(id1));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass LastSegment {\n\n\t\t@Test\n\t\tvoid returnsLastSegment() {\n\t\t\tvar uniqueId = UniqueId.forEngine(\"foo\");\n\t\t\tassertSame(uniqueId.getSegments().get(0), uniqueId.getLastSegment());\n\n\t\t\tuniqueId = UniqueId.forEngine(\"foo\").append(\"type\", \"bar\");\n\t\t\tassertSame(uniqueId.getSegments().get(1), uniqueId.getLastSegment());\n\t\t}\n\n\t\t@Test\n\t\tvoid removesLastSegment() {\n\t\t\tvar uniqueId = UniqueId.forEngine(\"foo\");\n\t\t\tassertPreconditionViolationFor(uniqueId::removeLastSegment);\n\n\t\t\tvar newUniqueId = uniqueId.append(\"type\", \"bar\").removeLastSegment();\n\t\t\tassertEquals(uniqueId, newUniqueId);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass Serialization {\n\n\t\tfinal UniqueId uniqueId = UniqueId.root(\"engine\", \"junit-jupiter\");\n\n\t\t@Test\n\t\tvoid roundTrip() throws IOException, ClassNotFoundException {\n\t\t\tvar bytesOut = new ByteArrayOutputStream();\n\t\t\tvar out = new ObjectOutputStream(bytesOut);\n\t\t\tout.writeObject(uniqueId);\n\n\t\t\tvar bytesIn = new ByteArrayInputStream(bytesOut.toByteArray());\n\t\t\tvar in = new ObjectInputStream(bytesIn);\n\t\t\tvar actual = in.readObject();\n\n\t\t\tassertEquals(uniqueId, actual);\n\t\t\tassertEquals(uniqueId.toString(), actual.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid deserializeFromJunit60() throws IOException, ClassNotFoundException {\n\t\t\t/*\n\t\t\t Serialized representation of:\n\t\t\t \tnew UniqueId(\n\t\t\t \t\tnew UniqueIdFormat('[', ':', ']', '/'),\n\t\t\t \t\tList.of(new Segment(\"engine\", \"junit-jupiter\"))\n\t\t\t );\n\t\t\t */\n\t\t\tvar uniqueIdFromJunit60 = Base64.getMimeDecoder().decode(\"\"\"\n\t\t\t\t\trO0ABXNyACJvcmcuanVuaXQucGxhdGZvcm0uZW5naW5lLlVuaXF1ZUlkAAAAAAAAAAECAAJMAAhzZWdtZW50c3QAEExqYXZhL3V0\n\t\t\t\t\taWwvTGlzdDtMAA51bmlxdWVJZEZvcm1hdHQAKkxvcmcvanVuaXQvcGxhdGZvcm0vZW5naW5lL1VuaXF1ZUlkRm9ybWF0O3hwc3IA\n\t\t\t\t\tEWphdmEudXRpbC5Db2xsU2VyV46rtjobqBEDAAFJAAN0YWd4cAAAAAF3BAAAAAFzcgAqb3JnLmp1bml0LnBsYXRmb3JtLmVuZ2lu\n\t\t\t\t\tZS5VbmlxdWVJZCRTZWdtZW50AAAAAAAAAAECAAJMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7TAAFdmFsdWVxAH4AB3hwdAAG\n\t\t\t\t\tZW5naW5ldAANanVuaXQtanVwaXRlcnhzcgAob3JnLmp1bml0LnBsYXRmb3JtLmVuZ2luZS5VbmlxdWVJZEZvcm1hdAAAAAAAAAAB\n\t\t\t\t\tAgAGQwAMY2xvc2VTZWdtZW50QwALb3BlblNlZ21lbnRDABBzZWdtZW50RGVsaW1pdGVyQwASdHlwZVZhbHVlU2VwYXJhdG9yTAAT\n\t\t\t\t\tZW5jb2RlZENoYXJhY3Rlck1hcHQAE0xqYXZhL3V0aWwvSGFzaE1hcDtMAA5zZWdtZW50UGF0dGVybnQAGUxqYXZhL3V0aWwvcmVn\n\t\t\t\t\tZXgvUGF0dGVybjt4cABdAFsALwA6c3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNo\n\t\t\t\t\tb2xkeHA/QAAAAAAADHcIAAAAEAAAAAZzcgATamF2YS5sYW5nLkNoYXJhY3RlcjSLR9lrGiZ4AgABQwAFdmFsdWV4cAAldAADJTI1\n\t\t\t\t\tc3EAfgARADp0AAMlM0FzcQB+ABEAW3QAAyU1QnNxAH4AEQArdAADJTJCc3EAfgARAF10AAMlNURzcQB+ABEAL3QAAyUyRnhzcgAX\n\t\t\t\t\tamF2YS51dGlsLnJlZ2V4LlBhdHRlcm5GZ9VrbkkCDQIAAkkABWZsYWdzTAAHcGF0dGVybnEAfgAHeHAAAAAgdAAXXFFbXEUoLisp\n\t\t\t\t\tXFE6XEUoLispXFFdXEU=\"\"\");\n\n\t\t\tvar bytesIn = new ByteArrayInputStream(uniqueIdFromJunit60);\n\t\t\tvar in = new ObjectInputStream(bytesIn);\n\t\t\tvar actual = in.readObject();\n\n\t\t\tassertEquals(uniqueId, actual);\n\t\t\tassertEquals(uniqueId.toString(), actual.toString());\n\t\t}\n\n\t\t@Test\n\t\tvoid deserializeFromJunit60IgnoresCustomFormat() throws IOException, ClassNotFoundException {\n\t\t\t/*\n\t\t\t  Serialized representation of:\n\t\t\t\tnew UniqueId(\n\t\t\t\t\tnew UniqueIdFormat('{', '=', '}', ','),\n\t\t\t\t\tList.of(new Segment(\"engine\", \"junit-jupiter\"))\n\t\t\t );\n\t\t\t */\n\t\t\tvar uniqueIdWithCustomFormatFromJunit60 = Base64.getMimeDecoder().decode(\"\"\"\n\t\t\t\t\trO0ABXNyACJvcmcuanVuaXQucGxhdGZvcm0uZW5naW5lLlVuaXF1ZUlkAAAAAAAAAAECAAJMAAhzZWdtZW50c3QAEExqYXZhL3V0\n\t\t\t\t\taWwvTGlzdDtMAA51bmlxdWVJZEZvcm1hdHQAKkxvcmcvanVuaXQvcGxhdGZvcm0vZW5naW5lL1VuaXF1ZUlkRm9ybWF0O3hwc3IA\n\t\t\t\t\tEWphdmEudXRpbC5Db2xsU2VyV46rtjobqBEDAAFJAAN0YWd4cAAAAAF3BAAAAAFzcgAqb3JnLmp1bml0LnBsYXRmb3JtLmVuZ2lu\n\t\t\t\t\tZS5VbmlxdWVJZCRTZWdtZW50AAAAAAAAAAECAAJMAAR0eXBldAASTGphdmEvbGFuZy9TdHJpbmc7TAAFdmFsdWVxAH4AB3hwdAAG\n\t\t\t\t\tZW5naW5ldAANanVuaXQtanVwaXRlcnhzcgAob3JnLmp1bml0LnBsYXRmb3JtLmVuZ2luZS5VbmlxdWVJZEZvcm1hdAAAAAAAAAAB\n\t\t\t\t\tAgAGQwAMY2xvc2VTZWdtZW50QwALb3BlblNlZ21lbnRDABBzZWdtZW50RGVsaW1pdGVyQwASdHlwZVZhbHVlU2VwYXJhdG9yTAAT\n\t\t\t\t\tZW5jb2RlZENoYXJhY3Rlck1hcHQAE0xqYXZhL3V0aWwvSGFzaE1hcDtMAA5zZWdtZW50UGF0dGVybnQAGUxqYXZhL3V0aWwvcmVn\n\t\t\t\t\tZXgvUGF0dGVybjt4cAB9AHsALAA9c3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNo\n\t\t\t\t\tb2xkeHA/QAAAAAAADHcIAAAAEAAAAAZzcgATamF2YS5sYW5nLkNoYXJhY3RlcjSLR9lrGiZ4AgABQwAFdmFsdWV4cAAldAADJTI1\n\t\t\t\t\tc3EAfgARAHt0AAMlN0JzcQB+ABEAK3QAAyUyQnNxAH4AEQAsdAADJTJDc3EAfgARAH10AAMlN0RzcQB+ABEAPXQAAyUzRHhzcgAX\n\t\t\t\t\tamF2YS51dGlsLnJlZ2V4LlBhdHRlcm5GZ9VrbkkCDQIAAkkABWZsYWdzTAAHcGF0dGVybnEAfgAHeHAAAAAgdAAXXFF7XEUoLisp\n\t\t\t\t\tXFE9XEUoLispXFF9XEU=\"\"\");\n\n\t\t\tvar bytesIn = new ByteArrayInputStream(uniqueIdWithCustomFormatFromJunit60);\n\t\t\tvar in = new ObjectInputStream(bytesIn);\n\t\t\tvar actual = in.readObject();\n\n\t\t\tassertEquals(uniqueId, actual);\n\t\t\tassertEquals(uniqueId.toString(), actual.toString());\n\t\t}\n\n\t}\n\n\tprivate static void assertSegment(Segment segment, String expectedType, String expectedValue) {\n\t\tassertEquals(expectedType, segment.getType(), \"segment type\");\n\t\tassertEquals(expectedValue, segment.getValue(), \"segment value\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassNameFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationContainsNoNullElementsFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.0\n */\nclass ClassNameFilterTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid includeClassNamePatternsChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.includeClassNamePatterns((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.includeClassNamePatterns(new String[0]));\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.includeClassNamePatterns(new String[] { null }));\n\t}\n\n\t@Test\n\tvoid includeClassNamePatternsWithSinglePattern() {\n\t\tvar regex = \"^java\\\\.lang\\\\..*\";\n\n\t\tvar filter = ClassNameFilter.includeClassNamePatterns(regex);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"IncludeClassNameFilter that includes class names that match one of the following regular expressions: '\"\n\t\t\t\t\t+ regex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.lang.String\"));\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Class name [java.lang.String] matches included pattern: '\" + regex + \"'\");\n\n\t\tassertFalse(filter.apply(\"java.time.Instant\").included());\n\t\tassertFalse(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Class name [java.time.Instant] does not match any included pattern: '\" + regex + \"'\");\n\t}\n\n\t@Test\n\tvoid includeClassNamePatternsWithMultiplePatterns() {\n\t\tvar firstRegex = \"^java\\\\.lang\\\\..*\";\n\t\tvar secondRegex = \"^java\\\\.util\\\\..*\";\n\n\t\tvar filter = ClassNameFilter.includeClassNamePatterns(firstRegex, secondRegex);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"IncludeClassNameFilter that includes class names that match one of the following regular expressions: '\"\n\t\t\t\t\t+ firstRegex + \"' OR '\" + secondRegex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.lang.String\"));\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Class name [java.lang.String] matches included pattern: '\" + firstRegex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.Collection\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.util.Collection\"));\n\t\tassertThat(filter.apply(\"java.util.Collection\").getReason()).contains(\n\t\t\t\"Class name [java.util.Collection] matches included pattern: '\" + secondRegex + \"'\");\n\n\t\tassertFalse(filter.apply(\"java.time.Instant\").included());\n\t\tassertFalse(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Class name [java.time.Instant] does not match any included pattern: '\" + firstRegex + \"' OR '\"\n\t\t\t\t\t+ secondRegex + \"'\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid excludeClassNamePatternsChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.excludeClassNamePatterns((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.excludeClassNamePatterns(new String[0]));\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"patterns array\",\n\t\t\t() -> ClassNameFilter.excludeClassNamePatterns(new String[] { null }));\n\t}\n\n\t@Test\n\tvoid excludeClassNamePatternsWithSinglePattern() {\n\t\tvar regex = \"^java\\\\.lang\\\\..*\";\n\n\t\tvar filter = ClassNameFilter.excludeClassNamePatterns(regex);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"ExcludeClassNameFilter that excludes class names that match one of the following regular expressions: '\"\n\t\t\t\t\t+ regex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.lang.String\"));\n\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Class name [java.lang.String] matches excluded pattern: '\" + regex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.time.Instant\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Class name [java.time.Instant] does not match any excluded pattern: '\" + regex + \"'\");\n\t}\n\n\t@Test\n\tvoid excludeClassNamePatternsWithMultiplePatterns() {\n\t\tvar firstRegex = \"^java\\\\.lang\\\\..*\";\n\t\tvar secondRegex = \"^java\\\\.util\\\\..*\";\n\n\t\tvar filter = ClassNameFilter.excludeClassNamePatterns(firstRegex, secondRegex);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"ExcludeClassNameFilter that excludes class names that match one of the following regular expressions: '\"\n\t\t\t\t\t+ firstRegex + \"' OR '\" + secondRegex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.lang.String\"));\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Class name [java.lang.String] matches excluded pattern: '\" + firstRegex + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.Collection\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.util.Collection\"));\n\t\tassertThat(filter.apply(\"java.util.Collection\").getReason()).contains(\n\t\t\t\"Class name [java.util.Collection] matches excluded pattern: '\" + secondRegex + \"'\");\n\n\t\tassertFalse(filter.apply(\"java.time.Instant\").excluded());\n\t\tassertTrue(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Class name [java.time.Instant] does not match any excluded pattern: '\" + firstRegex + \"' OR '\"\n\t\t\t\t\t+ secondRegex + \"'\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/ClassSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClassSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass ClassSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new ClassSelector(null, \"org.example.TestClass\");\n\t\tvar selector2 = new ClassSelector(null, \"org.example.TestClass\");\n\t\tvar selector3 = new ClassSelector(null, \"org.example.X\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadClass() {\n\t\tvar selector = new ClassSelector(null, \"org.example.TestClass\");\n\n\t\tassertPreconditionViolationFor(selector::getJavaClass)//\n\t\t\t\t.withMessage(\"Could not load class with name: org.example.TestClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid usesClassClassLoader() {\n\t\tvar selector = new ClassSelector(getClass());\n\n\t\tassertThat(selector.getClassLoader()).isNotNull().isSameAs(getClass().getClassLoader());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathResourceSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClasspathResourceSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass ClasspathResourceSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new ClasspathResourceSelector(\"/foo/bar.txt\", null);\n\t\tvar selector2 = new ClasspathResourceSelector(\"/foo/bar.txt\", null);\n\t\tvar selector3 = new ClasspathResourceSelector(\"/foo/X.txt\", null);\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeWithFilePosition() {\n\t\tvar selector1 = new ClasspathResourceSelector(\"/foo/bar.txt\", FilePosition.from(1));\n\t\tvar selector2 = new ClasspathResourceSelector(\"/foo/bar.txt\", FilePosition.from(1));\n\t\tvar selector3 = new ClasspathResourceSelector(\"/foo/bar.txt\", FilePosition.from(2));\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/ClasspathRootSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClasspathRootSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass ClasspathRootSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() throws Exception {\n\t\tvar selector1 = new ClasspathRootSelector(new URI(\"file://example/path\"));\n\t\tvar selector2 = new ClasspathRootSelector(new URI(\"file://example/path\"));\n\t\tvar selector3 = new ClasspathRootSelector(new URI(\"file://example/foo\"));\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/DirectorySelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link DirectorySelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass DirectorySelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new DirectorySelector(\"/example/foo\");\n\t\tvar selector2 = new DirectorySelector(\"/example/foo\");\n\t\tvar selector3 = new DirectorySelector(\"/example/bar\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/DiscoverySelectorsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static java.lang.String.join;\nimport static java.util.Collections.singletonList;\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.InstanceOfAssertFactories.type;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.engine.discovery.JupiterUniqueIdBuilder.uniqueIdForMethod;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClassesByName;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResourceByName;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResources;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathRoots;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModules;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectNestedMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.commons.io.Resource;\nimport org.junit.platform.commons.test.TestClassLoader;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\n/**\n * Unit tests for {@link DiscoverySelectors}.\n *\n * @since 1.0\n */\nclass DiscoverySelectorsTests {\n\n\t@Nested\n\tclass SelectUriTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectUriByName() {\n\t\t\tassertPreconditionViolationFor(() -> selectUri((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectUri(\"   \"));\n\t\t\tassertPreconditionViolationFor(() -> selectUri(\"foo:\"));\n\n\t\t\tvar uri = \"https://junit.org\";\n\n\t\t\tvar selector = selectUri(uri);\n\t\t\tassertEquals(uri, selector.getUri().toString());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectUriByURI() {\n\t\t\tassertPreconditionViolationFor(() -> selectUri((URI) null));\n\t\t\tassertPreconditionViolationFor(() -> selectUri(\"   \"));\n\n\t\t\tvar uri = URI.create(\"https://junit.org\");\n\n\t\t\tvar selector = selectUri(uri);\n\t\t\tassertEquals(uri, selector.getUri());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseUriSelector() {\n\t\t\tvar selector = parseIdentifier(selectUri(\"https://junit.org\"));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(UriSelector.class)) //\n\t\t\t\t\t.extracting(UriSelector::getUri) //\n\t\t\t\t\t.isEqualTo(URI.create(\"https://junit.org\"));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectFileTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectFileByName() {\n\t\t\tassertPreconditionViolationFor(() -> selectFile((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectFile(\"   \"));\n\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\n\t\t\tvar selector = selectFile(path);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(new File(path), selector.getFile());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectFileByNameAndPosition() {\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\tassertPreconditionViolationFor(() -> selectFile((String) null, filePosition));\n\t\t\tassertPreconditionViolationFor(() -> selectFile(\"   \", filePosition));\n\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\n\t\t\tvar selector = selectFile(path, filePosition);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(new File(path), selector.getFile());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t\tassertEquals(filePosition, selector.getPosition().orElseThrow());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectFileByFileReference() throws Exception {\n\t\t\tassertPreconditionViolationFor(() -> selectFile((File) null));\n\t\t\tassertPreconditionViolationFor(() -> selectFile(new File(\"bogus/nonexistent.txt\")));\n\n\t\t\tvar currentDir = new File(\".\").getCanonicalFile();\n\t\t\tvar relativeDir = new File(\"..\", currentDir.getName());\n\t\t\tvar file = new File(relativeDir, \"src/test/resources/do_not_delete_me.txt\");\n\t\t\tvar path = file.getCanonicalFile().getPath();\n\n\t\t\tvar selector = selectFile(file);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(file.getCanonicalFile(), selector.getFile());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectFileByFileReferenceAndPosition() throws Exception {\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\tassertPreconditionViolationFor(() -> selectFile((File) null, filePosition));\n\t\t\tassertPreconditionViolationFor(() -> selectFile(new File(\"bogus/nonexistent.txt\"), filePosition));\n\n\t\t\tvar currentDir = new File(\".\").getCanonicalFile();\n\t\t\tvar relativeDir = new File(\"..\", currentDir.getName());\n\t\t\tvar file = new File(relativeDir, \"src/test/resources/do_not_delete_me.txt\");\n\t\t\tvar path = file.getCanonicalFile().getPath();\n\n\t\t\tvar selector = selectFile(file, filePosition);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(file.getCanonicalFile(), selector.getFile());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t\tassertEquals(FilePosition.from(12, 34), selector.getPosition().orElseThrow());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFileSelectorWithRelativePath() {\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\t\t\tvar selector = parseIdentifier(selectFile(path));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(FileSelector.class)) //\n\t\t\t\t\t.extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath,\n\t\t\t\t\t\tFileSelector::getPosition) //\n\t\t\t\t\t.containsExactly(path, new File(path), Path.of(path), Optional.empty());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFileSelectorWithAbsolutePath() {\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\t\t\tvar absolutePath = new File(path).getAbsolutePath();\n\t\t\tvar selector = parseIdentifier(selectFile(absolutePath));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(FileSelector.class)) //\n\t\t\t\t\t.extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath,\n\t\t\t\t\t\tFileSelector::getPosition) //\n\t\t\t\t\t.containsExactly(absolutePath, new File(absolutePath), Path.of(absolutePath), Optional.empty());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFileSelectorWithRelativePathAndFilePosition() {\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\tvar selector = parseIdentifier(selectFile(path, filePosition));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(FileSelector.class)) //\n\t\t\t\t\t.extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath,\n\t\t\t\t\t\tFileSelector::getPosition) //\n\t\t\t\t\t.containsExactly(path, new File(path), Path.of(path), Optional.of(filePosition));\n\t\t}\n\n\t\t@Test\n\t\tvoid parseFileSelectorWithAbsolutePathAndFilePosition() {\n\t\t\tvar path = \"src/test/resources/do_not_delete_me.txt\";\n\t\t\tvar absolutePath = new File(path).getAbsolutePath();\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\tvar selector = parseIdentifier(selectFile(absolutePath, filePosition));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(FileSelector.class)) //\n\t\t\t\t\t.extracting(FileSelector::getRawPath, FileSelector::getFile, FileSelector::getPath,\n\t\t\t\t\t\tFileSelector::getPosition) //\n\t\t\t\t\t.containsExactly(absolutePath, new File(absolutePath), Path.of(absolutePath),\n\t\t\t\t\t\tOptional.of(filePosition));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectDirectoryTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectDirectoryByName() {\n\t\t\tassertPreconditionViolationFor(() -> selectDirectory((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectDirectory(\"   \"));\n\n\t\t\tvar path = \"src/test/resources\";\n\n\t\t\tvar selector = selectDirectory(path);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(new File(path), selector.getDirectory());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectDirectoryByFileReference() throws Exception {\n\t\t\tassertPreconditionViolationFor(() -> selectDirectory((File) null));\n\t\t\tassertPreconditionViolationFor(() -> selectDirectory(new File(\"bogus/nonexistent\")));\n\n\t\t\tvar currentDir = new File(\".\").getCanonicalFile();\n\t\t\tvar relativeDir = new File(\"..\", currentDir.getName());\n\t\t\tvar directory = new File(relativeDir, \"src/test/resources\");\n\t\t\tvar path = directory.getCanonicalFile().getPath();\n\n\t\t\tvar selector = selectDirectory(directory);\n\t\t\tassertEquals(path, selector.getRawPath());\n\t\t\tassertEquals(directory.getCanonicalFile(), selector.getDirectory());\n\t\t\tassertEquals(Path.of(path), selector.getPath());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseDirectorySelectorWithRelativePath() {\n\t\t\tvar path = \"src/test/resources\";\n\n\t\t\tvar selector = parseIdentifier(selectDirectory(path));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(DirectorySelector.class)) //\n\t\t\t\t\t.extracting(DirectorySelector::getRawPath, DirectorySelector::getDirectory,\n\t\t\t\t\t\tDirectorySelector::getPath) //\n\t\t\t\t\t.containsExactly(path, new File(path), Path.of(path));\n\t\t}\n\n\t\t@Test\n\t\tvoid parseDirectorySelectorWithAbsolutePath() {\n\t\t\tvar path = new File(\"src/test/resources\").getAbsolutePath();\n\n\t\t\tvar selector = parseIdentifier(selectDirectory(path));\n\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(DirectorySelector.class)) //\n\t\t\t\t\t.extracting(DirectorySelector::getRawPath, DirectorySelector::getDirectory,\n\t\t\t\t\t\tDirectorySelector::getPath) //\n\t\t\t\t\t.containsExactly(path, new File(path), Path.of(path));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectClasspathResourceTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectClasspathResourcePreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"/\"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"    \"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"/   \"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"\\t\"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"/\\t\"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(null));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(Collections.emptySet()));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(Collections.singleton(null)));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(Set.of(new StubResource(null))));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(Set.of(new StubResource(\"\"))));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResourceByName(Set.of(new StubResource(\"a\"), new StubResource(\"b\"))));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid selectIndividualClasspathResource() {\n\t\t\t// with unnecessary \"/\" prefix\n\t\t\tvar selector = selectClasspathResource(\"/foo/bar/spec.xml\");\n\t\t\tassertEquals(\"foo/bar/spec.xml\", selector.getClasspathResourceName());\n\n\t\t\t// standard use case\n\t\t\tselector = selectClasspathResource(\"A/B/C/spec.json\");\n\t\t\tassertEquals(\"A/B/C/spec.json\", selector.getClasspathResourceName());\n\t\t}\n\n\t\t@Test\n\t\tvoid getSelectedClasspathResource() {\n\t\t\tvar selector = selectClasspathResource(\"org/junit/platform/commons/example.resource\");\n\t\t\tvar classpathResources = selector.getResources();\n\t\t\tassertAll(() -> assertThat(classpathResources).hasSize(1), //\n\t\t\t\t() -> assertThat(classpathResources) //\n\t\t\t\t\t\t.extracting(Resource::getName) //\n\t\t\t\t\t\t.containsExactly(\"org/junit/platform/commons/example.resource\") //\n\t\t\t);\n\t\t}\n\n\t\t@Test\n\t\tvoid getMissingClasspathResource() {\n\t\t\tvar selector = selectClasspathResource(\"org/junit/platform/commons/no-such-example.resource\");\n\t\t\tassertPreconditionViolationFor(selector::getResources);\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectClasspathResourcesWithFilePosition() {\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(null, filePosition));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"\", filePosition));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"    \", filePosition));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResource(\"\\t\", filePosition));\n\n\t\t\t// with unnecessary \"/\" prefix\n\t\t\tvar selector = selectClasspathResource(\"/foo/bar/spec.xml\", filePosition);\n\t\t\tassertEquals(\"foo/bar/spec.xml\", selector.getClasspathResourceName());\n\t\t\tassertEquals(FilePosition.from(12, 34), selector.getPosition().orElseThrow());\n\n\t\t\t// standard use case\n\t\t\tselector = selectClasspathResource(\"A/B/C/spec.json\", filePosition);\n\t\t\tassertEquals(\"A/B/C/spec.json\", selector.getClasspathResourceName());\n\t\t\tassertEquals(filePosition, selector.getPosition().orElseThrow());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathResources() {\n\t\t\t// with unnecessary \"/\" prefix\n\t\t\tvar selector = parseIdentifier(selectClasspathResource(\"/foo/bar/spec.xml\"));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathResourceSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathResourceSelector::getClasspathResourceName,\n\t\t\t\t\t\tClasspathResourceSelector::getPosition) //\n\t\t\t\t\t.containsExactly(\"foo/bar/spec.xml\", Optional.empty());\n\n\t\t\t// standard use case\n\t\t\tselector = parseIdentifier(selectClasspathResource(\"A/B/C/spec.json\"));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathResourceSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathResourceSelector::getClasspathResourceName,\n\t\t\t\t\t\tClasspathResourceSelector::getPosition) //\n\t\t\t\t\t.containsExactly(\"A/B/C/spec.json\", Optional.empty());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathResourcesWithFilePosition() {\n\t\t\tvar filePosition = FilePosition.from(12, 34);\n\t\t\t// with unnecessary \"/\" prefix\n\t\t\tvar selector = parseIdentifier(selectClasspathResource(\"/foo/bar/spec.xml\", FilePosition.from(12, 34)));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathResourceSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathResourceSelector::getClasspathResourceName,\n\t\t\t\t\t\tClasspathResourceSelector::getPosition) //\n\t\t\t\t\t.containsExactly(\"foo/bar/spec.xml\", Optional.of(filePosition));\n\n\t\t\t// standard use case\n\t\t\tselector = parseIdentifier(selectClasspathResource(\"A/B/C/spec.json\", FilePosition.from(12, 34)));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathResourceSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathResourceSelector::getClasspathResourceName,\n\t\t\t\t\t\tClasspathResourceSelector::getPosition) //\n\t\t\t\t\t.containsExactly(\"A/B/C/spec.json\", Optional.of(filePosition));\n\t\t}\n\n\t\tprivate record StubResource(String name) implements Resource {\n\n\t\t\t@Override\n\t\t\tpublic String getName() {\n\t\t\t\treturn name();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic URI getUri() {\n\t\t\t\tthrow new UnsupportedOperationException();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * @since 6.1\n\t */\n\t@Nested\n\tclass SelectClasspathResourcesTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectClasspathResourcesPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResources((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResources((String[]) null));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResources(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResources(singletonList(null)));\n\t\t\tassertPreconditionViolationFor(() -> selectClasspathResources(singletonList(\"\")));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMultipleClasspathResources() {\n\t\t\tvar selectors = selectClasspathResources( //\n\t\t\t\t\"org/junit/platform/example-a.resource\", //\n\t\t\t\t\"org/junit/platform/example-b.resource\" //\n\t\t\t);\n\t\t\tassertThat(selectors).extracting(ClasspathResourceSelector::getClasspathResourceName) //\n\t\t\t\t\t.containsExactly( //\n\t\t\t\t\t\t\"org/junit/platform/example-a.resource\", //\n\t\t\t\t\t\t\"org/junit/platform/example-b.resource\" //\n\t\t\t\t\t);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectDistinctClasspathResources() {\n\t\t\tvar selectors = selectClasspathResources( //\n\t\t\t\t\"org/junit/platform/example-a.resource\", //\n\t\t\t\t\"org/junit/platform/example-b.resource\", //\n\t\t\t\t\"org/junit/platform/example-a.resource\", //\n\t\t\t\t\"org/junit/platform/example-c.resource\" //\n\t\t\t);\n\t\t\tassertThat(selectors).extracting(ClasspathResourceSelector::getClasspathResourceName) //\n\t\t\t\t\t.containsExactly( //\n\t\t\t\t\t\t\"org/junit/platform/example-a.resource\", //\n\t\t\t\t\t\t\"org/junit/platform/example-b.resource\", //\n\t\t\t\t\t\t\"org/junit/platform/example-c.resource\" //\n\t\t\t\t\t);\n\t\t}\n\t}\n\n\t@Nested\n\tclass SelectModuleTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectModuleByNamePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectModule((String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectModule(\"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectModule(\"   \"));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectModuleByName() {\n\t\t\tvar selector = selectModule(\"java.base\");\n\t\t\tassertEquals(\"java.base\", selector.getModuleName());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectModuleByInstancePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectModule((Module) null));\n\t\t\tassertPreconditionViolationFor(() -> selectModule(getClass().getClassLoader().getUnnamedModule()));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectModuleByInstance() {\n\t\t\tvar module = Object.class.getModule();\n\t\t\tvar selector = selectModule(module);\n\t\t\tassertEquals(\"java.base\", selector.getModuleName());\n\t\t\tassertSame(module, selector.getModule().orElseThrow());\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectModulesByNamesPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectModules(null));\n\t\t\tassertPreconditionViolationFor(() -> selectModules(Set.of(\"a\", \" \")));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectModulesByNames() {\n\t\t\tvar selectors = selectModules(Set.of(\"a\", \"b\"));\n\t\t\tvar names = selectors.stream().map(ModuleSelector::getModuleName).toList();\n\t\t\tassertThat(names).containsExactlyInAnyOrder(\"b\", \"a\");\n\t\t}\n\n\t\t@Test\n\t\tvoid parseModuleSelector() {\n\t\t\tvar selector = parseIdentifier(selectModule(\"java.base\"));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ModuleSelector.class)) //\n\t\t\t\t\t.extracting(ModuleSelector::getModuleName) //\n\t\t\t\t\t.isEqualTo(\"java.base\");\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectPackageTests {\n\n\t\t@Test\n\t\tvoid selectPackageByName() {\n\t\t\tvar selector = selectPackage(getClass().getPackage().getName());\n\t\t\tassertEquals(getClass().getPackage().getName(), selector.getPackageName());\n\t\t}\n\n\t\t@Test\n\t\tvoid parsePackageSelector() {\n\t\t\tvar selector = parseIdentifier(selectPackage(getClass().getPackage().getName()));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(PackageSelector.class)) //\n\t\t\t\t\t.extracting(PackageSelector::getPackageName) //\n\t\t\t\t\t.isEqualTo(getClass().getPackage().getName());\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectClasspathRootsTests {\n\n\t\t@Test\n\t\tvoid selectClasspathRootsWithNonExistingDirectory() {\n\t\t\tvar selectors = selectClasspathRoots(Set.of(Path.of(\"some\", \"local\", \"path\")));\n\n\t\t\tassertThat(selectors).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClasspathRootsWithNonExistingJarFile() {\n\t\t\tvar selectors = selectClasspathRoots(Set.of(Path.of(\"some.jar\")));\n\n\t\t\tassertThat(selectors).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClasspathRootsWithExistingDirectory(@TempDir Path tempDir) {\n\t\t\tvar selectors = selectClasspathRoots(Set.of(tempDir));\n\n\t\t\tassertThat(selectors).extracting(ClasspathRootSelector::getClasspathRoot).containsExactly(tempDir.toUri());\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClasspathRootsWithExistingJarFile() throws Exception {\n\t\t\tvar jarUri = requireNonNull(getClass().getResource(\"/jartest.jar\")).toURI();\n\t\t\tvar jarFile = Path.of(jarUri);\n\n\t\t\tvar selectors = selectClasspathRoots(Set.of(jarFile));\n\n\t\t\tassertThat(selectors).extracting(ClasspathRootSelector::getClasspathRoot).containsExactly(jarUri);\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathRootSelectorWithNonExistingDirectory() {\n\t\t\tvar selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(Path.of(\"some/local/path\"))));\n\t\t\tassertThat(selectorStream).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathRootSelectorWithNonExistingJarFile() {\n\t\t\tvar selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(Path.of(\"some.jar\"))));\n\t\t\tassertThat(selectorStream).isEmpty();\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathRootSelectorWithExistingDirectory(@TempDir Path tempDir) {\n\t\t\tvar selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(tempDir)));\n\t\t\tvar selector = selectorStream.findAny().orElseThrow();\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathRootSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathRootSelector::getClasspathRoot) //\n\t\t\t\t\t.isEqualTo(tempDir.toUri());\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClasspathRootSelectorWithExistingJarFile() throws Exception {\n\t\t\tvar jarUri = requireNonNull(getClass().getResource(\"/jartest.jar\")).toURI();\n\t\t\tvar jarPath = Path.of(jarUri);\n\n\t\t\tvar selectorStream = parseIdentifiers(selectClasspathRoots(Set.of(jarPath)));\n\t\t\tvar selector = selectorStream.findAny().orElseThrow();\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClasspathRootSelector.class)) //\n\t\t\t\t\t.extracting(ClasspathRootSelector::getClasspathRoot) //\n\t\t\t\t\t.isEqualTo(jarUri);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectClassTests {\n\n\t\t@Test\n\t\tvoid selectClassByReference() {\n\t\t\tvar selector = selectClass(getClass());\n\t\t\tassertEquals(getClass(), selector.getJavaClass());\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassByName() {\n\t\t\tvar selector = selectClass(getClass().getName());\n\t\t\tassertEquals(getClass(), selector.getJavaClass());\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassByNameWithExplicitClassLoader() throws Exception {\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(getClass())) {\n\t\t\t\tvar selector = selectClass(testClassLoader, getClass().getName());\n\n\t\t\t\tassertThat(selector.getJavaClass().getName()).isEqualTo(getClass().getName());\n\t\t\t\tassertThat(selector.getJavaClass()).isNotEqualTo(getClass());\n\t\t\t\tassertThat(selector.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\tassertThat(selector.getJavaClass().getClassLoader()).isSameAs(testClassLoader);\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid parseClassSelector() {\n\t\t\tvar selector = parseIdentifier(selectClass(getClass()));\n\t\t\tassertThat(selector) //\n\t\t\t\t\t.asInstanceOf(type(ClassSelector.class)) //\n\t\t\t\t\t.extracting(ClassSelector::getJavaClass) //\n\t\t\t\t\t.isEqualTo(getClass());\n\t\t}\n\n\t}\n\n\t/**\n\t * @since 6.0\n\t */\n\t@Nested\n\tclass SelectClassesTests {\n\n\t\t@Test\n\t\tvoid selectClassesByReferenceViaVarargs() {\n\t\t\tassertSelectClassesByReferenceResults(selectClasses(String.class, Integer.class));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassesByReferenceViaList() {\n\t\t\tassertSelectClassesByReferenceResults(selectClasses(List.of(String.class, Integer.class)));\n\t\t}\n\n\t\tprivate static void assertSelectClassesByReferenceResults(List<ClassSelector> selectors) {\n\t\t\tClass<?> class1 = String.class;\n\t\t\tClass<?> class2 = Integer.class;\n\n\t\t\tassertThat(selectors).satisfiesExactly(//\n\t\t\t\tselector1 -> {\n\t\t\t\t\tassertThat(selector1.getJavaClass()).isEqualTo(class1);\n\t\t\t\t\tassertThat(selector1.getClassName()).isEqualTo(class1.getName());\n\t\t\t\t}, //\n\t\t\t\tselector2 -> {\n\t\t\t\t\tassertThat(selector2.getJavaClass()).isEqualTo(class2);\n\t\t\t\t\tassertThat(selector2.getClassName()).isEqualTo(class2.getName());\n\t\t\t\t});\n\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassesByNameViaVarargsWithExplicitClassLoader() throws Exception {\n\t\t\tClass<?> class1 = Foo.class;\n\t\t\tClass<?> class2 = Bar.class;\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(class1, class2)) {\n\t\t\t\tassertThat(selectClassesByName(testClassLoader, class1.getName(), class2.getName())).satisfiesExactly(\n\t\t\t\t\tselector1 -> checkClassSelector(testClassLoader, selector1, class1), //\n\t\t\t\t\tselector2 -> checkClassSelector(testClassLoader, selector2, class2)); //\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassesByNameViaListWithExplicitClassLoader() throws Exception {\n\t\t\tClass<?> class1 = Foo.class;\n\t\t\tClass<?> class2 = Bar.class;\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(class1, class2)) {\n\t\t\t\tassertThat(selectClassesByName(testClassLoader, List.of(class1.getName(), class2.getName())))//\n\t\t\t\t\t\t.satisfiesExactly(//\n\t\t\t\t\t\t\tselector1 -> checkClassSelector(testClassLoader, selector1, class1), //\n\t\t\t\t\t\t\tselector2 -> checkClassSelector(testClassLoader, selector2, class2)); //\n\t\t\t}\n\t\t}\n\n\t\tprivate static void checkClassSelector(TestClassLoader testClassLoader, ClassSelector selector,\n\t\t\t\tClass<?> clazz) {\n\n\t\t\tassertThat(selector.getJavaClass().getName()).isEqualTo(clazz.getName());\n\t\t\tassertThat(selector.getJavaClass()).isNotEqualTo(clazz);\n\t\t\tassertThat(selector.getJavaClass().getClassLoader()).isSameAs(testClassLoader);\n\t\t\tassertThat(selector.getClassLoader()).isSameAs(testClassLoader);\n\t\t}\n\n\t\tclass Foo {\n\t\t}\n\n\t\tclass Bar {\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectMethodTests {\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(className, methodName)\")\n\t\tvoid selectMethodByClassNameAndMethodNamePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", null));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"  \"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod((String) null, \"method\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"\", \"method\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"   \", \"method\"));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(className, methodName, parameterTypeNames)\")\n\t\tvoid selectMethodByClassNameMethodNameAndParameterTypeNamesPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", null, \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"  \", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod((String) null, \"method\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"\", \"method\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"   \", \"method\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"method\", (String) null));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(className, methodName, parameterTypes)\")\n\t\tvoid selectMethodByClassNameMethodNameAndParameterTypesPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", null, int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"  \", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod((String) null, \"method\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"\", \"method\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"   \", \"method\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"method\", (Class<?>) null));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(\"TestClass\", \"method\", new Class<?>[] { int.class, null }));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(class, methodName)\")\n\t\tvoid selectMethodByClassAndMethodNamePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), (String) null));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), \"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), \"  \"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod((Class<?>) null, \"method\"));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(class, methodName, parameterTypeNames)\")\n\t\tvoid selectMethodByClassMethodNameAndParameterTypeNamesPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectMethod((Class<?>) null, \"method\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), null, \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), \"\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), \"  \", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), \"method\", (String) null));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectMethod(class, method)\")\n\t\tvoid selectMethodByClassAndMethodPreconditions() {\n\t\t\tvar method = getClass().getDeclaredMethods()[0];\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(null, method));\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(testClass(), (Method) null));\n\t\t}\n\n\t\t@ParameterizedTest(name = \"FQMN: ''{0}''\")\n\t\t@MethodSource(\"invalidFullyQualifiedMethodNames\")\n\t\t@DisplayName(\"Preconditions: selectMethod(FQMN)\")\n\t\tvoid selectMethodByFullyQualifiedNamePreconditions(String fqmn, String message) {\n\t\t\tassertPreconditionViolationFor(() -> selectMethod(fqmn)).withMessageContaining(message);\n\t\t}\n\n\t\tstatic Stream<Arguments> invalidFullyQualifiedMethodNames() {\n\t\t\t// @formatter:off\n\t\t\treturn Stream.of(\n\t\t\t\targuments(null, \"must not be null or blank\"),\n\t\t\t\targuments(\"\", \"must not be null or blank\"),\n\t\t\t\targuments(\"   \", \"must not be null or blank\"),\n\t\t\t\targuments(\"com.example\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"com.example.Foo\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"method\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"#method\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"#method()\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"#method(int)\", \"not a valid fully qualified method name\"),\n\t\t\t\targuments(\"java.lang.String#\", \"not a valid fully qualified method name\")\n\t\t\t);\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedName() throws Exception {\n\t\t\tClass<?> clazz = testClass();\n\t\t\tvar method = clazz.getDeclaredMethod(\"myTest\");\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(clazz, method);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithExplicitClassLoader() throws Exception {\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(testClass())) {\n\t\t\t\tvar clazz = testClassLoader.loadClass(testClass().getName());\n\t\t\t\tassertThat(clazz).isNotEqualTo(testClass());\n\n\t\t\t\tvar method = clazz.getDeclaredMethod(\"myTest\");\n\t\t\t\tvar selector = selectMethod(testClassLoader, testClass().getName(), \"myTest\");\n\t\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\t\tassertThat(selector.getJavaClass()).isEqualTo(clazz);\n\t\t\t\tassertThat(selector.getClassName()).isEqualTo(clazz.getName());\n\t\t\t\tassertThat(selector.getMethodName()).isEqualTo(method.getName());\n\t\t\t\tassertThat(selector.getParameterTypeNames()).isEmpty();\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameForDefaultMethodInInterface() throws Exception {\n\t\t\tClass<?> clazz = TestCaseWithDefaultMethod.class;\n\t\t\tvar method = clazz.getMethod(\"myTest\");\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(clazz, method);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithPrimitiveParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int.class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int.class, \"int\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithPrimitiveParameterUsingSourceCodeSyntax() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int.class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"int\", \"int\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithObjectParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String.class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String.class,\n\t\t\t\tString.class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithObjectParameterUsingSourceCodeSyntax() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String.class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"java.lang.String\",\n\t\t\t\tString.class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithPrimitiveArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[].class,\n\t\t\t\tint[].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithPrimitiveArrayParameterUsingSourceCodeSyntax() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"int[]\", \"int[]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithObjectArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String[].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String[].class,\n\t\t\t\tString[].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithObjectArrayParameterUsingSourceCodeSyntax() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String[].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"java.lang.String[]\",\n\t\t\t\t\"java.lang.String[]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithTwoDimensionalPrimitiveArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[][].class,\n\t\t\t\tint[][].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithTwoDimensionalPrimitiveArrayParameterUsingSourceCodeSyntax()\n\t\t\t\tthrows Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"int[][]\", \"int[][]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithTwoDimensionalObjectArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String[][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, String[][].class,\n\t\t\t\tString[][].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithTwoDimensionalObjectArrayParameterUsingSourceCodeSyntax()\n\t\t\t\tthrows Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", String[][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"java.lang.String[][]\",\n\t\t\t\t\"java.lang.String[][]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithMultidimensionalPrimitiveArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[][][][][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, int[][][][][].class,\n\t\t\t\tint[][][][][].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithMultidimensionalPrimitiveArrayParameterUsingSourceCodeSyntax()\n\t\t\t\tthrows Exception {\n\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", int[][][][][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"int[][][][][]\",\n\t\t\t\t\"int[][][][][]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithMultidimensionalObjectArrayParameter() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", Double[][][][][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, Double[][][][][].class,\n\t\t\t\tDouble[][][][][].class.getName());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameWithMultidimensionalObjectArrayParameterUsingSourceCodeSyntax()\n\t\t\t\tthrows Exception {\n\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\", Double[][][][][].class);\n\t\t\tvar selector = assertSelectMethodByFullyQualifiedName(testClass(), method, \"java.lang.Double[][][][][]\",\n\t\t\t\t\"java.lang.Double[][][][][]\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameEndingInOpeningParenthesis() {\n\t\t\tvar className = \"org.example.MyClass\";\n\t\t\t// The following bizarre method name is not permissible in Java source\n\t\t\t// code; however, it'selector permitted by the JVM -- for example, in Groovy\n\t\t\t// or Kotlin source code using back ticks.\n\t\t\tvar methodName = \")--(\";\n\t\t\tvar fqmn = className + \"#\" + methodName;\n\n\t\t\tvar selector = selectMethod(fqmn);\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * Inspired by Spock specifications.\n\t\t */\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameContainingHashtags() {\n\t\t\tvar className = \"org.example.CalculatorSpec\";\n\t\t\tvar methodName = \"#a plus #b equals #c\";\n\t\t\tvar fqmn = className + \"#\" + methodName;\n\n\t\t\tvar selector = selectMethod(fqmn);\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * Inspired by Spock specifications.\n\t\t */\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameContainingHashtagsAndWithParameterList() {\n\t\t\tvar className = \"org.example.CalculatorSpec\";\n\t\t\tvar methodName = \"#a plus #b equals #c\";\n\t\t\tvar methodParameters = \"int, int, int\";\n\t\t\tvar fqmn = \"%s#%s(%s)\".formatted(className, methodName, methodParameters);\n\n\t\t\tvar selector = selectMethod(fqmn);\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(methodParameters, selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * Inspired by Kotlin tests.\n\t\t */\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameContainingParentheses() {\n\t\t\tvar className = \"org.example.KotlinTestCase\";\n\t\t\tvar methodName = \"🦆 ~|~test with a really, (really) terrible name & that needs to be changed!~|~\";\n\t\t\tvar fqmn = className + \"#\" + methodName;\n\n\t\t\tvar selector = selectMethod(fqmn);\n\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * Inspired by Kotlin tests.\n\t\t */\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameEndingWithParentheses() {\n\t\t\tvar className = \"org.example.KotlinTestCase\";\n\t\t\tvar methodName = \"test name ends with parentheses()\";\n\t\t\tvar fqmn = className + \"#\" + methodName + \"()\";\n\n\t\t\tvar selector = selectMethod(fqmn);\n\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * Inspired by Kotlin tests.\n\t\t */\n\t\t@Test\n\t\tvoid selectMethodByFullyQualifiedNameEndingWithParenthesesAndWithParameterList() {\n\t\t\tvar className = \"org.example.KotlinTestCase\";\n\t\t\tvar methodName = \"test name ends with parentheses()\";\n\t\t\tvar methodParameters = \"int, int, int\";\n\t\t\tvar fqmn = \"%s#%s(%s)\".formatted(className, methodName, methodParameters);\n\n\t\t\tvar selector = selectMethod(fqmn);\n\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(methodParameters, selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\tprivate MethodSelector assertSelectMethodByFullyQualifiedName(Class<?> clazz, Method method) {\n\t\t\tvar selector = selectMethod(fqmn(clazz, method.getName()));\n\t\t\tassertEquals(method, selector.getJavaMethod());\n\t\t\tassertEquals(clazz, selector.getJavaClass());\n\t\t\tassertEquals(clazz.getName(), selector.getClassName());\n\t\t\tassertEquals(method.getName(), selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\treturn selector;\n\t\t}\n\n\t\tprivate MethodSelector assertSelectMethodByFullyQualifiedName(Class<?> clazz, Method method,\n\t\t\t\tClass<?> parameterType, String expectedParameterTypes) {\n\n\t\t\tvar selector = selectMethod(fqmn(parameterType));\n\t\t\tassertEquals(method, selector.getJavaMethod());\n\t\t\tassertEquals(clazz, selector.getJavaClass());\n\t\t\tassertEquals(clazz.getName(), selector.getClassName());\n\t\t\tassertEquals(method.getName(), selector.getMethodName());\n\t\t\tassertEquals(expectedParameterTypes, selector.getParameterTypeNames());\n\t\t\treturn selector;\n\t\t}\n\n\t\tprivate MethodSelector assertSelectMethodByFullyQualifiedName(Class<?> clazz, Method method,\n\t\t\t\tString parameterName, String expectedParameterTypes) {\n\n\t\t\tvar selector = selectMethod(fqmnWithParamNames(parameterName));\n\t\t\tassertEquals(method, selector.getJavaMethod());\n\t\t\tassertEquals(clazz, selector.getJavaClass());\n\t\t\tassertEquals(clazz.getName(), selector.getClassName());\n\t\t\tassertEquals(method.getName(), selector.getMethodName());\n\t\t\tassertEquals(expectedParameterTypes, selector.getParameterTypeNames());\n\t\t\treturn selector;\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassAndMethodName() throws Exception {\n\t\t\tvar method = testClass().getDeclaredMethod(\"myTest\");\n\n\t\t\tvar selector = selectMethod(testClass(), \"myTest\");\n\t\t\tassertEquals(testClass(), selector.getJavaClass());\n\t\t\tassertEquals(testClass().getName(), selector.getClassName());\n\t\t\tassertEquals(method, selector.getJavaMethod());\n\t\t\tassertEquals(\"myTest\", selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassMethodNameAndParameterTypeNames() throws Exception {\n\t\t\tvar testClass = testClass();\n\t\t\tvar method = testClass.getDeclaredMethod(\"myTest\", String.class, boolean[].class);\n\n\t\t\tvar selector = selectMethod(testClass, \"myTest\", \"java.lang.String, boolean[]\");\n\n\t\t\tassertThat(selector.getClassName()).isEqualTo(testClass.getName());\n\t\t\tassertThat(selector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(\"myTest\");\n\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String, boolean[]\");\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassNameMethodNameAndParameterTypes() throws Exception {\n\t\t\tvar testClass = testClass();\n\t\t\tvar method = testClass.getDeclaredMethod(\"myTest\", String.class, boolean[].class);\n\n\t\t\tvar selector = selectMethod(testClass.getName(), \"myTest\", String.class, boolean[].class);\n\n\t\t\tassertThat(selector.getClassName()).isEqualTo(testClass.getName());\n\t\t\tassertThat(selector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(\"myTest\");\n\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String, boolean[]\");\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassNameMethodNameAndParameterTypeNamesWithExplicitClassLoader() throws Exception {\n\t\t\tvar testClass = testClass();\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(testClass)) {\n\t\t\t\tvar clazz = testClassLoader.loadClass(testClass.getName());\n\t\t\t\tassertThat(clazz).isNotEqualTo(testClass);\n\n\t\t\t\tvar method = clazz.getDeclaredMethod(\"myTest\", String.class, boolean[].class);\n\t\t\t\tvar selector = selectMethod(testClassLoader, testClass.getName(), \"myTest\",\n\t\t\t\t\t\"java.lang.String, boolean[]\");\n\n\t\t\t\tassertThat(selector.getClassName()).isEqualTo(clazz.getName());\n\t\t\t\tassertThat(selector.getJavaClass()).isEqualTo(clazz);\n\t\t\t\tassertThat(selector.getMethodName()).isEqualTo(method.getName());\n\t\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String, boolean[]\");\n\t\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class);\n\t\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassMethodNameAndParameterTypes() throws Exception {\n\t\t\tvar testClass = testClass();\n\t\t\tvar method = testClass.getDeclaredMethod(\"myTest\", String.class, boolean[].class);\n\n\t\t\tvar selector = selectMethod(testClass, \"myTest\", String.class, boolean[].class);\n\n\t\t\tassertThat(selector.getClassName()).isEqualTo(testClass.getName());\n\t\t\tassertThat(selector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(\"myTest\");\n\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String, boolean[]\");\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodWithParametersByMethodReference() throws Exception {\n\t\t\tvar testClass = testClass();\n\t\t\tvar method = testClass.getDeclaredMethod(\"myTest\", String.class, boolean[].class);\n\n\t\t\tvar selector = selectMethod(testClass, method);\n\n\t\t\tassertThat(selector.getClassName()).isEqualTo(testClass.getName());\n\t\t\tassertThat(selector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(\"myTest\");\n\t\t\tassertThat(selector.getJavaMethod()).isEqualTo(method);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String, boolean[]\");\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class, boolean[].class);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectClassByNameForSpockSpec() {\n\t\t\tvar className = \"org.example.CalculatorSpec\";\n\t\t\tvar selector = selectClass(className);\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t}\n\n\t\t@Test\n\t\tvoid selectMethodByClassAndNameForSpockSpec() {\n\t\t\tvar className = \"org.example.CalculatorSpec\";\n\t\t\tvar methodName = \"#a plus #b equals #c\";\n\n\t\t\tvar selector = selectMethod(className, methodName);\n\t\t\tassertEquals(className, selector.getClassName());\n\t\t\tassertEquals(methodName, selector.getMethodName());\n\t\t\tassertEquals(\"\", selector.getParameterTypeNames());\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\tprivate static Class<?> testClass() {\n\t\t\treturn DiscoverySelectorsTests.class;\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectNestedClassAndSelectNestedMethodTests {\n\n\t\tprivate final String enclosingClassName = getClass().getName() + \"$ClassWithNestedInnerClass\";\n\t\tprivate final String nestedClassName = getClass().getName() + \"$ClassWithNestedInnerClass$NestedClass\";\n\t\tprivate final String doubleNestedClassName = nestedClassName + \"$DoubleNestedClass\";\n\t\tprivate final String methodName = \"nestedTest\";\n\n\t\t@Test\n\t\tvoid selectNestedClassByClassNames() {\n\t\t\tvar selector = selectNestedClass(List.of(enclosingClassName), nestedClassName);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectNestedClassByClassNamesWithExplicitClassLoader() throws Exception {\n\t\t\tvar testClasses = List.of(ClassWithNestedInnerClass.class, ClassWithNestedInnerClass.NestedClass.class);\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(testClasses)) {\n\t\t\t\tvar selector = selectNestedClass(testClassLoader, List.of(enclosingClassName), nestedClassName);\n\n\t\t\t\tassertThat(selector.getEnclosingClasses()).doesNotContain(ClassWithNestedInnerClass.class);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getName).containsOnly(enclosingClassName);\n\t\t\t\tassertThat(selector.getNestedClass()).isNotEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\t\tassertThat(selector.getNestedClass().getName()).isEqualTo(nestedClassName);\n\n\t\t\t\tassertThat(selector.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getClassLoader).containsOnly(\n\t\t\t\t\ttestClassLoader);\n\t\t\t\tassertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid selectDoubleNestedClassByClassNames() {\n\t\t\tvar selector = selectNestedClass(List.of(enclosingClassName, nestedClassName), doubleNestedClassName);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsExactly(ClassWithNestedInnerClass.class,\n\t\t\t\tClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(\n\t\t\t\tClassWithNestedInnerClass.NestedClass.DoubleNestedClass.class);\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsExactly(enclosingClassName, nestedClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(doubleNestedClassName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid selectNestedClassPreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectNestedClass(null, \"ClassName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedClass(List.of(), \"ClassName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedClass(List.of(\"ClassName\"), null));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedClass(List.of(\"ClassName\"), \"\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedClass(List.of(\"ClassName\"), \" \"));\n\t\t}\n\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassNamesAndMethodName() throws Exception {\n\t\t\tvar selector = selectNestedMethod(List.of(enclosingClassName), nestedClassName, methodName);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(selector.getNestedClass().getDeclaredMethod(methodName));\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassNamesAndMethodNameWithExplicitClassLoader() throws Exception {\n\t\t\tvar testClasses = List.of(ClassWithNestedInnerClass.class, ClassWithNestedInnerClass.NestedClass.class);\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(testClasses)) {\n\t\t\t\tvar selector = selectNestedMethod(testClassLoader, List.of(enclosingClassName), nestedClassName,\n\t\t\t\t\tmethodName);\n\n\t\t\t\tassertThat(selector.getEnclosingClasses()).doesNotContain(ClassWithNestedInnerClass.class);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getName).containsOnly(enclosingClassName);\n\t\t\t\tassertThat(selector.getNestedClass()).isNotEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\t\tassertThat(selector.getNestedClass().getName()).isEqualTo(nestedClassName);\n\n\t\t\t\tassertThat(selector.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getClassLoader).containsOnly(\n\t\t\t\t\ttestClassLoader);\n\t\t\t\tassertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassesAndMethodName() throws Exception {\n\t\t\tvar selector = selectNestedMethod(List.of(ClassWithNestedInnerClass.class),\n\t\t\t\tClassWithNestedInnerClass.NestedClass.class, methodName);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(selector.getNestedClass().getDeclaredMethod(methodName));\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypeNames() throws Exception {\n\t\t\tvar selector = selectNestedMethod(List.of(enclosingClassName), nestedClassName, methodName,\n\t\t\t\tString.class.getName());\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(\n\t\t\t\tselector.getNestedClass().getDeclaredMethod(methodName, String.class));\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * @since 1.0\n\t\t */\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypes() throws Exception {\n\t\t\tvar selector = selectNestedMethod(List.of(enclosingClassName), nestedClassName, methodName, String.class);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(\n\t\t\t\tselector.getNestedClass().getDeclaredMethod(methodName, String.class));\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class);\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t/**\n\t\t * @since 1.10\n\t\t */\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypeNamesWithExplicitClassLoader()\n\t\t\t\tthrows Exception {\n\n\t\t\tvar enclosingClass = ClassWithNestedInnerClass.class;\n\t\t\tvar nestedClass = ClassWithNestedInnerClass.NestedClass.class;\n\n\t\t\ttry (var testClassLoader = TestClassLoader.forClasses(enclosingClass, nestedClass)) {\n\t\t\t\tvar selector = selectNestedMethod(testClassLoader, List.of(enclosingClassName), nestedClassName,\n\t\t\t\t\tmethodName, String.class.getName());\n\n\t\t\t\tassertThat(selector.getEnclosingClasses()).doesNotContain(enclosingClass);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getName).containsOnly(enclosingClassName);\n\t\t\t\tassertThat(selector.getNestedClass()).isNotEqualTo(nestedClass);\n\t\t\t\tassertThat(selector.getNestedClass().getName()).isEqualTo(nestedClassName);\n\n\t\t\t\tassertThat(selector.getClassLoader()).isSameAs(testClassLoader);\n\t\t\t\tassertThat(selector.getEnclosingClasses()).extracting(Class::getClassLoader).containsOnly(\n\t\t\t\t\ttestClassLoader);\n\t\t\t\tassertThat(selector.getNestedClass().getClassLoader()).isSameAs(testClassLoader);\n\n\t\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(String.class.getName());\n\t\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * @since 1.10\n\t\t */\n\t\t@Test\n\t\tvoid selectNestedMethodByEnclosingClassesMethodNameAndParameterTypes() throws Exception {\n\t\t\tvar selector = selectNestedMethod(List.of(ClassWithNestedInnerClass.class),\n\t\t\t\tClassWithNestedInnerClass.NestedClass.class, methodName, String.class);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsOnly(ClassWithNestedInnerClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(ClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(\n\t\t\t\tselector.getNestedClass().getDeclaredMethod(methodName, String.class));\n\t\t\tassertThat(selector.getParameterTypes()).containsExactly(String.class);\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsOnly(enclosingClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(nestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(methodName);\n\t\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"java.lang.String\");\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@Test\n\t\tvoid selectDoubleNestedMethodByEnclosingClassNamesAndMethodName() throws Exception {\n\t\t\tvar doubleNestedMethodName = \"doubleNestedTest\";\n\t\t\tvar selector = selectNestedMethod(List.of(enclosingClassName, nestedClassName), doubleNestedClassName,\n\t\t\t\tdoubleNestedMethodName);\n\n\t\t\tassertThat(selector.getEnclosingClasses()).containsExactly(ClassWithNestedInnerClass.class,\n\t\t\t\tClassWithNestedInnerClass.NestedClass.class);\n\t\t\tassertThat(selector.getNestedClass()).isEqualTo(\n\t\t\t\tClassWithNestedInnerClass.NestedClass.DoubleNestedClass.class);\n\t\t\tassertThat(selector.getMethod()).isEqualTo(\n\t\t\t\tselector.getNestedClass().getDeclaredMethod(doubleNestedMethodName));\n\n\t\t\tassertThat(selector.getEnclosingClassNames()).containsExactly(enclosingClassName, nestedClassName);\n\t\t\tassertThat(selector.getNestedClassName()).isEqualTo(doubleNestedClassName);\n\t\t\tassertThat(selector.getMethodName()).isEqualTo(doubleNestedMethodName);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectNestedMethod(enclosingClassNames, nestedClassName, methodName)\")\n\t\tvoid selectNestedMethodByEnclosingClassNamesAndMethodNamePreconditions() {\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(null, \"ClassName\", \"methodName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(), \"ClassName\", \"methodName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), null, \"methodName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \" \", \"methodName\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", null));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \" \"));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectNestedMethod(enclosingClassNames, nestedClassName, methodName, parameterTypeNames)\")\n\t\tvoid selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypeNamesPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(null, \"ClassName\", \"methodName\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(), \"ClassName\", \"methodName\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), null, \"methodName\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \" \", \"methodName\", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", null, \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \" \", \"int\"));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \"methodName\", (String) null));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t/**\n\t\t * @since 1.10\n\t\t */\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectNestedMethod(enclosingClassNames, nestedClassName, methodName, parameterTypes)\")\n\t\tvoid selectNestedMethodByEnclosingClassNamesMethodNameAndParameterTypesPreconditions() {\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(null, \"ClassName\", \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(), \"ClassName\", \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), null, \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \" \", \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", null, int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \" \", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \"methodName\", (Class<?>) null));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(\"ClassName\"), \"ClassName\", \"methodName\", new Class<?>[] { int.class, null }));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t/**\n\t\t * @since 1.10\n\t\t */\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\t@DisplayName(\"Preconditions: selectNestedMethod(enclosingClasses, nestedClass, methodName, parameterTypes)\")\n\t\tvoid selectNestedMethodByEnclosingClassesClassMethodNameAndParameterTypesPreconditions() {\n\t\t\tList<Class<?>> enclosingClassList = List.of(ClassWithNestedInnerClass.class);\n\t\t\tClass<?> nestedClass = ClassWithNestedInnerClass.NestedClass.class;\n\n\t\t\t// @formatter:off\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(null, nestedClass, \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(List.of(), nestedClass, \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(enclosingClassList, null, \"methodName\", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(enclosingClassList, nestedClass, null, int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(enclosingClassList, nestedClass, \" \", int.class));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(enclosingClassList, nestedClass, \"methodName\", (Class<?>) null));\n\t\t\tassertPreconditionViolationFor(() -> selectNestedMethod(enclosingClassList, nestedClass, \"methodName\", new Class<?>[] { int.class, null }));\n\t\t\t// @formatter:on\n\t\t}\n\n\t\tstatic class ClassWithNestedInnerClass {\n\n\t\t\t// @Nested\n\t\t\t@SuppressWarnings({ \"InnerClassMayBeStatic\", \"unused\" })\n\t\t\tclass NestedClass {\n\n\t\t\t\t// @Test\n\t\t\t\tvoid nestedTest() {\n\t\t\t\t}\n\n\t\t\t\t// @Test\n\t\t\t\tvoid nestedTest(String parameter) {\n\t\t\t\t}\n\n\t\t\t\t// @Nested\n\t\t\t\tclass DoubleNestedClass {\n\n\t\t\t\t\t// @Test\n\t\t\t\t\tvoid doubleNestedTest() {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectIterationTests {\n\n\t\t@Test\n\t\tvoid selectsIteration() throws Exception {\n\t\t\tClass<?> clazz = DiscoverySelectorsTests.class;\n\t\t\tvar method = clazz.getDeclaredMethod(\"myTest\", int.class);\n\t\t\tvar parentSelector = selectMethod(clazz, method);\n\t\t\tvar selector = DiscoverySelectors.selectIteration(parentSelector, 23, 42);\n\t\t\tassertThat(selector.getParentSelector()).isSameAs(parentSelector);\n\t\t\tassertThat(selector.getIterationIndices()).containsExactly(23, 42);\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass SelectUniqueIdTests {\n\n\t\t@Test\n\t\tvoid selectsUniqueId() {\n\t\t\tvar selector = selectUniqueId(uniqueIdForMethod(DiscoverySelectorsTests.class, \"myTest(int)\"));\n\t\t\tassertThat(selector.getUniqueId()).isNotNull();\n\t\t\tassertThat(parseIdentifier(selector)).isEqualTo(selector);\n\t\t}\n\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static DiscoverySelector parseIdentifier(DiscoverySelector selector) {\n\t\treturn DiscoverySelectors.parse(toIdentifierString(selector)).orElseThrow();\n\t}\n\n\tprivate static Stream<? extends DiscoverySelector> parseIdentifiers(\n\t\t\tCollection<? extends DiscoverySelector> selectors) {\n\t\treturn DiscoverySelectors.parseAll(\n\t\t\tselectors.stream().map(it -> DiscoverySelectorIdentifier.parse(toIdentifierString(it))).toList());\n\t}\n\n\tprivate static String toIdentifierString(DiscoverySelector selector) {\n\t\treturn selector.toIdentifier().orElseThrow().toString();\n\t}\n\n\tprivate static String fqmn(Class<?>... params) {\n\t\treturn fqmn(DiscoverySelectorsTests.class, \"myTest\", params);\n\t}\n\n\tprivate static String fqmn(Class<?> clazz, String methodName, Class<?>... params) {\n\t\treturn ReflectionUtils.getFullyQualifiedMethodName(clazz, methodName, params);\n\t}\n\n\tprivate static String fqmnWithParamNames(String... params) {\n\t\treturn \"%s#%s(%s)\".formatted(DiscoverySelectorsTests.class.getName(), \"myTest\", join(\", \", params));\n\t}\n\n\tinterface TestInterface {\n\n\t\t@Test\n\t\tdefault void myTest() {\n\t\t}\n\n\t}\n\n\tprivate static class TestCaseWithDefaultMethod implements TestInterface {\n\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest() {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(int num) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(int[] nums) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(int[][] grid) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(int[][][][][] grid) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(String info) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(String info, boolean[] flags) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(String[] info) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(String[][] info) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid myTest(Double[][][][][] data) {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/FilePositionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\n/**\n * Unit tests for {@link FilePosition}.\n *\n * @since 1.7\n */\n@DisplayName(\"FilePosition unit tests\")\nclass FilePositionTests {\n\n\t@Test\n\t@DisplayName(\"factory method preconditions\")\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> FilePosition.from(-1));\n\t\tassertPreconditionViolationFor(() -> FilePosition.from(0, -1));\n\t}\n\n\t@Test\n\t@DisplayName(\"create FilePosition from factory method with line number\")\n\tvoid filePositionFromLine() {\n\t\tvar filePosition = FilePosition.from(42);\n\n\t\tassertThat(filePosition.getLine()).isEqualTo(42);\n\t\tassertThat(filePosition.getColumn()).isEmpty();\n\t}\n\n\t@Test\n\t@DisplayName(\"create FilePosition from factory method with line number and column number\")\n\tvoid filePositionFromLineAndColumn() {\n\t\tvar filePosition = FilePosition.from(42, 99);\n\n\t\tassertThat(filePosition.getLine()).isEqualTo(42);\n\t\tassertThat(filePosition.getColumn()).contains(99);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@ParameterizedTest\n\t@MethodSource\n\tvoid filePositionFromQuery(String query, int expectedLine, int expectedColumn) {\n\t\tvar optionalFilePosition = FilePosition.fromQuery(query);\n\n\t\tif (optionalFilePosition.isPresent()) {\n\t\t\tvar filePosition = optionalFilePosition.get();\n\n\t\t\tassertThat(filePosition.getLine()).isEqualTo(expectedLine);\n\t\t\tassertThat(filePosition.getColumn().orElse(-1)).isEqualTo(expectedColumn);\n\t\t}\n\t\telse {\n\t\t\tassertEquals(-1, expectedColumn);\n\t\t\tassertEquals(-1, expectedLine);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tstatic Stream<Arguments> filePositionFromQuery() {\n\t\treturn Stream.of( //\n\t\t\targuments(null, -1, -1), //\n\t\t\targuments(\"?!\", -1, -1), //\n\t\t\targuments(\"line=ZZ\", -1, -1), //\n\t\t\targuments(\"line=42\", 42, -1), //\n\t\t\targuments(\"line=42&column=99\", 42, 99), //\n\t\t\targuments(\"line=42&column=ZZ\", 42, -1), //\n\t\t\targuments(\"line=42&abc=xyz&column=99\", 42, 99), //\n\t\t\targuments(\"1=3&foo=X&line=42&abc=xyz&column=99&enigma=393939\", 42, 99), //\n\t\t\t// First one wins:\n\t\t\targuments(\"line=42&line=555\", 42, -1), //\n\t\t\targuments(\"line=42&line=555&column=99&column=555\", 42, 99) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"equals() and hashCode() with column number cached by Integer.valueOf()\")\n\tvoid equalsAndHashCode() {\n\t\tvar same = FilePosition.from(42, 99);\n\t\tvar sameSame = FilePosition.from(42, 99);\n\t\tvar different = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(same, sameSame, different);\n\t}\n\n\t@Test\n\t@DisplayName(\"equals() and hashCode() with column number not cached by Integer.valueOf()\")\n\tvoid equalsAndHashCodeWithColumnNumberNotCachedByJavaLangIntegerDotValueOf() {\n\t\tvar same = FilePosition.from(42, 99999);\n\t\tvar sameSame = FilePosition.from(42, 99999);\n\t\tvar different = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(same, sameSame, different);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/FileSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link FileSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass FileSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new FileSelector(\"/example/foo.txt\", null);\n\t\tvar selector2 = new FileSelector(\"/example/foo.txt\", null);\n\t\tvar selector3 = new FileSelector(\"/example/bar.txt\", null);\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeWithFilePosition() {\n\t\tvar selector1 = new FileSelector(\"/example/foo.txt\", FilePosition.from(1));\n\t\tvar selector2 = new FileSelector(\"/example/foo.txt\", FilePosition.from(1));\n\t\tvar selector3 = new FileSelector(\"/example/bar.txt\", FilePosition.from(2));\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/IterationSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectIteration;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.lang.reflect.Array;\nimport java.util.Optional;\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.extension.AnnotatedElementContext;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.aggregator.AggregateWith;\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor;\nimport org.junit.jupiter.params.aggregator.ArgumentsAggregationException;\nimport org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\n\nclass IterationSelectorTests {\n\n\t@ParameterizedTest\n\t@CsvSource(delimiter = '|', textBlock = \"\"\"\n\t\t\t\t1         | 1\n\t\t\t\t1,2       | 1 | 2\n\t\t\t\t1..3      | 1 | 2 | 3\n\t\t\t\t1,3       | 1 | 3\n\t\t\t\t1..3,5..7 | 1 | 2 | 3 | 5 | 6 | 7\n\t\t\t\"\"\")\n\tvoid collapsesRangesWhenConvertingToIdentifier(String expected,\n\t\t\t@AggregateWith(VarargsAggregator.class) int... iterationIndices) {\n\t\tvar parent = \"parent:value\";\n\t\tvar parentSelector = selectorWithIdentifier(parent);\n\t\tvar selector = selectIteration(parentSelector, iterationIndices);\n\n\t\tvar identifier = selector.toIdentifier().orElseThrow();\n\t\tassertEquals(\"iteration:%s[%s]\".formatted(parent, expected), identifier.toString());\n\n\t\tDiscoverySelectorIdentifierParser.Context context = mock();\n\t\twhen(context.parse(parent)).thenAnswer(__ -> Optional.of(parentSelector));\n\t\tassertEquals(selector, new IterationSelector.IdentifierParser().parse(identifier, context).orElseThrow());\n\t}\n\n\tprivate static DiscoverySelector selectorWithIdentifier(String identifier) {\n\t\tDiscoverySelector parent = mock();\n\t\twhen(parent.toIdentifier()) //\n\t\t\t\t.thenReturn(Optional.of(DiscoverySelectorIdentifier.parse(identifier)));\n\t\treturn parent;\n\t}\n\n\tprivate static class VarargsAggregator extends SimpleArgumentsAggregator {\n\t\t@Override\n\t\tprotected Object aggregateArguments(ArgumentsAccessor accessor, Class<?> targetType,\n\t\t\t\tAnnotatedElementContext context, int parameterIndex) throws ArgumentsAggregationException {\n\n\t\t\tPreconditions.condition(targetType.isArray(), () -> \"must be an array type, but was \" + targetType);\n\t\t\tClass<?> componentType = targetType.getComponentType();\n\t\t\tIntStream indices = IntStream.range(parameterIndex, accessor.size());\n\t\t\tif (componentType == int.class) {\n\t\t\t\treturn indices.map(accessor::getInteger).toArray();\n\t\t\t}\n\t\t\treturn indices.mapToObj(index -> accessor.get(index, componentType)).toArray(\n\t\t\t\tsize -> (Object[]) Array.newInstance(componentType, size));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/MethodSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatExceptionOfType;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.JUnitException;\n\n/**\n * Unit tests for {@link MethodSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass MethodSelectorTests {\n\n\tprivate static final String TEST_CASE_NAME = TestCase.class.getName();\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new MethodSelector(null, TEST_CASE_NAME, \"method\", \"int, boolean\");\n\t\tvar selector2 = new MethodSelector(null, TEST_CASE_NAME, \"method\", \"int, boolean\");\n\t\tvar selector3 = new MethodSelector(TestCase.class, \"method\", \"int, boolean\");\n\t\tvar selector4 = new MethodSelector(TestCase.class, \"method\", int.class, boolean.class);\n\n\t\tStream.of(selector2, selector3, selector4).forEach(selector -> {\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, \"method\", \"int\"));\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, \"method\", \"\"));\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, \"X\", \"int, boolean\"));\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, TEST_CASE_NAME, \"X\", \"\"));\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, \"X\", \"method\", \"int, boolean\"));\n\t\t\tassertEqualsAndHashCode(selector1, selector, new MethodSelector(null, \"X\", \"method\", \"\"));\n\t\t});\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadJavaClass() {\n\t\tvar selector = new MethodSelector(null, \"org.example.BogusClass\", \"method\", \"int, boolean\");\n\n\t\tassertThat(selector.getClassName()).isEqualTo(\"org.example.BogusClass\");\n\t\tassertThat(selector.getMethodName()).isEqualTo(\"method\");\n\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"int, boolean\");\n\n\t\tassertPreconditionViolationFor(selector::getJavaClass)//\n\t\t\t\t.withMessage(\"Could not load class with name: org.example.BogusClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadClassForParameterType() {\n\t\tvar selector = new MethodSelector(null, TEST_CASE_NAME, \"method\", \"int[], org.example.Bogus\");\n\n\t\tassertThat(selector.getClassName()).isEqualTo(TEST_CASE_NAME);\n\t\tassertThat(selector.getMethodName()).isEqualTo(\"method\");\n\t\tassertThat(selector.getParameterTypeNames()).isEqualTo(\"int[], org.example.Bogus\");\n\n\t\tassertThatExceptionOfType(JUnitException.class)//\n\t\t\t\t.isThrownBy(selector::getJavaMethod)//\n\t\t\t\t.withMessage(\"Failed to load parameter type [org.example.Bogus] for method [method] in class [%s].\",\n\t\t\t\t\tTEST_CASE_NAME)//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid usesClassClassLoader() {\n\t\tvar selector = new MethodSelector(getClass(), \"usesClassClassLoader\", \"\");\n\n\t\tassertThat(selector.getClassLoader()).isNotNull().isSameAs(getClass().getClassLoader());\n\t}\n\n\tprivate static class TestCase {\n\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid method(int num, boolean flag) {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/ModuleSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport java.util.logging.Logger;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ModuleSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass ModuleSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new ModuleSelector(\"foo-api\");\n\t\tvar selector2 = new ModuleSelector(\"foo-api\");\n\t\tvar selector3 = new ModuleSelector(\"bar-impl\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForModuleInstances() {\n\t\tvar selector1 = new ModuleSelector(Object.class.getModule()); // java.base\n\t\tvar selector2 = new ModuleSelector(Object.class.getModule()); // java.base\n\t\tvar selector3 = new ModuleSelector(Logger.class.getModule()); // java.logging\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedClassSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link NestedClassSelector}.\n *\n * @since 1.6\n * @see DiscoverySelectorsTests\n */\nclass NestedClassSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new NestedClassSelector(null, List.of(\"org.example.EnclosingTestClass\"),\n\t\t\t\"org.example.NestedTestClass\");\n\t\tvar selector2 = new NestedClassSelector(null, List.of(\"org.example.EnclosingTestClass\"),\n\t\t\t\"org.example.NestedTestClass\");\n\t\tvar selector3 = new NestedClassSelector(null, List.of(\"org.example.X\"), \"org.example.Y\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadEnclosingClasses() {\n\t\tvar selector = new NestedClassSelector(null, List.of(\"org.example.EnclosingTestClass\"),\n\t\t\t\"org.example.NestedTestClass\");\n\n\t\tassertPreconditionViolationFor(selector::getEnclosingClasses)//\n\t\t\t\t.withMessage(\"Could not load class with name: org.example.EnclosingTestClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadNestedClass() {\n\t\tvar selector = new NestedClassSelector(null, List.of(\"org.example.EnclosingTestClass\"),\n\t\t\t\"org.example.NestedTestClass\");\n\n\t\tassertPreconditionViolationFor(selector::getNestedClass)//\n\t\t\t\t.withMessage(\"Could not load class with name: org.example.NestedTestClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid usesClassClassLoader() {\n\t\tvar selector = new NestedClassSelector(List.of(getClass()), NestedTestCase.class);\n\n\t\tassertThat(selector.getClassLoader()).isNotNull().isSameAs(getClass().getClassLoader());\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tclass NestedTestCase {\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/NestedMethodSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link NestedMethodSelector}.\n *\n * @since 1.6\n * @see DiscoverySelectorsTests\n */\nclass NestedMethodSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\",\n\t\t\t\"int, boolean\");\n\t\tvar selector2 = new NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\",\n\t\t\t\"int, boolean\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"X\"), \"NestedTestClass\", \"method\", \"int, boolean\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"X\"), \"NestedTestClass\", \"method\", \"\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\", \"int\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\", \"\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"X\", \"int, boolean\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"X\", \"\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"X\", \"method\", \"int, boolean\"));\n\t\tassertEqualsAndHashCode(selector1, selector2,\n\t\t\tnew NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"X\", \"method\", \"\"));\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadEnclosingClass() {\n\t\tvar selector = new NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\",\n\t\t\t\"int, boolean\");\n\n\t\tassertPreconditionViolationFor(selector::getEnclosingClasses)//\n\t\t\t\t.withMessage(\"Could not load class with name: EnclosingClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid preservesOriginalExceptionWhenTryingToLoadNestedClass() {\n\t\tvar selector = new NestedMethodSelector(null, List.of(\"EnclosingClass\"), \"NestedTestClass\", \"method\",\n\t\t\t\"int, boolean\");\n\n\t\tassertPreconditionViolationFor(selector::getNestedClass)//\n\t\t\t\t.withMessage(\"Could not load class with name: NestedTestClass\")//\n\t\t\t\t.withCauseInstanceOf(ClassNotFoundException.class);\n\t}\n\n\t@Test\n\tvoid usesClassClassLoader() {\n\t\tvar selector = new NestedMethodSelector(List.of(getClass()), NestedTestCase.class, \"method\", \"\");\n\n\t\tassertThat(selector.getClassLoader()).isNotNull().isSameAs(getClass().getClassLoader());\n\t}\n\n\t@SuppressWarnings(\"InnerClassMayBeStatic\")\n\tclass NestedTestCase {\n\t\tvoid method() {\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageNameFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationContainsNoNullElementsFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.0\n */\nclass PackageNameFilterTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid includePackageChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"packageNames array\",\n\t\t\t() -> PackageNameFilter.includePackageNames((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"packageNames array\",\n\t\t\t() -> PackageNameFilter.includePackageNames(new String[0]));\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"packageNames array\",\n\t\t\t() -> PackageNameFilter.includePackageNames(new String[] { null }));\n\t}\n\n\t@Test\n\tvoid includePackageWithMultiplePackages() {\n\t\tvar includedPackage1 = \"java.lang\";\n\t\tvar includedPackage2 = \"java.util\";\n\t\tvar filter = PackageNameFilter.includePackageNames(includedPackage1, includedPackage2);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"IncludePackageNameFilter that includes packages whose names are either equal to or start with one of the following: '\"\n\t\t\t\t\t+ includedPackage1 + \"' OR '\" + includedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.lang.String\"));\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Package name [java.lang.String] matches included name: '\" + includedPackage1 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.Collection\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.util.Collection\"));\n\t\tassertThat(filter.apply(\"java.util.Collection\").getReason()).contains(\n\t\t\t\"Package name [java.util.Collection] matches included name: '\" + includedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.function.Consumer\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.util.function.Consumer\"));\n\t\tassertThat(filter.apply(\"java.util.function.Consumer\").getReason()).contains(\n\t\t\t\"Package name [java.util.function.Consumer] matches included name: '\" + includedPackage2 + \"'\");\n\n\t\tassertFalse(filter.apply(\"java.time.Instant\").included());\n\t\tassertFalse(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Package name [java.time.Instant] does not match any included names: '\" + includedPackage1 + \"' OR '\"\n\t\t\t\t\t+ includedPackage2 + \"'\");\n\n\t\tassertFalse(filter.apply(\"java.language.Test\").included());\n\t\tassertFalse(filter.toPredicate().test(\"java.language.Test\"));\n\t\tassertThat(filter.apply(\"java.language.Test\").getReason()).contains(\n\t\t\t\"Package name [java.language.Test] does not match any included names: '\" + includedPackage1 + \"' OR '\"\n\t\t\t\t\t+ includedPackage2 + \"'\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid excludePackageChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"packageNames\",\n\t\t\t() -> PackageNameFilter.excludePackageNames((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"packageNames\",\n\t\t\t() -> PackageNameFilter.excludePackageNames(new String[0]));\n\t\tassertPreconditionViolationFor(() -> PackageNameFilter.excludePackageNames(new String[] { null }))//\n\t\t\t\t.withMessage(\"packageNames must not contain null elements\");\n\t}\n\n\t@Test\n\tvoid excludePackageWithMultiplePackages() {\n\t\tvar excludedPackage1 = \"java.lang\";\n\t\tvar excludedPackage2 = \"java.util\";\n\t\tvar filter = PackageNameFilter.excludePackageNames(excludedPackage1, excludedPackage2);\n\n\t\tassertThat(filter).hasToString(\n\t\t\t\"ExcludePackageNameFilter that excludes packages whose names are either equal to or start with one of the following: '\"\n\t\t\t\t\t+ excludedPackage1 + \"' OR '\" + excludedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.lang.String\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.lang.String\"));\n\t\tassertThat(filter.apply(\"java.lang.String\").getReason()).contains(\n\t\t\t\"Package name [java.lang.String] matches excluded name: '\" + excludedPackage1 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.Collection\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.util.Collection\"));\n\t\tassertThat(filter.apply(\"java.util.Collection\").getReason()).contains(\n\t\t\t\"Package name [java.util.Collection] matches excluded name: '\" + excludedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.util.function.Consumer\").excluded());\n\t\tassertFalse(filter.toPredicate().test(\"java.util.function.Consumer\"));\n\t\tassertThat(filter.apply(\"java.util.function.Consumer\").getReason()).contains(\n\t\t\t\"Package name [java.util.function.Consumer] matches excluded name: '\" + excludedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.time.Instant\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.time.Instant\"));\n\t\tassertThat(filter.apply(\"java.time.Instant\").getReason()).contains(\n\t\t\t\"Package name [java.time.Instant] does not match any excluded names: '\" + excludedPackage1 + \"' OR '\"\n\t\t\t\t\t+ excludedPackage2 + \"'\");\n\n\t\tassertTrue(filter.apply(\"java.language.Test\").included());\n\t\tassertTrue(filter.toPredicate().test(\"java.language.Test\"));\n\t\tassertThat(filter.apply(\"java.language.Test\").getReason()).contains(\n\t\t\t\"Package name [java.language.Test] does not match any excluded names: '\" + excludedPackage1 + \"' OR '\"\n\t\t\t\t\t+ excludedPackage2 + \"'\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/PackageSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link PackageSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass PackageSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar selector1 = new PackageSelector(\"org.example.foo\");\n\t\tvar selector2 = new PackageSelector(\"org.example.foo\");\n\t\tvar selector3 = new PackageSelector(\"org.example.bar\");\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/UniqueIdSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Unit tests for {@link UniqueIdSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass UniqueIdSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar testEngine = UniqueId.forEngine(\"test-engine\");\n\t\tvar selector1 = new UniqueIdSelector(testEngine.append(\"test-class\", \"org.example.TestClass\"));\n\t\tvar selector2 = new UniqueIdSelector(testEngine.append(\"test-class\", \"org.example.TestClass\"));\n\t\tvar selector3 = new UniqueIdSelector(testEngine.append(\"test-class\", \"org.example.FooBar\"));\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/discovery/UriSelectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.discovery;\n\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport java.net.URI;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link UriSelector}.\n *\n * @since 1.3\n * @see DiscoverySelectorsTests\n */\nclass UriSelectorTests {\n\n\t@Test\n\tvoid equalsAndHashCode() throws Exception {\n\t\tvar selector1 = new UriSelector(new URI(\"https://junit.org\"));\n\t\tvar selector2 = new UriSelector(new URI(\"https://junit.org\"));\n\t\tvar selector3 = new UriSelector(new URI(\"https://example.org\"));\n\n\t\tassertEqualsAndHashCode(selector1, selector2, selector3);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/config/PrefixedConfigurationParametersTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.config;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\n/**\n * Unit tests for {@link PrefixedConfigurationParameters}.\n *\n * @since 1.3\n */\n@ExtendWith(MockitoExtension.class)\nclass PrefixedConfigurationParametersTests {\n\n\t@Mock\n\tprivate ConfigurationParameters delegate;\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> new PrefixedConfigurationParameters(null, \"example.\"));\n\t\tassertPreconditionViolationFor(() -> new PrefixedConfigurationParameters(delegate, null));\n\t\tassertPreconditionViolationFor(() -> new PrefixedConfigurationParameters(delegate, \"\"));\n\t\tassertPreconditionViolationFor(() -> new PrefixedConfigurationParameters(delegate, \"    \"));\n\t}\n\n\t@Test\n\tvoid delegatesGetCalls() {\n\t\twhen(delegate.get(any())).thenReturn(Optional.of(\"result\"));\n\t\tvar parameters = new PrefixedConfigurationParameters(delegate, \"foo.bar.\");\n\n\t\tassertThat(parameters.get(\"qux\")).contains(\"result\");\n\n\t\tverify(delegate).get(\"foo.bar.qux\");\n\t}\n\n\t@Test\n\tvoid delegatesGetBooleanCalls() {\n\t\twhen(delegate.getBoolean(any())).thenReturn(Optional.of(true));\n\t\tvar parameters = new PrefixedConfigurationParameters(delegate, \"foo.bar.\");\n\n\t\tassertThat(parameters.getBoolean(\"qux\")).contains(true);\n\n\t\tverify(delegate).getBoolean(\"foo.bar.qux\");\n\t}\n\n\t@Test\n\tvoid delegatesGetWithTransformerCalls() {\n\t\twhen(delegate.get(any(), any())).thenReturn(Optional.of(\"QUX\"));\n\t\tvar parameters = new PrefixedConfigurationParameters(delegate, \"foo.bar.\");\n\n\t\tFunction<String, String> transformer = String::toUpperCase;\n\t\tassertThat(parameters.get(\"qux\", transformer)).contains(\"QUX\");\n\n\t\tverify(delegate).get(\"foo.bar.qux\", transformer);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * Unit tests for {@link AbstractTestDescriptor} and {@link EngineDescriptor}.\n *\n * @since 1.0\n */\nclass AbstractTestDescriptorTests implements TestDescriptorOrderChildrenTests {\n\n\tprivate EngineDescriptor engineDescriptor;\n\tprivate GroupDescriptor group1;\n\tprivate GroupDescriptor group11;\n\tprivate LeafDescriptor leaf111;\n\n\t@BeforeEach\n\tvoid initTree() {\n\t\tengineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"testEngine\"), \"testEngine\");\n\t\tgroup1 = new GroupDescriptor(UniqueId.root(\"group\", \"group1\"));\n\t\tengineDescriptor.addChild(group1);\n\t\tvar group2 = new GroupDescriptor(UniqueId.root(\"group\", \"group2\"));\n\t\tengineDescriptor.addChild(group2);\n\t\tgroup11 = new GroupDescriptor(UniqueId.root(\"group\", \"group1-1\"));\n\t\tgroup1.addChild(group11);\n\n\t\tgroup1.addChild(new LeafDescriptor(UniqueId.root(\"leaf\", \"leaf1-1\")));\n\t\tgroup1.addChild(new LeafDescriptor(UniqueId.root(\"leaf\", \"leaf1-2\")));\n\n\t\tgroup2.addChild(new LeafDescriptor(UniqueId.root(\"leaf\", \"leaf2-1\")));\n\n\t\tleaf111 = new LeafDescriptor(UniqueId.root(\"leaf\", \"leaf11-1\"));\n\t\tgroup11.addChild(leaf111);\n\t}\n\n\t@Override\n\tpublic TestDescriptor createEmptyTestDescriptor() {\n\t\treturn new GroupDescriptor(UniqueId.root(\"group\", \"1\"));\n\t}\n\n\t@Test\n\tvoid removeRootFromHierarchyFails() {\n\t\tvar e = assertThrows(JUnitException.class, () -> engineDescriptor.removeFromHierarchy());\n\t\tassertTrue(e.toString().contains(\"cannot remove the root of a hierarchy\"));\n\t}\n\n\t@Test\n\tvoid removeFromHierarchyClearsParentFromAllChildren() {\n\t\tvar group = engineDescriptor.getChildren().iterator().next();\n\t\tassertSame(engineDescriptor, group.getParent().orElseThrow(Error::new));\n\t\tassertTrue(group.getChildren().stream().allMatch(d -> d.getParent().orElseThrow(Error::new) == group));\n\n\t\tvar formerChildren = group.getChildren();\n\t\tgroup.removeFromHierarchy();\n\n\t\tassertFalse(group.getParent().isPresent());\n\t\tassertTrue(group.getChildren().isEmpty());\n\t\tassertTrue(formerChildren.stream().noneMatch(d -> d.getParent().isPresent()));\n\t}\n\n\t@Test\n\tvoid setParentToOtherInstance() {\n\t\tTestDescriptor newEngine = new EngineDescriptor(UniqueId.forEngine(\"newEngine\"), \"newEngine\");\n\t\tvar group = engineDescriptor.getChildren().iterator().next();\n\t\tassertSame(engineDescriptor, group.getParent().orElseThrow(Error::new));\n\t\tgroup.setParent(newEngine);\n\t\tassertSame(newEngine, group.getParent().orElseThrow(Error::new));\n\t}\n\n\t@Test\n\tvoid setParentToNull() {\n\t\tvar group = engineDescriptor.getChildren().iterator().next();\n\t\tassertTrue(group.getParent().isPresent());\n\t\tgroup.setParent(null);\n\t\tassertFalse(group.getParent().isPresent());\n\t}\n\n\t@Test\n\tvoid visitAllNodes() {\n\t\tList<TestDescriptor> visited = new ArrayList<>();\n\t\tengineDescriptor.accept(visited::add);\n\n\t\tassertEquals(8, visited.size());\n\t}\n\n\t@Test\n\tvoid pruneLeaf() {\n\t\tTestDescriptor.Visitor visitor = descriptor -> {\n\t\t\tif (descriptor.getUniqueId().equals(UniqueId.root(\"leaf\", \"leaf1-1\"))) {\n\t\t\t\tdescriptor.removeFromHierarchy();\n\t\t\t}\n\t\t};\n\t\tengineDescriptor.accept(visitor);\n\n\t\tList<UniqueId> visited = new ArrayList<>();\n\t\tengineDescriptor.accept(descriptor -> visited.add(descriptor.getUniqueId()));\n\n\t\tassertEquals(7, visited.size());\n\t\tassertTrue(visited.contains(UniqueId.root(\"group\", \"group1\")));\n\t\tassertFalse(visited.contains(UniqueId.root(\"leaf\", \"leaf1-1\")));\n\t}\n\n\t@Test\n\tvoid pruneGroup() {\n\t\tfinal var countVisited = new AtomicInteger();\n\t\tTestDescriptor.Visitor visitor = descriptor -> {\n\t\t\tif (descriptor.getUniqueId().equals(UniqueId.root(\"group\", \"group1\"))) {\n\t\t\t\tdescriptor.removeFromHierarchy();\n\t\t\t}\n\t\t\tcountVisited.incrementAndGet();\n\t\t};\n\t\tengineDescriptor.accept(visitor);\n\n\t\tassertEquals(4, countVisited.get(), \"Children of pruned element are not visited\");\n\n\t\tList<UniqueId> visited = new ArrayList<>();\n\t\tengineDescriptor.accept(descriptor -> visited.add(descriptor.getUniqueId()));\n\n\t\tassertEquals(3, visited.size());\n\t\tassertFalse(visited.contains(UniqueId.root(\"group\", \"group1\")));\n\t}\n\n\t@Test\n\tvoid getAncestors() {\n\t\tassertThat(getAncestorsUniqueIds(engineDescriptor)).isEmpty();\n\n\t\tassertThat(getAncestorsUniqueIds(group1)).containsExactly( //\n\t\t\tUniqueId.forEngine(\"testEngine\"));\n\n\t\tassertThat(getAncestorsUniqueIds(group11)).containsExactly( //\n\t\t\tUniqueId.root(\"group\", \"group1\"), //\n\t\t\tUniqueId.forEngine(\"testEngine\"));\n\n\t\tassertThat(getAncestorsUniqueIds(leaf111)).containsExactly( //\n\t\t\tUniqueId.root(\"group\", \"group1-1\"), //\n\t\t\tUniqueId.root(\"group\", \"group1\"), //\n\t\t\tUniqueId.forEngine(\"testEngine\"));\n\t}\n\n\tprivate List<UniqueId> getAncestorsUniqueIds(TestDescriptor descriptor) {\n\t\treturn descriptor.getAncestors().stream().map(TestDescriptor::getUniqueId).toList();\n\t}\n\n\t@ParameterizedTest(name = \"{0} \\u27A1 {1}\")\n\t// NOTE: \"\\uFFFD\" is the Unicode replacement character: �\n\t@CsvSource(delimiterString = \"->\", textBlock = \"\"\"\n\t\t\t'carriage \\r return'           -> 'carriage <CR> return'\n\t\t\t'line \\n feed'                 -> 'line <LF> feed'\n\t\t\t'form \\f feed'                 -> 'form \\uFFFD feed'\n\t\t\t'back \\b space'                -> 'back \\uFFFD space'\n\t\t\t'tab \\t tab'                   -> 'tab \\uFFFD tab'\n\t\t\t# Latin-1\n\t\t\t'üñåé'                         -> 'üñåé'\n\t\t\t# \"hello\" in Japanese\n\t\t\t'こんにちは'                     -> 'こんにちは'\n\t\t\t# 'hello world' in Thai\n\t\t\t'สวัสดีชาวโลก'                   -> 'สวัสดีชาวโลก'\n\t\t\t# bell sound/character\n\t\t\t'ding \\u0007 dong'             -> 'ding \\uFFFD dong'\n\t\t\t'Munch 😱 emoji'               -> 'Munch 😱 emoji'\n\t\t\t'Zero\\u200BWidth\\u200BSpaces'  -> 'Zero\\u200BWidth\\u200BSpaces'\n\t\t\t\"\"\")\n\tvoid specialCharactersInDisplayNamesAreEscaped(String input, String expected) {\n\t\tassertThat(new DemoDescriptor(input).getDisplayName()).isEqualTo(expected);\n\t}\n\n}\n\nclass GroupDescriptor extends AbstractTestDescriptor {\n\n\tGroupDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, \"group: \" + uniqueId);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n}\n\nclass LeafDescriptor extends AbstractTestDescriptor {\n\n\tLeafDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, \"leaf: \" + uniqueId);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.TEST;\n\t}\n\n}\n\nclass DemoDescriptor extends AbstractTestDescriptor {\n\n\tDemoDescriptor(String displayName) {\n\t\tsuper(mock(), displayName);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/AbstractTestSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.Serializable;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.platform.engine.TestSource;\n\n/**\n * Abstract base class for unit tests involving {@link TestSource TestSources}\n * and {@link FilePosition FilePositions}.\n *\n * @since 1.0\n */\nabstract class AbstractTestSourceTests {\n\n\tabstract Stream<? extends Serializable> createSerializableInstances() throws Exception;\n\n\t@TestFactory\n\tStream<DynamicTest> assertToString() throws Exception {\n\t\treturn createSerializableInstances() //\n\t\t\t\t.map(instance -> dynamicTest(instance.toString(), () -> assertToString(instance)));\n\t}\n\n\tprivate void assertToString(Object instance) {\n\t\tassertNotNull(instance);\n\t\tassertTrue(instance.toString().startsWith(instance.getClass().getSimpleName()));\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> assertSerializable() throws Exception {\n\t\treturn createSerializableInstances() //\n\t\t\t\t.map(instance -> dynamicTest(instance.toString(), () -> assertSerializable(instance)));\n\t}\n\n\tprivate <T extends Serializable> void assertSerializable(T instance) {\n\t\ttry {\n\t\t\tClass<?> type = instance.getClass();\n\t\t\tvar serialized = serialize(instance);\n\t\t\tvar deserialized = deserialize(serialized);\n\n\t\t\tassertTrue(type.isAssignableFrom(deserialized.getClass()));\n\t\t\tassertEquals(instance, deserialized);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tfail(\"assertSerializable failed: \" + instance, e);\n\t\t}\n\t}\n\n\tprivate byte[] serialize(Object obj) throws Exception {\n\t\tvar b = new ByteArrayOutputStream();\n\t\tvar o = new ObjectOutputStream(b);\n\t\to.writeObject(obj);\n\t\treturn b.toByteArray();\n\t}\n\n\tprivate Object deserialize(byte[] bytes) throws Exception {\n\t\tvar b = new ByteArrayInputStream(bytes);\n\t\tvar o = new ObjectInputStream(b);\n\t\treturn o.readObject();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClassSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.Serializable;\nimport java.net.URI;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClassSource}.\n *\n * @since 1.0\n */\nclass ClassSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<Serializable> createSerializableInstances() {\n\t\treturn Stream.of( //\n\t\t\tClassSource.from(\"class.source\"), //\n\t\t\tClassSource.from(\"class.and.position\", FilePosition.from(1, 2)), //\n\t\t\tClassSource.from(getClass()), //\n\t\t\tClassSource.from(getClass(), FilePosition.from(1, 2)) //\n\t\t);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> ClassSource.from((String) null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from(\"    \"));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from((String) null, null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from(\"    \", null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from((Class<?>) null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from((Class<?>) null, null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from((URI) null));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from(new URI(\"badscheme:/com.foo.Bar\")));\n\t\tassertPreconditionViolationFor(() -> ClassSource.from(new URI(\"class:?line=1\")));\n\t}\n\n\t@Test\n\tvoid classSourceFromName() {\n\t\tvar testClassName = \"com.unknown.mypackage.ClassByName\";\n\t\tvar source = ClassSource.from(testClassName);\n\n\t\tassertThat(source.getClassName()).isEqualTo(testClassName);\n\t\tassertThat(source.getPosition()).isEmpty();\n\n\t\tassertPreconditionViolationFor(source::getJavaClass)//\n\t\t\t\t.withMessage(\"Could not load class with name: \" + testClassName);\n\t}\n\n\t@Test\n\tvoid classSourceFromNameAndFilePosition() {\n\t\tvar testClassName = \"com.unknown.mypackage.ClassByName\";\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = ClassSource.from(testClassName, position);\n\n\t\tassertThat(source.getClassName()).isEqualTo(testClassName);\n\t\tassertThat(source.getPosition()).isNotEmpty();\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid classSourceFromReference() {\n\t\tvar testClass = getClass();\n\t\tvar source = ClassSource.from(testClass);\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(testClass);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid classSourceFromReferenceAndFilePosition() {\n\t\tvar testClass = getClass();\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = ClassSource.from(testClass, position);\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(testClass);\n\t\tassertThat(source.getPosition()).isNotEmpty();\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid classSourceFromUri() throws Exception {\n\t\tvar source = ClassSource.from(new URI(\"class:java.lang.Object\"));\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(Object.class);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid classSourceFromUriWithLineNumber() throws Exception {\n\t\tvar position = FilePosition.from(42);\n\t\tvar source = ClassSource.from(new URI(\"class:java.lang.Object?line=42\"));\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(Object.class);\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid classSourceFromUriWithLineAndColumnNumbers() throws Exception {\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = ClassSource.from(new URI(\"class:java.lang.Object?line=42&foo=bar&column=23\"));\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(Object.class);\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid classSourceFromUriWithEmptyQuery() throws Exception {\n\t\tvar source = ClassSource.from(new URI(\"class:java.lang.Object?\"));\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(Object.class);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid classSourceFromUriWithUnsupportedParametersInQuery() throws Exception {\n\t\tvar source = ClassSource.from(new URI(\"class:java.lang.Object?foo=42&bar\"));\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(Object.class);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForClassSourceFromName() {\n\t\tvar name1 = String.class.getName();\n\t\tvar name2 = Number.class.getName();\n\n\t\tassertEqualsAndHashCode(ClassSource.from(name1), ClassSource.from(name1), ClassSource.from(name2));\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForClassSourceFromNameAndFilePosition() {\n\t\tvar name1 = String.class.getName();\n\t\tvar name2 = Number.class.getName();\n\t\tvar position1 = FilePosition.from(42, 23);\n\t\tvar position2 = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(ClassSource.from(name1, position1), ClassSource.from(name1, position1),\n\t\t\tClassSource.from(name2, position1));\n\t\tassertEqualsAndHashCode(ClassSource.from(name1, position1), ClassSource.from(name1, position1),\n\t\t\tClassSource.from(name1, position2));\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForClassSourceFromReference() {\n\t\tvar class1 = String.class;\n\t\tvar class2 = Number.class;\n\n\t\tassertEqualsAndHashCode(ClassSource.from(class1), ClassSource.from(class1), ClassSource.from(class2));\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForClassSourceFromReferenceAndFilePosition() {\n\t\tvar class1 = String.class;\n\t\tvar class2 = Number.class;\n\t\tvar position1 = FilePosition.from(42, 23);\n\t\tvar position2 = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(ClassSource.from(class1, position1), ClassSource.from(class1, position1),\n\t\t\tClassSource.from(class2, position1));\n\t\tassertEqualsAndHashCode(ClassSource.from(class1, position1), ClassSource.from(class1, position1),\n\t\t\tClassSource.from(class1, position2));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/ClasspathResourceSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.support.descriptor.ClasspathResourceSource.CLASSPATH_SCHEME;\n\nimport java.net.URI;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link ClasspathResourceSource}.\n *\n * @since 1.0\n */\nclass ClasspathResourceSourceTests extends AbstractTestSourceTests {\n\n\tprivate static final String FOO_RESOURCE = \"test/foo.xml\";\n\tprivate static final String BAR_RESOURCE = \"/config/bar.json\";\n\n\tprivate static final URI FOO_RESOURCE_URI = URI.create(CLASSPATH_SCHEME + \":/\" + FOO_RESOURCE);\n\n\t@Override\n\tStream<ClasspathResourceSource> createSerializableInstances() {\n\t\treturn Stream.of(ClasspathResourceSource.from(FOO_RESOURCE));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> ClasspathResourceSource.from((String) null));\n\t\tassertPreconditionViolationFor(() -> ClasspathResourceSource.from(\"\"));\n\t\tassertPreconditionViolationFor(() -> ClasspathResourceSource.from(\"   \"));\n\n\t\tassertPreconditionViolationFor(() -> ClasspathResourceSource.from((URI) null));\n\t\tassertPreconditionViolationFor(() -> ClasspathResourceSource.from(URI.create(\"file:/foo.txt\")));\n\t}\n\n\t@Test\n\tvoid resourceWithoutPosition() {\n\t\tvar source = ClasspathResourceSource.from(FOO_RESOURCE);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid resourceWithLeadingSlashWithoutPosition() {\n\t\tvar source = ClasspathResourceSource.from(\"/\" + FOO_RESOURCE);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid resourceWithPosition() {\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = ClasspathResourceSource.from(FOO_RESOURCE, position);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid resourceFromUriWithoutPosition() {\n\t\tvar source = ClasspathResourceSource.from(FOO_RESOURCE_URI);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid resourceFromUriWithLineNumber() {\n\t\tvar position = FilePosition.from(42);\n\t\tvar uri = URI.create(FOO_RESOURCE_URI + \"?line=42\");\n\t\tvar source = ClasspathResourceSource.from(uri);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid resourceFromUriWithLineAndColumnNumbers() {\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar uri = URI.create(FOO_RESOURCE_URI + \"?line=42&foo=bar&column=23\");\n\t\tvar source = ClasspathResourceSource.from(uri);\n\n\t\tassertThat(source).isNotNull();\n\t\tassertThat(source.getClasspathResourceName()).isEqualTo(FOO_RESOURCE);\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tassertEqualsAndHashCode(ClasspathResourceSource.from(FOO_RESOURCE), ClasspathResourceSource.from(FOO_RESOURCE),\n\t\t\tClasspathResourceSource.from(BAR_RESOURCE));\n\n\t\tvar position = FilePosition.from(42, 23);\n\t\tassertEqualsAndHashCode(ClasspathResourceSource.from(FOO_RESOURCE, position),\n\t\t\tClasspathResourceSource.from(FOO_RESOURCE, position), ClasspathResourceSource.from(BAR_RESOURCE, position));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/CompositeTestSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link CompositeTestSource}.\n *\n * @since 1.0\n */\nclass CompositeTestSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<CompositeTestSource> createSerializableInstances() {\n\t\tvar fileSource = FileSource.from(new File(\"sample.instance\"));\n\t\tvar classSource = ClassSource.from(getClass());\n\t\tvar sources = List.of(fileSource, classSource);\n\t\treturn Stream.of(CompositeTestSource.from(sources));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid createCompositeTestSourceFromNullList() {\n\t\tassertPreconditionViolationFor(() -> CompositeTestSource.from(null));\n\t}\n\n\t@Test\n\tvoid createCompositeTestSourceFromEmptyList() {\n\t\tassertPreconditionViolationFor(() -> CompositeTestSource.from(List.of()));\n\t}\n\n\t@Test\n\tvoid createCompositeTestSourceFromClassAndFileSources() {\n\t\tvar fileSource = FileSource.from(new File(\"example.test\"));\n\t\tvar classSource = ClassSource.from(getClass());\n\t\tvar sources = new ArrayList<>(List.of(fileSource, classSource));\n\t\tvar compositeTestSource = CompositeTestSource.from(sources);\n\n\t\tassertThat(compositeTestSource.getSources()).hasSize(2);\n\t\tassertThat(compositeTestSource.getSources()).contains(fileSource, classSource);\n\n\t\t// Ensure the supplied sources list was defensively copied.\n\t\tsources.remove(1);\n\t\tassertThat(compositeTestSource.getSources()).hasSize(2);\n\n\t\t// Ensure the returned sources list is immutable.\n\t\tassertThrows(UnsupportedOperationException.class, () -> compositeTestSource.getSources().add(fileSource));\n\t}\n\n\t@Test\n\tvoid equalsAndHashCode() {\n\t\tvar sources1 = List.of(ClassSource.from(Number.class));\n\t\tvar sources2 = List.of(ClassSource.from(String.class));\n\t\tassertEqualsAndHashCode(CompositeTestSource.from(sources1), CompositeTestSource.from(sources1),\n\t\t\tCompositeTestSource.from(sources2));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DefaultUriSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.net.URI;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link DefaultUriSource}.\n *\n * @since 1.3\n */\nclass DefaultUriSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<UriSource> createSerializableInstances() {\n\t\treturn Stream.of(new DefaultUriSource(URI.create(\"sample://instance\")));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid nullSourceUriYieldsException() {\n\t\tassertPreconditionViolationFor(() -> new DefaultUriSource(null));\n\t}\n\n\t@Test\n\tvoid getterReturnsSameUriInstanceAsSuppliedToTheConstructor() throws Exception {\n\t\tvar expected = new URI(\"foo.txt\");\n\t\tvar actual = new DefaultUriSource(expected).getUri();\n\t\tassertSame(expected, actual);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCode() throws Exception {\n\t\tvar uri1 = new URI(\"foo.txt\");\n\t\tvar uri2 = new URI(\"bar.txt\");\n\t\tassertEqualsAndHashCode(new DefaultUriSource(uri1), new DefaultUriSource(uri1), new DefaultUriSource(uri2));\n\t}\n\n\t@Test\n\tvoid testToString() {\n\t\tvar actual = new DefaultUriSource(URI.create(\"foo.txt\")).toString();\n\t\tassertEquals(\"DefaultUriSource [uri = foo.txt]\", actual);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoClassTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static java.util.stream.Collectors.toCollection;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 1.0\n */\npublic class DemoClassTestDescriptor extends AbstractTestDescriptor {\n\n\tprivate final Class<?> testClass;\n\n\tpublic DemoClassTestDescriptor(UniqueId uniqueId, Class<?> testClass) {\n\t\tsuper(uniqueId, Preconditions.notNull(testClass, \"Class must not be null\").getSimpleName(),\n\t\t\tClassSource.from(testClass));\n\t\tthis.testClass = testClass;\n\t}\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\treturn findRepeatableAnnotations(this.testClass, Tag.class).stream() //\n\t\t\t\t.map(Tag::value) //\n\t\t\t\t.filter(TestTag::isValid) //\n\t\t\t\t.map(TestTag::create) //\n\t\t\t\t.collect(toCollection(LinkedHashSet::new));\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.CONTAINER;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/DemoMethodTestDescriptor.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static java.util.stream.Collectors.toCollection;\nimport static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations;\n\nimport java.lang.reflect.Method;\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.platform.commons.util.ClassUtils;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\n/**\n * @since 1.0\n */\npublic class DemoMethodTestDescriptor extends AbstractTestDescriptor {\n\n\tprivate final Method testMethod;\n\n\tpublic DemoMethodTestDescriptor(UniqueId uniqueId, Method testMethod) {\n\t\tsuper(uniqueId,\n\t\t\t\"%s(%s)\".formatted(Preconditions.notNull(testMethod, \"Method must not be null\").getName(),\n\t\t\t\tClassUtils.nullSafeToString(Class::getSimpleName, testMethod.getParameterTypes())),\n\t\t\tMethodSource.from(testMethod));\n\n\t\tthis.testMethod = testMethod;\n\t}\n\n\t@Override\n\tpublic Set<TestTag> getTags() {\n\t\tSet<TestTag> methodTags = findRepeatableAnnotations(this.testMethod, Tag.class).stream() //\n\t\t\t\t.map(Tag::value) //\n\t\t\t\t.filter(TestTag::isValid) //\n\t\t\t\t.map(TestTag::create) //\n\t\t\t\t.collect(toCollection(LinkedHashSet::new));\n\n\t\tgetParent().ifPresent(parentDescriptor -> methodTags.addAll(parentDescriptor.getTags()));\n\t\treturn methodTags;\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.TEST;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FilePositionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\n/**\n * Unit tests for {@link FilePosition}.\n *\n * @since 1.0\n */\n@DisplayName(\"FilePosition unit tests\")\nclass FilePositionTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<FilePosition> createSerializableInstances() {\n\t\treturn Stream.of(FilePosition.from(42, 99));\n\t}\n\n\t@Test\n\t@DisplayName(\"factory method preconditions\")\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> FilePosition.from(-1));\n\t\tassertPreconditionViolationFor(() -> FilePosition.from(0, -1));\n\t}\n\n\t@Test\n\t@DisplayName(\"create FilePosition from factory method with line number\")\n\tvoid filePositionFromLine() {\n\t\tvar filePosition = FilePosition.from(42);\n\n\t\tassertThat(filePosition.getLine()).isEqualTo(42);\n\t\tassertThat(filePosition.getColumn()).isEmpty();\n\t}\n\n\t@Test\n\t@DisplayName(\"create FilePosition from factory method with line number and column number\")\n\tvoid filePositionFromLineAndColumn() {\n\t\tvar filePosition = FilePosition.from(42, 99);\n\n\t\tassertThat(filePosition.getLine()).isEqualTo(42);\n\t\tassertThat(filePosition.getColumn()).contains(99);\n\t}\n\n\t/**\n\t * @since 1.3\n\t */\n\t@ParameterizedTest\n\t@MethodSource\n\tvoid filePositionFromQuery(String query, int expectedLine, int expectedColumn) {\n\t\tvar optionalFilePosition = FilePosition.fromQuery(query);\n\n\t\tif (optionalFilePosition.isPresent()) {\n\t\t\tvar filePosition = optionalFilePosition.get();\n\n\t\t\tassertThat(filePosition.getLine()).isEqualTo(expectedLine);\n\t\t\tassertThat(filePosition.getColumn().orElse(-1)).isEqualTo(expectedColumn);\n\t\t}\n\t\telse {\n\t\t\tassertEquals(-1, expectedColumn);\n\t\t\tassertEquals(-1, expectedLine);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tstatic Stream<Arguments> filePositionFromQuery() {\n\t\treturn Stream.of( //\n\t\t\targuments(null, -1, -1), //\n\t\t\targuments(\"?!\", -1, -1), //\n\t\t\targuments(\"line=ZZ\", -1, -1), //\n\t\t\targuments(\"line=42\", 42, -1), //\n\t\t\targuments(\"line=42&column=99\", 42, 99), //\n\t\t\targuments(\"line=42&column=ZZ\", 42, -1), //\n\t\t\targuments(\"line=42&abc=xyz&column=99\", 42, 99), //\n\t\t\targuments(\"1=3&foo=X&line=42&abc=xyz&column=99&enigma=393939\", 42, 99), //\n\t\t\t// First one wins:\n\t\t\targuments(\"line=42&line=555\", 42, -1), //\n\t\t\targuments(\"line=42&line=555&column=99&column=555\", 42, 99) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"equals() and hashCode() with column number cached by Integer.valueOf()\")\n\tvoid equalsAndHashCode() {\n\t\tvar same = FilePosition.from(42, 99);\n\t\tvar sameSame = FilePosition.from(42, 99);\n\t\tvar different = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(same, sameSame, different);\n\t}\n\n\t@Test\n\t@DisplayName(\"equals() and hashCode() with column number not cached by Integer.valueOf()\")\n\tvoid equalsAndHashCodeWithColumnNumberNotCachedByJavaLangIntegerDotValueOf() {\n\t\tvar same = FilePosition.from(42, 99999);\n\t\tvar sameSame = FilePosition.from(42, 99999);\n\t\tvar different = FilePosition.from(1, 2);\n\n\t\tassertEqualsAndHashCode(same, sameSame, different);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/FileSystemSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.File;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link FileSource} and {@link DirectorySource}.\n *\n * @since 1.0\n */\nclass FileSystemSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<FileSource> createSerializableInstances() {\n\t\treturn Stream.of( //\n\t\t\tFileSource.from(new File(\"file.source\")), //\n\t\t\tFileSource.from(new File(\"file.and.position\"), FilePosition.from(42, 23)));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid nullSourceFileOrDirectoryYieldsException() {\n\t\tassertPreconditionViolationFor(() -> FileSource.from(null));\n\t}\n\n\t@Test\n\tvoid directory() throws Exception {\n\t\tvar canonicalDir = new File(\".\").getCanonicalFile();\n\t\tvar relativeDir = new File(\"..\", canonicalDir.getName());\n\n\t\tvar source = DirectorySource.from(relativeDir);\n\n\t\tassertThat(source.getUri()).isEqualTo(canonicalDir.toURI());\n\t\tassertThat(source.getFile()).isEqualTo(canonicalDir);\n\t}\n\n\t@Test\n\tvoid fileWithoutPosition() throws Exception {\n\t\tvar canonicalDir = new File(\".\").getCanonicalFile();\n\t\tvar relativeDir = new File(\"..\", canonicalDir.getName());\n\t\tvar relativeFile = new File(relativeDir, \"test.txt\");\n\t\tvar canonicalFile = relativeFile.getCanonicalFile();\n\n\t\tvar source = FileSource.from(relativeFile);\n\n\t\tassertThat(source.getUri()).isEqualTo(canonicalFile.toURI());\n\t\tassertThat(source.getFile()).isEqualTo(canonicalFile);\n\t\tassertThat(source.getPosition()).isEmpty();\n\t}\n\n\t@Test\n\tvoid fileWithPosition() {\n\t\tvar file = new File(\"test.txt\");\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = FileSource.from(file, position);\n\n\t\tassertThat(source.getUri()).isEqualTo(file.getAbsoluteFile().toURI());\n\t\tassertThat(source.getFile()).isEqualTo(file.getAbsoluteFile());\n\t\tassertThat(source.getPosition()).hasValue(position);\n\t}\n\n\t@Test\n\tvoid fileReuseWithPosition() {\n\t\tvar file = new File(\"test.txt\");\n\t\tvar position = FilePosition.from(42, 23);\n\t\tvar source = FileSource.from(file);\n\t\tvar sourceWithPosition = source.withPosition(position);\n\n\t\tassertThat(source.getUri()).isEqualTo(file.getAbsoluteFile().toURI());\n\t\tassertThat(source.getFile()).isEqualTo(file.getAbsoluteFile());\n\t\tassertThat(source.getPosition()).isEmpty();\n\n\t\tassertThat(source).isNotSameAs(sourceWithPosition);\n\t\tassertThat(source.getFile()).isSameAs(sourceWithPosition.getFile());\n\t\tassertThat(sourceWithPosition.getPosition()).hasValue(position);\n\n\t\tassertThat(sourceWithPosition.withPosition(null).getPosition()).isEmpty();\n\n\t\tassertThat(source.withPosition(null)).isSameAs(source);\n\t\tassertThat(sourceWithPosition.withPosition(position)).isSameAs(sourceWithPosition);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForFileSource() {\n\t\tvar file1 = new File(\"foo.txt\");\n\t\tvar file2 = new File(\"bar.txt\");\n\t\tassertEqualsAndHashCode(FileSource.from(file1), FileSource.from(file1), FileSource.from(file2));\n\n\t\tvar position = FilePosition.from(42, 23);\n\t\tassertEqualsAndHashCode(FileSource.from(file1, position), FileSource.from(file1, position),\n\t\t\tFileSource.from(file2, position));\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForDirectorySource() {\n\t\tvar dir1 = new File(\".\");\n\t\tvar dir2 = new File(\"..\");\n\t\tassertEqualsAndHashCode(DirectorySource.from(dir1), DirectorySource.from(dir1), DirectorySource.from(dir2));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/MethodSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Method;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link MethodSource}.\n *\n * @since 1.0\n */\nclass MethodSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<Serializable> createSerializableInstances() throws Exception {\n\t\treturn Stream.of( //\n\t\t\tMethodSource.from(getMethod(\"method1\")), //\n\t\t\tMethodSource.from(getMethod(\"method2\")) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid methodSource() throws Exception {\n\t\tvar testMethod = getMethod(\"method1\");\n\t\tvar source = MethodSource.from(testMethod);\n\n\t\tassertThat(source.getClassName()).isEqualTo(getClass().getName());\n\t\tassertThat(source.getMethodName()).isEqualTo(testMethod.getName());\n\t\tassertThat(source.getMethodParameterTypes()).isEqualTo(String.class.getName());\n\t\tassertThat(source.getJavaClass()).isEqualTo(getClass());\n\t\tassertThat(source.getJavaMethod()).isEqualTo(testMethod);\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForMethodSource() throws Exception {\n\t\tvar method1 = getMethod(\"method1\");\n\t\tvar method2 = getMethod(\"method2\");\n\t\tassertEqualsAndHashCode(MethodSource.from(method1), MethodSource.from(method1), MethodSource.from(method2));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid instantiatingWithNullNamesShouldThrowPreconditionViolationException() {\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(\"foo\", null));\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(null, \"foo\"));\n\t}\n\n\t@Test\n\tvoid instantiatingWithEmptyNamesShouldThrowPreconditionViolationException() {\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(\"foo\", \"\"));\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(\"\", \"foo\"));\n\t}\n\n\t@Test\n\tvoid instantiatingWithBlankNamesShouldThrowPreconditionViolationException() {\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(\"foo\", \"  \"));\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(\"  \", \"foo\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid instantiationWithNullMethodShouldThrowPreconditionViolationException() {\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(null));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid instantiationWithNullClassOrMethodShouldThrowPreconditionViolationException() {\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(null, String.class.getDeclaredMethod(\"getBytes\")));\n\t\tassertPreconditionViolationFor(() -> MethodSource.from(String.class, null));\n\t}\n\n\t@Test\n\tvoid instantiationWithClassAndMethodShouldResultInACorrectObject() throws Exception {\n\t\tvar source = MethodSource.from(String.class,\n\t\t\tString.class.getDeclaredMethod(\"lastIndexOf\", String.class, int.class));\n\t\tassertEquals(String.class.getName(), source.getClassName());\n\t\tassertEquals(\"lastIndexOf\", source.getMethodName());\n\t\tassertEquals(\"java.lang.String, int\", source.getMethodParameterTypes());\n\t}\n\n\t@Test\n\tvoid instantiationWithClassAndMethodAsStringAndParamsAsClassVarargsShouldResultInACorrectObject() {\n\t\tvar source = MethodSource.from(String.class.getName(), \"lastIndexOf\", String.class, int.class);\n\t\tassertEquals(String.class.getName(), source.getClassName());\n\t\tassertEquals(\"lastIndexOf\", source.getMethodName());\n\t\tassertEquals(\"java.lang.String, int\", source.getMethodParameterTypes());\n\t}\n\n\t@Test\n\tvoid twoEqualMethodsShouldHaveEqualMethodSourceObjects() {\n\t\tassertEquals(MethodSource.from(\"TestClass1\", \"testMethod1\"), MethodSource.from(\"TestClass1\", \"testMethod1\"));\n\t}\n\n\t@Test\n\tvoid twoUnequalMethodsShouldHaveUnequalMethodSourceObjects() {\n\t\tassertNotEquals(MethodSource.from(\"TestClass1\", \"testMethod1\"), MethodSource.from(\"TestClass2\", \"testMethod1\"));\n\t}\n\n\t@Test\n\tvoid twoUnequalMethodsInTheSameClassShouldHaveUnequalMethodSourceObjects() {\n\t\tassertNotEquals(MethodSource.from(\"TestClass1\", \"testMethod1\"), MethodSource.from(\"TestClass1\", \"testMethod2\"));\n\t}\n\n\t@Test\n\tvoid twoEqualMethodSourceObjectsShouldHaveEqualHashCodes() {\n\t\tassertEquals(MethodSource.from(\"TestClass1\", \"testMethod1\").hashCode(),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod1\").hashCode());\n\t}\n\n\t@Test\n\tvoid twoEqualMethodsWithEqualParametersShouldHaveEqualMethodSourceObjects() {\n\t\tassertEquals(MethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\"),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\"));\n\t}\n\n\t@Test\n\tvoid twoUnequalMethodsWithEqualParametersShouldHaveUnequalMethodSourceObjects() {\n\t\tassertNotEquals(MethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\"),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod2\", \"int, String\"));\n\t}\n\n\t@Test\n\tvoid twoEqualMethodsWithUnequalParametersShouldHaveUnequalMethodSourceObjects() {\n\t\tassertNotEquals(MethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\"),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod1\", \"float, int, String\"));\n\t}\n\n\t@Test\n\tvoid twoEqualMethodsWithEqualParametersShouldHaveEqualMethodSourceHashCodes() {\n\t\tassertEquals(MethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\").hashCode(),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\").hashCode());\n\t}\n\n\t@Test\n\tvoid twoEqualMethodsWithUnequalParametersShouldHaveUnequalMethodSourceHashCodes() {\n\t\tassertNotEquals(MethodSource.from(\"TestClass1\", \"testMethod1\", \"int, String\").hashCode(),\n\t\t\tMethodSource.from(\"TestClass1\", \"testMethod1\", \"float, int, String\").hashCode());\n\t}\n\n\t@Test\n\tvoid aReflectedMethodsClassNameShouldBeConsistent() throws Exception {\n\t\tvar m = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\n\t\tassertEquals(\"java.lang.String\", MethodSource.from(m).getClassName());\n\t}\n\n\t@Test\n\tvoid aReflectedMethodsMethodNameShouldBeConsistent() throws Exception {\n\t\tvar m = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\n\t\tassertEquals(\"valueOf\", MethodSource.from(m).getMethodName());\n\t}\n\n\t@Test\n\tvoid aReflectedMethodsParameterTypesShouldBeConsistent() throws Exception {\n\t\tvar m = String.class.getDeclaredMethod(\"valueOf\", float.class);\n\n\t\tassertEquals(\"float\", MethodSource.from(m).getMethodParameterTypes());\n\t}\n\n\t@Test\n\tvoid twoEqualReflectedMethodsShouldHaveEqualMethodSourceObjects() throws Exception {\n\t\tvar m1 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\t\tvar m2 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\n\t\tassertEquals(MethodSource.from(m1), MethodSource.from(m2));\n\t}\n\n\t@Test\n\tvoid twoEqualReflectedMethodsShouldHaveEqualMethodSourceHashCodes() throws Exception {\n\t\tvar m1 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\t\tvar m2 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\n\t\tassertEquals(MethodSource.from(m1).hashCode(), MethodSource.from(m2).hashCode());\n\t}\n\n\t@Test\n\tvoid twoUnequalReflectedMethodsShouldNotHaveEqualMethodSourceObjects() throws Exception {\n\t\tvar m1 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\t\tvar m2 = Byte.class.getDeclaredMethod(\"byteValue\");\n\n\t\tassertNotEquals(MethodSource.from(m1), MethodSource.from(m2));\n\t}\n\n\t@Test\n\tvoid twoUnequalReflectedMethodsShouldNotHaveEqualMethodSourceHashCodes() throws Exception {\n\t\tvar m1 = String.class.getDeclaredMethod(\"valueOf\", int.class);\n\t\tvar m2 = Byte.class.getDeclaredMethod(\"byteValue\");\n\n\t\tassertNotEquals(MethodSource.from(m1).hashCode(), MethodSource.from(m2).hashCode());\n\t}\n\n\t@Test\n\tvoid getJavaClassFromString() {\n\t\tvar source = MethodSource.from(getClass().getName(), \"method1\");\n\n\t\tassertThat(source.getJavaClass()).isEqualTo(getClass());\n\t}\n\n\t@Test\n\tvoid getJavaClassShouldThrowExceptionIfClassNotFound() {\n\t\tvar source = MethodSource.from(getClass().getName() + \"X\", \"method1\");\n\n\t\tassertPreconditionViolationFor(source::getJavaClass);\n\t}\n\n\t@Test\n\tvoid getJavaMethodShouldReturnGivenMethodIfOverloadExists() throws Exception {\n\t\tvar testMethod = getMethod(\"method3\");\n\t\tvar source = MethodSource.from(testMethod);\n\n\t\tassertThat(source.getJavaMethod()).isEqualTo(testMethod);\n\t}\n\n\t@Test\n\tvoid getJavaMethodFromStringShouldFindVoidMethod() throws Exception {\n\t\tvar testMethod = getClass().getDeclaredMethod(\"methodVoid\");\n\t\tvar source = MethodSource.from(getClass().getName(), testMethod.getName());\n\n\t\tassertThat(source.getJavaMethod()).isEqualTo(testMethod);\n\t}\n\n\t@Test\n\tvoid getJavaMethodFromStringShouldFindMethodWithParameter() throws Exception {\n\t\tvar testMethod = getClass().getDeclaredMethod(\"method3\", int.class);\n\t\tvar source = MethodSource.from(getClass().getName(), testMethod.getName(), testMethod.getParameterTypes());\n\n\t\tassertThat(source.getJavaMethod()).isEqualTo(testMethod);\n\t}\n\n\t@Test\n\tvoid getJavaMethodFromStringShouldThrowExceptionIfParameterTypesAreNotSupplied() {\n\t\tvar source = MethodSource.from(getClass().getName(), \"method3\");\n\n\t\tassertPreconditionViolationFor(source::getJavaMethod);\n\t}\n\n\t@Test\n\tvoid getJavaMethodFromStringShouldThrowExceptionIfParameterTypesDoNotMatch() {\n\t\tvar source = MethodSource.from(getClass().getName(), \"method3\", double.class);\n\n\t\tassertPreconditionViolationFor(source::getJavaMethod);\n\t}\n\n\t@Test\n\tvoid getJavaMethodFromStringShouldThrowExceptionIfMethodDoesNotExist() {\n\t\tvar source = MethodSource.from(getClass().getName(), \"methodX\");\n\n\t\tassertPreconditionViolationFor(source::getJavaMethod);\n\t}\n\n\tprivate Method getMethod(String name) throws Exception {\n\t\treturn getClass().getDeclaredMethod(name, String.class);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid method1(String text) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid method2(String text) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid method3(String text) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid method3(int number) {\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tvoid methodVoid() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/PackageSourceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.io.Serializable;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n/**\n * Unit tests for {@link PackageSource}.\n *\n * @since 1.0\n */\nclass PackageSourceTests extends AbstractTestSourceTests {\n\n\t@Override\n\tStream<Serializable> createSerializableInstances() {\n\t\treturn Stream.of(PackageSource.from(\"package.source\"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid packageSourceFromNullPackageName() {\n\t\tassertPreconditionViolationFor(() -> PackageSource.from((String) null));\n\t}\n\n\t@Test\n\tvoid packageSourceFromEmptyPackageName() {\n\t\tassertPreconditionViolationFor(() -> PackageSource.from(\"  \"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid packageSourceFromNullPackageReference() {\n\t\tassertPreconditionViolationFor(() -> PackageSource.from((Package) null));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = PackageSourceTests.class)\n\t@ValueSource(strings = \"DefaultPackageTestCase\")\n\tvoid packageSourceFromPackageName(Class<?> testClass) {\n\t\tvar testPackage = testClass.getPackage().getName();\n\t\tvar source = PackageSource.from(testPackage);\n\n\t\tassertThat(source.getPackageName()).isEqualTo(testPackage);\n\t}\n\n\t@Test\n\tvoid packageSourceFromPackageReference() {\n\t\tvar testPackage = getClass().getPackage();\n\t\tvar source = PackageSource.from(testPackage);\n\n\t\tassertThat(source.getPackageName()).isEqualTo(testPackage.getName());\n\t}\n\n\t@Test\n\tvoid equalsAndHashCodeForPackageSource() {\n\t\tvar pkg1 = getClass().getPackage();\n\t\tvar pkg2 = String.class.getPackage();\n\t\tassertEqualsAndHashCode(PackageSource.from(pkg1), PackageSource.from(pkg1), PackageSource.from(pkg2));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorOrderChildrenTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport static java.util.Collections.emptyList;\nimport static java.util.Comparator.comparing;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.UnaryOperator;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\n\npublic interface TestDescriptorOrderChildrenTests {\n\n\t/**\n\t * @return a test descriptor without any children.\n\t */\n\tTestDescriptor createEmptyTestDescriptor();\n\n\tdefault TestDescriptor createTestDescriptorWithChildren() {\n\t\tvar testDescriptor = createEmptyTestDescriptor();\n\t\ttestDescriptor.addChild(new StubTestDescriptor(UniqueId.root(\"child\", \"0\")));\n\t\ttestDescriptor.addChild(new StubTestDescriptor(UniqueId.root(\"child\", \"1\")));\n\t\ttestDescriptor.addChild(new StubTestDescriptor(UniqueId.root(\"child\", \"2\")));\n\t\treturn testDescriptor;\n\t}\n\n\t@Test\n\tdefault void orderChildrenInReverseOrder() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tvar childrenInOriginalOrder = new ArrayList<>(testDescriptor.getChildren());\n\t\ttestDescriptor.orderChildren(children -> {\n\t\t\tchildren.sort(comparing((TestDescriptor o) -> childrenInOriginalOrder.indexOf(o)).reversed());\n\t\t\treturn children;\n\t\t});\n\t\tList<TestDescriptor> children = new ArrayList<>(testDescriptor.getChildren());\n\t\tassertThat(children).isEqualTo(childrenInOriginalOrder.reversed());\n\t}\n\n\t@Test\n\tdefault void orderChildrenEmptyList() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(children -> emptyList()))//\n\t\t\t\t.withMessage(\"orderer may not add or remove test descriptors\");\n\t}\n\n\t@Test\n\tdefault void orderChildrenInSameOrder() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tvar childrenInOriginalOrder = new ArrayList<>(testDescriptor.getChildren());\n\t\ttestDescriptor.orderChildren(children -> {\n\t\t\tchildren.sort(comparing(childrenInOriginalOrder::indexOf));\n\t\t\treturn children;\n\t\t});\n\t\tList<TestDescriptor> children = new ArrayList<>(testDescriptor.getChildren());\n\t\tassertThat(children).isEqualTo(childrenInOriginalOrder);\n\t}\n\n\t@Test\n\tdefault void orderChildrenRemovesDescriptor() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tUnaryOperator<List<TestDescriptor>> orderer = children -> {\n\t\t\tchildren.remove(1);\n\t\t\treturn children;\n\t\t};\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(orderer))//\n\t\t\t\t.withMessage(\"orderer may not add or remove test descriptors\");\n\t}\n\n\t@Test\n\tdefault void orderChildrenAddsDescriptor() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tUnaryOperator<List<TestDescriptor>> orderer = children -> {\n\t\t\tchildren.add(1, new StubTestDescriptor(UniqueId.root(\"extra\", \"extra1\")));\n\t\t\treturn children;\n\t\t};\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(orderer))//\n\t\t\t\t.withMessage(\"orderer may not add or remove test descriptors\");\n\t}\n\n\t@Test\n\tdefault void orderChildrenReplacesDescriptor() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tUnaryOperator<List<TestDescriptor>> orderer = children -> {\n\t\t\tchildren.set(1, new StubTestDescriptor(UniqueId.root(\"replaced\", \"replaced1\")));\n\t\t\treturn children;\n\t\t};\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(orderer))//\n\t\t\t\t.withMessage(\"orderer may not add or remove test descriptors\");\n\t}\n\n\t@Test\n\tdefault void orderChildrenDuplicatesDescriptor() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tUnaryOperator<List<TestDescriptor>> orderer = children -> {\n\t\t\tchildren.add(1, children.getLast());\n\t\t\treturn children;\n\t\t};\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(orderer))//\n\t\t\t\t.withMessage(\"orderer may not add or remove test descriptors\");\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tdefault void orderChildrenOrdererReturnsNull() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tassertPreconditionViolationFor(() -> testDescriptor.orderChildren(children -> null))//\n\t\t\t\t.withMessage(\"orderer may not return null\");\n\t}\n\n\t@Test\n\tdefault void orderChildrenProvidedChildrenAreModifiable() {\n\t\tvar testDescriptor = createTestDescriptorWithChildren();\n\t\tAtomicReference<List<TestDescriptor>> childrenRef = new AtomicReference<>();\n\t\ttestDescriptor.orderChildren(children -> {\n\t\t\tchildrenRef.set(children);\n\t\t\treturn children;\n\t\t});\n\t\tassertThat(childrenRef.get()).isInstanceOf(ArrayList.class);\n\t}\n}\n\nclass StubTestDescriptor extends AbstractTestDescriptor {\n\n\tStubTestDescriptor(UniqueId uniqueId) {\n\t\tsuper(uniqueId, \"stub: \" + uniqueId);\n\t}\n\n\t@Override\n\tpublic Type getType() {\n\t\treturn Type.TEST;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/descriptor/TestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.descriptor;\n\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\n\nclass TestDescriptorTests implements TestDescriptorOrderChildrenTests {\n\n\t@Override\n\tpublic TestDescriptor createEmptyTestDescriptor() {\n\t\treturn new MinimalTestDescriptorImplementation();\n\t}\n\n\tprivate static class MinimalTestDescriptorImplementation implements TestDescriptor {\n\n\t\tprivate final Set<TestDescriptor> children = Collections.synchronizedSet(new LinkedHashSet<>());\n\n\t\t@Override\n\t\tpublic UniqueId getUniqueId() {\n\t\t\treturn UniqueId.root(\"root\", \"value\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String getDisplayName() {\n\t\t\treturn \"TestDescriptorImplementation\";\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<TestTag> getTags() {\n\t\t\treturn Set.of();\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<TestSource> getSource() {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<TestDescriptor> getParent() {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic void setParent(@Nullable TestDescriptor parent) {\n\t\t\tthrow new UnsupportedOperationException(\"Not implemented\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<? extends TestDescriptor> getChildren() {\n\t\t\treturn Collections.unmodifiableSet(children);\n\t\t}\n\n\t\t@Override\n\t\tpublic void addChild(TestDescriptor descriptor) {\n\t\t\tchildren.add(descriptor);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeChild(TestDescriptor descriptor) {\n\t\t\tchildren.remove(descriptor);\n\t\t}\n\n\t\t@Override\n\t\tpublic void removeFromHierarchy() {\n\t\t\tthrow new UnsupportedOperationException(\"Not implemented\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\treturn Type.CONTAINER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<? extends TestDescriptor> findByUniqueId(UniqueId uniqueId) {\n\t\t\tthrow new UnsupportedOperationException(\"Not implemented\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/discovery/EngineDiscoveryRequestResolverTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static org.junit.platform.engine.DiscoveryIssue.Severity.INFO;\nimport static org.junit.platform.engine.DiscoveryIssue.Severity.WARNING;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.unresolved;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\n@NullMarked\nclass EngineDiscoveryRequestResolverTests {\n\n\t@Test\n\tvoid allowsSelectorResolversToReportDiscoveryIssues() {\n\t\tvar resolver = EngineDiscoveryRequestResolver.builder() //\n\t\t\t\t.addSelectorResolver(ctx -> new SelectorResolver() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Resolution resolve(ClassSelector selector, Context context) {\n\t\t\t\t\t\tctx.getIssueReporter() //\n\t\t\t\t\t\t\t\t.reportIssue(DiscoveryIssue.builder(INFO, \"test\") //\n\t\t\t\t\t\t\t\t\t\t.source(ClassSource.from(selector.getClassName())));\n\t\t\t\t\t\treturn unresolved();\n\t\t\t\t\t}\n\t\t\t\t}) //\n\t\t\t\t.build();\n\n\t\tvar engineId = UniqueId.forEngine(\"engine\");\n\t\tvar engineDescriptor = new EngineDescriptor(engineId, \"Engine\");\n\t\tvar listener = mock(LauncherDiscoveryListener.class);\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(EngineDiscoveryRequestResolverTests.class)) //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\tvar issue = DiscoveryIssue.builder(INFO, \"test\") //\n\t\t\t\t.source(ClassSource.from(EngineDiscoveryRequestResolverTests.class)) //\n\t\t\t\t.build();\n\t\tverify(listener).issueEncountered(engineId, issue);\n\t}\n\n\t@Test\n\tvoid allowsVisitorsToReportDiscoveryIssues() {\n\t\tvar resolver = EngineDiscoveryRequestResolver.builder() //\n\t\t\t\t.addTestDescriptorVisitor(ctx -> //\n\t\t\t\tdescriptor -> ctx.getIssueReporter() //\n\t\t\t\t\t\t.reportIssue(DiscoveryIssue.create(WARNING, descriptor.getDisplayName()))) //\n\t\t\t\t.build();\n\n\t\tvar engineId = UniqueId.forEngine(\"engine\");\n\t\tvar engineDescriptor = new EngineDescriptor(engineId, \"Engine\");\n\t\tvar listener = mock(LauncherDiscoveryListener.class);\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(EngineDiscoveryRequestResolverTests.class)) //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\tverify(listener).issueEncountered(engineId, DiscoveryIssue.create(WARNING, \"Engine\"));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/discovery/ResourceContainerSelectorResolverTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathRoots;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.PackageNameFilter.includePackageNames;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Match.exact;\nimport static org.junit.platform.engine.support.discovery.SelectorResolver.Resolution.match;\n\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.io.ResourceFilter;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\n\nclass ResourceContainerSelectorResolverTest {\n\n\tfinal TestDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"resource-engine\"),\n\t\t\"Resource Engine\");\n\tfinal ResourceFilter resourceFilter = ResourceFilter.of(resource -> resource.getName().endsWith(\".resource\"));\n\n\tfinal EngineDiscoveryRequestResolver<TestDescriptor> resolver = EngineDiscoveryRequestResolver.builder() //\n\t\t\t.addResourceContainerSelectorResolver(resourceFilter) //\n\t\t\t.addSelectorResolver(new ResourceSelectorResolver()) //\n\t\t\t.build();\n\n\t@Test\n\tvoid shouldDiscoverAllResourcesInPackage() {\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectPackage(\"org.junit.platform.commons\")) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t\"org/junit/platform/commons/example.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/other-example.resource\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid shouldDiscoverAllResourcesInRootPackage() {\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectPackage(\"\")) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t\"default-package.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/example.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/other-example.resource\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid shouldFilterPackages() {\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectPackage(\"\")) //\n\t\t\t\t.filters(includePackageNames(\"org.junit.platform\")) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t\"org/junit/platform/commons/example.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/other-example.resource\");\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid shouldDiscoverAllResourcesInClasspathRoot() {\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.selectors(selectClasspathRoots(getTestClasspathResourceRoot())) //\n\t\t\t\t.build();\n\n\t\tresolver.resolve(request, engineDescriptor);\n\n\t\t// @formatter:off\n\t\tassertThat(engineDescriptor.getChildren())\n\t\t\t\t.extracting(TestDescriptor::getDisplayName)\n\t\t\t\t.containsExactlyInAnyOrder(\n\t\t\t\t\t\t\"default-package.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/example.resource\",\n\t\t\t\t\t\t\"org/junit/platform/commons/other-example.resource\");\n\t\t// @formatter:on\n\t}\n\n\tprivate Set<Path> getTestClasspathResourceRoot() {\n\t\t// Gradle puts classes and resources in different roots.\n\t\tvar defaultPackageResource = \"/default-package.resource\";\n\t\tvar resourceUri = getClass().getResource(defaultPackageResource).toString();\n\t\tvar uri = URI.create(resourceUri.substring(0, resourceUri.length() - defaultPackageResource.length()));\n\t\treturn Set.of(Path.of(uri));\n\t}\n\n\tprivate static class ResourceSelectorResolver implements SelectorResolver {\n\t\t@Override\n\t\tpublic Resolution resolve(ClasspathResourceSelector selector, Context context) {\n\t\t\treturn context.addToParent(parent -> createTestDescriptor(parent, selector.getClasspathResourceName())) //\n\t\t\t\t\t.map(testDescriptor -> match(exact(testDescriptor))) //\n\t\t\t\t\t.orElseGet(Resolution::unresolved);\n\t\t}\n\n\t\tprivate static Optional<TestDescriptorStub> createTestDescriptor(TestDescriptor parent,\n\t\t\t\tString classpathResourceName) {\n\t\t\tvar uniqueId = parent.getUniqueId().append(\"resource\", classpathResourceName);\n\t\t\tvar descriptor = new TestDescriptorStub(uniqueId, classpathResourceName);\n\t\t\treturn Optional.of(descriptor);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/CompositeLockTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.mockito.Mockito.doAnswer;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.locks.Lock;\nimport java.util.stream.IntStream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\n\n/**\n * @since 1.3\n */\nclass CompositeLockTests {\n\n\t@Test\n\t@SuppressWarnings({ \"resource\", \"ResultOfMethodCallIgnored\" })\n\tvoid triesToAcquireAllLocksInOrder() {\n\t\tvar lock1 = mock(Lock.class, \"lock1\");\n\t\tvar lock2 = mock(Lock.class, \"lock2\");\n\n\t\twhen(lock1.tryLock()).thenReturn(true);\n\t\twhen(lock2.tryLock()).thenReturn(true);\n\n\t\tnew CompositeLock(anyResources(2), List.of(lock1, lock2)).tryAcquire();\n\n\t\tvar inOrder = inOrder(lock1, lock2);\n\t\tinOrder.verify(lock1).tryLock();\n\t\tinOrder.verify(lock2).tryLock();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid acquiresAllLocksInOrder() throws Exception {\n\t\tvar lock1 = mock(Lock.class, \"lock1\");\n\t\tvar lock2 = mock(Lock.class, \"lock2\");\n\n\t\tnew CompositeLock(anyResources(2), List.of(lock1, lock2)).acquire();\n\n\t\tvar inOrder = inOrder(lock1, lock2);\n\t\tinOrder.verify(lock1).lockInterruptibly();\n\t\tinOrder.verify(lock2).lockInterruptibly();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid releasesAllLocksInReverseOrder() throws Exception {\n\t\tvar lock1 = mock(Lock.class, \"lock1\");\n\t\tvar lock2 = mock(Lock.class, \"lock2\");\n\n\t\tnew CompositeLock(anyResources(2), List.of(lock1, lock2)).acquire().close();\n\n\t\tvar inOrder = inOrder(lock1, lock2);\n\t\tinOrder.verify(lock2).unlock();\n\t\tinOrder.verify(lock1).unlock();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid releasesLocksInReverseOrderWhenInterruptedDuringAcquire() throws Exception {\n\t\tvar firstTwoLocksWereLocked = new CountDownLatch(2);\n\t\tvar firstLock = mockLock(\"firstLock\", firstTwoLocksWereLocked::countDown);\n\t\tvar secondLock = mockLock(\"secondLock\", firstTwoLocksWereLocked::countDown);\n\t\tvar unavailableLock = mockLock(\"unavailableLock\", new CountDownLatch(1)::await);\n\n\t\tvar thread = new Thread(() -> {\n\t\t\ttry {\n\t\t\t\tnew CompositeLock(anyResources(3), List.of(firstLock, secondLock, unavailableLock)).acquire();\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t}\n\t\t});\n\t\tthread.start();\n\t\tfirstTwoLocksWereLocked.await();\n\t\tthread.interrupt();\n\t\tthread.join();\n\n\t\tvar inOrder = inOrder(firstLock, secondLock);\n\t\tinOrder.verify(secondLock).unlock();\n\t\tinOrder.verify(firstLock).unlock();\n\t\tverify(unavailableLock, never()).unlock();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid releasesLocksInReverseOrderOnUnsuccessfulAttempt() {\n\t\tvar firstLock = mock(Lock.class, \"firstLock\");\n\t\tvar secondLock = mock(Lock.class, \"secondLock\");\n\t\tvar unavailableLock = mock(Lock.class, \"unavailableLock\");\n\n\t\twhen(firstLock.tryLock()).thenReturn(true);\n\t\twhen(secondLock.tryLock()).thenReturn(true);\n\t\twhen(unavailableLock.tryLock()).thenReturn(false);\n\n\t\tnew CompositeLock(anyResources(3), List.of(firstLock, secondLock, unavailableLock)).tryAcquire();\n\n\t\tvar inOrder = inOrder(firstLock, secondLock);\n\t\tinOrder.verify(secondLock).unlock();\n\t\tinOrder.verify(firstLock).unlock();\n\t\tverify(unavailableLock, never()).unlock();\n\t}\n\n\tprivate Lock mockLock(String name, Executable lockAction) throws InterruptedException {\n\t\tvar lock = mock(Lock.class, name);\n\t\tdoAnswer(invocation -> {\n\t\t\tlockAction.execute();\n\t\t\treturn null;\n\t\t}).when(lock).lockInterruptibly();\n\t\treturn lock;\n\t}\n\n\tprivate List<ExclusiveResource> anyResources(int n) {\n\t\treturn IntStream.range(0, n) //\n\t\t\t\t.mapToObj(j -> new ExclusiveResource(\"key\" + j, LockMode.READ)) //\n\t\t\t\t.toList();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/DefaultParallelExecutionConfigurationStrategyTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.ConfigurationParameters;\n\n/**\n * @since 1.3\n */\nclass DefaultParallelExecutionConfigurationStrategyTests {\n\n\tfinal ConfigurationParameters configParams = mock();\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\twhen(configParams.get(any(), any())).thenCallRealMethod();\n\t}\n\n\t@Test\n\tvoid fixedStrategyCreatesValidConfiguration() {\n\t\twhen(configParams.get(\"fixed.parallelism\")).thenReturn(Optional.of(\"42\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.FIXED;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tassertThat(configuration.getParallelism()).isEqualTo(42);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(42);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(42);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(256 + 42);\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(30);\n\t\tassertThat(configuration.getSaturatePredicate()).isNotNull();\n\t\tassertThat(configuration.getSaturatePredicate().test(null)).isTrue();\n\t}\n\n\t@Test\n\tvoid fixedSaturateStrategyCreatesValidConfiguration() {\n\t\twhen(configParams.get(\"fixed.parallelism\")).thenReturn(Optional.of(\"42\"));\n\t\twhen(configParams.get(\"fixed.max-pool-size\")).thenReturn(Optional.of(\"42\"));\n\t\twhen(configParams.get(\"fixed.saturate\")).thenReturn(Optional.of(\"false\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.FIXED;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\t\tassertThat(configuration.getParallelism()).isEqualTo(42);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(42);\n\t\tassertThat(configuration.getSaturatePredicate()).isNotNull();\n\t\tassertThat(configuration.getSaturatePredicate().test(null)).isFalse();\n\t}\n\n\t@Test\n\tvoid dynamicStrategyCreatesValidConfiguration() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"2.0\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tvar availableProcessors = Runtime.getRuntime().availableProcessors();\n\t\tassertThat(configuration.getParallelism()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(256 + (availableProcessors * 2));\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(30);\n\t\tassertThat(configuration.getSaturatePredicate()).isNotNull();\n\t\tassertThat(configuration.getSaturatePredicate().test(null)).isTrue();\n\t}\n\n\t@Test\n\tvoid dynamicSaturateStrategyCreatesValidConfiguration() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"2.0\"));\n\t\twhen(configParams.get(\"dynamic.max-pool-size-factor\")).thenReturn(Optional.of(\"3.0\"));\n\t\twhen(configParams.get(\"dynamic.saturate\")).thenReturn(Optional.of(\"false\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tvar availableProcessors = Runtime.getRuntime().availableProcessors();\n\t\tassertThat(configuration.getParallelism()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(availableProcessors * 2);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(availableProcessors * 6);\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(30);\n\t\tassertThat(configuration.getSaturatePredicate()).isNotNull();\n\t\tassertThat(configuration.getSaturatePredicate().test(null)).isFalse();\n\t}\n\n\t@Test\n\tvoid customStrategyCreatesValidConfiguration() {\n\t\twhen(configParams.get(\"custom.class\")).thenReturn(\n\t\t\tOptional.of(CustomParallelExecutionConfigurationStrategy.class.getName()));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.CUSTOM;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tassertThat(configuration.getParallelism()).isEqualTo(1);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(4);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(2);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(3);\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(5);\n\t\tassertThat(configuration.getSaturatePredicate()).isNotNull();\n\t\tassertThat(configuration.getSaturatePredicate().test(null)).isTrue();\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid createsStrategyFromConfigParam(DefaultParallelExecutionConfigurationStrategy strategy) {\n\t\twhen(configParams.get(\"strategy\")).thenReturn(Optional.of(strategy.name().toLowerCase()));\n\n\t\tassertThat(DefaultParallelExecutionConfigurationStrategy.getStrategy(configParams)).isSameAs(strategy);\n\t}\n\n\t@Test\n\tvoid fixedStrategyThrowsExceptionWhenPropertyIsNotPresent() {\n\t\twhen(configParams.get(\"fixed.parallelism\")).thenReturn(Optional.empty());\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.FIXED;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid fixedStrategyThrowsExceptionWhenPropertyIsNotAnInteger() {\n\t\twhen(configParams.get(\"fixed.parallelism\")).thenReturn(Optional.of(\"foo\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.FIXED;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid dynamicStrategyUsesDefaultWhenPropertyIsNotPresent() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.empty());\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tvar availableProcessors = Runtime.getRuntime().availableProcessors();\n\t\tassertThat(configuration.getParallelism()).isEqualTo(availableProcessors);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(availableProcessors);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(availableProcessors);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(256 + availableProcessors);\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(30);\n\t}\n\n\t@Test\n\tvoid dynamicStrategyThrowsExceptionWhenPropertyIsNotAnInteger() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"foo\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid dynamicStrategyThrowsExceptionWhenFactorIsZero() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"0\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid dynamicStrategyThrowsExceptionWhenFactorIsNegative() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"-1\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid dynamicStrategyUsesAtLeastParallelismOfOneWhenPropertyIsTooSmall() {\n\t\twhen(configParams.get(\"dynamic.factor\")).thenReturn(Optional.of(\"0.00000000001\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.DYNAMIC;\n\t\tvar configuration = strategy.createConfiguration(configParams);\n\n\t\tassertThat(configuration.getParallelism()).isEqualTo(1);\n\t\tassertThat(configuration.getCorePoolSize()).isEqualTo(1);\n\t\tassertThat(configuration.getMinimumRunnable()).isEqualTo(1);\n\t\tassertThat(configuration.getMaxPoolSize()).isEqualTo(256 + 1);\n\t\tassertThat(configuration.getKeepAliveSeconds()).isEqualTo(30);\n\t}\n\n\t@Test\n\tvoid customStrategyThrowsExceptionWhenPropertyIsNotPresent() {\n\t\twhen(configParams.get(\"custom.class\")).thenReturn(Optional.empty());\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.CUSTOM;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\t@Test\n\tvoid customStrategyThrowsExceptionWhenClassDoesNotExist() {\n\t\twhen(configParams.get(\"custom.class\")).thenReturn(Optional.of(\"com.acme.ClassDoesNotExist\"));\n\n\t\tParallelExecutionConfigurationStrategy strategy = DefaultParallelExecutionConfigurationStrategy.CUSTOM;\n\t\tassertThrows(JUnitException.class, () -> strategy.createConfiguration(configParams));\n\t}\n\n\tstatic class CustomParallelExecutionConfigurationStrategy implements ParallelExecutionConfigurationStrategy {\n\t\t@Override\n\t\tpublic ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {\n\t\t\treturn new DefaultParallelExecutionConfiguration(1, 2, 3, 4, 5, __ -> true);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinDeadLockTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.jupiter.api.parallel.Resources.SYSTEM_PROPERTIES;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\n\nimport java.time.LocalTime;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.junit.jupiter.api.Constants;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.extension.AfterAllCallback;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.Isolated;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n// https://github.com/junit-team/junit-framework/issues/3945\n@Timeout(10)\n@ParameterizedClass\n@EnumSource(ParallelExecutorServiceType.class)\nrecord ForkJoinDeadLockTests(ParallelExecutorServiceType executorServiceType) {\n\n\t@Test\n\tvoid forkJoinExecutionDoesNotLeadToDeadLock() {\n\t\trun(NonIsolatedTestCase.class, IsolatedTestCase.class, Isolated2TestCase.class);\n\t}\n\n\t@Test\n\tvoid nestedResourceLocksShouldStillWork() {\n\t\trun(SharedResourceTestCase.class);\n\t}\n\n\t@Test\n\tvoid multiLevelLocks() {\n\t\trun(ClassLevelTestCase.class);\n\t}\n\n\tprivate void run(Class<?>... classes) {\n\t\tEngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.selectors(selectClasses(classes)) //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, \"true\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME,\n\t\t\t\t\texecutorServiceType.name()) //\n\t\t\t\t.configurationParameter(Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\") //\n\t\t\t\t.configurationParameter(Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, \"fixed\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME, \"3\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, \"3\") //\n\t\t\t\t.configurationParameter(Constants.PARALLEL_CONFIG_FIXED_SATURATE_PROPERTY_NAME, \"false\") //\n\t\t\t\t.execute();\n\t}\n\n\t@ExtendWith(StartFinishLogger.class)\n\tstatic class BaseTestCase {\n\t}\n\n\t@Execution(CONCURRENT)\n\tpublic static class NonIsolatedTestCase extends BaseTestCase {\n\n\t\tpublic static CountDownLatch otherThreadRunning = new CountDownLatch(1);\n\t\tpublic static CountDownLatch sameThreadFinishing = new CountDownLatch(1);\n\n\t\t@Test\n\t\t@Execution(CONCURRENT)\n\t\tvoid otherThread() throws Exception {\n\t\t\totherThreadRunning.countDown();\n\t\t\tsameThreadFinishing.await();\n\t\t\tThread.sleep(100);\n\t\t}\n\n\t\t@Test\n\t\t@Execution(SAME_THREAD)\n\t\tvoid sameThread() throws Exception {\n\t\t\totherThreadRunning.await();\n\t\t\tsameThreadFinishing.countDown();\n\t\t}\n\t}\n\n\t@Isolated\n\tpublic static class IsolatedTestCase extends BaseTestCase {\n\n\t\t@Test\n\t\tvoid test() throws Exception {\n\t\t\tThread.sleep(100);\n\t\t}\n\t}\n\n\tstatic class Isolated2TestCase extends IsolatedTestCase {\n\t}\n\n\tpublic static class SharedResourceTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)\n\t\tvoid customPropertyIsNotSetByDefault() {\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\t\tvoid canSetCustomPropertyToApple() {\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\t\tvoid canSetCustomPropertyToBanana() {\n\t\t}\n\t}\n\n\t@ResourceLock(value = \"foo\", mode = READ_WRITE)\n\tpublic static class ClassLevelTestCase {\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ)\n\t\tvoid customPropertyIsNotSetByDefault() {\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\t\tvoid canSetCustomPropertyToApple() {\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = SYSTEM_PROPERTIES, mode = READ_WRITE)\n\t\tvoid canSetCustomPropertyToBanana() {\n\t\t}\n\t}\n\n\tstatic class StartFinishLogger\n\t\t\timplements BeforeTestExecutionCallback, AfterTestExecutionCallback, BeforeAllCallback, AfterAllCallback {\n\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tlog(\"starting class \" + context.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tlog(\"starting method \" + context.getTestClass().orElseThrow().getSimpleName() + \".\"\n\t\t\t\t\t+ context.getTestMethod().orElseThrow().getName());\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tlog(\"finishing method \" + context.getTestClass().orElseThrow().getSimpleName() + \".\"\n\t\t\t\t\t+ context.getTestMethod().orElseThrow().getName());\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterAll(ExtensionContext context) {\n\t\t\tlog(\"finishing class \" + context.getTestClass().orElseThrow().getSimpleName());\n\t\t}\n\t}\n\n\tprivate static void log(String message) {\n\t\tSystem.out.println(\"[\" + LocalTime.now() + \"] \" + Thread.currentThread().getName() + \" - \" + message);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ForkJoinPoolHierarchicalTestExecutorServiceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.concurrent.TimeUnit.SECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\nimport org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.TaskEventListener;\nimport org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService.TestTask;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\n\n@Timeout(5)\nclass ForkJoinPoolHierarchicalTestExecutorServiceTests {\n\n\tDummyTaskFactory taskFactory = new DummyTaskFactory();\n\tLockManager lockManager = new LockManager();\n\n\t@Test\n\tvoid exceptionsFromInvalidConfigurationAreNotSwallowed() {\n\t\tvar configuration = new DefaultParallelExecutionConfiguration(2, 1, 1, 1, 0, __ -> true);\n\n\t\tJUnitException exception = assertThrows(JUnitException.class, () -> {\n\t\t\ttry (var pool = new ForkJoinPoolHierarchicalTestExecutorService(configuration, TaskEventListener.NOOP)) {\n\t\t\t\tassertNotNull(pool, \"we won't get here\");\n\t\t\t}\n\t\t});\n\n\t\tassertThat(exception).hasMessage(\"Failed to create ForkJoinPool\");\n\t\tassertThat(exception).rootCause().isInstanceOf(IllegalArgumentException.class);\n\t}\n\n\tstatic List<Arguments> incompatibleLockCombinations() {\n\t\treturn List.of(//\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ), //\n\t\t\t\tSet.of(GLOBAL_READ_WRITE) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ)), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ_WRITE)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ_WRITE)), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ_WRITE)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ_WRITE)), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"b\", LockMode.READ_WRITE)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(new ExclusiveResource(\"b\", LockMode.READ)), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ_WRITE)), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ_WRITE), //\n\t\t\t\tSet.of(GLOBAL_READ) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ),\n\t\t\t\t\tnew ExclusiveResource(\"b\", LockMode.READ), new ExclusiveResource(\"d\", LockMode.READ)),\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ),\n\t\t\t\t\tnew ExclusiveResource(\"c\", LockMode.READ)) //\n\t\t\t)//\n\t\t);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@ParameterizedTest\n\t@MethodSource(\"incompatibleLockCombinations\")\n\tvoid defersTasksWithIncompatibleLocks(Set<ExclusiveResource> initialResources,\n\t\t\tSet<ExclusiveResource> incompatibleResources) throws Throwable {\n\n\t\tvar initialLock = lockManager.getLockForResources(initialResources);\n\t\tvar incompatibleLock = lockManager.getLockForResources(incompatibleResources);\n\n\t\tvar deferred = new CountDownLatch(1);\n\t\tvar deferredTask = new AtomicReference<TestTask>();\n\n\t\tTaskEventListener taskEventListener = testTask -> {\n\t\t\tdeferredTask.set(testTask);\n\t\t\tdeferred.countDown();\n\t\t};\n\n\t\tvar incompatibleTask = taskFactory.create(\"incompatibleTask\", incompatibleLock);\n\n\t\tvar tasks = runWithAttemptedWorkStealing(taskEventListener, incompatibleTask, initialLock,\n\t\t\t() -> await(deferred, \"Interrupted while waiting for task to be deferred\"));\n\n\t\tassertEquals(incompatibleTask, deferredTask.get());\n\t\tassertEquals(tasks.get(\"nestedTask\").threadName, tasks.get(\"leafTaskB\").threadName);\n\t\tassertNotEquals(tasks.get(\"leafTaskA\").threadName, tasks.get(\"leafTaskB\").threadName);\n\t}\n\n\tstatic List<Arguments> compatibleLockCombinations() {\n\t\treturn List.of(//\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ_WRITE)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ_WRITE)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ),\n\t\t\t\t\tnew ExclusiveResource(\"b\", LockMode.READ), new ExclusiveResource(\"c\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)), //\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"b\", LockMode.READ)) //\n\t\t\t), //\n\t\t\targuments(//\n\t\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)), //\n\t\t\t\tSet.of(new ExclusiveResource(\"a\", LockMode.READ), new ExclusiveResource(\"b\", LockMode.READ),\n\t\t\t\t\tnew ExclusiveResource(\"c\", LockMode.READ)) //\n\t\t\t)//\n\t\t);\n\t}\n\n\t@SuppressWarnings(\"NullAway\")\n\t@ParameterizedTest\n\t@MethodSource(\"compatibleLockCombinations\")\n\tvoid canWorkStealTaskWithCompatibleLocks(Set<ExclusiveResource> initialResources,\n\t\t\tSet<ExclusiveResource> compatibleResources) throws Throwable {\n\n\t\tvar initialLock = lockManager.getLockForResources(initialResources);\n\t\tvar compatibleLock = lockManager.getLockForResources(compatibleResources);\n\n\t\tvar deferredTask = new AtomicReference<TestTask>();\n\n\t\tvar workStolen = new CountDownLatch(1);\n\t\tvar compatibleTask = taskFactory.create(\"compatibleTask\", compatibleLock, workStolen::countDown);\n\n\t\tvar tasks = runWithAttemptedWorkStealing(deferredTask::set, compatibleTask, initialLock,\n\t\t\t() -> await(workStolen, \"Interrupted while waiting for work to be stolen\"));\n\n\t\tassertNull(deferredTask.get());\n\t\tassertEquals(tasks.get(\"nestedTask\").threadName, tasks.get(\"leafTaskB\").threadName);\n\t\tassertNotEquals(tasks.get(\"leafTaskA\").threadName, tasks.get(\"leafTaskB\").threadName);\n\t}\n\n\t@Test\n\tvoid defersTasksWithIncompatibleLocksOnMultipleLevels() throws Throwable {\n\n\t\tvar initialLock = lockManager.getLockForResources(\n\t\t\tSet.of(GLOBAL_READ, new ExclusiveResource(\"a\", LockMode.READ)));\n\t\tvar incompatibleLock1 = lockManager.getLockForResource(new ExclusiveResource(\"a\", LockMode.READ_WRITE));\n\t\tvar compatibleLock1 = lockManager.getLockForResource(new ExclusiveResource(\"b\", LockMode.READ));\n\t\tvar incompatibleLock2 = lockManager.getLockForResource(new ExclusiveResource(\"b\", LockMode.READ_WRITE));\n\n\t\tvar deferred = new ConcurrentHashMap<TestTask, CountDownLatch>();\n\t\tvar deferredTasks = new CopyOnWriteArrayList<TestTask>();\n\n\t\t@SuppressWarnings(\"NullAway\")\n\t\tTaskEventListener taskEventListener = testTask -> {\n\t\t\tdeferredTasks.add(testTask);\n\t\t\tdeferred.get(testTask).countDown();\n\t\t};\n\n\t\tvar incompatibleTask1 = taskFactory.create(\"incompatibleTask1\", incompatibleLock1);\n\t\tdeferred.put(incompatibleTask1, new CountDownLatch(1));\n\n\t\tvar incompatibleTask2 = taskFactory.create(\"incompatibleTask2\", incompatibleLock2);\n\t\tdeferred.put(incompatibleTask2, new CountDownLatch(1));\n\n\t\tvar configuration = new DefaultParallelExecutionConfiguration(2, 2, 2, 2, 1, __1 -> true);\n\n\t\twithForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener, service -> {\n\n\t\t\tvar nestedTask2 = createNestedTaskWithTwoConcurrentLeafTasks(service, \"2\", compatibleLock1,\n\t\t\t\tList.of(incompatibleTask2), //\n\t\t\t\t() -> await(deferred.get(incompatibleTask2), incompatibleTask2.identifier + \" to be deferred\"));\n\n\t\t\tvar nestedTask1 = createNestedTaskWithTwoConcurrentLeafTasks(service, \"1\", initialLock,\n\t\t\t\tList.of(incompatibleTask1, nestedTask2), //\n\t\t\t\t() -> {\n\t\t\t\t\tawait(deferred.get(incompatibleTask1), incompatibleTask1.identifier + \" to be deferred\");\n\t\t\t\t\tawait(nestedTask2.started, nestedTask2.identifier + \" to be started\");\n\t\t\t\t});\n\n\t\t\tservice.submit(nestedTask1).get();\n\t\t});\n\n\t\tassertThat(deferredTasks) //\n\t\t\t\t.startsWith(incompatibleTask1, incompatibleTask2) //\n\t\t\t\t.containsOnly(incompatibleTask1, incompatibleTask2) // incompatibleTask1 may be deferred multiple times\n\t\t\t\t.containsOnlyOnce(incompatibleTask2);\n\t\tassertThat(taskFactory.tasks) //\n\t\t\t\t.hasSize(3 + 3 + 2) //\n\t\t\t\t.values().extracting(it -> it.completion.isDone()).containsOnly(true);\n\t\tassertThat(taskFactory.tasks) //\n\t\t\t\t.values().extracting(it -> it.completion.isCompletedExceptionally()).containsOnly(false);\n\t}\n\n\tprivate Map<String, DummyTestTask> runWithAttemptedWorkStealing(TaskEventListener taskEventListener,\n\t\t\tDummyTestTask taskToBeStolen, ResourceLock initialLock, Runnable waitAction) throws Throwable {\n\n\t\tvar configuration = new DefaultParallelExecutionConfiguration(2, 2, 2, 2, 1, __ -> true);\n\n\t\twithForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener, service -> {\n\n\t\t\tvar nestedTask = createNestedTaskWithTwoConcurrentLeafTasks(service, \"\", initialLock,\n\t\t\t\tList.of(taskToBeStolen), waitAction);\n\n\t\t\tservice.submit(nestedTask).get();\n\t\t});\n\n\t\treturn taskFactory.tasks;\n\t}\n\n\tprivate DummyTestTask createNestedTaskWithTwoConcurrentLeafTasks(\n\t\t\tForkJoinPoolHierarchicalTestExecutorService service, String identifierSuffix, ResourceLock parentLock,\n\t\t\tList<DummyTestTask> tasksToFork, Runnable waitAction) {\n\n\t\treturn taskFactory.create(\"nestedTask\" + identifierSuffix, parentLock, () -> {\n\n\t\t\tvar bothLeafTasksAreRunning = new CountDownLatch(2);\n\n\t\t\tvar leafTaskA = taskFactory.create(\"leafTaskA\" + identifierSuffix, NopLock.INSTANCE, () -> {\n\t\t\t\ttasksToFork.forEach(task -> service.new ExclusiveTask(task).fork());\n\t\t\t\tbothLeafTasksAreRunning.countDown();\n\t\t\t\tbothLeafTasksAreRunning.await();\n\t\t\t\twaitAction.run();\n\t\t\t});\n\n\t\t\tvar leafTaskB = taskFactory.create(\"leafTaskB\" + identifierSuffix, NopLock.INSTANCE, () -> {\n\t\t\t\tbothLeafTasksAreRunning.countDown();\n\t\t\t\tbothLeafTasksAreRunning.await();\n\t\t\t});\n\n\t\t\tservice.invokeAll(List.of(leafTaskA, leafTaskB));\n\t\t});\n\t}\n\n\tprivate static void await(CountDownLatch latch, String message) {\n\t\ttry {\n\t\t\tlatch.await();\n\t\t}\n\t\tcatch (InterruptedException e) {\n\t\t\tSystem.out.println(\"Interrupted while waiting for \" + message);\n\t\t}\n\t}\n\n\tprivate void withForkJoinPoolHierarchicalTestExecutorService(ParallelExecutionConfiguration configuration,\n\t\t\tTaskEventListener taskEventListener,\n\t\t\tThrowingConsumer<@NonNull ForkJoinPoolHierarchicalTestExecutorService> action) throws Throwable {\n\t\ttry (var service = new ForkJoinPoolHierarchicalTestExecutorService(configuration, taskEventListener)) {\n\n\t\t\taction.accept(service);\n\n\t\t\tservice.forkJoinPool.shutdown();\n\t\t\tassertTrue(service.forkJoinPool.awaitTermination(5, SECONDS), \"Pool did not terminate within timeout\");\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic final class DummyTestTask implements TestTask {\n\n\t\tprivate final String identifier;\n\t\tprivate final ResourceLock resourceLock;\n\t\tprivate final Executable action;\n\n\t\tprivate volatile @Nullable String threadName;\n\n\t\tprivate final CountDownLatch started = new CountDownLatch(1);\n\t\tprivate final CompletableFuture<?> completion = new CompletableFuture<>();\n\n\t\tDummyTestTask(String identifier, ResourceLock resourceLock, Executable action) {\n\t\t\tthis.identifier = identifier;\n\t\t\tthis.resourceLock = resourceLock;\n\t\t\tthis.action = action;\n\t\t}\n\n\t\t@Override\n\t\tpublic ExecutionMode getExecutionMode() {\n\t\t\treturn CONCURRENT;\n\t\t}\n\n\t\t@Override\n\t\tpublic ResourceLock getResourceLock() {\n\t\t\treturn resourceLock;\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute() {\n\t\t\tthreadName = Thread.currentThread().getName();\n\t\t\tstarted.countDown();\n\t\t\ttry {\n\t\t\t\taction.execute();\n\t\t\t\tcompletion.complete(null);\n\t\t\t}\n\t\t\tcatch (Throwable e) {\n\t\t\t\tcompletion.completeExceptionally(e);\n\t\t\t\tthrow new RuntimeException(\"Action \" + identifier + \" failed\", e);\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn identifier;\n\t\t}\n\t}\n\n\tstatic final class DummyTaskFactory {\n\n\t\tfinal Map<String, DummyTestTask> tasks = new HashMap<>();\n\n\t\tDummyTestTask create(String identifier, ResourceLock resourceLock) {\n\t\t\treturn create(identifier, resourceLock, () -> {\n\t\t\t});\n\t\t}\n\n\t\tDummyTestTask create(String identifier, ResourceLock resourceLock, Executable action) {\n\t\t\tDummyTestTask task = new DummyTestTask(identifier, resourceLock, action);\n\t\t\ttasks.put(task.identifier, task);\n\t\t\treturn task;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/HierarchicalTestExecutorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.quality.Strictness.LENIENT;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.jspecify.annotations.NonNull;\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.function.ThrowingConsumer;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\nimport org.junit.platform.engine.support.hierarchical.Node.DynamicTestExecutor;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.launcher.core.ConfigurationParametersFactoryForTests;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mock;\nimport org.mockito.Spy;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport org.mockito.junit.jupiter.MockitoSettings;\nimport org.mockito.stubbing.Answer;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Micro-tests that verify behavior of {@link HierarchicalTestExecutor}.\n *\n * @since 1.0\n */\n@ExtendWith(MockitoExtension.class)\nclass HierarchicalTestExecutorTests {\n\n\t@Spy\n\tMyContainer root = new MyContainer(UniqueId.root(\"container\", \"root\"));\n\n\t@Mock\n\tEngineExecutionListener listener;\n\n\tCancellationToken cancellationToken = CancellationToken.create();\n\n\tMyEngineExecutionContext rootContext = new MyEngineExecutionContext();\n\tHierarchicalTestExecutor<@NonNull MyEngineExecutionContext> executor;\n\n\t@BeforeEach\n\tvoid init() {\n\t\texecutor = createExecutor(new SameThreadHierarchicalTestExecutorService());\n\t}\n\n\tprivate HierarchicalTestExecutor<@NonNull MyEngineExecutionContext> createExecutor(\n\t\t\tHierarchicalTestExecutorService executorService) {\n\t\tExecutionRequest request = mock();\n\t\twhen(request.getRootTestDescriptor()).thenReturn(root);\n\t\twhen(request.getEngineExecutionListener()).thenReturn(listener);\n\t\twhen(request.getCancellationToken()).thenReturn(cancellationToken);\n\t\treturn new HierarchicalTestExecutor<>(request, rootContext, executorService,\n\t\t\tOpenTest4JAwareThrowableCollector::new);\n\t}\n\n\t@Test\n\tvoid emptyRootDescriptor() throws Exception {\n\n\t\tvar inOrder = inOrder(listener, root);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid rootDescriptorWithOneChildContainer() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).before(rootContext);\n\t\tinOrder.verify(child).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid rootDescriptorWithOneChildLeaf() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child leaf\")));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar aTestExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).executionFinished(eq(child), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(aTestExecutionResult.getValue().getStatus()).isEqualTo(SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid skippingAContainer() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\twhen(child.shouldBeSkipped(rootContext)).thenReturn(Node.SkipResult.skip(\"in test\"));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(child).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tverify(listener, never()).executionStarted(child);\n\t\tverify(child, never()).execute(any(), any());\n\t\tverify(listener, never()).executionFinished(eq(child), any(TestExecutionResult.class));\n\t}\n\n\t@Test\n\tvoid skippingALeaf() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child leaf\")));\n\t\twhen(child.shouldBeSkipped(rootContext)).thenReturn(Node.SkipResult.skip(\"in test\"));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(child).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tverify(listener, never()).executionStarted(child);\n\t\tverify(child, never()).execute(any(), any());\n\t\tverify(listener, never()).executionFinished(eq(child), any(TestExecutionResult.class));\n\t}\n\n\t@Test\n\tvoid exceptionInShouldBeSkipped() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\tvar anException = new RuntimeException(\"in skip\");\n\t\twhen(child.shouldBeSkipped(rootContext)).thenThrow(anException);\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(child).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tverify(child, never()).execute(any(), any());\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable()).containsSame(anException);\n\t}\n\n\t@Test\n\tvoid exceptionInContainerBeforeAll() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\troot.addChild(child);\n\t\tvar anException = new RuntimeException(\"in test\");\n\t\twhen(root.before(rootContext)).thenThrow(anException);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(anException);\n\n\t\tverify(child, never()).execute(any(), any());\n\t}\n\n\t@Test\n\tvoid exceptionInContainerAfterAllAndCleanUp() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child container\")));\n\t\troot.addChild(child);\n\t\tvar afterException = new RuntimeException(\"in after()\");\n\t\tdoThrow(afterException).when(root).after(rootContext);\n\t\tvar cleanUpException = new RuntimeException(\"in cleanUp()\");\n\t\tdoThrow(cleanUpException).when(root).cleanUp(rootContext);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tvar inOrder = inOrder(listener, root, child);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).executionFinished(eq(child), any(TestExecutionResult.class));\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(root).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\t\tinOrder.verifyNoMoreInteractions();\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(afterException);\n\t\tassertThat(afterException.getSuppressed()).containsExactly(cleanUpException);\n\t}\n\n\t@Test\n\tvoid exceptionInPrepare() throws Exception {\n\t\tvar prepareException = new RuntimeException(\"in prepare()\");\n\t\tdoThrow(prepareException).when(root).prepare(rootContext);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tvar inOrder = inOrder(listener, root);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\t\tinOrder.verifyNoMoreInteractions();\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(prepareException);\n\t\tassertThat(prepareException.getSuppressed()).isEmpty();\n\t}\n\n\t@Test\n\tvoid exceptionInCleanUp() throws Exception {\n\t\tvar cleanUpException = new RuntimeException(\"in cleanUp()\");\n\t\tdoThrow(cleanUpException).when(root).cleanUp(rootContext);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tvar inOrder = inOrder(listener, root);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).execute(eq(rootContext), any());\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(root).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\t\tinOrder.verifyNoMoreInteractions();\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(cleanUpException);\n\t\tassertThat(cleanUpException.getSuppressed()).isEmpty();\n\t}\n\n\t@Test\n\tvoid exceptionInShouldBeSkippedAndCleanUp() throws Exception {\n\t\tvar shouldBeSkippedException = new RuntimeException(\"in prepare()\");\n\t\tdoThrow(shouldBeSkippedException).when(root).shouldBeSkipped(rootContext);\n\t\tvar cleanUpException = new RuntimeException(\"in cleanUp()\");\n\t\tdoThrow(cleanUpException).when(root).cleanUp(rootContext);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tvar inOrder = inOrder(listener, root);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(root).cleanUp(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\t\tinOrder.verifyNoMoreInteractions();\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(shouldBeSkippedException);\n\t\tassertThat(shouldBeSkippedException.getSuppressed()).containsExactly(cleanUpException);\n\t}\n\n\t@Test\n\tvoid exceptionInLeafExecute() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"leaf\")));\n\t\tvar anException = new RuntimeException(\"in test\");\n\t\twhen(child.execute(eq(rootContext), any())).thenThrow(anException);\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable()).containsSame(anException);\n\t}\n\n\t@Test\n\tvoid abortInRootBeforeAll() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\troot.addChild(child);\n\t\tvar anAbortedException = new TestAbortedException(\"in BeforeAll\");\n\t\twhen(root.before(rootContext)).thenThrow(anAbortedException);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar rootExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), rootExecutionResult.capture());\n\n\t\tassertThat(rootExecutionResult.getValue().getStatus()).isEqualTo(ABORTED);\n\t\tassertThat(rootExecutionResult.getValue().getThrowable()).containsSame(anAbortedException);\n\n\t\tverify(child, never()).execute(any(), any());\n\t}\n\n\t@Test\n\tvoid abortInChildContainerBeforeAll() throws Exception {\n\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\troot.addChild(child);\n\t\tvar anAbortedException = new TestAbortedException(\"in BeforeAll\");\n\t\twhen(child.before(rootContext)).thenThrow(anAbortedException);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(child).before(rootContext);\n\t\tinOrder.verify(child).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\t\tinOrder.verify(root).after(rootContext);\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(ABORTED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable()).containsSame(anAbortedException);\n\n\t\tverify(child, never()).execute(any(), any());\n\t}\n\n\t@Test\n\tvoid abortInLeafExecute() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"leaf\")));\n\t\tvar anAbortedException = new TestAbortedException(\"in test\");\n\t\twhen(child.execute(eq(rootContext), any())).thenThrow(anAbortedException);\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\t\tinOrder.verify(root).after(rootContext);\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(ABORTED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable()).containsSame(anAbortedException);\n\t}\n\n\t@Test\n\tvoid executesDynamicTestDescriptors() throws Exception {\n\n\t\tvar leafUniqueId = UniqueId.root(\"leaf\", \"child leaf\");\n\t\tvar child = spy(new MyLeaf(leafUniqueId));\n\t\tvar dynamicTestDescriptor = spy(new MyLeaf(leafUniqueId.append(\"dynamic\", \"child\")));\n\n\t\twhen(child.execute(any(), any())).thenAnswer(execute(dynamicTestDescriptor));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, root, child, dynamicTestDescriptor);\n\n\t\texecutor.execute();\n\n\t\tvar aTestExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).dynamicTestRegistered(dynamicTestDescriptor);\n\t\tinOrder.verify(dynamicTestDescriptor).prepare(rootContext);\n\t\tinOrder.verify(dynamicTestDescriptor).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(dynamicTestDescriptor);\n\t\tinOrder.verify(dynamicTestDescriptor).execute(eq(rootContext), any());\n\t\tinOrder.verify(listener).executionFinished(eq(dynamicTestDescriptor), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(child), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(aTestExecutionResult.getAllValues()).extracting(TestExecutionResult::getStatus).containsExactly(\n\t\t\tSUCCESSFUL, SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid executesDynamicTestDescriptorsUsingContainerAndTestType() throws Exception {\n\n\t\tvar child = spy(new MyContainerAndTestTestCase(root.getUniqueId().append(\"c&t\", \"child\")));\n\t\tvar dynamicContainerAndTest = spy(\n\t\t\tnew MyContainerAndTestTestCase(child.getUniqueId().append(\"c&t\", \"dynamicContainerAndTest\")));\n\t\tvar dynamicLeaf = spy(new MyLeaf(dynamicContainerAndTest.getUniqueId().append(\"test\", \"dynamicLeaf\")));\n\n\t\troot.addChild(child);\n\t\twhen(child.execute(any(), any())).thenAnswer(execute(dynamicContainerAndTest));\n\t\twhen(dynamicContainerAndTest.execute(any(), any())).thenAnswer(execute(dynamicLeaf));\n\t\twhen(dynamicLeaf.execute(any(), any())).thenAnswer(invocation -> {\n\t\t\tthrow new AssertionError(\"test fails\");\n\t\t});\n\n\t\tvar inOrder = inOrder(listener, root, child, dynamicContainerAndTest, dynamicLeaf);\n\n\t\texecutor.execute();\n\n\t\tvar aTestExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(listener).executionStarted(root);\n\n\t\tinOrder.verify(child).prepare(rootContext);\n\t\tinOrder.verify(child).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(child);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\n\t\tinOrder.verify(listener).dynamicTestRegistered(dynamicContainerAndTest);\n\t\tinOrder.verify(dynamicContainerAndTest).prepare(rootContext);\n\t\tinOrder.verify(dynamicContainerAndTest).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(dynamicContainerAndTest);\n\t\tinOrder.verify(dynamicContainerAndTest).execute(eq(rootContext), any());\n\n\t\tinOrder.verify(listener).dynamicTestRegistered(dynamicLeaf);\n\t\tinOrder.verify(dynamicLeaf).prepare(rootContext);\n\t\tinOrder.verify(dynamicLeaf).shouldBeSkipped(rootContext);\n\t\tinOrder.verify(listener).executionStarted(dynamicLeaf);\n\t\tinOrder.verify(dynamicLeaf).execute(eq(rootContext), any());\n\n\t\tinOrder.verify(listener).executionFinished(eq(dynamicLeaf), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(dynamicContainerAndTest), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(child), aTestExecutionResult.capture());\n\t\tinOrder.verify(listener).executionFinished(eq(root), any(TestExecutionResult.class));\n\n\t\tassertThat(aTestExecutionResult.getAllValues()).extracting(TestExecutionResult::getStatus).containsExactly(\n\t\t\tFAILED, SUCCESSFUL, SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid executesDynamicTestDescriptorsWithCustomListener() {\n\n\t\tvar leafUniqueId = UniqueId.root(\"leaf\", \"child leaf\");\n\t\tvar child = spy(new MyLeaf(leafUniqueId));\n\t\tvar dynamicTestDescriptor = spy(new MyLeaf(leafUniqueId.append(\"dynamic\", \"child\")));\n\t\troot.addChild(child);\n\n\t\tvar anotherListener = mock(EngineExecutionListener.class);\n\t\twhen(child.execute(any(), any())).thenAnswer(\n\t\t\tuseDynamicTestExecutor(executor -> executor.execute(dynamicTestDescriptor, anotherListener)));\n\n\t\texecutor.execute();\n\n\t\tvar inOrder = inOrder(listener, anotherListener, root, child, dynamicTestDescriptor);\n\t\tinOrder.verify(anotherListener).dynamicTestRegistered(dynamicTestDescriptor);\n\t\tinOrder.verify(anotherListener).executionStarted(dynamicTestDescriptor);\n\t\tinOrder.verify(dynamicTestDescriptor).execute(eq(rootContext), any());\n\t\tinOrder.verify(dynamicTestDescriptor).nodeFinished(rootContext, dynamicTestDescriptor, successful());\n\t\tinOrder.verify(anotherListener).executionFinished(dynamicTestDescriptor, successful());\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(ParallelExecutorServiceType.class)\n\t@MockitoSettings(strictness = LENIENT)\n\tvoid canAbortExecutionOfDynamicChild(ParallelExecutorServiceType executorServiceType) throws Exception {\n\n\t\tvar leafUniqueId = UniqueId.root(\"leaf\", \"child leaf\");\n\t\tvar child = spy(new MyLeaf(leafUniqueId));\n\t\tvar dynamicTestDescriptor = spy(new MyLeaf(leafUniqueId.append(\"dynamic\", \"child\")));\n\t\troot.addChild(child);\n\n\t\tvar startedLatch = new CountDownLatch(1);\n\t\tvar interrupted = new CompletableFuture<Boolean>();\n\n\t\twhen(child.execute(any(), any())).thenAnswer(useDynamicTestExecutor(executor -> {\n\t\t\tvar future = executor.execute(dynamicTestDescriptor, EngineExecutionListener.NOOP);\n\t\t\tstartedLatch.await();\n\t\t\tfuture.cancel(true);\n\t\t\texecutor.awaitFinished();\n\t\t}));\n\t\twhen(dynamicTestDescriptor.execute(any(), any())).thenAnswer(invocation -> {\n\t\t\tstartedLatch.countDown();\n\t\t\ttry {\n\t\t\t\tnew CountDownLatch(1).await(); // block until interrupted\n\t\t\t\tinterrupted.complete(false);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tinterrupted.complete(true);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t});\n\n\t\tvar parameters = ConfigurationParametersFactoryForTests.create(Map.of(//\n\t\t\tParallelHierarchicalTestExecutorServiceFactory.EXECUTOR_SERVICE_PROPERTY_NAME, executorServiceType, //\n\t\t\tDefaultParallelExecutionConfigurationStrategy.CONFIG_STRATEGY_PROPERTY_NAME, \"fixed\", //\n\t\t\tDefaultParallelExecutionConfigurationStrategy.CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, 2));\n\n\t\ttry (var executorService = ParallelHierarchicalTestExecutorServiceFactory.create(parameters)) {\n\t\t\tcreateExecutor(executorService).execute().get();\n\t\t}\n\n\t\tverify(listener).executionFinished(child, successful());\n\t\tassertTrue(interrupted.get(), \"dynamic node was interrupted\");\n\t}\n\n\tprivate Answer<Object> execute(TestDescriptor dynamicChild) {\n\t\treturn useDynamicTestExecutor(executor -> executor.execute(dynamicChild));\n\t}\n\n\tprivate Answer<Object> useDynamicTestExecutor(ThrowingConsumer<@NonNull DynamicTestExecutor> action) {\n\t\treturn invocation -> {\n\t\t\tDynamicTestExecutor dynamicTestExecutor = invocation.getArgument(1);\n\t\t\taction.accept(dynamicTestExecutor);\n\t\t\treturn invocation.getArgument(0);\n\t\t};\n\t}\n\n\t/**\n\t * Verifies support for unrecoverable exceptions.\n\t */\n\t@Test\n\tvoid outOfMemoryErrorInShouldBeSkipped() throws Exception {\n\t\tvar child = spy(new MyContainer(UniqueId.root(\"container\", \"child container\")));\n\t\tvar outOfMemoryError = new OutOfMemoryError(\"in skip\");\n\t\twhen(child.shouldBeSkipped(rootContext)).thenThrow(outOfMemoryError);\n\t\troot.addChild(child);\n\n\t\tThrowable actualException = assertThrows(OutOfMemoryError.class, () -> executor.execute());\n\t\tassertSame(outOfMemoryError, actualException);\n\t}\n\n\t/**\n\t * Verifies support for unrecoverable exceptions.\n\t */\n\t@Test\n\tvoid outOfMemoryErrorInLeafExecution() {\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"leaf\")));\n\t\tvar outOfMemoryError = new OutOfMemoryError(\"in test\");\n\t\twhen(child.execute(eq(rootContext), any())).thenThrow(outOfMemoryError);\n\t\troot.addChild(child);\n\n\t\tThrowable actualException = assertThrows(OutOfMemoryError.class, () -> executor.execute());\n\t\tassertSame(outOfMemoryError, actualException);\n\t}\n\n\t@Test\n\tvoid exceptionInAfterDoesNotHideEarlierException() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"leaf\")));\n\t\tException exceptionInExecute = new RuntimeException(\"execute\");\n\t\tException exceptionInAfter = new RuntimeException(\"after\");\n\t\tdoThrow(exceptionInExecute).when(child).execute(eq(rootContext), any());\n\t\tdoThrow(exceptionInAfter).when(child).after(eq(rootContext));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(child).after(eq(rootContext));\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable().orElseThrow()).isSameAs(\n\t\t\texceptionInExecute).hasSuppressedException(exceptionInAfter);\n\t}\n\n\t@Test\n\tvoid dynamicTestDescriptorsMustNotDeclareExclusiveResources() {\n\n\t\tvar leafUniqueId = UniqueId.root(\"leaf\", \"child leaf\");\n\t\tvar child = spy(new MyLeaf(leafUniqueId));\n\t\tvar dynamicTestDescriptor = spy(new MyLeaf(leafUniqueId.append(\"dynamic\", \"child\")));\n\t\twhen(dynamicTestDescriptor.getExclusiveResources()).thenReturn(\n\t\t\tSet.of(new ExclusiveResource(\"foo\", LockMode.READ)));\n\n\t\twhen(child.execute(any(), any())).thenAnswer(execute(dynamicTestDescriptor));\n\t\troot.addChild(child);\n\n\t\texecutor.execute();\n\n\t\tvar aTestExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(dynamicTestDescriptor);\n\t\tverify(listener).executionFinished(eq(dynamicTestDescriptor), aTestExecutionResult.capture());\n\n\t\tvar executionResult = aTestExecutionResult.getValue();\n\t\tassertThat(executionResult.getStatus()).isEqualTo(FAILED);\n\t\tassertThat(executionResult.getThrowable()).isPresent();\n\t\tassertThat(executionResult.getThrowable().get()).hasMessageContaining(\n\t\t\t\"Dynamic test descriptors must not declare exclusive resources\");\n\t}\n\n\t@Test\n\tvoid exceptionInAfterIsReportedInsteadOfEarlierTestAbortedException() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"leaf\")));\n\t\tException exceptionInExecute = new TestAbortedException(\"execute\");\n\t\tException exceptionInAfter = new RuntimeException(\"after\");\n\t\tdoThrow(exceptionInExecute).when(child).execute(eq(rootContext), any());\n\t\tdoThrow(exceptionInAfter).when(child).after(eq(rootContext));\n\t\troot.addChild(child);\n\n\t\tvar inOrder = inOrder(listener, child);\n\n\t\texecutor.execute();\n\n\t\tvar childExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(child).execute(eq(rootContext), any());\n\t\tinOrder.verify(child).after(eq(rootContext));\n\t\tinOrder.verify(listener).executionFinished(eq(child), childExecutionResult.capture());\n\n\t\tassertThat(childExecutionResult.getValue().getStatus()).isEqualTo(FAILED);\n\t\tassertThat(childExecutionResult.getValue().getThrowable().orElseThrow()).isSameAs(\n\t\t\texceptionInAfter).hasSuppressedException(exceptionInExecute);\n\t}\n\n\t@Test\n\tvoid reportsNodeAsSkippedWhenCancelledPriorToExecution() {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child container\")));\n\t\troot.addChild(child);\n\n\t\tcancellationToken.cancel();\n\t\texecutor.execute();\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\t\tinOrder.verify(root).nodeSkipped(rootContext, root, NodeTestTask.CANCELLED_SKIP_RESULT);\n\t\tinOrder.verify(listener).executionSkipped(root, NodeTestTask.CANCELLED_SKIP_RESULT.getReason().orElseThrow());\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\t@Test\n\tvoid reportsNodeAsSkippedWhenCancelledDuringPrepare() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child container\")));\n\t\troot.addChild(child);\n\n\t\twhen(root.prepare(any())).thenAnswer(invocation -> {\n\t\t\tcancellationToken.cancel();\n\t\t\treturn invocation.callRealMethod();\n\t\t});\n\n\t\texecutor.execute();\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\t\tinOrder.verify(root).prepare(rootContext);\n\t\tinOrder.verify(root).nodeSkipped(rootContext, root, NodeTestTask.CANCELLED_SKIP_RESULT);\n\t\tinOrder.verify(listener).executionSkipped(root, NodeTestTask.CANCELLED_SKIP_RESULT.getReason().orElseThrow());\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\t@Test\n\tvoid reportsNodeAsSkippedWhenCancelledDuringBefore() throws Exception {\n\n\t\tvar child = spy(new MyLeaf(UniqueId.root(\"leaf\", \"child container\")));\n\t\troot.addChild(child);\n\n\t\twhen(root.before(any())).thenAnswer(invocation -> {\n\t\t\tcancellationToken.cancel();\n\t\t\treturn invocation.callRealMethod();\n\t\t});\n\n\t\texecutor.execute();\n\n\t\tvar inOrder = inOrder(listener, root, child);\n\t\tinOrder.verify(listener).executionStarted(root);\n\t\tinOrder.verify(root).before(any());\n\t\tinOrder.verify(root).execute(any(), any());\n\t\tinOrder.verify(child).nodeSkipped(any(), eq(child), eq(NodeTestTask.CANCELLED_SKIP_RESULT));\n\t\tinOrder.verify(listener).executionSkipped(child, NodeTestTask.CANCELLED_SKIP_RESULT.getReason().orElseThrow());\n\t\tinOrder.verify(root).after(any());\n\t\tinOrder.verify(root).cleanUp(any());\n\t\tinOrder.verify(listener).executionFinished(root, TestExecutionResult.successful());\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\t// -------------------------------------------------------------------\n\n\tprivate static class MyEngineExecutionContext implements EngineExecutionContext {\n\t}\n\n\t@NullMarked\n\tprivate static class MyContainer extends AbstractTestDescriptor implements Node<MyEngineExecutionContext> {\n\n\t\tMyContainer(UniqueId uniqueId) {\n\t\t\tsuper(uniqueId, uniqueId.toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\treturn Type.CONTAINER;\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class MyLeaf extends AbstractTestDescriptor implements Node<MyEngineExecutionContext> {\n\n\t\tMyLeaf(UniqueId uniqueId) {\n\t\t\tsuper(uniqueId, uniqueId.toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic MyEngineExecutionContext execute(MyEngineExecutionContext context,\n\t\t\t\tDynamicTestExecutor dynamicTestExecutor) {\n\t\t\treturn context;\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\treturn Type.TEST;\n\t\t}\n\t}\n\n\t@NullMarked\n\tprivate static class MyContainerAndTestTestCase extends AbstractTestDescriptor\n\t\t\timplements Node<MyEngineExecutionContext> {\n\n\t\tMyContainerAndTestTestCase(UniqueId uniqueId) {\n\t\t\tsuper(uniqueId, uniqueId.toString());\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\treturn Type.CONTAINER_AND_TEST;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/LockManagerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ_WRITE;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\n\n/**\n * @since 1.3\n */\nclass LockManagerTests {\n\n\tprivate final LockManager lockManager = new LockManager();\n\n\t@Test\n\tvoid returnsNopLockWithoutExclusiveResources() {\n\t\tCollection<ExclusiveResource> resources = Set.of();\n\n\t\tvar locks = getLocks(resources, NopLock.class);\n\n\t\tassertThat(locks).isEmpty();\n\t}\n\n\t@Test\n\tvoid returnsSingleLockForSingleExclusiveResource() {\n\t\tCollection<ExclusiveResource> resources = Set.of(new ExclusiveResource(\"foo\", READ));\n\n\t\tvar locks = getLocks(resources, SingleLock.class);\n\n\t\tassertThat(locks).hasSize(1);\n\t\tassertThat(locks.getFirst()).isInstanceOf(ReadLock.class);\n\t}\n\n\t@Test\n\tvoid returnsCompositeLockForMultipleDifferentExclusiveResources() {\n\t\tCollection<ExclusiveResource> resources = List.of( //\n\t\t\tnew ExclusiveResource(\"a\", READ), //\n\t\t\tnew ExclusiveResource(\"b\", READ_WRITE));\n\n\t\tvar locks = getLocks(resources, CompositeLock.class);\n\n\t\tassertThat(locks).hasSize(2);\n\t\tassertThat(locks.get(0)).isInstanceOf(ReadLock.class);\n\t\tassertThat(locks.get(1)).isInstanceOf(WriteLock.class);\n\t}\n\n\t@Test\n\tvoid reusesSameLockForExclusiveResourceWithSameKey() {\n\t\tCollection<ExclusiveResource> resources = Set.of(new ExclusiveResource(\"foo\", READ));\n\n\t\tvar locks1 = getLocks(resources, SingleLock.class);\n\t\tvar locks2 = getLocks(resources, SingleLock.class);\n\n\t\tassertThat(locks1).hasSize(1);\n\t\tassertThat(locks2).hasSize(1);\n\t\tassertThat(locks1.getFirst()).isSameAs(locks2.getFirst());\n\t}\n\n\t@Test\n\tvoid returnsWriteLockForExclusiveResourceWithBothLockModes() {\n\t\tCollection<ExclusiveResource> resources = List.of( //\n\t\t\tnew ExclusiveResource(\"bar\", READ), //\n\t\t\tnew ExclusiveResource(\"foo\", READ), //\n\t\t\tnew ExclusiveResource(\"foo\", READ_WRITE), //\n\t\t\tnew ExclusiveResource(\"bar\", READ_WRITE));\n\n\t\tvar locks = getLocks(resources, CompositeLock.class);\n\n\t\tassertThat(locks).hasSize(2);\n\t\tassertThat(locks.get(0)).isInstanceOf(WriteLock.class);\n\t\tassertThat(locks.get(1)).isInstanceOf(WriteLock.class);\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource\n\tvoid globalLockComesFirst(LockMode globalLockMode) {\n\t\tCollection<ExclusiveResource> resources = List.of( //\n\t\t\tnew ExclusiveResource(\"___foo\", READ), //\n\t\t\tnew ExclusiveResource(\"foo\", READ_WRITE), //\n\t\t\tnew ExclusiveResource(GLOBAL_KEY, globalLockMode), //\n\t\t\tnew ExclusiveResource(\"bar\", READ_WRITE));\n\n\t\tvar locks = getLocks(resources, CompositeLock.class);\n\n\t\tassertThat(locks).hasSize(4);\n\t\tassertThat(locks.get(0)).isEqualTo(getSingleLock(GLOBAL_KEY, globalLockMode));\n\t\tassertThat(locks.get(1)).isEqualTo(getSingleLock(\"___foo\", READ));\n\t\tassertThat(locks.get(2)).isEqualTo(getSingleLock(\"bar\", READ_WRITE));\n\t\tassertThat(locks.get(3)).isEqualTo(getSingleLock(\"foo\", READ_WRITE));\n\t}\n\n\t@Test\n\tvoid usesSingleInstanceForGlobalReadLock() {\n\t\tvar lock = lockManager.getLockForResources(List.of(ExclusiveResource.GLOBAL_READ));\n\n\t\tassertThat(lock) //\n\t\t\t\t.isInstanceOf(SingleLock.class) //\n\t\t\t\t.isSameAs(lockManager.getLockForResource(ExclusiveResource.GLOBAL_READ));\n\t}\n\n\t@Test\n\tvoid usesSingleInstanceForGlobalReadWriteLock() {\n\t\tvar lock = lockManager.getLockForResources(List.of(ExclusiveResource.GLOBAL_READ_WRITE));\n\n\t\tassertThat(lock) //\n\t\t\t\t.isInstanceOf(SingleLock.class) //\n\t\t\t\t.isSameAs(lockManager.getLockForResource(ExclusiveResource.GLOBAL_READ_WRITE));\n\t}\n\n\tprivate Lock getSingleLock(String key, LockMode lockMode) {\n\t\treturn getLocks(Set.of(new ExclusiveResource(key, lockMode)), SingleLock.class).getFirst();\n\t}\n\n\tprivate List<Lock> getLocks(Collection<ExclusiveResource> resources, Class<? extends ResourceLock> type) {\n\t\tvar lock = lockManager.getLockForResources(resources);\n\t\tassertThat(lock).isInstanceOf(type);\n\t\treturn ResourceLockSupport.getLocks(lock);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/MemoryLeakTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.junit.jupiter.api.TestInstance.Lifecycle;\n\n/**\n * Integration tests intended to verify that memory leaks do not\n * exist with regard to the \"context\" held by {@link NodeTestTask}.\n *\n * @since 5.3.1\n * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1578\">GitHub issue #1578</a>\n */\n// Explicitly specifying Lifecycle.PER_METHOD to be certain that the\n// test instance state is recreated for every test method executed.\n@TestInstance(Lifecycle.PER_METHOD)\nclass MemoryLeakTests {\n\n\t// Allocate 500 MB of memory per test method.\n\t//\n\t// If the test instance is garbage collected, this should not cause any\n\t// problems for the JUnit build; however, if the instances of this test\n\t// class are NOT garbage collected, we should run out of memory pretty\n\t// quickly since the instances of this test class would consume 5GB of\n\t// heap space.\n\tfinal byte[] state = new byte[524_288_000];\n\n\t@Test\n\tvoid test01() {\n\t}\n\n\t@Test\n\tvoid test02() {\n\t}\n\n\t@Test\n\tvoid test03() {\n\t}\n\n\t@Test\n\tvoid test04() {\n\t}\n\n\t@Test\n\tvoid test05() {\n\t}\n\n\t@Test\n\tvoid test06() {\n\t}\n\n\t@Test\n\tvoid test07() {\n\t}\n\n\t@Test\n\tvoid test08() {\n\t}\n\n\t@Test\n\tvoid test09() {\n\t}\n\n\t@Test\n\tvoid test10() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/NodeTreeWalkerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.InstanceOfAssertFactories.LIST;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode.READ_WRITE;\nimport static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.SAME_THREAD;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.locks.Lock;\nimport java.util.function.Function;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.parallel.ResourceAccessMode;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.api.parallel.Resources;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\n\n/**\n * @since 1.3\n */\nclass NodeTreeWalkerIntegrationTests {\n\n\tLockManager lockManager = new LockManager();\n\tNodeTreeWalker nodeTreeWalker = new NodeTreeWalker(lockManager);\n\n\t@Test\n\tvoid pullUpExclusiveChildResourcesToTestClass() {\n\t\tvar engineDescriptor = discover(TestCaseWithResourceLock.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ), getReadWriteLock(\"a\"), getReadWriteLock(\"b\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).contains(SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid setsForceExecutionModeForChildrenWithWriteLocksOnClass() {\n\t\tvar engineDescriptor = discover(TestCaseWithResourceWriteLockOnClass.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ), getReadWriteLock(\"a\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).contains(SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid doesntSetForceExecutionModeForChildrenWithReadLocksOnClass() {\n\t\tvar engineDescriptor = discover(TestCaseWithResourceReadLockOnClass.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ), getReadLock(\"a\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).isEmpty();\n\t}\n\n\t@Test\n\tvoid setsForceExecutionModeForChildrenWithReadLocksOnClassAndWriteLockOnTest() {\n\t\tvar engineDescriptor = discover(TestCaseWithResourceReadLockOnClassAndWriteClockOnTestCase.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ), getReadWriteLock(\"a\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).contains(SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid doesntSetForceExecutionModeForChildrenWithReadLocksOnClassAndReadLockOnTest() {\n\t\tvar engineDescriptor = discover(TestCaseWithResourceReadLockOnClassAndReadClockOnTestCase.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ), getReadLock(\"a\"), getReadLock(\"b\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar testMethodDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).isEmpty();\n\t}\n\n\t@Test\n\tvoid leavesResourceLockOnTestMethodWhenClassDoesNotUseResource() {\n\t\tvar engineDescriptor = discover(TestCaseWithoutResourceLock.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ)));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tassertThat(testClassDescriptor.getChildren()).hasSize(2);\n\t\tvar children = testClassDescriptor.getChildren().iterator();\n\t\tvar testMethodDescriptor = children.next();\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getReadWriteLock(\"a\")));\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).isEmpty();\n\n\t\tvar nestedTestClassDescriptor = children.next();\n\t\tassertThat(advisor.getResourceLock(nestedTestClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getReadWriteLock(\"b\"), getReadWriteLock(\"c\")));\n\t\tassertThat(advisor.getForcedExecutionMode(nestedTestClassDescriptor)).isEmpty();\n\n\t\tvar nestedTestMethodDescriptor = getOnlyElement(nestedTestClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(nestedTestMethodDescriptor)).extracting(allLocks()).isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(nestedTestMethodDescriptor)).contains(SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid coarsensGlobalLockToEngineDescriptorChild() {\n\t\tvar engineDescriptor = discover(TestCaseWithGlobalLockRequiringChild.class);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tvar testClassDescriptor = getOnlyElement(engineDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of(getLock(GLOBAL_READ_WRITE)));\n\t\tassertThat(advisor.getForcedExecutionMode(testClassDescriptor)).isEmpty();\n\n\t\tvar nestedTestClassDescriptor = getOnlyElement(testClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(nestedTestClassDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(nestedTestClassDescriptor)).contains(SAME_THREAD);\n\n\t\tvar testMethodDescriptor = getOnlyElement(nestedTestClassDescriptor.getChildren());\n\t\tassertThat(advisor.getResourceLock(testMethodDescriptor)).extracting(allLocks()) //\n\t\t\t\t.isEqualTo(List.of());\n\t\tassertThat(advisor.getForcedExecutionMode(testMethodDescriptor)).contains(SAME_THREAD);\n\t}\n\n\t@Test\n\tvoid putsGlobalReadLockOnFirstNodeThatRequiresIt() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"dummy\"), \"Dummy\");\n\n\t\tvar containerWithoutBehavior = new NodeStub(engineDescriptor.getUniqueId().append(\"container\", \"1\"),\n\t\t\t\"Container 1\") //\n\t\t\t\t\t.withGlobalReadLockRequired(false);\n\t\tvar test1 = new NodeStub(containerWithoutBehavior.getUniqueId().append(\"test\", \"1\"), \"Test 1\") //\n\t\t\t\t.withExclusiveResource(new ExclusiveResource(\"key1\", READ_WRITE));\n\t\tcontainerWithoutBehavior.addChild(test1);\n\n\t\tvar containerWithBehavior = new NodeStub(engineDescriptor.getUniqueId().append(\"container\", \"2\"), \"Container 2\") //\n\t\t\t\t.withGlobalReadLockRequired(true);\n\t\tvar test2 = new NodeStub(containerWithBehavior.getUniqueId().append(\"test\", \"2\"), \"Test 2\") //\n\t\t\t\t.withExclusiveResource(new ExclusiveResource(\"key2\", READ_WRITE));\n\t\tcontainerWithBehavior.addChild(test2);\n\n\t\tengineDescriptor.addChild(containerWithoutBehavior);\n\t\tengineDescriptor.addChild(containerWithBehavior);\n\n\t\tvar advisor = nodeTreeWalker.walk(engineDescriptor);\n\n\t\tassertThat(advisor.getResourceLock(containerWithoutBehavior)) //\n\t\t\t\t.extracting(allLocks(), LIST) //\n\t\t\t\t.isEmpty();\n\t\tassertThat(advisor.getResourceLock(test1)) //\n\t\t\t\t.extracting(allLocks(), LIST) //\n\t\t\t\t.containsExactly(getLock(GLOBAL_READ), getReadWriteLock(\"key1\"));\n\n\t\tassertThat(advisor.getResourceLock(containerWithBehavior)) //\n\t\t\t\t.extracting(allLocks(), LIST) //\n\t\t\t\t.containsExactly(getLock(GLOBAL_READ));\n\t\tassertThat(advisor.getResourceLock(test2)) //\n\t\t\t\t.extracting(allLocks(), LIST) //\n\t\t\t\t.containsExactly(getReadWriteLock(\"key2\"));\n\t}\n\n\t@Test\n\tvoid doesNotAllowExclusiveResourcesWithoutRequiringGlobalReadLock() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"dummy\"), \"Dummy\");\n\t\tvar invalidNode = new NodeStub(engineDescriptor.getUniqueId().append(\"container\", \"1\"), \"Container\") //\n\t\t\t\t.withGlobalReadLockRequired(false) //\n\t\t\t\t.withExclusiveResource(new ExclusiveResource(\"key\", READ_WRITE));\n\t\tengineDescriptor.addChild(invalidNode);\n\n\t\tassertPreconditionViolationFor(() -> nodeTreeWalker.walk(engineDescriptor)) //\n\t\t\t\t.withMessage(\"Node requiring exclusive resources must also require global read lock: \" + invalidNode);\n\t}\n\n\tprivate static Function<org.junit.platform.engine.support.hierarchical.ResourceLock, List<Lock>> allLocks() {\n\t\treturn ResourceLockSupport::getLocks;\n\t}\n\n\tprivate Lock getReadWriteLock(String key) {\n\t\treturn getLock(new ExclusiveResource(key, READ_WRITE));\n\t}\n\n\tprivate Lock getReadLock(String key) {\n\t\treturn getLock(new ExclusiveResource(key, READ));\n\t}\n\n\tprivate Lock getLock(ExclusiveResource exclusiveResource) {\n\t\treturn getOnlyElement(ResourceLockSupport.getLocks(lockManager.getLockForResource(exclusiveResource)));\n\t}\n\n\tprivate TestDescriptor discover(Class<?> testClass) {\n\t\tvar discoveryRequest = request().selectors(selectClass(testClass)).build();\n\t\treturn new JupiterTestEngine().discover(discoveryRequest, UniqueId.forEngine(\"junit-jupiter\"));\n\t}\n\n\t@ResourceLock(\"a\")\n\tstatic class TestCaseWithResourceLock {\n\t\t@Test\n\t\t@ResourceLock(\"b\")\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tstatic class TestCaseWithoutResourceLock {\n\t\t@Test\n\t\t@ResourceLock(\"a\")\n\t\tvoid test() {\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(\"c\")\n\t\tclass NestedTestCaseWithResourceLock {\n\t\t\t@Test\n\t\t\t@ResourceLock(\"b\")\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock(Resources.SYSTEM_PROPERTIES)\n\tstatic class TestCaseWithGlobalLockRequiringChild {\n\t\t@Nested\n\t\tclass NestedTestCaseWithResourceLock {\n\t\t\t@Test\n\t\t\t@ResourceLock(ExclusiveResource.GLOBAL_KEY)\n\t\t\tvoid test() {\n\t\t\t}\n\t\t}\n\t}\n\n\t@ResourceLock(\"a\")\n\tstatic class TestCaseWithResourceWriteLockOnClass {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@ResourceLock(value = \"a\", mode = ResourceAccessMode.READ)\n\tstatic class TestCaseWithResourceReadLockOnClass {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@ResourceLock(value = \"a\", mode = ResourceAccessMode.READ)\n\tstatic class TestCaseWithResourceReadLockOnClassAndWriteClockOnTestCase {\n\t\t@Test\n\t\t@ResourceLock(\"a\")\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@ResourceLock(value = \"a\", mode = ResourceAccessMode.READ)\n\tstatic class TestCaseWithResourceReadLockOnClassAndReadClockOnTestCase {\n\t\t@Test\n\t\t@ResourceLock(value = \"b\", mode = ResourceAccessMode.READ)\n\t\tvoid test() {\n\t\t}\n\t}\n\n\t@NullMarked\n\tstatic class NodeStub extends AbstractTestDescriptor implements Node<EngineExecutionContext> {\n\n\t\tprivate final Set<ExclusiveResource> exclusiveResources = new LinkedHashSet<>();\n\t\tprivate boolean globalReadLockRequired = true;\n\n\t\tNodeStub(UniqueId uniqueId, String displayName) {\n\t\t\tsuper(uniqueId, displayName);\n\t\t}\n\n\t\tNodeStub withExclusiveResource(ExclusiveResource exclusiveResource) {\n\t\t\texclusiveResources.add(exclusiveResource);\n\t\t\treturn this;\n\t\t}\n\n\t\tNodeStub withGlobalReadLockRequired(boolean globalReadLockRequired) {\n\t\t\tthis.globalReadLockRequired = globalReadLockRequired;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isGlobalReadLockRequired() {\n\t\t\treturn globalReadLockRequired;\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\tthrow new UnsupportedOperationException(\"should not be called\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<ExclusiveResource> getExclusiveResources() {\n\t\t\treturn exclusiveResources;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ParallelExecutionIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.concurrent.TimeUnit.MILLISECONDS;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Constants.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME;\nimport static org.junit.jupiter.api.Constants.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static org.junit.jupiter.api.parallel.ResourceAccessMode.READ_WRITE;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_KEY;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.EventConditions.type;\nimport static org.junit.platform.testkit.engine.EventType.REPORTING_ENTRY_PUBLISHED;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.time.Instant;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.CyclicBarrier;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.function.UnaryOperator;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DynamicContainer;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer.MethodName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.DynamicTestInvocationContext;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.InvocationInterceptor;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.api.parallel.Isolated;\nimport org.junit.jupiter.api.parallel.ResourceLock;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.hierarchical.ParallelHierarchicalTestExecutorServiceFactory.ParallelExecutorServiceType;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Event;\nimport org.junit.platform.testkit.engine.Events;\n\n/**\n * @since 1.3\n */\n@ParameterizedClass\n@EnumSource(ParallelExecutorServiceType.class)\nrecord ParallelExecutionIntegrationTests(ParallelExecutorServiceType executorServiceType) {\n\n\t@Test\n\tvoid successfulParallelTest(TestReporter reporter) {\n\t\tvar events = executeConcurrentlySuccessfully(3, SuccessfulParallelTestCase.class).list();\n\n\t\tvar startedTimestamps = getTimestampsFor(events, event(test(), started()));\n\t\tvar finishedTimestamps = getTimestampsFor(events, event(test(), finishedSuccessfully()));\n\t\treporter.publishEntry(\"startedTimestamps\", startedTimestamps.toString());\n\t\treporter.publishEntry(\"finishedTimestamps\", finishedTimestamps.toString());\n\n\t\tassertThat(startedTimestamps).hasSize(3);\n\t\tassertThat(finishedTimestamps).hasSize(3);\n\t\tassertThat(startedTimestamps).allMatch(startTimestamp -> finishedTimestamps.stream().noneMatch(\n\t\t\tfinishedTimestamp -> finishedTimestamp.isBefore(startTimestamp)));\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(3);\n\t}\n\n\t@Test\n\tvoid failingTestWithoutLock() {\n\t\tvar events = executeConcurrently(3, FailingWithoutLockTestCase.class).list();\n\t\tassertThat(events.stream().filter(event(test(), finishedWithFailure())::matches)).hasSize(2);\n\t}\n\n\t@Test\n\tvoid successfulTestWithMethodLock() {\n\t\tvar events = executeConcurrentlySuccessfully(3, SuccessfulWithMethodLockTestCase.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(3);\n\t}\n\n\t@Test\n\tvoid successfulTestWithClassLock() {\n\t\tvar events = executeConcurrentlySuccessfully(3, SuccessfulWithClassLockTestCase.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(1);\n\t}\n\n\t@Test\n\tvoid testCaseWithFactory() {\n\t\tvar events = executeConcurrentlySuccessfully(3, TestCaseWithTestFactory.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(1);\n\t}\n\n\t@Test\n\tvoid customContextClassLoader() {\n\t\tvar currentThread = Thread.currentThread();\n\t\tvar currentLoader = currentThread.getContextClassLoader();\n\t\tvar smilingLoader = new URLClassLoader(\"(-:\", new URL[0], ClassLoader.getSystemClassLoader());\n\t\tcurrentThread.setContextClassLoader(smilingLoader);\n\t\ttry {\n\t\t\tvar events = executeConcurrentlySuccessfully(3, SuccessfulWithMethodLockTestCase.class).list();\n\n\t\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(3);\n\t\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(3);\n\t\t\tassertThat(ThreadReporter.getLoaderNames(events)).containsExactly(\"(-:\");\n\t\t}\n\t\tfinally {\n\t\t\tcurrentThread.setContextClassLoader(currentLoader);\n\t\t}\n\t}\n\n\t@RepeatedTest(10)\n\tvoid mixingClassAndMethodLevelLocks() {\n\t\tvar events = executeConcurrentlySuccessfully(4, TestCaseWithSortedLocks.class,\n\t\t\tTestCaseWithUnsortedLocks.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(6);\n\t\tassertThat(ThreadReporter.getThreadNames(events).count()).isLessThanOrEqualTo(2);\n\t}\n\n\t@RepeatedTest(10)\n\tvoid locksOnNestedTests() {\n\t\tvar events = executeConcurrentlySuccessfully(3, TestCaseWithNestedLocks.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(6);\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(1);\n\t}\n\n\t@Test\n\tvoid afterHooksAreCalledAfterConcurrentDynamicTestsAreFinished() {\n\t\tvar events = executeConcurrentlySuccessfully(3, ConcurrentDynamicTestCase.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(1);\n\t\tvar timestampedEvents = ConcurrentDynamicTestCase.events;\n\t\tassertThat(timestampedEvents.get(\"afterEach\")).isAfterOrEqualTo(timestampedEvents.get(\"dynamicTestFinished\"));\n\t}\n\n\t/**\n\t * @since 1.4\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1688\">gh-1688</a>\n\t */\n\t@Test\n\tvoid threadInterruptedByUserCode() {\n\t\tvar events = executeConcurrentlySuccessfully(3, InterruptedThreadTestCase.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(4);\n\t}\n\n\t@Test\n\tvoid executesTestTemplatesWithResourceLocksInSameThread() {\n\t\tvar events = executeConcurrentlySuccessfully(2, ConcurrentTemplateTestCase.class).list();\n\n\t\tassertThat(events.stream().filter(event(test(), finishedSuccessfully())::matches)).hasSize(10);\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(1);\n\t}\n\n\t@Test\n\tvoid executesClassesInParallelIfEnabledViaConfigurationParameter() {\n\t\tParallelClassesTestCase.GLOBAL_BARRIER.reset();\n\n\t\tvar configParams = Map.of(DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\");\n\t\tvar results = executeWithFixedParallelism(3, configParams, ParallelClassesTestCaseA.class,\n\t\t\tParallelClassesTestCaseB.class, ParallelClassesTestCaseC.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.succeeded(9));\n\t\tassertThat(ThreadReporter.getThreadNames(results.allEvents().list())).hasSize(3);\n\t\tvar testClassA = findFirstTestDescriptor(results, container(ParallelClassesTestCaseA.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassA))).hasSize(1);\n\t\tvar testClassB = findFirstTestDescriptor(results, container(ParallelClassesTestCaseB.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassB))).hasSize(1);\n\t\tvar testClassC = findFirstTestDescriptor(results, container(ParallelClassesTestCaseC.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassC))).hasSize(1);\n\t}\n\n\t@Test\n\tvoid executesMethodsInParallelIfEnabledViaConfigurationParameter() {\n\t\tParallelMethodsTestCase.barriersPerClass.clear();\n\n\t\tvar configParams = Map.of( //\n\t\t\tDEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\", //\n\t\t\tDEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME, \"same_thread\");\n\t\tvar results = executeWithFixedParallelism(3, configParams, ParallelMethodsTestCaseA.class,\n\t\t\tParallelMethodsTestCaseB.class, ParallelMethodsTestCaseC.class);\n\n\t\tresults.testEvents().assertStatistics(stats -> stats.succeeded(9));\n\t\tassertThat(ThreadReporter.getThreadNames(results.allEvents().list())).hasSizeGreaterThanOrEqualTo(3);\n\t\tvar testClassA = findFirstTestDescriptor(results, container(ParallelMethodsTestCaseA.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassA))).hasSize(3);\n\t\tvar testClassB = findFirstTestDescriptor(results, container(ParallelMethodsTestCaseB.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassB))).hasSize(3);\n\t\tvar testClassC = findFirstTestDescriptor(results, container(ParallelMethodsTestCaseC.class));\n\t\tassertThat(ThreadReporter.getThreadNames(getEventsOfChildren(results, testClassC))).hasSize(3);\n\t}\n\n\t@Test\n\tvoid canRunTestsIsolatedFromEachOther() {\n\t\texecuteConcurrentlySuccessfully(2, IsolatedTestCase.class);\n\t}\n\n\t@Test\n\tvoid canRunTestsIsolatedFromEachOtherWithNestedCases() {\n\t\texecuteConcurrentlySuccessfully(4, NestedIsolatedTestCase.class);\n\t}\n\n\t@Test\n\tvoid canRunTestsIsolatedFromEachOtherAcrossClasses() {\n\t\texecuteConcurrentlySuccessfully(4, IndependentClasses.A.class, IndependentClasses.B.class);\n\t}\n\n\t@RepeatedTest(10)\n\tvoid canRunTestsIsolatedFromEachOtherAcrossClassesWithOtherResourceLocks() {\n\t\texecuteConcurrentlySuccessfully(4, IndependentClasses.B.class, IndependentClasses.C.class);\n\t}\n\n\t@Test\n\tvoid runsIsolatedTestsLastToMaximizeParallelism() {\n\t\tvar configParams = Map.of( //\n\t\t\tDEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\", //\n\t\t\tPARALLEL_CONFIG_FIXED_MAX_POOL_SIZE_PROPERTY_NAME, \"3\" //\n\t\t);\n\t\tClass<?>[] testClasses = { IsolatedTestCase.class, SuccessfulParallelTestCase.class };\n\t\tvar events = executeWithFixedParallelism(3, configParams, testClasses) //\n\t\t\t\t.allEvents() //\n\t\t\t\t.assertStatistics(it -> it.failed(0));\n\n\t\tList<Event> parallelTestMethodEvents = events.reportingEntryPublished() //\n\t\t\t\t.filter(e -> e.getTestDescriptor().getSource() //\n\t\t\t\t\t\t.filter(it -> //\n\t\t\t\t\t\tit instanceof MethodSource methodSource\n\t\t\t\t\t\t\t\t&& SuccessfulParallelTestCase.class.equals(methodSource.getJavaClass()) //\n\t\t\t\t\t\t).isPresent() //\n\t\t\t\t) //\n\t\t\t\t.toList();\n\t\tassertThat(ThreadReporter.getThreadNames(parallelTestMethodEvents)).hasSize(3);\n\n\t\tvar parallelClassFinish = getOnlyElement(getTimestampsFor(events.list(),\n\t\t\tevent(container(SuccessfulParallelTestCase.class), finishedSuccessfully())));\n\t\tvar isolatedClassStart = getOnlyElement(\n\t\t\tgetTimestampsFor(events.list(), event(container(IsolatedTestCase.class), started())));\n\t\tassertThat(isolatedClassStart).isAfterOrEqualTo(parallelClassFinish);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { IsolatedMethodFirstTestCase.class, IsolatedMethodLastTestCase.class,\n\t\t\tIsolatedNestedMethodFirstTestCase.class, IsolatedNestedMethodLastTestCase.class })\n\tvoid canRunTestsIsolatedFromEachOtherWhenDeclaredOnMethodLevel(Class<?> testClass) {\n\t\tList<Event> events = executeConcurrentlySuccessfully(1, testClass).list();\n\n\t\tassertThat(ThreadReporter.getThreadNames(events)).hasSize(1);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"testFactoryImplicitContainerAndExplicitChildExecutionMode\",\n\t\t\t\"testFactoryExplicitContainerAndExplicitChildExecutionModeOnContainer\",\n\t\t\t\"testFactoryExplicitContainerAndExplicitChildExecutionModeOnEachChild\" })\n\tvoid allowsToControlExecutionModeOfDynamicTestsAndContainers(String methodName) {\n\n\t\tvar results = executeWithFixedParallelism(3, Map.of(),\n\t\t\tList.of(selectMethod(ConcurrentDynamicContainerTestCase.class, methodName)));\n\n\t\tresults.testEvents().assertStatistics(it -> it.succeeded(4));\n\t\tassertThat(ThreadReporter.getThreadNames(results.testEvents().list())).hasSize(2);\n\t}\n\n\t@Isolated(\"testing\")\n\tstatic class IsolatedTestCase {\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(2);\n\t\t}\n\n\t\t@Test\n\t\tvoid a() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid b() throws Exception {\n\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\tstatic class NestedIsolatedTestCase {\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(6);\n\t\t}\n\n\t\t@Test\n\t\tvoid a() throws Exception {\n\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid b() throws Exception {\n\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Nested\n\t\tclass Inner {\n\n\t\t\t@Test\n\t\t\tvoid a() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid b() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\t@Isolated\n\t\t\tclass InnerInner {\n\n\t\t\t\t@Test\n\t\t\t\tvoid a() throws Exception {\n\t\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t\t}\n\n\t\t\t\t@Test\n\t\t\t\tvoid b() throws Exception {\n\t\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class IsolatedMethodFirstTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(2);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated\n\t\tvoid test1() throws InterruptedException {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = \"b\", mode = READ_WRITE)\n\t\tvoid test2() throws InterruptedException {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class IsolatedMethodLastTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(2);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = \"b\", mode = READ_WRITE)\n\t\tvoid test1() throws InterruptedException {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated\n\t\tvoid test2() throws InterruptedException {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class IsolatedNestedMethodFirstTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(2);\n\t\t}\n\n\t\t@Nested\n\t\tclass Test1 {\n\n\t\t\t@Test\n\t\t\t@ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated\n\t\t\tvoid test1() throws InterruptedException {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\tclass Test2 {\n\n\t\t\t@Test\n\t\t\t@ResourceLock(value = \"b\", mode = READ_WRITE)\n\t\t\tvoid test2() throws InterruptedException {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class IsolatedNestedMethodLastTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(2);\n\t\t}\n\n\t\t@Nested\n\t\tclass Test1 {\n\n\t\t\t@Test\n\t\t\t@ResourceLock(value = \"b\", mode = READ_WRITE)\n\t\t\tvoid test1() throws InterruptedException {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\n\t\t@Nested\n\t\tclass Test2 {\n\n\t\t\t@Test\n\t\t\t@ResourceLock(value = GLOBAL_KEY, mode = READ_WRITE) // effectively @Isolated\n\t\t\tvoid test2() throws InterruptedException {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class IndependentClasses {\n\t\tstatic AtomicInteger sharedResource = new AtomicInteger();\n\t\tstatic CountDownLatch countDownLatch = new CountDownLatch(4);\n\n\t\tstatic class A {\n\t\t\t@Test\n\t\t\tvoid a() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid b() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\n\t\t@Isolated\n\t\tstatic class B {\n\t\t\t@Test\n\t\t\tvoid a() throws Exception {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid b() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\n\t\t@ResourceLock(\"other\")\n\t\tstatic class C {\n\t\t\t@Test\n\t\t\tvoid a() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid b() throws Exception {\n\t\t\t\tstoreAndBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate List<Event> getEventsOfChildren(EngineExecutionResults results, TestDescriptor container) {\n\t\treturn results.testEvents().filter(\n\t\t\tevent -> event.getTestDescriptor().getParent().orElseThrow().equals(container)).toList();\n\t}\n\n\tprivate TestDescriptor findFirstTestDescriptor(EngineExecutionResults results, Condition<Event> condition) {\n\t\treturn results.allEvents().filter(condition::matches).map(Event::getTestDescriptor).findFirst().orElseThrow();\n\t}\n\n\tprivate List<Instant> getTimestampsFor(List<Event> events, Condition<Event> condition) {\n\t\t// @formatter:off\n\t\treturn events.stream()\n\t\t\t\t.filter(condition::matches)\n\t\t\t\t.map(Event::getTimestamp)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\t}\n\n\tprivate Events executeConcurrentlySuccessfully(int parallelism, Class<?>... testClasses) {\n\t\tvar events = executeConcurrently(parallelism, testClasses);\n\t\ttry {\n\t\t\treturn events.assertStatistics(it -> it.failed(0));\n\t\t}\n\t\tcatch (AssertionError error) {\n\t\t\tevents.debug();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate Events executeConcurrently(int parallelism, Class<?>... testClasses) {\n\t\tMap<String, String> configParams = Map.of(DEFAULT_EXECUTION_MODE_PROPERTY_NAME, \"concurrent\");\n\t\treturn executeWithFixedParallelism(parallelism, configParams, testClasses) //\n\t\t\t\t.allEvents();\n\t}\n\n\tprivate EngineExecutionResults executeWithFixedParallelism(int parallelism, Map<String, String> configParams,\n\t\t\tClass<?>... testClasses) {\n\t\treturn executeWithFixedParallelism(parallelism, configParams, selectClasses(testClasses));\n\t}\n\n\tprivate EngineExecutionResults executeWithFixedParallelism(int parallelism, Map<String, String> configParams,\n\t\t\tList<? extends DiscoverySelector> selectors) {\n\t\treturn EngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.selectors(selectors) //\n\t\t\t\t.configurationParameter(PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME, String.valueOf(true)) //\n\t\t\t\t.configurationParameter(PARALLEL_CONFIG_EXECUTOR_SERVICE_PROPERTY_NAME, executorServiceType.name()) //\n\t\t\t\t.configurationParameter(PARALLEL_CONFIG_STRATEGY_PROPERTY_NAME, \"fixed\") //\n\t\t\t\t.configurationParameter(PARALLEL_CONFIG_FIXED_PARALLELISM_PROPERTY_NAME, String.valueOf(parallelism)) //\n\t\t\t\t.configurationParameters(configParams) //\n\t\t\t\t.execute();\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@ExtendWith(ThreadReporter.class)\n\t@Execution(SAME_THREAD)\n\tstatic class SuccessfulParallelTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(3);\n\t\t}\n\n\t\t@Test\n\t\t@Execution(CONCURRENT)\n\t\tvoid firstTest() throws Exception {\n\t\t\tincrementAndBlock(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@Execution(CONCURRENT)\n\t\tvoid secondTest() throws Exception {\n\t\t\tincrementAndBlock(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@Execution(CONCURRENT)\n\t\tvoid thirdTest() throws Exception {\n\t\t\tincrementAndBlock(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class FailingWithoutLockTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(3);\n\t\t}\n\n\t\t@Test\n\t\tvoid firstTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid secondTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid thirdTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class SuccessfulWithMethodLockTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(3);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(\"sharedResource\")\n\t\tvoid firstTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(\"sharedResource\")\n\t\tvoid secondTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\t@ResourceLock(\"sharedResource\")\n\t\tvoid thirdTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\t@ResourceLock(\"sharedResource\")\n\tstatic class SuccessfulWithClassLockTestCase {\n\n\t\tstatic AtomicInteger sharedResource;\n\t\tstatic CountDownLatch countDownLatch;\n\n\t\t@BeforeAll\n\t\tstatic void initialize() {\n\t\t\tsharedResource = new AtomicInteger();\n\t\t\tcountDownLatch = new CountDownLatch(3);\n\t\t}\n\n\t\t@Test\n\t\tvoid firstTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid secondTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\n\t\t@Test\n\t\tvoid thirdTest() throws Exception {\n\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t}\n\t}\n\n\tstatic class TestCaseWithTestFactory {\n\t\t@TestFactory\n\t\t@Execution(SAME_THREAD)\n\t\tStream<DynamicTest> testFactory(TestReporter testReporter) {\n\t\t\tvar sharedResource = new AtomicInteger(0);\n\t\t\tvar countDownLatch = new CountDownLatch(3);\n\t\t\treturn IntStream.range(0, 3).mapToObj(i -> dynamicTest(\"test \" + i, () -> {\n\t\t\t\tincrementBlockAndCheck(sharedResource, countDownLatch);\n\t\t\t\ttestReporter.publishEntry(\"thread\", Thread.currentThread().getName());\n\t\t\t}));\n\t\t}\n\t}\n\n\tprivate static final ReentrantLock A = new ReentrantLock();\n\tprivate static final ReentrantLock B = new ReentrantLock();\n\n\t@ExtendWith(ThreadReporter.class)\n\t@ResourceLock(\"A\")\n\tstatic class TestCaseWithSortedLocks {\n\t\t@ResourceLock(\"B\")\n\t\t@Test\n\t\tvoid firstTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@Execution(CONCURRENT)\n\t\t@ResourceLock(\"B\")\n\t\t@Test\n\t\tvoid secondTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@ResourceLock(\"B\")\n\t\t@Test\n\t\tvoid thirdTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid unlock() {\n\t\t\tB.unlock();\n\t\t\tA.unlock();\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\t@ResourceLock(\"B\")\n\tstatic class TestCaseWithUnsortedLocks {\n\t\t@ResourceLock(\"A\")\n\t\t@Test\n\t\tvoid firstTest() {\n\t\t\tassertTrue(B.tryLock());\n\t\t\tassertTrue(A.tryLock());\n\t\t}\n\n\t\t@Execution(CONCURRENT)\n\t\t@ResourceLock(\"A\")\n\t\t@Test\n\t\tvoid secondTest() {\n\t\t\tassertTrue(B.tryLock());\n\t\t\tassertTrue(A.tryLock());\n\t\t}\n\n\t\t@ResourceLock(\"A\")\n\t\t@Test\n\t\tvoid thirdTest() {\n\t\t\tassertTrue(B.tryLock());\n\t\t\tassertTrue(A.tryLock());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid unlock() {\n\t\t\tA.unlock();\n\t\t\tB.unlock();\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\t@ResourceLock(\"A\")\n\tstatic class TestCaseWithNestedLocks {\n\n\t\t@ResourceLock(\"B\")\n\t\t@Test\n\t\tvoid firstTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@Execution(CONCURRENT)\n\t\t@ResourceLock(\"B\")\n\t\t@Test\n\t\tvoid secondTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@Test\n\t\tvoid thirdTest() {\n\t\t\tassertTrue(A.tryLock());\n\t\t\tassertTrue(B.tryLock());\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid unlock() {\n\t\t\tA.unlock();\n\t\t\tB.unlock();\n\t\t}\n\n\t\t@Nested\n\t\t@ResourceLock(\"B\")\n\t\tclass B {\n\n\t\t\t@ResourceLock(\"A\")\n\t\t\t@Test\n\t\t\tvoid firstTest() {\n\t\t\t\tassertTrue(B.tryLock());\n\t\t\t\tassertTrue(A.tryLock());\n\t\t\t}\n\n\t\t\t@ResourceLock(\"A\")\n\t\t\t@Test\n\t\t\tvoid secondTest() {\n\t\t\t\tassertTrue(B.tryLock());\n\t\t\t\tassertTrue(A.tryLock());\n\t\t\t}\n\n\t\t\t@Test\n\t\t\tvoid thirdTest() {\n\t\t\t\tassertTrue(B.tryLock());\n\t\t\t\tassertTrue(A.tryLock());\n\t\t\t}\n\t\t}\n\t}\n\n\t@Execution(CONCURRENT)\n\tstatic class ConcurrentDynamicTestCase {\n\t\tstatic Map<String, Instant> events;\n\n\t\t@BeforeAll\n\t\tstatic void beforeAll() {\n\t\t\tevents = new ConcurrentHashMap<>();\n\t\t}\n\n\t\t@AfterEach\n\t\tvoid afterEach() {\n\t\t\tevents.put(\"afterEach\", Instant.now());\n\t\t}\n\n\t\t@TestFactory\n\t\tDynamicTest testFactory() {\n\t\t\treturn dynamicTest(\"slow\", () -> {\n\t\t\t\tThread.sleep(100);\n\t\t\t\tevents.put(\"dynamicTestFinished\", Instant.now());\n\t\t\t});\n\t\t}\n\t}\n\n\t@TestMethodOrder(MethodName.class)\n\tstatic class InterruptedThreadTestCase {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() throws InterruptedException {\n\t\t\tThread.sleep(10);\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\n\t\t@Test\n\t\tvoid test4() throws InterruptedException {\n\t\t\tThread.sleep(10);\n\t\t}\n\n\t}\n\n\t@Execution(CONCURRENT)\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class ConcurrentTemplateTestCase {\n\t\t@RepeatedTest(10)\n\t\t@ResourceLock(\"a\")\n\t\tvoid repeatedTest() throws Exception {\n\t\t\tThread.sleep(100);\n\t\t}\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic abstract class BarrierTestCase {\n\n\t\t@Test\n\t\tvoid test1() throws Exception {\n\t\t\tgetBarrier().await();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() throws Exception {\n\t\t\tgetBarrier().await();\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() throws Exception {\n\t\t\tgetBarrier().await();\n\t\t}\n\n\t\tabstract CyclicBarrier getBarrier();\n\n\t}\n\n\tstatic class ParallelMethodsTestCase extends BarrierTestCase {\n\n\t\tstatic final Map<Class<?>, CyclicBarrier> barriersPerClass = new ConcurrentHashMap<>();\n\n\t\t@Override\n\t\tCyclicBarrier getBarrier() {\n\t\t\treturn barriersPerClass.computeIfAbsent(this.getClass(), key -> new CyclicBarrier(3));\n\t\t}\n\t}\n\n\tstatic class ParallelClassesTestCase extends BarrierTestCase {\n\n\t\tstatic final CyclicBarrier GLOBAL_BARRIER = new CyclicBarrier(3);\n\n\t\t@Override\n\t\tCyclicBarrier getBarrier() {\n\t\t\treturn GLOBAL_BARRIER;\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelClassesTestCaseA extends ParallelClassesTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelClassesTestCaseB extends ParallelClassesTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelClassesTestCaseC extends ParallelClassesTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelMethodsTestCaseA extends ParallelMethodsTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelMethodsTestCaseB extends ParallelMethodsTestCase {\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class ParallelMethodsTestCaseC extends ParallelMethodsTestCase {\n\t}\n\n\t@ExtendWith(ThreadReporter.class)\n\tstatic class ConcurrentDynamicContainerTestCase {\n\n\t\t@TestFactory\n\t\t@Execution(CONCURRENT)\n\t\tStream<DynamicContainer> testFactoryImplicitContainerAndExplicitChildExecutionMode() {\n\t\t\treturn testFactory(container -> container.childExecutionMode(SAME_THREAD), UnaryOperator.identity());\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicContainer> testFactoryExplicitContainerAndExplicitChildExecutionModeOnContainer() {\n\t\t\treturn testFactory(container -> container.executionMode(CONCURRENT).childExecutionMode(SAME_THREAD),\n\t\t\t\tUnaryOperator.identity());\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicContainer> testFactoryExplicitContainerAndExplicitChildExecutionModeOnEachChild() {\n\t\t\treturn testFactory(container -> container.executionMode(CONCURRENT),\n\t\t\t\ttest -> test.executionMode(SAME_THREAD));\n\t\t}\n\n\t\tprivate Stream<DynamicContainer> testFactory(UnaryOperator<DynamicContainer.Configuration> containerConfigurer,\n\t\t\t\tUnaryOperator<DynamicTest.Configuration> testsConfigurer) {\n\n\t\t\tvar sharedResource1 = new AtomicInteger();\n\t\t\tvar latch1 = new CountDownLatch(2);\n\n\t\t\tvar sharedResource2 = new AtomicInteger();\n\t\t\tvar latch2 = new CountDownLatch(2);\n\n\t\t\tvar tests1 = Stream.of( //\n\t\t\t\tdynamicTest(config -> testsConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"test1\") //\n\t\t\t\t\t\t.executable(() -> incrementAndBlock(sharedResource1, latch1))), //\n\t\t\t\tdynamicTest(config -> testsConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"test2\") //\n\t\t\t\t\t\t.executable(() -> incrementAndBlock(sharedResource2, latch2))));\n\n\t\t\tvar tests2 = Stream.of( //\n\t\t\t\tdynamicTest(config -> testsConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"test3\") //\n\t\t\t\t\t\t.executable(() -> incrementAndBlock(sharedResource1, latch1))), //\n\t\t\t\tdynamicTest(config -> testsConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"test4\") //\n\t\t\t\t\t\t.executable(() -> incrementAndBlock(sharedResource2, latch2))));\n\n\t\t\treturn Stream.of( //\n\t\t\t\tdynamicContainer(config -> containerConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"suite1\") //\n\t\t\t\t\t\t.children(tests1)), //\n\t\t\t\tdynamicContainer(config -> containerConfigurer.apply(config) //\n\t\t\t\t\t\t.displayName(\"suite2\") //\n\t\t\t\t\t\t.children(tests2)));\n\t\t}\n\n\t}\n\n\tprivate static void incrementBlockAndCheck(AtomicInteger sharedResource, CountDownLatch countDownLatch)\n\t\t\tthrows InterruptedException {\n\t\tvar value = incrementAndBlock(sharedResource, countDownLatch);\n\t\tassertEquals(value, sharedResource.get());\n\t}\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tprivate static int incrementAndBlock(AtomicInteger sharedResource, CountDownLatch countDownLatch)\n\t\t\tthrows InterruptedException {\n\t\tvar value = sharedResource.incrementAndGet();\n\t\tcountDownLatch.countDown();\n\t\tcountDownLatch.await(estimateSimulatedTestDurationInMilliseconds(), MILLISECONDS);\n\t\treturn value;\n\t}\n\n\t@SuppressWarnings(\"ResultOfMethodCallIgnored\")\n\tprivate static void storeAndBlockAndCheck(AtomicInteger sharedResource, CountDownLatch countDownLatch)\n\t\t\tthrows InterruptedException {\n\t\tvar value = sharedResource.get();\n\t\tcountDownLatch.countDown();\n\t\tcountDownLatch.await(estimateSimulatedTestDurationInMilliseconds(), MILLISECONDS);\n\t\tassertEquals(value, sharedResource.get());\n\t}\n\n\t/*\n\t * To simulate tests running in parallel tests will modify a shared\n\t * resource, simulate work by waiting, then check if the shared resource was\n\t * not modified by any other thread.\n\t *\n\t * Depending on system performance the simulation of work needs to be longer\n\t * on slower systems to ensure tests can run in parallel.\n\t *\n\t * Currently, CI is known to be slow.\n\t */\n\tprivate static long estimateSimulatedTestDurationInMilliseconds() {\n\t\tvar runningInCi = Boolean.parseBoolean(System.getenv(\"CI\"));\n\t\treturn runningInCi ? 1000 : 100;\n\t}\n\n\t@NullMarked\n\tstatic class ThreadReporter implements AfterTestExecutionCallback, InvocationInterceptor {\n\n\t\tprivate static Stream<String> getLoaderNames(List<Event> events) {\n\t\t\treturn getValues(events, \"loader\");\n\t\t}\n\n\t\tprivate static Stream<String> getThreadNames(List<Event> events) {\n\t\t\treturn getValues(events, \"thread\");\n\t\t}\n\n\t\tprivate static Stream<String> getValues(List<Event> events, String key) {\n\t\t\t// @formatter:off\n\t\t\treturn events.stream()\n\t\t\t\t\t.filter(type(REPORTING_ENTRY_PUBLISHED)::matches)\n\t\t\t\t\t.map(event -> event.getPayload(ReportEntry.class).orElseThrow())\n\t\t\t\t\t.map(ReportEntry::getKeyValuePairs)\n\t\t\t\t\t.filter(keyValuePairs -> keyValuePairs.containsKey(key))\n\t\t\t\t\t.map(keyValuePairs -> keyValuePairs.get(key))\n\t\t\t\t\t.distinct();\n\t\t\t// @formatter:on\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tpublishReportEntries(context);\n\t\t}\n\n\t\t@Override\n\t\tpublic void interceptDynamicTest(Invocation<@Nullable Void> invocation,\n\t\t\t\tDynamicTestInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {\n\n\t\t\ttry {\n\t\t\t\tinvocation.proceed();\n\t\t\t}\n\t\t\tfinally {\n\t\t\t\tpublishReportEntries(extensionContext);\n\t\t\t}\n\t\t}\n\n\t\tprivate static void publishReportEntries(ExtensionContext context) {\n\t\t\tcontext.publishReportEntry(\"thread\", Thread.currentThread().getName());\n\t\t\tcontext.publishReportEntry(\"loader\", Thread.currentThread().getContextClassLoader().getName());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockSupport.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport java.util.List;\nimport java.util.concurrent.locks.Lock;\n\nclass ResourceLockSupport {\n\n\tstatic List<Lock> getLocks(ResourceLock resourceLock) {\n\t\tif (resourceLock instanceof NopLock) {\n\t\t\treturn List.of();\n\t\t}\n\t\tif (resourceLock instanceof SingleLock lock) {\n\t\t\treturn List.of(lock.getLock());\n\t\t}\n\t\treturn ((CompositeLock) resourceLock).getLocks();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ResourceLockTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ;\nimport static org.junit.platform.engine.support.hierarchical.ExclusiveResource.GLOBAL_READ_WRITE;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\n\nclass ResourceLockTests {\n\n\t@Test\n\tvoid nopLocks() {\n\t\tassertCompatible(nopLock(), nopLock());\n\t\tassertCompatible(nopLock(), singleLock(anyReadOnlyResource()));\n\t\tassertCompatible(nopLock(), compositeLock(anyReadOnlyResource()));\n\t}\n\n\t@Test\n\tvoid readOnlySingleLocks() {\n\t\tExclusiveResource bR = readOnlyResource(\"b\");\n\n\t\tassertCompatible(singleLock(bR), nopLock());\n\t\tassertCompatible(singleLock(bR), singleLock(bR));\n\t\tassertIncompatible(singleLock(bR), singleLock(readWriteResource(\"b\")), \"read-write conflict\");\n\t\tassertIncompatible(singleLock(bR), singleLock(readOnlyResource(\"a\")), \"lock acquisition order\");\n\t\tassertCompatible(singleLock(bR), singleLock(readOnlyResource(\"c\")));\n\t\tassertIncompatible(singleLock(bR), singleLock(GLOBAL_READ), \"lock acquisition order\");\n\t\tassertIncompatible(singleLock(bR), singleLock(GLOBAL_READ_WRITE), \"lock acquisition order\");\n\t\tassertCompatible(singleLock(bR), compositeLock(bR, readOnlyResource(\"c\")));\n\t\tassertIncompatible(singleLock(bR), compositeLock(readOnlyResource(\"a1\"), readOnlyResource(\"a2\"), bR),\n\t\t\t\"lock acquisition order\");\n\t}\n\n\t@Test\n\tvoid readWriteSingleLocks() {\n\t\tExclusiveResource bRW = readWriteResource(\"b\");\n\n\t\tassertCompatible(singleLock(bRW), nopLock());\n\t\tassertIncompatible(singleLock(bRW), singleLock(bRW), \"isolation guarantees\");\n\t\tassertIncompatible(singleLock(bRW), compositeLock(bRW), \"isolation guarantees\");\n\t\tassertIncompatible(singleLock(bRW), singleLock(readOnlyResource(\"a\")), \"lock acquisition order\");\n\t\tassertIncompatible(singleLock(bRW), singleLock(readOnlyResource(\"b\")), \"isolation guarantees\");\n\t\tassertIncompatible(singleLock(bRW), singleLock(readOnlyResource(\"c\")), \"isolation guarantees\");\n\t\tassertIncompatible(singleLock(bRW), singleLock(GLOBAL_READ), \"lock acquisition order\");\n\t\tassertIncompatible(singleLock(bRW), singleLock(GLOBAL_READ_WRITE), \"lock acquisition order\");\n\t\tassertIncompatible(singleLock(bRW), compositeLock(bRW, readOnlyResource(\"c\")), \"isolation guarantees\");\n\t\tassertIncompatible(singleLock(bRW), compositeLock(readOnlyResource(\"a1\"), readOnlyResource(\"a2\"), bRW),\n\t\t\t\"lock acquisition order\");\n\t}\n\n\t@Test\n\tvoid globalReadLock() {\n\t\tassertCompatible(singleLock(GLOBAL_READ), nopLock());\n\t\tassertCompatible(singleLock(GLOBAL_READ), singleLock(GLOBAL_READ));\n\t\tassertCompatible(singleLock(GLOBAL_READ), singleLock(anyReadOnlyResource()));\n\t\tassertCompatible(singleLock(GLOBAL_READ), singleLock(anyReadWriteResource()));\n\t\tassertIncompatible(singleLock(GLOBAL_READ), singleLock(GLOBAL_READ_WRITE), \"read-write conflict\");\n\t}\n\n\t@Test\n\tvoid readOnlyCompositeLocks() {\n\t\tExclusiveResource bR = readOnlyResource(\"b\");\n\n\t\tassertCompatible(compositeLock(bR), nopLock());\n\t\tassertCompatible(compositeLock(bR), singleLock(bR));\n\t\tassertCompatible(compositeLock(bR), compositeLock(bR));\n\t\tassertIncompatible(compositeLock(bR), singleLock(GLOBAL_READ), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bR), singleLock(GLOBAL_READ_WRITE), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bR), compositeLock(readOnlyResource(\"a\")), \"lock acquisition order\");\n\t\tassertCompatible(compositeLock(bR), compositeLock(readOnlyResource(\"c\")));\n\t\tassertIncompatible(compositeLock(bR), compositeLock(readWriteResource(\"b\")), \"read-write conflict\");\n\t\tassertIncompatible(compositeLock(bR), compositeLock(bR, readWriteResource(\"b\")), \"read-write conflict\");\n\t}\n\n\t@Test\n\tvoid readWriteCompositeLocks() {\n\t\tExclusiveResource bRW = readWriteResource(\"b\");\n\n\t\tassertCompatible(compositeLock(bRW), nopLock());\n\t\tassertIncompatible(compositeLock(bRW), singleLock(bRW), \"isolation guarantees\");\n\t\tassertIncompatible(compositeLock(bRW), compositeLock(bRW), \"isolation guarantees\");\n\t\tassertIncompatible(compositeLock(bRW), singleLock(readOnlyResource(\"a\")), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bRW), singleLock(readOnlyResource(\"b\")), \"isolation guarantees\");\n\t\tassertIncompatible(compositeLock(bRW), singleLock(readOnlyResource(\"c\")), \"isolation guarantees\");\n\t\tassertIncompatible(compositeLock(bRW), singleLock(GLOBAL_READ), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bRW), singleLock(GLOBAL_READ_WRITE), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bRW), compositeLock(readOnlyResource(\"a\")), \"lock acquisition order\");\n\t\tassertIncompatible(compositeLock(bRW), compositeLock(readOnlyResource(\"b\"), readOnlyResource(\"c\")),\n\t\t\t\"isolation guarantees\");\n\t}\n\n\tprivate static void assertCompatible(ResourceLock first, ResourceLock second) {\n\t\tassertTrue(first.isCompatible(second),\n\t\t\t\"Expected locks to be compatible:\\n(1) %s\\n(2) %s\".formatted(first, second));\n\t}\n\n\tprivate static void assertIncompatible(ResourceLock first, ResourceLock second, String reason) {\n\t\tassertFalse(first.isCompatible(second),\n\t\t\t\"Expected locks to be incompatible due to %s:\\n(1) %s\\n(2) %s\".formatted(reason, first, second));\n\t}\n\n\tprivate static ResourceLock nopLock() {\n\t\treturn NopLock.INSTANCE;\n\t}\n\n\tprivate static SingleLock singleLock(ExclusiveResource resource) {\n\t\treturn new SingleLock(resource, anyLock());\n\t}\n\n\tprivate static CompositeLock compositeLock(ExclusiveResource... resources) {\n\t\treturn new CompositeLock(List.of(resources), Arrays.stream(resources).map(__ -> anyLock()).toList());\n\t}\n\n\tprivate static ExclusiveResource anyReadOnlyResource() {\n\t\treturn readOnlyResource(\"key\");\n\t}\n\n\tprivate static ExclusiveResource anyReadWriteResource() {\n\t\treturn readWriteResource(\"key\");\n\t}\n\n\tprivate static ExclusiveResource readOnlyResource(String key) {\n\t\treturn new ExclusiveResource(key, LockMode.READ);\n\t}\n\n\tprivate static ExclusiveResource readWriteResource(String key) {\n\t\treturn new ExclusiveResource(key, LockMode.READ_WRITE);\n\t}\n\n\tprivate static Lock anyLock() {\n\t\treturn new ReentrantLock();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SameThreadExecutionIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.MethodOrderer.MethodName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n/**\n * @since 1.4\n */\nclass SameThreadExecutionIntegrationTests {\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1688\">gh-1688</a>\n\t */\n\t@Test\n\tvoid threadInterruptedByUserCode(@TrackLogRecords LogRecordListener listener) {\n\t\tEngineTestKit.engine(\"junit-jupiter\")//\n\t\t\t\t.selectors(selectClass(InterruptedThreadTestCase.class))//\n\t\t\t\t.execute()//\n\t\t\t\t.testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.succeeded(4));\n\n\t\tassertThat(firstDebugLogRecord(listener).getMessage()).matches(\n\t\t\t\"Execution of TestDescriptor with display name .+test1.+ and \"\n\t\t\t\t\t+ \"unique ID .+ failed to clear the 'interrupted status' flag \"\n\t\t\t\t\t+ \"for the current thread. JUnit has cleared the flag, but you \"\n\t\t\t\t\t+ \"may wish to investigate why the flag was not cleared by user code.\");\n\t}\n\n\tprivate LogRecord firstDebugLogRecord(LogRecordListener listener) throws AssertionError {\n\t\treturn listener.stream(NodeTestTask.class, Level.FINE).findFirst().orElseThrow(\n\t\t\t() -> new AssertionError(\"Failed to find debug log record\"));\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@TestMethodOrder(MethodName.class)\n\tstatic class InterruptedThreadTestCase {\n\n\t\t@Test\n\t\tvoid test1() {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() throws Exception {\n\t\t\tThread.sleep(10);\n\t\t}\n\n\t\t@Test\n\t\tvoid test3() {\n\t\t\tThread.currentThread().interrupt();\n\t\t}\n\n\t\t@Test\n\t\tvoid test4() throws Exception {\n\t\t\tThread.sleep(10);\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/SingleLockTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\n\n/**\n * @since 1.3\n */\nclass SingleLockTests {\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid acquire() throws Exception {\n\t\tvar lock = new ReentrantLock();\n\n\t\tnew SingleLock(anyResource(), lock).acquire();\n\n\t\tassertTrue(lock.isLocked());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid release() throws Exception {\n\t\tvar lock = new ReentrantLock();\n\n\t\tnew SingleLock(anyResource(), lock).acquire().close();\n\n\t\tassertFalse(lock.isLocked());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid tryAcquireAndRelease() {\n\t\tvar lock = new ReentrantLock();\n\n\t\tvar singleLock = new SingleLock(anyResource(), lock);\n\n\t\tsingleLock.tryAcquire();\n\t\tassertTrue(lock.isLocked());\n\n\t\tsingleLock.release();\n\t\tassertFalse(lock.isLocked());\n\t}\n\n\tprivate static ExclusiveResource anyResource() {\n\t\treturn new ExclusiveResource(\"key\", LockMode.READ);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/ThrowableCollectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.FAILED;\nimport static org.junit.platform.engine.TestExecutionResult.Status.SUCCESSFUL;\n\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.6\n */\nclass ThrowableCollectorTests {\n\n\t@Test\n\tvoid successfulExecution() {\n\t\tvar collector = new ThrowableCollector(x -> true);\n\t\tcollector.execute(() -> {\n\t\t});\n\t\tvar result = collector.toTestExecutionResult();\n\n\t\tassertEquals(SUCCESSFUL, result.getStatus());\n\t\tassertEquals(Optional.empty(), result.getThrowable());\n\t}\n\n\t@Test\n\tvoid abortedExecution() {\n\t\tvar customAbort = new CustomAbort();\n\n\t\tvar collector = new ThrowableCollector(CustomAbort.class::isInstance);\n\t\tcollector.execute(() -> {\n\t\t\tthrow customAbort;\n\t\t});\n\t\tvar result = collector.toTestExecutionResult();\n\n\t\tassertEquals(ABORTED, result.getStatus());\n\t\tassertSame(customAbort, result.getThrowable().get());\n\t}\n\n\t@Test\n\tvoid failedExecution() {\n\t\tvar assertionError = new AssertionError(\"assertion violated\");\n\n\t\tvar collector = new ThrowableCollector(CustomAbort.class::isInstance);\n\t\tcollector.execute(() -> {\n\t\t\tthrow assertionError;\n\t\t});\n\t\tvar result = collector.toTestExecutionResult();\n\n\t\tassertEquals(FAILED, result.getStatus());\n\t\tassertSame(assertionError, result.getThrowable().get());\n\t}\n\n\tprivate static class CustomAbort extends Error {\n\t\tprivate static final long serialVersionUID = 1L;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/WorkerLeaseManagerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.support.hierarchical.WorkerThreadPoolHierarchicalTestExecutorService.WorkerLeaseManager;\n\nclass WorkerLeaseManagerTests {\n\n\t@Test\n\tvoid releasingIsIdempotent() {\n\t\tvar released = new AtomicInteger();\n\t\tvar manager = new WorkerLeaseManager(1, __ -> released.incrementAndGet());\n\n\t\tvar lease = manager.tryAcquire();\n\t\tassertThat(lease).isNotNull();\n\n\t\tlease.release();\n\t\tassertThat(released.get()).isEqualTo(1);\n\n\t\tlease.release();\n\t\tassertThat(released.get()).isEqualTo(1);\n\t}\n\n\t@Test\n\tvoid leaseCanBeReacquired() throws Exception {\n\t\tvar released = new AtomicInteger();\n\t\tvar manager = new WorkerLeaseManager(1, __ -> released.incrementAndGet());\n\n\t\tvar lease = manager.tryAcquire();\n\t\tassertThat(lease).isNotNull();\n\n\t\tlease.release();\n\t\tassertThat(released.get()).isEqualTo(1);\n\n\t\tlease.reacquire();\n\t\tassertThat(released.get()).isEqualTo(1);\n\n\t\tlease.release();\n\t\tassertThat(released.get()).isEqualTo(2);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/hierarchical/WorkerThreadPoolHierarchicalTestExecutorServiceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.hierarchical;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.concurrent.Future.State.SUCCESS;\nimport static java.util.function.Predicate.isEqual;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.ExceptionUtils.throwAsUncheckedException;\nimport static org.junit.platform.engine.TestDescriptor.Type.CONTAINER;\nimport static org.junit.platform.engine.TestDescriptor.Type.TEST;\nimport static org.mockito.Mockito.atLeast;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.time.Instant;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.CyclicBarrier;\nimport java.util.concurrent.locks.ReentrantLock;\nimport java.util.concurrent.locks.ReentrantReadWriteLock;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AutoClose;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.commons.util.Preconditions;\nimport org.junit.platform.commons.util.ToStringBuilder;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.ExclusiveResource.LockMode;\nimport org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService.TestTask;\nimport org.junit.platform.engine.support.hierarchical.Node.ExecutionMode;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * @since 6.1\n */\n@SuppressWarnings(\"resource\")\n@Timeout(5)\nclass WorkerThreadPoolHierarchicalTestExecutorServiceTests {\n\n\t@AutoClose\n\t@Nullable\n\tWorkerThreadPoolHierarchicalTestExecutorService service;\n\n\t@ParameterizedTest\n\t@EnumSource(ExecutionMode.class)\n\tvoid executesSingleTask(ExecutionMode executionMode) throws Exception {\n\n\t\tvar task = new TestTaskStub(executionMode);\n\n\t\tvar customClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader());\n\t\ttry (customClassLoader) {\n\t\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1), customClassLoader);\n\t\t\tservice.submit(task).get();\n\t\t}\n\n\t\ttask.assertExecutedSuccessfully();\n\n\t\tvar executionThread = task.executionThread();\n\t\tassertThat(executionThread).isNotNull().isNotSameAs(Thread.currentThread());\n\t\tassertThat(executionThread.getName()).matches(\"junit-\\\\d+-worker-1\");\n\t\tassertThat(executionThread.getContextClassLoader()).isSameAs(customClassLoader);\n\t}\n\n\t@Test\n\tvoid invokeAllMustBeExecutedFromWithinThreadPool() {\n\t\tvar tasks = List.of(new TestTaskStub(ExecutionMode.CONCURRENT));\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1));\n\n\t\tassertPreconditionViolationFor(() -> requiredService().invokeAll(tasks)) //\n\t\t\t\t.withMessage(\"invokeAll() must be called from a worker thread that belongs to this executor\");\n\t}\n\n\t@ParameterizedTest\n\t@EnumSource(ExecutionMode.class)\n\tvoid executesSingleChildInSameThreadRegardlessOfItsExecutionMode(ExecutionMode childExecutionMode)\n\t\t\tthrows Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1));\n\n\t\tvar child = new TestTaskStub(childExecutionMode);\n\t\tvar root = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(List.of(child)));\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tchild.assertExecutedSuccessfully();\n\n\t\tassertThat(root.executionThread()).isNotNull();\n\t\tassertThat(child.executionThread()).isSameAs(root.executionThread());\n\t}\n\n\t@Test\n\tvoid executesTwoChildrenConcurrently() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));\n\n\t\tvar latch = new CountDownLatch(2);\n\t\tExecutable behavior = () -> {\n\t\t\tlatch.countDown();\n\t\t\tlatch.await();\n\t\t};\n\n\t\tvar children = List.of(new TestTaskStub(ExecutionMode.CONCURRENT, behavior),\n\t\t\tnew TestTaskStub(ExecutionMode.CONCURRENT, behavior));\n\t\tvar root = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(children));\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t}\n\n\t@Test\n\tvoid executesTwoChildrenInSameThread() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1));\n\n\t\tvar children = List.of(new TestTaskStub(ExecutionMode.SAME_THREAD),\n\t\t\tnew TestTaskStub(ExecutionMode.SAME_THREAD));\n\t\tvar root = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(children));\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(root.executionThread()).isNotNull();\n\t\tassertThat(children).extracting(TestTaskStub::executionThread).containsOnly(root.executionThread());\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t}\n\n\t@Test\n\tvoid acquiresResourceLockForRootTask() throws Exception {\n\t\tvar resourceLock = mock(ResourceLock.class);\n\t\twhen(resourceLock.acquire()).thenReturn(resourceLock);\n\n\t\tvar task = new TestTaskStub(ExecutionMode.CONCURRENT).withResourceLock(resourceLock);\n\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1));\n\t\tservice.submit(task).get();\n\n\t\ttask.assertExecutedSuccessfully();\n\n\t\tvar inOrder = inOrder(resourceLock);\n\t\tinOrder.verify(resourceLock).acquire();\n\t\tinOrder.verify(resourceLock).close();\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\t@Test\n\tvoid acquiresResourceLockForChildTasks() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));\n\n\t\tvar resourceLock = mock(ResourceLock.class);\n\t\twhen(resourceLock.tryAcquire()).thenReturn(true, false);\n\t\twhen(resourceLock.acquire()).thenReturn(resourceLock);\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT).withResourceLock(resourceLock).withName(\"child1\");\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT).withResourceLock(resourceLock).withName(\"child2\");\n\t\tvar children = List.of(child1, child2);\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> requiredService().invokeAll(children)).withName(\n\t\t\t\"root\");\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\tassertThat(children).extracting(TestTaskStub::executionThread) //\n\t\t\t\t.filteredOn(isEqual(root.executionThread())).hasSizeLessThanOrEqualTo(2);\n\n\t\tverify(resourceLock, atLeast(2)).tryAcquire();\n\t\tverify(resourceLock, atLeast(1)).acquire();\n\t\tverify(resourceLock, times(2)).close();\n\t}\n\n\t@Test\n\tvoid runsTasksWithoutConflictingLocksConcurrently() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(3));\n\n\t\tvar resourceLock = new SingleLock(exclusiveResource(LockMode.READ_WRITE), new ReentrantLock());\n\n\t\tvar latch = new CountDownLatch(3);\n\t\tExecutable behavior = () -> {\n\t\t\tlatch.countDown();\n\t\t\tlatch.await();\n\t\t};\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior).withResourceLock(resourceLock).withName(\n\t\t\t\"child1\");\n\t\tvar child2 = new TestTaskStub(ExecutionMode.SAME_THREAD).withResourceLock(resourceLock).withName(\"child2\");\n\t\tvar leaf1 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior).withName(\"leaf1\");\n\t\tvar leaf2 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior).withName(\"leaf2\");\n\t\tvar leaves = List.of(leaf1, leaf2);\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(leaves)).withName(\n\t\t\t\"child3\");\n\t\tvar children = List.of(child1, child2, child3);\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> requiredService().invokeAll(children)).withName(\n\t\t\t\"root\");\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(leaves).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t}\n\n\t@Test\n\tvoid processingQueueEntriesSkipsOverUnavailableResources() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));\n\n\t\tvar resourceLock = new SingleLock(exclusiveResource(LockMode.READ_WRITE), new ReentrantLock());\n\n\t\tvar lockFreeChildrenStarted = new CountDownLatch(2);\n\t\tvar child1Started = new CountDownLatch(1);\n\n\t\tExecutable child1Behaviour = () -> {\n\t\t\tchild1Started.countDown();\n\t\t\tlockFreeChildrenStarted.await();\n\t\t};\n\t\tExecutable child4Behaviour = () -> {\n\t\t\tlockFreeChildrenStarted.countDown();\n\t\t\tchild1Started.await();\n\t\t};\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, child1Behaviour) //\n\t\t\t\t.withResourceLock(resourceLock) //\n\t\t\t\t.withName(\"child1\");\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, lockFreeChildrenStarted::countDown) //\n\t\t\t\t.withName(\"child2\"); //\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withResourceLock(resourceLock) //\n\t\t\t\t.withName(\"child3\");\n\t\tvar child4 = new TestTaskStub(ExecutionMode.CONCURRENT, child4Behaviour) //\n\t\t\t\t.withName(\"child4\");\n\t\tvar children = List.of(child1, child2, child3, child4);\n\t\tvar root = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(children)) //\n\t\t\t\t.withName(\"root\");\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(child4.executionThread).isEqualTo(child2.executionThread);\n\t\tassertThat(child3.startTime).isAfterOrEqualTo(child2.startTime);\n\t}\n\n\t@Test\n\tvoid invokeAllQueueEntriesSkipsOverUnavailableResources() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));\n\n\t\tvar resourceLock = new SingleLock(exclusiveResource(LockMode.READ_WRITE), new ReentrantLock());\n\n\t\tvar lockFreeChildrenStarted = new CountDownLatch(2);\n\t\tvar child2Started = new CountDownLatch(1);\n\n\t\tExecutable child1Behaviour = () -> {\n\t\t\tlockFreeChildrenStarted.countDown();\n\t\t\tchild2Started.await();\n\t\t};\n\t\tExecutable child2Behaviour = () -> {\n\t\t\tchild2Started.countDown();\n\t\t\tlockFreeChildrenStarted.await();\n\t\t};\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, child1Behaviour) //\n\t\t\t\t.withName(\"child1\");\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, child2Behaviour) //\n\t\t\t\t.withResourceLock(resourceLock) //\n\t\t\t\t.withName(\"child2\");\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withResourceLock(resourceLock) //\n\t\t\t\t.withName(\"child3\"); //\n\t\tvar child4 = new TestTaskStub(ExecutionMode.CONCURRENT, lockFreeChildrenStarted::countDown) //\n\t\t\t\t.withName(\"child4\");\n\t\tvar children = List.of(child1, child2, child3, child4);\n\t\tvar root = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().invokeAll(children)) //\n\t\t\t\t.withName(\"root\");\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(children).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(child1.executionThread).isEqualTo(child4.executionThread);\n\t\tassertThat(child3.startTime).isAfterOrEqualTo(child4.startTime);\n\t}\n\n\t@Test\n\tvoid prioritizesChildrenOfStartedContainers() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\tvar leafSubmitted = new CountDownLatch(1);\n\t\tvar child2AndLeafStarted = new CountDownLatch(2);\n\n\t\tvar leaf = new TestTaskStub(ExecutionMode.CONCURRENT, child2AndLeafStarted::countDown) //\n\t\t\t\t.withName(\"leaf\").withLevel(3);\n\n\t\tExecutable child3Behavior = () -> {\n\t\t\tvar future = requiredService().submit(leaf);\n\t\t\tleafSubmitted.countDown();\n\t\t\tchild2AndLeafStarted.await();\n\t\t\tfuture.get();\n\t\t};\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, leafSubmitted::await) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, child2AndLeafStarted::countDown) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT, child3Behavior) //\n\t\t\t\t.withType(CONTAINER).withName(\"child3\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD,\n\t\t\t() -> requiredService().invokeAll(List.of(child1, child2, child3))) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(List.of(root, child1, child2, leaf, child3)).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\tassertThat(leaf.startTime).isBeforeOrEqualTo(child2.startTime);\n\t\tassertThat(leaf.executionThread).isSameAs(child2.executionThread).isNotSameAs(child3.executionThread);\n\t}\n\n\t@Test\n\tvoid prioritizesTestsOverContainers() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2));\n\n\t\tvar leavesStarted = new CountDownLatch(2);\n\t\tvar leaf = new TestTaskStub(ExecutionMode.CONCURRENT, leavesStarted::countDown) //\n\t\t\t\t.withName(\"leaf\").withLevel(3).withType(TEST);\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> requiredService().submit(leaf).get()) //\n\t\t\t\t.withName(\"child1\").withLevel(2).withType(CONTAINER);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, leavesStarted::countDown) //\n\t\t\t\t.withName(\"child2\").withLevel(2).withType(TEST);\n\t\tvar child3 = new TestTaskStub(ExecutionMode.SAME_THREAD, leavesStarted::await) //\n\t\t\t\t.withName(\"child3\").withLevel(2).withType(TEST);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD,\n\t\t\t() -> requiredService().invokeAll(List.of(child1, child2, child3))) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\troot.assertExecutedSuccessfully();\n\t\tassertThat(List.of(child1, child2, child3)).allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tleaf.assertExecutedSuccessfully();\n\n\t\tassertThat(child2.startTime).isBeforeOrEqualTo(child1.startTime);\n\t}\n\n\t@Test\n\tvoid limitsWorkerThreadsToMaxPoolSize() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(3, 3));\n\n\t\tCountDownLatch latch = new CountDownLatch(3);\n\t\tExecutable behavior = () -> {\n\t\t\tlatch.countDown();\n\t\t\tlatch.await();\n\t\t};\n\t\tvar leaf1a = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"leaf1a\").withLevel(3);\n\t\tvar leaf1b = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"leaf1b\").withLevel(3);\n\t\tvar leaf2a = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"leaf2a\").withLevel(3);\n\t\tvar leaf2b = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"leaf2b\").withLevel(3);\n\n\t\t// When executed, there are 2 worker threads active and 1 available.\n\t\t// Both invokeAlls race each other trying to start 1 more.\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT,\n\t\t\t() -> requiredService().invokeAll(List.of(leaf1a, leaf1b))) //\n\t\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT,\n\t\t\t() -> requiredService().invokeAll(List.of(leaf2a, leaf2b))) //\n\t\t\t\t\t.withName(\"child2\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD,\n\t\t\t() -> requiredService().invokeAll(List.of(child1, child2))) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(List.of(root, child1, child2, leaf1a, leaf1b, leaf2a, leaf2b)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(Stream.of(leaf1a, leaf1b, leaf2a, leaf2b).map(TestTaskStub::executionThread).distinct()) //\n\t\t\t\t.hasSize(3);\n\t}\n\n\t@Test\n\tvoid stealsBlockingChildren() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\tvar child1Started = new CountDownLatch(1);\n\t\tvar leaf2aStarted = new CountDownLatch(1);\n\t\tvar leaf2bStarted = new CountDownLatch(1);\n\t\tvar readWriteLock = new ReentrantReadWriteLock();\n\t\tvar readOnlyResourceLock = new SingleLock(exclusiveResource(LockMode.READ), readWriteLock.readLock()) {\n\t\t\t@Override\n\t\t\tpublic void release() {\n\t\t\t\tsuper.release();\n\t\t\t\ttry {\n\t\t\t\t\tleaf2aStarted.await();\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tfail(e);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tvar readWriteResourceLock = new SingleLock(exclusiveResource(LockMode.READ_WRITE), readWriteLock.writeLock());\n\n\t\tvar leaf2a = new TestTaskStub(ExecutionMode.CONCURRENT, leaf2aStarted::countDown) //\n\t\t\t\t.withResourceLock(readWriteResourceLock) //\n\t\t\t\t.withName(\"leaf2a\").withLevel(3);\n\t\tvar leaf2b = new TestTaskStub(ExecutionMode.SAME_THREAD, leaf2bStarted::countDown) //\n\t\t\t\t.withName(\"leaf2b\").withLevel(3);\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tchild1Started.countDown();\n\t\t\tleaf2bStarted.await();\n\t\t}) //\n\t\t\t\t.withResourceLock(readOnlyResourceLock) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tchild1Started.await();\n\t\t\trequiredService().invokeAll(List.of(leaf2a, leaf2b));\n\t\t}) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD,\n\t\t\t() -> requiredService().invokeAll(List.of(child1, child2))) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(List.of(root, child1, child2, leaf2a, leaf2b)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(List.of(leaf2a, leaf2b)).map(TestTaskStub::executionThread) //\n\t\t\t\t.containsOnly(child2.executionThread);\n\t}\n\n\t@Test\n\tvoid executesChildrenInOrder() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1, 1));\n\n\t\tvar leaf1a = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1a\").withLevel(2);\n\t\tvar leaf1b = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1b\").withLevel(2);\n\t\tvar leaf1c = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1c\").withLevel(2);\n\t\tvar leaf1d = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1d\").withLevel(2);\n\n\t\tList<TestTaskStub> children = Arrays.asList(leaf1d, leaf1a, leaf1b, leaf1c);\n\t\tCollections.shuffle(children);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, //\n\t\t\t() -> requiredService().invokeAll(children)) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(List.of(root, leaf1a, leaf1b, leaf1c, leaf1d)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\tassertThat(children) //\n\t\t\t\t.extracting(TestTaskStub::startTime) //\n\t\t\t\t.isSorted();\n\t}\n\n\t@Test\n\tvoid testsAreStolenRatherThanContainers() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\t// Execute tasks pairwise\n\t\tCyclicBarrier cyclicBarrier = new CyclicBarrier(2);\n\t\tExecutable behavior = cyclicBarrier::await;\n\n\t\t// With half of the leaves being containers\n\t\tvar container1 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"container1\").withType(CONTAINER).withLevel(2);\n\t\tvar container2 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"container2\").withType(CONTAINER).withLevel(2);\n\t\tvar container3 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"container3\").withType(CONTAINER).withLevel(2);\n\n\t\t// And half of the leaves being tests, to be stolen\n\t\tvar test1 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"test1\").withType(TEST).withLevel(2);\n\t\tvar test2 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"test2\").withType(TEST).withLevel(2);\n\t\tvar test3 = new TestTaskStub(ExecutionMode.CONCURRENT, behavior) //\n\t\t\t\t.withName(\"test3\").withType(TEST).withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD,\n\t\t\t() -> requiredService().invokeAll(List.of(container1, container2, container3, test1, test2, test3))) //\n\t\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(List.of(root, container1, container2, container3, test1, test2, test3)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\t// If the last test node was stolen\n\t\tassertThat(container1.executionThread).isNotEqualTo(test3.executionThread);\n\t\t// Then it must follow that the test nodes were stolen\n\t\tassertThat(Stream.of(container1, container2, container3)) //\n\t\t\t\t.extracting(TestTaskStub::executionThread) //\n\t\t\t\t.containsOnly(container1.executionThread);\n\t\tassertThat(Stream.of(test1, test2, test3)) //\n\t\t\t\t.extracting(TestTaskStub::executionThread) //\n\t\t\t\t.containsOnly(test3.executionThread);\n\n\t\tassertThat(Stream.of(container1, container2, container3)) //\n\t\t\t\t.extracting(TestTaskStub::startTime) //\n\t\t\t\t.isSorted();\n\t\tassertThat(Stream.of(test1, test2, test3)) //\n\t\t\t\t.extracting(TestTaskStub::startTime) //\n\t\t\t\t.isSorted();\n\t}\n\n\t@Test\n\tvoid stealsDynamicChildren() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\tvar child1Started = new CountDownLatch(1);\n\t\tvar child2Finished = new CountDownLatch(1);\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tchild1Started.countDown();\n\t\t\tchild2Finished.await();\n\t\t}) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, child2Finished::countDown) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tvar future1 = requiredService().submit(child1);\n\t\t\tchild1Started.await();\n\t\t\tvar future2 = requiredService().submit(child2);\n\t\t\tfuture1.get();\n\t\t\tfuture2.get();\n\t\t}) //\n\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(Stream.of(root, child1, child2)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(child2.executionThread).isEqualTo(root.executionThread).isNotEqualTo(child1.executionThread);\n\t}\n\n\t@Test\n\tvoid stealsDynamicChildrenInOrder() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\tvar child1Started = new CountDownLatch(1);\n\t\tvar childrenSubmitted = new CountDownLatch(1);\n\t\tvar childrenFinished = new CountDownLatch(2);\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tchild1Started.countDown();\n\t\t\tchildrenSubmitted.await();\n\t\t}) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, childrenFinished::countDown) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT, childrenFinished::countDown) //\n\t\t\t\t.withName(\"child3\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tvar future1 = requiredService().submit(child1);\n\t\t\tchild1Started.await();\n\t\t\tvar future2 = requiredService().submit(child2);\n\t\t\tvar future3 = requiredService().submit(child3);\n\t\t\tchildrenSubmitted.countDown();\n\t\t\tchildrenFinished.await();\n\t\t\tfuture1.get();\n\t\t\tfuture2.get();\n\t\t\tfuture3.get();\n\t\t}) //\n\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(Stream.of(root, child1, child2, child3)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(List.of(child1, child2, child3)) //\n\t\t\t\t.extracting(TestTaskStub::startTime) //\n\t\t\t\t.isSorted();\n\t}\n\n\t@Test\n\tvoid executesDynamicChildrenInSubmitOrder() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(1, 1));\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"child3\").withLevel(2);\n\t\tvar child4 = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"child3\").withLevel(2);\n\n\t\tList<TestTaskStub> children = Arrays.asList(child1, child2, child3, child4);\n\t\tCollections.shuffle(children);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tvar executor = requiredService();\n\t\t\tvar features = children.stream().map(executor::submit).toList();\n\t\t\tfor (var future : features) {\n\t\t\t\tfuture.get();\n\t\t\t}\n\t\t}) //\n\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(Stream.of(root, child1, child2)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\tassertThat(children) //\n\t\t\t\t.extracting(TestTaskStub::startTime) //\n\t\t\t\t.isSorted();\n\t}\n\n\t@Test\n\tvoid stealsNestedDynamicChildren() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 2));\n\n\t\tvar barrier = new CyclicBarrier(2);\n\n\t\tvar leaf1a = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1a\").withLevel(3);\n\t\tvar leaf1b = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf1b\").withLevel(3);\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tbarrier.await();\n\t\t\tvar futureA = requiredService().submit(leaf1a);\n\t\t\tbarrier.await();\n\t\t\tvar futureB = requiredService().submit(leaf1b);\n\t\t\tfutureA.get();\n\t\t\tfutureB.get();\n\t\t\tbarrier.await();\n\t\t}) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\n\t\tvar leaf2a = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf2a\").withLevel(3);\n\t\tvar leaf2b = new TestTaskStub(ExecutionMode.CONCURRENT) //\n\t\t\t\t.withName(\"leaf2b\").withLevel(3);\n\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tbarrier.await();\n\t\t\tvar futureA = requiredService().submit(leaf2a);\n\t\t\tbarrier.await();\n\t\t\tvar futureB = requiredService().submit(leaf2b);\n\t\t\tfutureB.get();\n\t\t\tfutureA.get();\n\t\t\tbarrier.await();\n\t\t}) //\n\t\t\t\t.withName(\"child2\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tvar future1 = requiredService().submit(child1);\n\t\t\tvar future2 = requiredService().submit(child2);\n\t\t\tfuture1.get();\n\t\t\tfuture2.get();\n\t\t}) //\n\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(Stream.of(root, child1, child2, leaf1a, leaf1b, leaf2a, leaf2b)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\t\tassertThat(child2.executionThread).isNotEqualTo(child1.executionThread);\n\t\tassertThat(child1.executionThread).isEqualTo(leaf1a.executionThread).isEqualTo(leaf1b.executionThread);\n\t\tassertThat(child2.executionThread).isEqualTo(leaf2a.executionThread).isEqualTo(leaf2b.executionThread);\n\t}\n\n\t@Test\n\tvoid stealsSiblingDynamicChildrenOnly() throws Exception {\n\t\tservice = new WorkerThreadPoolHierarchicalTestExecutorService(configuration(2, 3));\n\n\t\tvar child1Started = new CountDownLatch(1);\n\t\tvar child3Started = new CountDownLatch(1);\n\t\tvar leaf2ASubmitted = new CountDownLatch(1);\n\t\tvar leaf2AStarted = new CountDownLatch(1);\n\n\t\tvar child1 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tchild1Started.countDown();\n\t\t\tleaf2ASubmitted.await();\n\t\t}) //\n\t\t\t\t.withName(\"child1\").withLevel(2);\n\n\t\tvar leaf2a = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tleaf2AStarted.countDown();\n\t\t\tchild3Started.await();\n\t\t}) //\n\t\t\t\t.withName(\"leaf1a\").withLevel(3);\n\n\t\tvar child2 = new TestTaskStub(ExecutionMode.CONCURRENT, () -> {\n\t\t\tvar futureA = requiredService().submit(leaf2a);\n\t\t\tleaf2ASubmitted.countDown();\n\t\t\tleaf2AStarted.await();\n\t\t\tfutureA.get();\n\t\t}) //\n\t\t\t\t.withName(\"child2\").withType(CONTAINER).withLevel(2);\n\n\t\tvar child3 = new TestTaskStub(ExecutionMode.CONCURRENT, child3Started::countDown) //\n\t\t\t\t.withName(\"child3\").withLevel(2);\n\n\t\tvar root = new TestTaskStub(ExecutionMode.SAME_THREAD, () -> {\n\t\t\tvar future1 = requiredService().submit(child1);\n\t\t\tchild1Started.await();\n\t\t\tvar future2 = requiredService().submit(child2);\n\t\t\tvar future3 = requiredService().submit(child3);\n\t\t\tfuture1.get();\n\t\t\tfuture2.get();\n\t\t\tfuture3.get();\n\t\t}) //\n\t\t\t\t.withName(\"root\").withLevel(1);\n\n\t\tservice.submit(root).get();\n\n\t\tassertThat(Stream.of(root, child1, child2, leaf2a, child3)) //\n\t\t\t\t.allSatisfy(TestTaskStub::assertExecutedSuccessfully);\n\n\t\tassertThat(child2.executionThread).isNotEqualTo(child1.executionThread).isNotEqualTo(child3.executionThread);\n\t\tassertThat(child1.executionThread).isNotEqualTo(child3.executionThread);\n\t\tassertThat(child1.executionThread).isEqualTo(leaf2a.executionThread);\n\t}\n\n\tprivate static ExclusiveResource exclusiveResource(LockMode lockMode) {\n\t\treturn new ExclusiveResource(\"key\", lockMode);\n\t}\n\n\tprivate WorkerThreadPoolHierarchicalTestExecutorService requiredService() {\n\t\treturn requireNonNull(service);\n\t}\n\n\tprivate static ParallelExecutionConfiguration configuration(int parallelism) {\n\t\treturn configuration(parallelism, 256 + parallelism);\n\t}\n\n\tprivate static ParallelExecutionConfiguration configuration(int parallelism, int maxPoolSize) {\n\t\treturn new DefaultParallelExecutionConfiguration(parallelism, parallelism, maxPoolSize, parallelism, 0,\n\t\t\t__ -> true);\n\t}\n\n\t@NullMarked\n\tprivate static final class TestTaskStub implements TestTask {\n\n\t\tprivate final ExecutionMode executionMode;\n\t\tprivate final Executable behavior;\n\n\t\tprivate ResourceLock resourceLock = NopLock.INSTANCE;\n\t\tprivate @Nullable String name;\n\t\tprivate int level = 1;\n\t\tprivate TestDescriptor.Type type = TEST;\n\n\t\tprivate final CompletableFuture<@Nullable Void> result = new CompletableFuture<>();\n\t\tprivate volatile @Nullable Instant startTime;\n\t\tprivate volatile @Nullable Thread executionThread;\n\n\t\tTestTaskStub(ExecutionMode executionMode) {\n\t\t\tthis(executionMode, () -> {\n\t\t\t});\n\t\t}\n\n\t\tTestTaskStub(ExecutionMode executionMode, Executable behavior) {\n\t\t\tthis.executionMode = executionMode;\n\t\t\tthis.behavior = behavior;\n\t\t}\n\n\t\tTestTaskStub withName(String name) {\n\t\t\tthis.name = name;\n\t\t\treturn this;\n\t\t}\n\n\t\tTestTaskStub withLevel(int level) {\n\t\t\tthis.level = level;\n\t\t\treturn this;\n\t\t}\n\n\t\tTestTaskStub withType(TestDescriptor.Type type) {\n\t\t\tthis.type = type;\n\t\t\treturn this;\n\t\t}\n\n\t\tTestTaskStub withResourceLock(ResourceLock resourceLock) {\n\t\t\tthis.resourceLock = resourceLock;\n\t\t\treturn this;\n\t\t}\n\n\t\t@Override\n\t\tpublic ExecutionMode getExecutionMode() {\n\t\t\treturn executionMode;\n\t\t}\n\n\t\t@Override\n\t\tpublic ResourceLock getResourceLock() {\n\t\t\treturn resourceLock;\n\t\t}\n\n\t\t@Override\n\t\tpublic TestDescriptor getTestDescriptor() {\n\t\t\tvar name = String.valueOf(this.name);\n\t\t\tvar uniqueId = UniqueId.root(\"root\", name);\n\t\t\tfor (var i = 1; i < level; i++) {\n\t\t\t\tuniqueId = uniqueId.append(\"child\", name);\n\t\t\t}\n\t\t\treturn new TestDescriptorStub(uniqueId, name) {\n\t\t\t\t@Override\n\t\t\t\tpublic Type getType() {\n\t\t\t\t\treturn type;\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute() {\n\t\t\tstartTime = Instant.now();\n\t\t\tPreconditions.condition(!result.isDone(), \"task was already executed\");\n\n\t\t\texecutionThread = Thread.currentThread();\n\t\t\ttry {\n\t\t\t\tbehavior.execute();\n\t\t\t\tresult.complete(null);\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tresult.completeExceptionally(t);\n\t\t\t\tthrow throwAsUncheckedException(t);\n\t\t\t}\n\t\t}\n\n\t\tvoid assertExecutedSuccessfully() {\n\t\t\tif (result.isCompletedExceptionally()) {\n\t\t\t\tthrow new AssertionFailedError(\"Failure during execution\", result.exceptionNow());\n\t\t\t}\n\t\t\tassertThat(result.state()).isEqualTo(SUCCESS);\n\t\t}\n\n\t\t@Nullable\n\t\tThread executionThread() {\n\t\t\treturn executionThread;\n\t\t}\n\n\t\t@Nullable\n\t\tInstant startTime() {\n\t\t\treturn startTime;\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"%s @ %s\".formatted(new ToStringBuilder(this).append(\"name\", name), Integer.toHexString(hashCode()));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespaceTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.store;\n\nimport static org.junit.jupiter.api.Assertions.assertNotEquals;\nimport static org.junit.jupiter.api.EqualsAndHashCodeAssertions.assertEqualsAndHashCode;\n\nimport org.junit.jupiter.api.Test;\n\nclass NamespaceTests {\n\n\t@Test\n\tvoid namespacesEqualForSamePartsSequence() {\n\t\tNamespace ns1 = Namespace.create(\"part1\", \"part2\");\n\t\tNamespace ns2 = Namespace.create(\"part1\", \"part2\");\n\t\tNamespace ns3 = Namespace.create(\"part2\", \"part1\");\n\n\t\tassertEqualsAndHashCode(ns1, ns2, ns3);\n\t}\n\n\t@Test\n\tvoid orderOfNamespacePartsDoesMatter() {\n\t\tNamespace ns1 = Namespace.create(\"part1\", \"part2\");\n\t\tNamespace ns2 = Namespace.create(\"part2\", \"part1\");\n\n\t\tassertNotEquals(ns1, ns2);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/engine/support/store/NamespacedHierarchicalStoreTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.engine.support.store;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNull;\nimport static org.junit.jupiter.api.Assertions.assertSame;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently;\nimport static org.mockito.Mockito.doThrow;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.verifyNoInteractions;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\nimport java.io.Serial;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Unit tests for {@link NamespacedHierarchicalStore}.\n *\n * @since 5.0\n */\npublic class NamespacedHierarchicalStoreTests {\n\n\tprivate final Object key = \"key\";\n\tprivate final Object value = \"value\";\n\n\tprivate final String namespace = \"ns\";\n\n\tprivate final NamespacedHierarchicalStore.CloseAction<String> closeAction = mock();\n\tprivate final NamespacedHierarchicalStore<String> grandParentStore = new NamespacedHierarchicalStore<>(null,\n\t\tcloseAction);\n\tprivate final NamespacedHierarchicalStore<String> parentStore = grandParentStore.newChild();\n\tprivate final NamespacedHierarchicalStore<String> store = parentStore.newChild();\n\n\t@Nested\n\tclass StoringValuesTests {\n\n\t\t@Test\n\t\tvoid getWithUnknownKeyReturnsNull() {\n\t\t\tassertNull(store.get(namespace, \"unknown key\"));\n\t\t}\n\n\t\t@Test\n\t\tvoid putAndGetWithSameKey() {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid valueCanBeReplaced() {\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\tObject newValue = new Object();\n\t\t\tassertEquals(value, store.put(namespace, key, newValue));\n\n\t\t\tassertEquals(newValue, store.get(namespace, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid valueIsComputedIfAbsent() {\n\t\t\tassertNull(store.get(namespace, key));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> value));\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid valueIsComputedIfNull() {\n\t\t\tassertNull(store.put(namespace, key, null));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> value));\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid valueIsNotComputedIfPresentLocally() {\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\tassertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid valueIsNotComputedIfPresentInParent() {\n\t\t\tparentStore.put(namespace, key, value);\n\n\t\t\tassertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid valueIsNotComputedIfPresentInGrandParent() {\n\t\t\tgrandParentStore.put(namespace, key, value);\n\n\t\t\tassertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid nullIsAValidValueToPut() {\n\t\t\tstore.put(namespace, key, null);\n\n\t\t\tassertNull(store.getOrComputeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertNull(store.get(namespace, key));\n\n\t\t\tassertEquals(\"a different value\", store.computeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(\"a different value\", store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid keysCanBeRemoved() {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tassertEquals(value, store.remove(namespace, key));\n\t\t\tassertNull(store.get(namespace, key));\n\n\t\t\tassertEquals(\"a different value\", store.getOrComputeIfAbsent(namespace, key, __ -> \"a different value\"));\n\t\t\tassertEquals(\"a different value\", store.remove(namespace, key));\n\t\t\tassertNull(store.get(namespace, key));\n\n\t\t\tassertEquals(\"another different value\",\n\t\t\t\tstore.computeIfAbsent(namespace, key, __ -> \"another different value\"));\n\t\t\tassertEquals(\"another different value\", store.remove(namespace, key));\n\t\t\tassertNull(store.get(namespace, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid sameKeyWithDifferentNamespaces() {\n\t\t\tObject value1 = createObject(\"value1\");\n\t\t\tString namespace1 = \"ns1\";\n\n\t\t\tObject value2 = createObject(\"value2\");\n\t\t\tString namespace2 = \"ns2\";\n\n\t\t\tstore.put(namespace1, key, value1);\n\t\t\tstore.put(namespace2, key, value2);\n\n\t\t\tassertEquals(value1, store.get(namespace1, key));\n\t\t\tassertEquals(value2, store.get(namespace2, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid valueIsComputedIfAbsentInDifferentNamespace() {\n\t\t\tString namespace1 = \"ns1\";\n\t\t\tString namespace2 = \"ns2\";\n\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace1, key, __ -> value));\n\t\t\tassertEquals(value, store.get(namespace1, key));\n\n\t\t\tassertNull(store.get(namespace2, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid keyIsOnlyRemovedInGivenNamespace() {\n\t\t\tString namespace1 = \"ns1\";\n\t\t\tString namespace2 = \"ns2\";\n\n\t\t\tObject value1 = createObject(\"value1\");\n\t\t\tObject value2 = createObject(\"value2\");\n\n\t\t\tstore.put(namespace1, key, value1);\n\t\t\tstore.put(namespace2, key, value2);\n\t\t\tstore.remove(namespace1, key);\n\n\t\t\tassertNull(store.get(namespace1, key));\n\t\t\tassertEquals(value2, store.get(namespace2, key));\n\t\t}\n\n\t\t@Test\n\t\tvoid getWithTypeSafetyAndInvalidRequiredTypeThrowsException() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\tException exception = assertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.get(namespace, key, Number.class));\n\t\t\tassertEquals(\n\t\t\t\t\"Object stored under key [42] is not of required type [java.lang.Number], but was [java.lang.String]: enigma\",\n\t\t\t\texception.getMessage());\n\t\t}\n\n\t\t@Test\n\t\tvoid getWithTypeSafety() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString requiredTypeValue = store.get(namespace, key, String.class);\n\t\t\tassertEquals(value, requiredTypeValue);\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid getWithTypeSafetyAndPrimitiveValueType() {\n\t\t\tString key = \"enigma\";\n\t\t\tint value = 42;\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// The fact that we can declare this as an int/Integer suffices for testing the required type.\n\t\t\tint requiredInt = store.get(namespace, key, int.class);\n\t\t\tInteger requiredInteger = store.get(namespace, key, Integer.class);\n\t\t\tassertEquals(value, requiredInt);\n\t\t\tassertEquals(value, requiredInteger.intValue());\n\t\t}\n\n\t\t@Test\n\t\tvoid getNullValueWithTypeSafety() {\n\t\t\tstore.put(namespace, key, null);\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString requiredTypeValue = store.get(namespace, key, String.class);\n\t\t\tassertNull(requiredTypeValue);\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid getOrComputeIfAbsentWithTypeSafetyAndInvalidRequiredTypeThrowsException() {\n\t\t\tString key = \"pi\";\n\t\t\tFloat value = 3.14f;\n\n\t\t\t// Store a Float...\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// But declare that our function creates a String...\n\t\t\tFunction<String, String> defaultCreator = k -> \"enigma\";\n\n\t\t\tException exception = assertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.getOrComputeIfAbsent(namespace, key, defaultCreator, String.class));\n\t\t\tassertEquals(\n\t\t\t\t\"Object stored under key [pi] is not of required type [java.lang.String], but was [java.lang.Float]: 3.14\",\n\t\t\t\texception.getMessage());\n\t\t}\n\n\t\t@Test\n\t\tvoid computeIfAbsentWithTypeSafetyAndInvalidRequiredTypeThrowsException() {\n\t\t\tString key = \"pi\";\n\t\t\tFloat value = 3.14f;\n\n\t\t\t// Store a Float...\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// But declare that our function creates a String...\n\t\t\tFunction<String, String> defaultCreator = k -> \"enigma\";\n\n\t\t\tException exception = assertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.computeIfAbsent(namespace, key, defaultCreator, String.class));\n\t\t\tassertEquals(\n\t\t\t\t\"Object stored under key [pi] is not of required type [java.lang.String], but was [java.lang.Float]: 3.14\",\n\t\t\t\texception.getMessage());\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid getOrComputeIfAbsentWithTypeSafety() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString computedValue = store.getOrComputeIfAbsent(namespace, key, k -> value, String.class);\n\t\t\tassertEquals(value, computedValue);\n\t\t}\n\n\t\t@Test\n\t\tvoid computeIfAbsentWithTypeSafety() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString computedValue = store.computeIfAbsent(namespace, key, __ -> value, String.class);\n\t\t\tassertEquals(value, computedValue);\n\t\t}\n\n\t\t@SuppressWarnings({ \"DataFlowIssue\", \"deprecation\" })\n\t\t@Test\n\t\tvoid getOrComputeIfAbsentWithTypeSafetyAndPrimitiveValueType() {\n\t\t\tString key = \"enigma\";\n\t\t\tint value = 42;\n\n\t\t\t// The fact that we can declare this as an int/Integer suffices for testing the required type.\n\t\t\tint computedInt = store.getOrComputeIfAbsent(namespace, key, k -> value, int.class);\n\t\t\tInteger computedInteger = store.getOrComputeIfAbsent(namespace, key, k -> value, Integer.class);\n\t\t\tassertEquals(value, computedInt);\n\t\t\tassertEquals(value, computedInteger.intValue());\n\t\t}\n\n\t\t@Test\n\t\tvoid computeIfAbsentWithTypeSafetyAndPrimitiveValueType() {\n\t\t\tString key = \"enigma\";\n\t\t\tint value = 42;\n\n\t\t\t// The fact that we can declare this as an int/Integer suffices for testing the required type.\n\t\t\tint computedInt = store.computeIfAbsent(namespace, key, k -> value, int.class);\n\t\t\tInteger computedInteger = store.computeIfAbsent(namespace, key, k -> value, Integer.class);\n\t\t\tassertEquals(value, computedInt);\n\t\t\tassertEquals(value, computedInteger.intValue());\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid getOrComputeIfAbsentWithExceptionThrowingCreatorFunction() {\n\t\t\tvar e = assertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tassertSame(e, assertThrows(ComputeException.class, () -> store.get(namespace, key)));\n\t\t\tassertSame(e, assertThrows(ComputeException.class, () -> store.remove(namespace, key)));\n\t\t}\n\n\t\t@Test\n\t\tvoid computeIfAbsentWithExceptionThrowingCreatorFunction() {\n\t\t\tassertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tassertNull(store.get(namespace, key));\n\t\t\tassertNull(store.remove(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid getOrComputeIfAbsentDoesNotSeeComputeIfAbsentWithExceptionThrowingCreatorFunction() {\n\t\t\tassertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tassertNull(store.get(namespace, key));\n\t\t\tassertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> value));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid computeIfAbsentSeesGetOrComputeIfAbsentWithExceptionThrowingCreatorFunction() {\n\t\t\tassertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, key, __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tassertThrows(ComputeException.class, () -> store.get(namespace, key));\n\t\t\tassertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, key, __ -> value));\n\t\t}\n\n\t\t@Test\n\t\tvoid removeWithTypeSafetyAndInvalidRequiredTypeThrowsException() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\tException exception = assertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.remove(namespace, key, Number.class));\n\t\t\tassertEquals(\n\t\t\t\t\"Object stored under key [42] is not of required type [java.lang.Number], but was [java.lang.String]: enigma\",\n\t\t\t\texception.getMessage());\n\t\t}\n\n\t\t@Test\n\t\tvoid removeWithTypeSafety() {\n\t\t\tInteger key = 42;\n\t\t\tString value = \"enigma\";\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString removedValue = store.remove(namespace, key, String.class);\n\t\t\tassertEquals(value, removedValue);\n\t\t\tassertNull(store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t@Test\n\t\tvoid removeWithTypeSafetyAndPrimitiveValueType() {\n\t\t\tString key = \"enigma\";\n\t\t\tint value = 42;\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\t// The fact that we can declare this as an int suffices for testing the required type.\n\t\t\tint requiredInt = store.remove(namespace, key, int.class);\n\t\t\tassertEquals(value, requiredInt);\n\n\t\t\tstore.put(namespace, key, value);\n\t\t\t// The fact that we can declare this as an Integer suffices for testing the required type.\n\t\t\tInteger requiredInteger = store.get(namespace, key, Integer.class);\n\t\t\tassertEquals(value, requiredInteger.intValue());\n\t\t}\n\n\t\t@Test\n\t\tvoid removeNullValueWithTypeSafety() {\n\t\t\tInteger key = 42;\n\t\t\tstore.put(namespace, key, null);\n\n\t\t\t// The fact that we can declare this as a String suffices for testing the required type.\n\t\t\tString removedValue = store.remove(namespace, key, String.class);\n\t\t\tassertNull(removedValue);\n\t\t\tassertNull(store.get(namespace, key));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid simulateRaceConditionInGetOrComputeIfAbsent() throws Exception {\n\t\t\tint threads = 10;\n\t\t\tAtomicInteger counter = new AtomicInteger();\n\t\t\tList<Object> values;\n\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvalues = executeConcurrently(threads, //\n\t\t\t\t\t() -> requireNonNull(\n\t\t\t\t\t\tlocalStore.getOrComputeIfAbsent(namespace, key, it -> counter.incrementAndGet())));\n\t\t\t}\n\n\t\t\tassertEquals(1, counter.get());\n\t\t\tassertThat(values).hasSize(threads).containsOnly(1);\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@RepeatedTest(value = 10, failureThreshold = 1)\n\t\tvoid simulateRaceConditionInGetOrComputeIfAbsentWithResizingMap() throws Exception {\n\t\t\tint threads = 10;\n\t\t\tAtomicInteger counter = new AtomicInteger();\n\t\t\tList<Object> values;\n\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvalues = executeConcurrently(threads, //\n\t\t\t\t\t() -> {\n\t\t\t\t\t\t// Simulate other extensions computing values,\n\t\t\t\t\t\t// this will trigger several resizes of the store\n\t\t\t\t\t\tfor (int i = 0; i < 16; i++) {\n\t\t\t\t\t\t\tlocalStore.getOrComputeIfAbsent(namespace, i, __ -> value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn requireNonNull(\n\t\t\t\t\t\t\tlocalStore.getOrComputeIfAbsent(namespace, key, it -> counter.incrementAndGet()));\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tassertEquals(1, counter.get());\n\t\t\tassertThat(values).hasSize(threads).containsOnly(1);\n\t\t}\n\n\t\t@Test\n\t\tvoid simulateRaceConditionInComputeIfAbsent() throws Exception {\n\t\t\tint threads = 10;\n\t\t\tAtomicInteger counter = new AtomicInteger();\n\t\t\tList<Object> values;\n\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvalues = executeConcurrently(threads, //\n\t\t\t\t\t() -> requireNonNull(localStore.computeIfAbsent(namespace, key, it -> counter.incrementAndGet())));\n\t\t\t}\n\n\t\t\tassertEquals(1, counter.get());\n\t\t\tassertThat(values).hasSize(threads).containsOnly(1);\n\t\t}\n\n\t\t@RepeatedTest(value = 10, failureThreshold = 1)\n\t\tvoid simulateRaceConditionInComputeIfAbsentWithResizingMap() throws Exception {\n\t\t\tint threads = 10;\n\t\t\tAtomicInteger counter = new AtomicInteger();\n\t\t\tList<Object> values;\n\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvalues = executeConcurrently(threads, //\n\t\t\t\t\t() -> {\n\t\t\t\t\t\t// Simulate other extensions computing values,\n\t\t\t\t\t\t// this will trigger several resizes of the store\n\t\t\t\t\t\tfor (int i = 0; i < 16; i++) {\n\t\t\t\t\t\t\tlocalStore.computeIfAbsent(namespace, i, s -> value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn requireNonNull(\n\t\t\t\t\t\t\tlocalStore.computeIfAbsent(namespace, key, it -> counter.incrementAndGet()));\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tassertEquals(1, counter.get());\n\t\t\tassertThat(values).hasSize(threads).containsOnly(1);\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid updateRecursivelyGetOrComputeIfAbsent() {\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvar value = localStore.getOrComputeIfAbsent(namespace, new CollidingKey(\"a\"), //\n\t\t\t\t\ta -> requireNonNull(localStore.getOrComputeIfAbsent(namespace, new CollidingKey(\"b\"), //\n\t\t\t\t\t\tb -> \"enigma\")));\n\t\t\t\tassertEquals(\"enigma\", value);\n\t\t\t}\n\t\t}\n\n\t\t@Test\n\t\tvoid updateRecursivelyComputeIfAbsent() {\n\t\t\ttry (var localStore = new NamespacedHierarchicalStore<>(null)) {\n\t\t\t\tvar value = localStore.computeIfAbsent(namespace, new CollidingKey(\"a\"), //\n\t\t\t\t\ta -> localStore.computeIfAbsent(namespace, new CollidingKey(\"b\"), //\n\t\t\t\t\t\tb -> \"enigma\"));\n\t\t\t\tassertEquals(\"enigma\", value);\n\t\t\t}\n\t\t}\n\n\t\tprivate record CollidingKey(String value) {\n\n\t\t\t@Override\n\t\t\tpublic int hashCode() {\n\t\t\t\treturn 42;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Nested\n\tclass InheritedValuesTests {\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid presentValueFromParentIsPresent() {\n\t\t\tparentStore.put(namespace, key, value);\n\t\t\tassertEquals(value, store.get(namespace, key));\n\t\t\tassertEquals(value, store.getOrComputeIfAbsent(namespace, key, __ -> \"enigma\"));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> \"enigma\"));\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid absentValueFromParentIsOverriddenByComputeIfAbsent() {\n\t\t\tparentStore.put(namespace, key, null);\n\t\t\tassertNull(store.get(namespace, key));\n\t\t\tassertNull(store.getOrComputeIfAbsent(namespace, key, __ -> value));\n\t\t\tassertEquals(value, store.computeIfAbsent(namespace, key, __ -> value));\n\t\t}\n\n\t\t@Test\n\t\tvoid valueFromParentCanBeOverriddenInChild() {\n\t\t\tparentStore.put(namespace, key, value);\n\n\t\t\tObject otherValue = new Object();\n\t\t\tstore.put(namespace, key, otherValue);\n\t\t\tassertEquals(otherValue, store.get(namespace, key));\n\n\t\t\tassertEquals(value, parentStore.get(namespace, key));\n\t\t}\n\t}\n\n\t@Nested\n\tclass CompositeNamespaceTests {\n\n\t\t@Test\n\t\tvoid additionNamespacePartMakesADifference() {\n\n\t\t\tString ns1 = \"part1/part2\";\n\t\t\tString ns2 = \"part1\";\n\n\t\t\tObject value2 = createObject(\"value2\");\n\n\t\t\tparentStore.put(ns1, key, value);\n\t\t\tparentStore.put(ns2, key, value2);\n\n\t\t\tassertEquals(value, store.get(ns1, key));\n\t\t\tassertEquals(value2, store.get(ns2, key));\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass CloseActionTests {\n\n\t\t@BeforeEach\n\t\tvoid prerequisites() {\n\t\t\tassertNotClosed();\n\t\t}\n\n\t\t@Test\n\t\tvoid callsCloseActionInReverseInsertionOrderWhenClosingStore() throws Throwable {\n\t\t\tstore.put(namespace, \"key1\", \"value1\");\n\t\t\tstore.put(namespace, \"key2\", \"value2\");\n\t\t\tstore.put(namespace, \"key3\", \"value3\");\n\t\t\tverifyNoInteractions(closeAction);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tvar inOrder = inOrder(closeAction);\n\t\t\tinOrder.verify(closeAction).close(namespace, \"key3\", \"value3\");\n\t\t\tinOrder.verify(closeAction).close(namespace, \"key2\", \"value2\");\n\t\t\tinOrder.verify(closeAction).close(namespace, \"key1\", \"value1\");\n\n\t\t\tverifyNoMoreInteractions(closeAction);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotCallCloseActionForRemovedValues() {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tstore.remove(namespace, key);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tverifyNoInteractions(closeAction);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotCallCloseActionForReplacedValues() throws Throwable {\n\t\t\tstore.put(namespace, key, \"value1\");\n\t\t\tstore.put(namespace, key, \"value2\");\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tverify(closeAction).close(namespace, key, \"value2\");\n\t\t\tverifyNoMoreInteractions(closeAction);\n\t\t}\n\n\t\t@Test\n\t\tvoid doesNotCallCloseActionForNullValues() {\n\t\t\tstore.put(namespace, key, null);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tverifyNoInteractions(closeAction);\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid doesNotCallCloseActionForValuesThatThrowExceptionsDuringCleanup() throws Throwable {\n\t\t\tstore.put(namespace, \"key1\", \"value1\");\n\t\t\tassertThrows(ComputeException.class, () -> store.computeIfAbsent(namespace, \"key2\", __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tassertThrows(ComputeException.class, () -> store.getOrComputeIfAbsent(namespace, \"key3\", __ -> {\n\t\t\t\tthrow new ComputeException(\"boom\");\n\t\t\t}));\n\t\t\tstore.put(namespace, \"key4\", \"value4\");\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tvar inOrder = inOrder(closeAction);\n\t\t\tinOrder.verify(closeAction).close(namespace, \"key4\", \"value4\");\n\t\t\tinOrder.verify(closeAction).close(namespace, \"key1\", \"value1\");\n\t\t\tinOrder.verifyNoMoreInteractions();\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid abortsCloseIfAnyStoredValueThrowsAnUnrecoverableExceptionDuringCleanup() throws Throwable {\n\t\t\tstore.put(namespace, \"key1\", \"value1\");\n\t\t\tassertThrows(OutOfMemoryError.class, () -> store.getOrComputeIfAbsent(namespace, \"key2\", __ -> {\n\t\t\t\tthrow new OutOfMemoryError(\"boom\");\n\t\t\t}));\n\t\t\tstore.put(namespace, \"key3\", \"value3\");\n\n\t\t\tassertThrows(OutOfMemoryError.class, store::close);\n\t\t\tassertClosed();\n\n\t\t\tverifyNoInteractions(closeAction);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\t\t}\n\n\t\t@Test\n\t\tvoid closesStoreEvenIfCloseActionThrowsException() throws Throwable {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tdoThrow(IllegalStateException.class).when(closeAction).close(namespace, key, value);\n\n\t\t\tassertThrows(IllegalStateException.class, store::close);\n\t\t\tassertClosed();\n\n\t\t\tverify(closeAction).close(namespace, key, value);\n\t\t\tverifyNoMoreInteractions(closeAction);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\t\t}\n\n\t\t@Test\n\t\tvoid closesStoreEvenIfCloseActionThrowsUnrecoverableException() throws Throwable {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tdoThrow(OutOfMemoryError.class).when(closeAction).close(namespace, key, value);\n\n\t\t\tassertThrows(OutOfMemoryError.class, store::close);\n\t\t\tassertClosed();\n\n\t\t\tverify(closeAction).close(namespace, key, value);\n\t\t\tverifyNoMoreInteractions(closeAction);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\t\t}\n\n\t\t@Test\n\t\tvoid closesStoreEvenIfNoCloseActionIsConfigured() {\n\t\t\t@SuppressWarnings(\"resource\")\n\t\t\tvar localStore = new NamespacedHierarchicalStore<>(null);\n\t\t\tassertThat(localStore.isClosed()).isFalse();\n\t\t\tlocalStore.close();\n\t\t\tassertThat(localStore.isClosed()).isTrue();\n\t\t}\n\n\t\t@Test\n\t\tvoid closeIsIdempotent() throws Throwable {\n\t\t\tstore.put(namespace, key, value);\n\n\t\t\tverifyNoInteractions(closeAction);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tverify(closeAction, times(1)).close(namespace, key, value);\n\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tverifyNoMoreInteractions(closeAction);\n\t\t}\n\n\t\t/**\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/3944\">#3944</a>\n\t\t */\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid acceptsQueryAfterClose() {\n\t\t\tstore.put(namespace, key, value);\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tassertThat(store.get(namespace, key)).isEqualTo(value);\n\t\t\tassertThat(store.get(namespace, key, String.class)).isEqualTo(value);\n\t\t\tassertThat(store.getOrComputeIfAbsent(namespace, key, __ -> \"new\")).isEqualTo(value);\n\t\t\tassertThat(store.getOrComputeIfAbsent(namespace, key, __ -> \"new\", String.class)).isEqualTo(value);\n\t\t\tassertThat(store.computeIfAbsent(namespace, key, __ -> \"new\")).isEqualTo(value);\n\t\t\tassertThat(store.computeIfAbsent(namespace, key, __ -> \"new\", String.class)).isEqualTo(value);\n\t\t}\n\n\t\t@SuppressWarnings(\"deprecation\")\n\t\t@Test\n\t\tvoid rejectsModificationAfterClose() {\n\t\t\tstore.close();\n\t\t\tassertClosed();\n\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class, () -> store.put(namespace, key, value));\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class, () -> store.remove(namespace, key));\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class, () -> store.remove(namespace, key, int.class));\n\n\t\t\t// Since key does not exist, an invocation of getOrComputeIfAbsent(...) or computeIfAbsent(...) will attempt\n\t\t\t// to compute a new value.\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.getOrComputeIfAbsent(namespace, key, __ -> \"new\"));\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.getOrComputeIfAbsent(namespace, key, __ -> \"new\", String.class));\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.computeIfAbsent(namespace, key, __ -> \"new\"));\n\t\t\tassertThrows(NamespacedHierarchicalStoreException.class,\n\t\t\t\t() -> store.computeIfAbsent(namespace, key, __ -> \"new\", String.class));\n\t\t}\n\n\t\tprivate void assertNotClosed() {\n\t\t\tassertThat(store.isClosed()).as(\"closed\").isFalse();\n\t\t}\n\n\t\tprivate void assertClosed() {\n\t\t\tassertThat(store.isClosed()).as(\"closed\").isTrue();\n\t\t}\n\n\t}\n\n\t@Nested\n\tclass DeferredSupplierTests {\n\n\t\t@Test\n\t\tvoid getCanBeInterrupted() {\n\t\t\tvar supplier = new NamespacedHierarchicalStore.DeferredSupplier(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(1000);\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tthrow new ComputeException(e);\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t\tThread.currentThread().interrupt();\n\t\t\tassertThrows(InterruptedException.class, () -> {\n\t\t\t\tsupplier.get();\n\t\t\t});\n\t\t\tassertTrue(Thread.interrupted());\n\t\t}\n\n\t\t@Test\n\t\tvoid getThrowsIfUnrecoverable() {\n\t\t\tvar supplier = new NamespacedHierarchicalStore.DeferredSupplier(() -> {\n\t\t\t\tthrow new OutOfMemoryError(\"boom\");\n\t\t\t});\n\t\t\tsupplier.run();\n\t\t\tassertThrows(OutOfMemoryError.class, () -> {\n\t\t\t\tsupplier.get();\n\t\t\t});\n\t\t}\n\n\t\t@Test\n\t\tvoid getOrThrowThrowsIfUnrecoverable() {\n\t\t\tvar supplier = new NamespacedHierarchicalStore.DeferredSupplier(() -> {\n\t\t\t\tthrow new OutOfMemoryError(\"boom\");\n\t\t\t});\n\t\t\tsupplier.run();\n\t\t\tassertThrows(OutOfMemoryError.class, () -> {\n\t\t\t\tsupplier.getOrThrow();\n\t\t\t});\n\t\t}\n\n\t\t@Test\n\t\tvoid getOrThrowCanBeInterrupted() {\n\t\t\tvar supplier = new NamespacedHierarchicalStore.DeferredSupplier(() -> {\n\t\t\t\ttry {\n\t\t\t\t\tThread.sleep(1000);\n\t\t\t\t}\n\t\t\t\tcatch (InterruptedException e) {\n\t\t\t\t\tthrow new ComputeException(e);\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t\tThread.currentThread().interrupt();\n\t\t\tassertThrows(InterruptedException.class, () -> {\n\t\t\t\tsupplier.getOrThrow();\n\t\t\t});\n\t\t\tassertTrue(Thread.interrupted());\n\t\t}\n\t}\n\n\tprivate static Object createObject(String display) {\n\t\treturn new Object() {\n\n\t\t\t@Override\n\t\t\tpublic String toString() {\n\t\t\t\treturn display;\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * To avoid confusion with other Runtime exceptions that can be thrown.\n\t */\n\tprivate static final class ComputeException extends RuntimeException {\n\n\t\t@Serial\n\t\tprivate static final long serialVersionUID = 1L;\n\n\t\tComputeException(String msg) {\n\t\t\tsuper(msg);\n\t\t}\n\n\t\tComputeException(InterruptedException e) {\n\t\t\tsuper(e);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/DiscoveryFilterStub.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * @since 1.0\n */\npublic class DiscoveryFilterStub<T> extends FilterStub<T> implements DiscoveryFilter<T> {\n\n\tpublic DiscoveryFilterStub(String toString) {\n\t\tsuper(toString);\n\t}\n\n\tpublic DiscoveryFilterStub(Function<T, FilterResult> function, Supplier<String> toString) {\n\t\tsuper(function, toString);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/FilterStub.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.engine.Filter;\nimport org.junit.platform.engine.FilterResult;\n\n/**\n * @since 1.0\n */\npublic class FilterStub<T> implements Filter<T> {\n\n\tprivate final Function<T, FilterResult> function;\n\tprivate final Supplier<String> toString;\n\n\tpublic FilterStub(String toString) {\n\t\tthis(o -> FilterResult.included(\"always\"), () -> toString);\n\t}\n\n\tpublic FilterStub(Function<T, FilterResult> function, Supplier<String> toString) {\n\t\tthis.function = function;\n\t\tthis.toString = toString;\n\t}\n\n\t@Override\n\tpublic FilterResult apply(T object) {\n\t\treturn function.apply(object);\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn toString.get();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/InterceptedTestEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport org.junit.platform.fakes.TestEngineSpy;\n\npublic class InterceptedTestEngine extends TestEngineSpy {\n\n\tpublic static final String ID = \"intercepted-engine\";\n\n\tpublic InterceptedTestEngine() {\n\t\tsuper(ID);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/InterceptorInjectedLauncherSessionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\npublic class InterceptorInjectedLauncherSessionListener implements LauncherSessionListener {\n\n\tpublic static int CALLS;\n\n\tpublic InterceptorInjectedLauncherSessionListener() {\n\t\tassertEquals(TestLauncherInterceptor1.CLASSLOADER_NAME,\n\t\t\tThread.currentThread().getContextClassLoader().getName());\n\t\tassertTrue(TestLauncherInterceptor2.INTERCEPTING);\n\t}\n\n\t@Override\n\tpublic void launcherSessionOpened(LauncherSession session) {\n\t\tCALLS++;\n\t}\n\n\t@Override\n\tpublic void launcherSessionClosed(LauncherSession session) {\n\t\tassertEquals(TestLauncherInterceptor1.CLASSLOADER_NAME,\n\t\t\tThread.currentThread().getContextClassLoader().getName());\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/MethodFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationContainsNoNullElementsFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\nimport static org.junit.platform.launcher.MethodFilter.excludeMethodNamePatterns;\nimport static org.junit.platform.launcher.MethodFilter.includeMethodNamePatterns;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.DemoMethodTestDescriptor;\n\n/**\n * Unit tests for {@link MethodFilter}.\n *\n * @since 1.12\n */\nclass MethodFilterTests {\n\tprivate static final String CLASS1_TEST1_NAME = \"org.junit.platform.launcher.MethodFilterTests$Class1#test1\";\n\tprivate static final String CLASS1_TEST2_NAME = \"org.junit.platform.launcher.MethodFilterTests$Class1#test2\";\n\tprivate static final String CLASS2_TEST1_NAME = \"org.junit.platform.launcher.MethodFilterTests$Class2#test1\";\n\tprivate static final String CLASS2_TEST2_NAME = \"org.junit.platform.launcher.MethodFilterTests$Class2#test2\";\n\tprivate static final TestDescriptor CLASS1_TEST1 = methodTestDescriptor(\"class1\", Class1.class, \"test1\");\n\tprivate static final TestDescriptor CLASS1_TEST2 = methodTestDescriptor(\"class1\", Class1.class, \"test2\");\n\tprivate static final TestDescriptor CLASS2_TEST1 = methodTestDescriptor(\"class2\", Class2.class, \"test1\");\n\tprivate static final TestDescriptor CLASS2_TEST2 = methodTestDescriptor(\"class2\", Class2.class, \"test2\");\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid includeMethodNamePatternsChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> includeMethodNamePatterns((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\", () -> includeMethodNamePatterns(new String[0]));\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"patterns array\",\n\t\t\t() -> includeMethodNamePatterns(new String[] { null }));\n\t}\n\n\t@Test\n\tvoid includeSingleMethodNamePattern() {\n\t\tvar regex = \"^org\\\\.junit\\\\.platform\\\\.launcher\\\\.MethodFilterTests\\\\$Class1#test.*\";\n\t\tvar filter = includeMethodNamePatterns(regex);\n\n\t\tassertIncluded(filter.apply(CLASS1_TEST1),\n\t\t\t\"Method name [%s] matches included pattern: '%s'\".formatted(CLASS1_TEST1_NAME, regex));\n\t\tassertIncluded(filter.apply(CLASS1_TEST2),\n\t\t\t\"Method name [%s] matches included pattern: '%s'\".formatted(CLASS1_TEST2_NAME, regex));\n\n\t\tassertExcluded(filter.apply(CLASS2_TEST1),\n\t\t\t\"Method name [%s] does not match any included pattern: '%s'\".formatted(CLASS2_TEST1_NAME, regex));\n\t\tassertExcluded(filter.apply(CLASS2_TEST2),\n\t\t\t\"Method name [%s] does not match any included pattern: '%s'\".formatted(CLASS2_TEST2_NAME, regex));\n\t}\n\n\t@Test\n\tvoid includeMultipleMethodNamePatterns() {\n\t\tvar firstRegex = \"^org\\\\.junit\\\\.platform\\\\.launcher\\\\.MethodFilterTests\\\\$Class1#test.*\";\n\t\tvar secondRegex = \".+Class.+#test1\";\n\t\tvar filter = includeMethodNamePatterns(firstRegex, secondRegex);\n\n\t\tassertIncluded(filter.apply(CLASS1_TEST1),\n\t\t\t\"Method name [%s] matches included pattern: '%s'\".formatted(CLASS1_TEST1_NAME, firstRegex));\n\t\tassertIncluded(filter.apply(CLASS1_TEST2),\n\t\t\t\"Method name [%s] matches included pattern: '%s'\".formatted(CLASS1_TEST2_NAME, firstRegex));\n\t\tassertIncluded(filter.apply(CLASS2_TEST1),\n\t\t\t\"Method name [%s] matches included pattern: '%s'\".formatted(CLASS2_TEST1_NAME, secondRegex));\n\n\t\tassertExcluded(filter.apply(CLASS2_TEST2),\n\t\t\t\"Method name [%s] does not match any included pattern: '%s' OR '%s'\".formatted(CLASS2_TEST2_NAME,\n\t\t\t\tfirstRegex, secondRegex));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid excludeMethodNamePatternsChecksPreconditions() {\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\",\n\t\t\t() -> excludeMethodNamePatterns((String[]) null));\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"patterns array\", () -> excludeMethodNamePatterns(new String[0]));\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"patterns array\",\n\t\t\t() -> excludeMethodNamePatterns(new String[] { null }));\n\t}\n\n\t@Test\n\tvoid excludeSingleMethodNamePattern() {\n\t\tvar regex = \"^org\\\\.junit\\\\.platform\\\\.launcher\\\\.MethodFilterTests\\\\$Class1#test.*\";\n\t\tvar filter = excludeMethodNamePatterns(regex);\n\n\t\tassertExcluded(filter.apply(CLASS1_TEST1),\n\t\t\t\"Method name [%s] matches excluded pattern: '%s'\".formatted(CLASS1_TEST1_NAME, regex));\n\t\tassertExcluded(filter.apply(CLASS1_TEST2),\n\t\t\t\"Method name [%s] matches excluded pattern: '%s'\".formatted(CLASS1_TEST2_NAME, regex));\n\n\t\tassertIncluded(filter.apply(CLASS2_TEST1),\n\t\t\t\"Method name [%s] does not match any excluded pattern: '%s'\".formatted(CLASS2_TEST1_NAME, regex));\n\t\tassertIncluded(filter.apply(CLASS2_TEST2),\n\t\t\t\"Method name [%s] does not match any excluded pattern: '%s'\".formatted(CLASS2_TEST2_NAME, regex));\n\t}\n\n\t@Test\n\tvoid excludeMultipleMethodNamePatterns() {\n\t\tvar firstRegex = \"^org\\\\.junit\\\\.platform\\\\.launcher\\\\.MethodFilterTests\\\\$Class1#test.*\";\n\t\tvar secondRegex = \".+Class.+#test1\";\n\t\tvar filter = excludeMethodNamePatterns(firstRegex, secondRegex);\n\n\t\tassertExcluded(filter.apply(CLASS1_TEST1),\n\t\t\t\"Method name [%s] matches excluded pattern: '%s'\".formatted(CLASS1_TEST1_NAME, firstRegex));\n\t\tassertExcluded(filter.apply(CLASS1_TEST2),\n\t\t\t\"Method name [%s] matches excluded pattern: '%s'\".formatted(CLASS1_TEST2_NAME, firstRegex));\n\t\tassertExcluded(filter.apply(CLASS2_TEST1),\n\t\t\t\"Method name [%s] matches excluded pattern: '%s'\".formatted(CLASS2_TEST1_NAME, secondRegex));\n\n\t\tassertIncluded(filter.apply(CLASS2_TEST2),\n\t\t\t\"Method name [%s] does not match any excluded pattern: '%s' OR '%s'\".formatted(CLASS2_TEST2_NAME,\n\t\t\t\tfirstRegex, secondRegex));\n\t}\n\n\tprivate void assertIncluded(FilterResult filterResult, String expectedReason) {\n\t\tassertTrue(filterResult.included());\n\t\tassertThat(filterResult.getReason()).isPresent().contains(expectedReason);\n\t}\n\n\tprivate void assertExcluded(FilterResult filterResult, String excludedPattern) {\n\t\tassertTrue(filterResult.excluded());\n\t\tassertThat(filterResult.getReason()).isPresent().contains(excludedPattern);\n\t}\n\n\tprivate static TestDescriptor methodTestDescriptor(String uniqueId, Class<?> testClass, String methodName) {\n\t\tvar method = ReflectionUtils.findMethod(testClass, methodName, new Class<?>[0]).orElseThrow();\n\t\treturn new DemoMethodTestDescriptor(UniqueId.root(\"method\", uniqueId), method);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tprivate static class Class1 {\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n\n\tprivate static class Class2 {\n\t\t@Test\n\t\tvoid test1() {\n\t\t}\n\n\t\t@Test\n\t\tvoid test2() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/PostDiscoveryFilterStub.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\n\n/**\n * @since 1.0\n */\npublic class PostDiscoveryFilterStub extends FilterStub<TestDescriptor> implements PostDiscoveryFilter {\n\n\tpublic PostDiscoveryFilterStub(String toString) {\n\t\tsuper(toString);\n\t}\n\n\tpublic PostDiscoveryFilterStub(Function<TestDescriptor, FilterResult> function, Supplier<String> toString) {\n\t\tsuper(function, toString);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TagFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.launcher.TagFilter.excludeTags;\nimport static org.junit.platform.launcher.TagFilter.includeTags;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.DemoClassTestDescriptor;\n\n/**\n * Unit tests for {@link TagFilter}.\n *\n * <p>NOTE: part of the behavior of these tests regarding tags is\n * influenced by the implementation of {@link DemoClassTestDescriptor#getTags()}\n * rather than any concrete test engine.\n *\n * @since 1.0\n */\nclass TagFilterTests {\n\n\tprivate static final TestDescriptor classWithTag1 = classTestDescriptor(\"class1\", ClassWithTag1.class);\n\tprivate static final TestDescriptor classWithTag1AndSurroundingWhitespace = classTestDescriptor(\n\t\t\"class1-surrounding-whitespace\", ClassWithTag1AndSurroundingWhitespace.class);\n\tprivate static final TestDescriptor classWithTag2 = classTestDescriptor(\"class2\", ClassWithTag2.class);\n\tprivate static final TestDescriptor classWithBothTags = classTestDescriptor(\"class12\", ClassWithBothTags.class);\n\tprivate static final TestDescriptor classWithDifferentTags = classTestDescriptor(\"classX\",\n\t\tClassWithDifferentTags.class);\n\tprivate static final TestDescriptor classWithNoTags = classTestDescriptor(\"class\", ClassWithNoTags.class);\n\n\t@Test\n\tvoid includeTagsWithInvalidSyntax() {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertSyntaxViolationForIncludes(null),\n\t\t\t() -> assertSyntaxViolationForIncludes(\"\"),\n\t\t\t() -> assertSyntaxViolationForIncludes(\"   \"),\n\t\t\t() -> assertSyntaxViolationForIncludes(\"foo bar\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tprivate void assertSyntaxViolationForIncludes(@Nullable String tag) {\n\t\tassertPreconditionViolationFor(() -> includeTags(tag))//\n\t\t\t\t.withMessageStartingWith(\"Unable to parse tag expression\");\n\t}\n\n\t@Test\n\tvoid excludeTagsWithInvalidSyntax() {\n\t\t// @formatter:off\n\t\tassertAll(\n\t\t\t() -> assertSyntaxViolationForExcludes(null),\n\t\t\t() -> assertSyntaxViolationForExcludes(\"\"),\n\t\t\t() -> assertSyntaxViolationForExcludes(\"   \"),\n\t\t\t() -> assertSyntaxViolationForExcludes(\"foo bar\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\tprivate void assertSyntaxViolationForExcludes(@Nullable String tag) {\n\t\tassertPreconditionViolationFor(() -> excludeTags(tag))//\n\t\t\t\t.withMessageStartingWith(\"Unable to parse tag expression\");\n\t}\n\n\t@Test\n\tvoid includeSingleTag() {\n\t\tincludeSingleTag(includeTags(\"tag1\"));\n\t}\n\n\t@Test\n\tvoid includeSingleTagAndWhitespace() {\n\t\tincludeSingleTag(includeTags(\"\\t \\n tag1  \"));\n\t}\n\n\t@Test\n\tvoid includeMultipleTags() {\n\t\tvar filter = includeTags(\"tag1\", \"  tag2  \");\n\n\t\tassertTrue(filter.apply(classWithBothTags).included());\n\t\tassertTrue(filter.apply(classWithTag1).included());\n\t\tassertTrue(filter.apply(classWithTag1AndSurroundingWhitespace).included());\n\t\tassertTrue(filter.apply(classWithTag2).included());\n\n\t\tassertTrue(filter.apply(classWithDifferentTags).excluded());\n\t\tassertTrue(filter.apply(classWithNoTags).excluded());\n\t}\n\n\t@Test\n\tvoid excludeSingleTag() {\n\t\texcludeSingleTag(excludeTags(\"tag1\"));\n\t}\n\n\t@Test\n\tvoid excludeSingleTagAndWhitespace() {\n\t\texcludeSingleTag(excludeTags(\"\\t \\n tag1  \"));\n\t}\n\n\t@Test\n\tvoid excludeMultipleTags() {\n\t\tvar filter = excludeTags(\"tag1\", \"  tag2  \");\n\n\t\tvar exclusionReason = \"excluded because tags match tag expression(s): [tag1,tag2]\";\n\t\tassertExcluded(filter.apply(classWithTag1), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithTag1AndSurroundingWhitespace), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithBothTags), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithTag2), exclusionReason);\n\n\t\tvar inclusionReason = \"included because tags do not match expression(s): [tag1,tag2]\";\n\t\tassertIncluded(filter.apply(classWithDifferentTags), inclusionReason);\n\t\tassertIncluded(filter.apply(classWithNoTags), inclusionReason);\n\t}\n\n\t@Test\n\tvoid rejectSingleUnparsableTagExpressions() {\n\t\tvar brokenTagExpression = \"tag & \";\n\t\tassertPreconditionViolationFor(() -> TagFilter.includeTags(brokenTagExpression))//\n\t\t\t\t.withMessageStartingWith(\"Unable to parse tag expression \\\"\" + brokenTagExpression + \"\\\"\");\n\t}\n\n\t@Test\n\tvoid rejectUnparsableTagExpressionFromArray() {\n\t\tvar brokenTagExpression = \"tag & \";\n\t\tassertPreconditionViolationFor(() -> TagFilter.excludeTags(brokenTagExpression, \"foo\", \"bar\"))//\n\t\t\t\t.withMessageStartingWith(\"Unable to parse tag expression \\\"\" + brokenTagExpression + \"\\\"\");\n\t}\n\n\tprivate void includeSingleTag(PostDiscoveryFilter filter) {\n\t\tvar inclusionReason = \"included because tags match expression(s): [tag1]\";\n\t\tassertIncluded(filter.apply(classWithTag1), inclusionReason);\n\t\tassertIncluded(filter.apply(classWithTag1AndSurroundingWhitespace), inclusionReason);\n\t\tassertIncluded(filter.apply(classWithBothTags), inclusionReason);\n\n\t\tvar exclusionReason = \"excluded because tags do not match tag expression(s): [tag1]\";\n\t\tassertExcluded(filter.apply(classWithTag2), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithDifferentTags), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithNoTags), exclusionReason);\n\t}\n\n\tprivate void excludeSingleTag(PostDiscoveryFilter filter) {\n\t\tvar exclusionReason = \"excluded because tags match tag expression(s): [tag1]\";\n\t\tassertExcluded(filter.apply(classWithTag1), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithTag1AndSurroundingWhitespace), exclusionReason);\n\t\tassertExcluded(filter.apply(classWithBothTags), exclusionReason);\n\n\t\tvar inclusionReason = \"included because tags do not match expression(s): [tag1]\";\n\t\tassertIncluded(filter.apply(classWithTag2), inclusionReason);\n\t\tassertIncluded(filter.apply(classWithDifferentTags), inclusionReason);\n\t\tassertIncluded(filter.apply(classWithNoTags), inclusionReason);\n\t}\n\n\tprivate void assertIncluded(FilterResult filterResult, String expectedReason) {\n\t\tassertTrue(filterResult.included());\n\t\tassertThat(filterResult.getReason()).contains(expectedReason);\n\t}\n\n\tprivate void assertExcluded(FilterResult filterResult, String expectedReason) {\n\t\tassertTrue(filterResult.excluded());\n\t\tassertThat(filterResult.getReason()).contains(expectedReason);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Tag(\"tag1\")\n\tprivate @interface Tag1 {\n\t}\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Tag(\"tag2\")\n\tprivate @interface Tag2 {\n\t}\n\n\t@Tag1\n\tprivate static class ClassWithTag1 {\n\t}\n\n\t@Tag(\"   tag1  \\t    \")\n\tprivate static class ClassWithTag1AndSurroundingWhitespace {\n\t}\n\n\t@Tag2\n\tprivate static class ClassWithTag2 {\n\t}\n\n\t@Tag1\n\t@Tag2\n\tprivate static class ClassWithBothTags {\n\t}\n\n\t@Tag(\"foo\")\n\t@Tag(\"bar\")\n\tprivate static class ClassWithDifferentTags {\n\t}\n\n\t@Tag(\"   \") // intentionally \"blank\"\n\tprivate static class ClassWithNoTags {\n\t}\n\n\tprivate static TestDescriptor classTestDescriptor(String uniqueId, Class<?> testClass) {\n\t\tvar rootUniqueId = UniqueId.root(\"class\", uniqueId);\n\t\treturn new DemoClassTestDescriptor(rootUniqueId, testClass);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TagIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.TagFilter.includeTags;\nimport static org.junit.platform.launcher.TagIntegrationTests.TaggedTestCase.doubleTaggedWasExecuted;\nimport static org.junit.platform.launcher.TagIntegrationTests.TaggedTestCase.tag1WasExecuted;\nimport static org.junit.platform.launcher.TagIntegrationTests.TaggedTestCase.tag2WasExecuted;\nimport static org.junit.platform.launcher.TagIntegrationTests.TaggedTestCase.unTaggedWasExecuted;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\nclass TagIntegrationTests {\n\n\t@BeforeEach\n\tvoid init() {\n\t\ttag1WasExecuted = false;\n\t\ttag2WasExecuted = false;\n\t\tunTaggedWasExecuted = false;\n\t\tdoubleTaggedWasExecuted = false;\n\t}\n\n\t@Test\n\tvoid includingWrongTagExecutesNothing() {\n\t\texecuteTaggedTestCase(includeTags(\"whatever\"));\n\n\t\tassertFalse(tag1WasExecuted);\n\t\tassertFalse(tag2WasExecuted);\n\t\tassertFalse(doubleTaggedWasExecuted);\n\t\tassertFalse(unTaggedWasExecuted);\n\t}\n\n\t@Test\n\tvoid includingSuitableTagExecutesTaggedTestOnly() {\n\t\texecuteTaggedTestCase(includeTags(\"tag1\"));\n\n\t\tassertTrue(tag1WasExecuted);\n\t\tassertFalse(tag2WasExecuted);\n\t\tassertTrue(doubleTaggedWasExecuted);\n\t\tassertFalse(unTaggedWasExecuted);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"any()\", \"!none()\" })\n\tvoid includingTheAnyKeywordExecutesAllTaggedTests(String tagExpression) {\n\t\texecuteTaggedTestCase(includeTags(tagExpression));\n\n\t\tassertTrue(tag1WasExecuted);\n\t\tassertTrue(tag2WasExecuted);\n\t\tassertTrue(doubleTaggedWasExecuted);\n\t\tassertFalse(unTaggedWasExecuted);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"none()\", \"!any()\" })\n\tvoid includingTheNoneKeywordExecutesAllUntaggedTests(String tagExpression) {\n\t\texecuteTaggedTestCase(includeTags(tagExpression));\n\n\t\tassertFalse(tag1WasExecuted);\n\t\tassertFalse(tag2WasExecuted);\n\t\tassertFalse(doubleTaggedWasExecuted);\n\t\tassertTrue(unTaggedWasExecuted);\n\t}\n\n\tprivate void executeTaggedTestCase(PostDiscoveryFilter filter) {\n\t\tEngineTestKit.engine(\"junit-jupiter\") //\n\t\t\t\t.selectors(selectClass(TaggedTestCase.class)) //\n\t\t\t\t.filters(filter) //\n\t\t\t\t.execute();\n\t}\n\n\tstatic class TaggedTestCase {\n\n\t\tstatic boolean tag1WasExecuted = false;\n\t\tstatic boolean tag2WasExecuted = false;\n\t\tstatic boolean unTaggedWasExecuted = false;\n\t\tstatic boolean doubleTaggedWasExecuted = false;\n\n\t\t@Test\n\t\t@Tag(\"tag1\")\n\t\tvoid tagged1() {\n\t\t\ttag1WasExecuted = true;\n\t\t}\n\n\t\t@Test\n\t\t@Tag(\"tag2\")\n\t\tvoid tagged2() {\n\t\t\ttag2WasExecuted = true;\n\t\t}\n\n\t\t@Test\n\t\t@Tag(\"tag1\")\n\t\t@Tag(\"tag2\")\n\t\tvoid doubleTagged() {\n\t\t\tdoubleTaggedWasExecuted = true;\n\t\t}\n\n\t\t@Test\n\t\tvoid unTagged() {\n\t\t\tunTaggedWasExecuted = true;\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestIdentifierTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static java.util.stream.Collectors.collectingAndThen;\nimport static java.util.stream.Collectors.toSet;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.util.SerializationUtils.deserialize;\nimport static org.junit.platform.commons.util.SerializationUtils.serialize;\n\nimport java.io.Serializable;\nimport java.util.AbstractSet;\nimport java.util.Iterator;\nimport java.util.Set;\nimport java.util.stream.IntStream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\n\n/**\n * @since 1.0\n */\n@NullMarked\nclass TestIdentifierTests {\n\n\t@Test\n\tvoid inheritsIdAndNamesFromDescriptor() {\n\t\tTestDescriptor testDescriptor = new TestDescriptorStub(UniqueId.root(\"aType\", \"uniqueId\"), \"displayName\");\n\t\tvar testIdentifier = TestIdentifier.from(testDescriptor);\n\n\t\tassertEquals(\"[aType:uniqueId]\", testIdentifier.getUniqueId());\n\t\tassertEquals(\"displayName\", testIdentifier.getDisplayName());\n\t}\n\n\t@Test\n\tvoid inheritsTypeFromDescriptor() {\n\t\tTestDescriptor descriptor = new TestDescriptorStub(UniqueId.root(\"aType\", \"uniqueId\"), \"displayName\");\n\t\tvar identifier = TestIdentifier.from(descriptor);\n\t\tassertEquals(TestDescriptor.Type.TEST, identifier.getType());\n\t\tassertTrue(identifier.isTest());\n\t\tassertFalse(identifier.isContainer());\n\n\t\tdescriptor.addChild(new TestDescriptorStub(UniqueId.root(\"aChild\", \"uniqueId\"), \"displayName\"));\n\t\tidentifier = TestIdentifier.from(descriptor);\n\t\tassertEquals(TestDescriptor.Type.CONTAINER, identifier.getType());\n\t\tassertFalse(identifier.isTest());\n\t\tassertTrue(identifier.isContainer());\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { 0, 1, 2 })\n\tvoid currentVersionCanBeSerializedAndDeserialized(int tagCount) throws Exception {\n\t\tvar tags = IntStream.range(0, tagCount) //\n\t\t\t\t.mapToObj(i -> TestTag.create(\"tag-\" + i)) //\n\t\t\t\t.collect(collectingAndThen(toSet(), TestIdentifierTests::unserializableSet));\n\n\t\tvar original = createOriginalTestIdentifier(tags);\n\n\t\tbyte[] bytes = serialize(original);\n\t\tvar roundTripped = (TestIdentifier) deserialize(bytes);\n\n\t\tassertDeepEquals(original, roundTripped);\n\t\tassertThat(original.getTags()).isInstanceOf(Serializable.class);\n\t}\n\n\tprivate static <T> Set<T> unserializableSet(Set<T> delegate) {\n\t\tvar wrapper = new AbstractSet<T>() {\n\n\t\t\t@Override\n\t\t\tpublic Iterator<T> iterator() {\n\t\t\t\treturn delegate.iterator();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic int size() {\n\t\t\t\treturn delegate.size();\n\t\t\t}\n\t\t};\n\t\tassertThat(wrapper).isNotInstanceOf(Serializable.class);\n\t\treturn wrapper;\n\t}\n\n\t@Test\n\tvoid identifierWithNoParentCanBeSerializedAndDeserialized() throws Exception {\n\t\tTestIdentifier originalIdentifier = TestIdentifier.from(\n\t\t\tnew AbstractTestDescriptor(UniqueId.root(\"example\", \"id\"), \"Example\") {\n\t\t\t\t@Override\n\t\t\t\tpublic Type getType() {\n\t\t\t\t\treturn Type.CONTAINER;\n\t\t\t\t}\n\t\t\t});\n\n\t\tvar deserializedIdentifier = (TestIdentifier) deserialize(serialize(originalIdentifier));\n\n\t\tassertDeepEquals(originalIdentifier, deserializedIdentifier);\n\t}\n\n\tprivate static void assertDeepEquals(TestIdentifier first, TestIdentifier second) {\n\t\tassertEquals(first, second);\n\t\tassertEquals(first.getUniqueId(), second.getUniqueId());\n\t\tassertEquals(first.getUniqueIdObject(), second.getUniqueIdObject());\n\t\tassertEquals(first.getDisplayName(), second.getDisplayName());\n\t\tassertEquals(first.getLegacyReportingName(), second.getLegacyReportingName());\n\t\tassertEquals(first.getSource(), second.getSource());\n\t\tassertEquals(first.getTags(), second.getTags());\n\t\tassertEquals(first.getType(), second.getType());\n\t\tassertEquals(first.getParentId(), second.getParentId());\n\t\tassertEquals(first.getParentIdObject(), second.getParentIdObject());\n\t}\n\n\tprivate static TestIdentifier createOriginalTestIdentifier(Set<TestTag> tags) {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"child\", \"child\");\n\t\tvar testSource = ClassSource.from(TestIdentifierTests.class);\n\n\t\tvar testDescriptor = new AbstractTestDescriptor(uniqueId, \"displayName\", testSource) {\n\t\t\t@Override\n\t\t\tpublic Type getType() {\n\t\t\t\treturn Type.TEST;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic String getLegacyReportingName() {\n\t\t\t\treturn \"reportingName\";\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic Set<TestTag> getTags() {\n\t\t\t\treturn tags;\n\t\t\t}\n\t\t};\n\n\t\tengineDescriptor.addChild(testDescriptor);\n\t\treturn TestIdentifier.from(testDescriptor);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherDiscoveryListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\npublic class TestLauncherDiscoveryListener implements LauncherDiscoveryListener {\n\tpublic static boolean called;\n\n\t@Override\n\tpublic void launcherDiscoveryStarted(LauncherDiscoveryRequest request) {\n\t\tcalled = true;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor1.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\n\npublic class TestLauncherInterceptor1 implements LauncherInterceptor {\n\n\tpublic static final String CLASSLOADER_NAME = \"interceptor-loader\";\n\n\tprivate final ClassLoader originalClassLoader;\n\tprivate final URLClassLoader replacedClassLoader;\n\n\tpublic TestLauncherInterceptor1() {\n\t\toriginalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\tvar url = getClass().getClassLoader().getResource(\"intercepted-testservices/\");\n\t\treplacedClassLoader = new URLClassLoader(CLASSLOADER_NAME, new URL[] { url }, originalClassLoader);\n\t\tThread.currentThread().setContextClassLoader(replacedClassLoader);\n\t}\n\n\t@Override\n\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\treturn invocation.proceed();\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\ttry {\n\t\t\treplacedClassLoader.close();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherInterceptor2.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\npublic class TestLauncherInterceptor2 implements LauncherInterceptor {\n\n\tpublic static boolean INTERCEPTING;\n\n\t@Override\n\tpublic <T> T intercept(Invocation<T> invocation) {\n\t\tINTERCEPTING = true;\n\t\ttry {\n\t\t\treturn invocation.proceed();\n\t\t}\n\t\tfinally {\n\t\t\tINTERCEPTING = false;\n\t\t}\n\t}\n\n\t@Override\n\tpublic void close() {\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestLauncherSessionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport org.jspecify.annotations.Nullable;\n\npublic class TestLauncherSessionListener implements LauncherSessionListener {\n\n\tpublic static @Nullable LauncherSession session;\n\n\t@Override\n\tpublic void launcherSessionOpened(LauncherSession session) {\n\t\tTestLauncherSessionListener.session = session;\n\t}\n\n\t@Override\n\tpublic void launcherSessionClosed(LauncherSession session) {\n\t\tTestLauncherSessionListener.session = null;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\n\nclass TestPlanTests {\n\n\tprivate final ConfigurationParameters configParams = mock();\n\n\tprivate final EngineDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"foo\"), \"Foo\");\n\n\t@Test\n\tvoid acceptsVisitorsInDepthFirstOrder() {\n\t\tvar container = new TestDescriptorStub(engineDescriptor.getUniqueId().append(\"container\", \"bar\"), \"Bar\");\n\t\tvar test1 = new TestDescriptorStub(container.getUniqueId().append(\"test\", \"bar\"), \"Bar\");\n\t\tcontainer.addChild(test1);\n\t\tengineDescriptor.addChild(container);\n\n\t\tvar engineDescriptor2 = new EngineDescriptor(UniqueId.forEngine(\"baz\"), \"Baz\");\n\t\tvar test2 = new TestDescriptorStub(engineDescriptor2.getUniqueId().append(\"test\", \"baz1\"), \"Baz\");\n\t\tvar test3 = new TestDescriptorStub(engineDescriptor2.getUniqueId().append(\"test\", \"baz2\"), \"Baz\");\n\t\tengineDescriptor2.addChild(test2);\n\t\tengineDescriptor2.addChild(test3);\n\n\t\tvar testPlan = TestPlan.from(true, List.of(engineDescriptor, engineDescriptor2), configParams,\n\t\t\tdummyOutputDirectoryCreator());\n\t\tvar visitor = mock(TestPlan.Visitor.class);\n\n\t\ttestPlan.accept(visitor);\n\n\t\tvar inOrder = inOrder(visitor);\n\n\t\tinOrder.verify(visitor).preVisitContainer(TestIdentifier.from(engineDescriptor));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(engineDescriptor));\n\t\tinOrder.verify(visitor).preVisitContainer(TestIdentifier.from(container));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(container));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(test1));\n\t\tinOrder.verify(visitor).postVisitContainer(TestIdentifier.from(container));\n\t\tinOrder.verify(visitor).postVisitContainer(TestIdentifier.from(engineDescriptor));\n\n\t\tinOrder.verify(visitor).preVisitContainer(TestIdentifier.from(engineDescriptor2));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(engineDescriptor2));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(test2));\n\t\tinOrder.verify(visitor).visit(TestIdentifier.from(test3));\n\t\tinOrder.verify(visitor).postVisitContainer(TestIdentifier.from(engineDescriptor2));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/TestPostDiscoveryTagFilter.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher;\n\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\n\npublic class TestPostDiscoveryTagFilter implements PostDiscoveryFilter {\n\t@Override\n\tpublic FilterResult apply(final TestDescriptor object) {\n\t\tvar include = object.getTags().stream().map(TestTag::getName).anyMatch(\"test-post-discovery\"::equals);\n\t\treturn FilterResult.includedIf(include);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/ClasspathAlignmentCheckerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.launcher.core.ClasspathAlignmentChecker.WELL_KNOWN_PACKAGES;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.nio.file.Path;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.function.Function;\nimport java.util.regex.Pattern;\n\nimport io.github.classgraph.ClassGraph;\nimport io.github.classgraph.PackageInfo;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.DisabledInEclipse;\n\nclass ClasspathAlignmentCheckerTests {\n\n\t@Test\n\tvoid classpathIsAligned() {\n\t\tassertThat(ClasspathAlignmentChecker.check(new LinkageError())).isEmpty();\n\t}\n\n\t@Test\n\tvoid wrapsLinkageErrorForUnalignedClasspath() {\n\t\tvar cause = new LinkageError();\n\t\tAtomicInteger counter = new AtomicInteger();\n\t\tFunction<String, @Nullable Package> packageLookup = name -> {\n\t\t\tvar pkg = mock(Package.class);\n\t\t\twhen(pkg.getName()).thenReturn(name);\n\t\t\twhen(pkg.getImplementationVersion()).thenReturn(counter.incrementAndGet() + \".0.0\");\n\t\t\treturn pkg;\n\t\t};\n\n\t\tvar result = ClasspathAlignmentChecker.check(cause, packageLookup);\n\n\t\tassertThat(result).isPresent();\n\t\tassertThat(result.get()) //\n\t\t\t\t.hasMessageStartingWith(\"The wrapped LinkageError is likely caused by the versions of \"\n\t\t\t\t\t\t+ \"JUnit jars on the classpath/module path not being properly aligned.\") //\n\t\t\t\t.hasMessageContaining(\"Please ensure consistent versions are used\") //\n\t\t\t\t.hasMessageFindingMatch(\"https://docs\\\\.junit\\\\.org/.*/appendix.html#dependency-metadata\") //\n\t\t\t\t.hasMessageContaining(\"The following conflicting versions were detected:\") //\n\t\t\t\t.hasMessageContaining(\"- org.junit.jupiter.api: 1.0.0\") //\n\t\t\t\t.hasMessageContaining(\"- org.junit.jupiter.engine: 2.0.0\") //\n\t\t\t\t.cause().isSameAs(cause);\n\t}\n\n\t@Test\n\t@DisabledInEclipse\n\tvoid allRootPackagesAreChecked() {\n\t\tvar allowedFileNames = Pattern.compile(\"junit-(?:platform|jupiter|vintage)-.+[\\\\d.]+(?:-SNAPSHOT)?\\\\.jar\");\n\t\tvar classGraph = new ClassGraph() //\n\t\t\t\t.acceptPackages(\"org.junit.platform\", \"org.junit.jupiter\", \"org.junit.vintage\") //\n\t\t\t\t.rejectPackages(\"org.junit.platform.reporting.shadow\", \"org.junit.jupiter.params.shadow\") //\n\t\t\t\t.filterClasspathElements(e -> {\n\t\t\t\t\tvar path = Path.of(e);\n\t\t\t\t\tvar fileName = path.getFileName().toString();\n\t\t\t\t\treturn allowedFileNames.matcher(fileName).matches();\n\t\t\t\t});\n\n\t\ttry (var scanResult = classGraph.scan()) {\n\t\t\tvar foundPackages = scanResult.getPackageInfo().stream() //\n\t\t\t\t\t.filter(it -> !it.getClassInfo().isEmpty()) //\n\t\t\t\t\t.map(PackageInfo::getName) //\n\t\t\t\t\t.sorted() //\n\t\t\t\t\t.toList();\n\n\t\t\tassertThat(foundPackages) //\n\t\t\t\t\t.allMatch(name -> WELL_KNOWN_PACKAGES.stream().anyMatch(name::startsWith));\n\t\t\tassertThat(WELL_KNOWN_PACKAGES) //\n\t\t\t\t\t.allMatch(name -> foundPackages.stream().anyMatch(it -> it.startsWith(name)));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeEngineExecutionListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.DemoMethodTestDescriptor;\nimport org.mockito.InOrder;\n\n@TrackLogRecords\nclass CompositeEngineExecutionListenerTests {\n\n\tprivate final List<EngineExecutionListener> listeners = new ArrayList<>(\n\t\tList.of(new ThrowingEngineExecutionListener()));\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfDynamicTestRegisteredListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeEngineExecutionListener().dynamicTestRegistered(anyTestDescriptor());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, \"dynamicTestRegistered\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionStartedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeEngineExecutionListener().executionStarted(anyTestDescriptor());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, \"executionStarted\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionSkippedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeEngineExecutionListener().executionSkipped(anyTestDescriptor(), \"deliberately skipped container\");\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, \"executionSkipped\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionFinishedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeEngineExecutionListener().executionFinished(anyTestDescriptor(), anyTestExecutionResult());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, \"executionFinished\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfReportingEntryPublishedListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tcompositeEngineExecutionListener().reportingEntryPublished(anyTestDescriptor(), ReportEntry.from(\"one\", \"two\"));\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, \"reportingEntryPublished\");\n\t}\n\n\t@Test\n\tvoid shouldThrowOutOfMemoryExceptionAndStopListenerWithoutLog(LogRecordListener logRecordListener) {\n\t\tlisteners.clear();\n\t\tlisteners.add(new EngineExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\t\t\tthrow new OutOfMemoryError();\n\t\t\t}\n\t\t});\n\t\tvar testDescriptor = anyTestDescriptor();\n\n\t\tassertThatThrownBy(() -> compositeEngineExecutionListener().executionStarted(testDescriptor)).isInstanceOf(\n\t\t\tOutOfMemoryError.class);\n\n\t\tassertNotLogs(logRecordListener);\n\t}\n\n\t@Test\n\tvoid callsListenersInReverseOrderForFinishedEvents() {\n\t\tlisteners.clear();\n\t\tvar firstListener = mock(EngineExecutionListener.class, \"firstListener\");\n\t\tvar secondListener = mock(EngineExecutionListener.class, \"secondListener\");\n\t\tlisteners.add(firstListener);\n\t\tlisteners.add(secondListener);\n\n\t\tvar testDescriptor = anyTestDescriptor();\n\t\tvar testExecutionResult = anyTestExecutionResult();\n\n\t\tvar composite = compositeEngineExecutionListener();\n\t\tcomposite.executionStarted(testDescriptor);\n\t\tcomposite.executionFinished(testDescriptor, testExecutionResult);\n\n\t\tInOrder inOrder = inOrder(firstListener, secondListener);\n\t\tinOrder.verify(firstListener).executionStarted(testDescriptor);\n\t\tinOrder.verify(secondListener).executionStarted(testDescriptor);\n\t\tinOrder.verify(secondListener).executionFinished(testDescriptor, testExecutionResult);\n\t\tinOrder.verify(firstListener).executionFinished(testDescriptor, testExecutionResult);\n\t}\n\n\tprivate EngineExecutionListener compositeEngineExecutionListener() {\n\t\treturn new CompositeEngineExecutionListener(listeners);\n\t}\n\n\tprivate LogRecord firstWarnLogRecord(LogRecordListener logRecordListener) throws AssertionError {\n\t\treturn logRecordListener.stream(CompositeEngineExecutionListener.class, Level.WARNING).findFirst().orElseThrow(\n\t\t\t() -> new AssertionError(\"Failed to find error log record\"));\n\t}\n\n\tprivate void assertNotLogs(LogRecordListener logRecordListener) throws AssertionError {\n\t\tassertThat(logRecordListener.stream(CompositeEngineExecutionListener.class, Level.WARNING).count()).isZero();\n\t}\n\n\tprivate static TestExecutionResult anyTestExecutionResult() {\n\t\treturn mock();\n\t}\n\n\tprivate void assertThatTestListenerErrorLogged(LogRecordListener logRecordListener, String methodName) {\n\t\tassertThat(firstWarnLogRecord(logRecordListener).getMessage()).startsWith(\"EngineExecutionListener [\"\n\t\t\t\t+ ThrowingEngineExecutionListener.class.getName() + \"] threw exception for method: \" + methodName);\n\t}\n\n\tprivate static TestDescriptor anyTestDescriptor() {\n\t\tvar testClass = CompositeEngineExecutionListenerTests.class;\n\t\tvar method = ReflectionUtils.findMethod(testClass, \"anyTestDescriptor\", new Class<?>[0]).orElseThrow();\n\t\treturn new DemoMethodTestDescriptor(UniqueId.root(\"method\", \"unique_id\"), method);\n\t}\n\n\tprivate static class ThrowingEngineExecutionListener implements EngineExecutionListener {\n\n\t\t@Override\n\t\tpublic void dynamicTestRegistered(TestDescriptor testDescriptor) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionStarted(TestDescriptor testDescriptor) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionSkipped(TestDescriptor testDescriptor, String reason) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionFinished(TestDescriptor testDescriptor, TestExecutionResult testExecutionResult) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void reportingEntryPublished(TestDescriptor testDescriptor, ReportEntry entry) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void fileEntryPublished(TestDescriptor testDescriptor, FileEntry file) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.assertThatThrownBy;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.DemoMethodTestDescriptor;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.CompositeTestExecutionListener.EagerTestExecutionListener;\nimport org.mockito.InOrder;\n\n@TrackLogRecords\nclass CompositeTestExecutionListenerTests {\n\n\tprivate final List<TestExecutionListener> listeners = new ArrayList<>(List.of(new ThrowingTestExecutionListener()));\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfDynamicTestRegisteredListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().dynamicTestRegistered(anyTestIdentifier());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class,\n\t\t\t\"dynamicTestRegistered\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionStartedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().executionStarted(anyTestIdentifier());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class, \"executionStarted\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionSkippedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().executionSkipped(anyTestIdentifier(), \"deliberately skipped container\");\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class, \"executionSkipped\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionFinishedListenerMethodFails(LogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().executionFinished(anyTestIdentifier(), anyTestExecutionResult());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class, \"executionFinished\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfReportingEntryPublishedListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().reportingEntryPublished(anyTestIdentifier(), ReportEntry.from(\"one\", \"two\"));\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class,\n\t\t\t\"reportingEntryPublished\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfTesPlanExecutionStartedListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().testPlanExecutionStarted(anyTestPlan());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class,\n\t\t\t\"testPlanExecutionStarted\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfTesPlanExecutionFinishedListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tcompositeTestExecutionListener().testPlanExecutionFinished(anyTestPlan());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingTestExecutionListener.class,\n\t\t\t\"testPlanExecutionFinished\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionJustStartedEagerTestListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tlisteners.add(new ThrowingEagerTestExecutionListener());\n\n\t\tcompositeTestExecutionListener().executionStarted(anyTestIdentifier());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingEagerTestExecutionListener.class,\n\t\t\t\"executionJustStarted\");\n\t}\n\n\t@Test\n\tvoid shouldNotThrowExceptionButLogIfExecutionJustFinishedEagerTestListenerMethodFails(\n\t\t\tLogRecordListener logRecordListener) {\n\t\tlisteners.add(new ThrowingEagerTestExecutionListener());\n\n\t\tcompositeTestExecutionListener().executionFinished(anyTestIdentifier(), anyTestExecutionResult());\n\n\t\tassertThatTestListenerErrorLogged(logRecordListener, ThrowingEagerTestExecutionListener.class,\n\t\t\t\"executionJustFinished\");\n\t}\n\n\t@Test\n\tvoid shouldThrowOutOfMemoryExceptionAndStopListenerWithoutLog(LogRecordListener logRecordListener) {\n\t\tlisteners.clear();\n\t\tlisteners.add(new TestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\t\t\tthrow new OutOfMemoryError();\n\t\t\t}\n\t\t});\n\n\t\tassertThatThrownBy(() -> compositeTestExecutionListener().executionStarted(anyTestIdentifier())).isInstanceOf(\n\t\t\tOutOfMemoryError.class);\n\n\t\tassertNotLogs(logRecordListener);\n\t}\n\n\t@Test\n\tvoid shouldThrowOutOfMemoryExceptionAndStopEagerListenerWithoutLog(LogRecordListener logRecordListener) {\n\t\tlisteners.add(new EagerTestExecutionListener() {\n\t\t\t@Override\n\t\t\tpublic void executionJustStarted(TestIdentifier testIdentifier) {\n\t\t\t\tthrow new OutOfMemoryError();\n\t\t\t}\n\t\t});\n\n\t\tassertThatThrownBy(() -> compositeTestExecutionListener().executionStarted(anyTestIdentifier())).isInstanceOf(\n\t\t\tOutOfMemoryError.class);\n\n\t\tassertNotLogs(logRecordListener);\n\t}\n\n\t@Test\n\tvoid callsListenersInReverseOrderForFinishedEvents() {\n\t\tlisteners.clear();\n\t\tvar firstListener = mock(TestExecutionListener.class, \"firstListener\");\n\t\tvar secondListener = mock(TestExecutionListener.class, \"secondListener\");\n\t\tlisteners.add(firstListener);\n\t\tlisteners.add(secondListener);\n\n\t\tvar testPlan = anyTestPlan();\n\t\tvar testIdentifier = anyTestIdentifier();\n\t\tvar testExecutionResult = anyTestExecutionResult();\n\n\t\tvar composite = compositeTestExecutionListener();\n\t\tcomposite.testPlanExecutionStarted(testPlan);\n\t\tcomposite.executionStarted(testIdentifier);\n\t\tcomposite.executionFinished(testIdentifier, testExecutionResult);\n\t\tcomposite.testPlanExecutionFinished(testPlan);\n\n\t\tInOrder inOrder = inOrder(firstListener, secondListener);\n\t\tinOrder.verify(firstListener).testPlanExecutionStarted(testPlan);\n\t\tinOrder.verify(secondListener).testPlanExecutionStarted(testPlan);\n\t\tinOrder.verify(firstListener).executionStarted(testIdentifier);\n\t\tinOrder.verify(secondListener).executionStarted(testIdentifier);\n\t\tinOrder.verify(secondListener).executionFinished(testIdentifier, testExecutionResult);\n\t\tinOrder.verify(firstListener).executionFinished(testIdentifier, testExecutionResult);\n\t\tinOrder.verify(secondListener).testPlanExecutionFinished(testPlan);\n\t\tinOrder.verify(firstListener).testPlanExecutionFinished(testPlan);\n\t}\n\n\tprivate TestExecutionListener compositeTestExecutionListener() {\n\t\treturn new CompositeTestExecutionListener(listeners);\n\t}\n\n\tprivate LogRecord firstWarnLogRecord(LogRecordListener logRecordListener) throws AssertionError {\n\t\treturn logRecordListener.stream(CompositeTestExecutionListener.class, Level.WARNING).findFirst().orElseThrow(\n\t\t\t() -> new AssertionError(\"Failed to find error log record\"));\n\t}\n\n\tprivate void assertNotLogs(LogRecordListener logRecordListener) throws AssertionError {\n\t\tassertThat(logRecordListener.stream(CompositeTestExecutionListener.class, Level.WARNING).count()).isZero();\n\t}\n\n\tprivate static TestExecutionResult anyTestExecutionResult() {\n\t\treturn TestExecutionResult.successful();\n\t}\n\n\tprivate static TestIdentifier anyTestIdentifier() {\n\t\treturn TestIdentifier.from(anyTestDescriptor());\n\t}\n\n\tprivate void assertThatTestListenerErrorLogged(LogRecordListener logRecordListener, Class<?> listenerClass,\n\t\t\tString methodName) {\n\t\tassertThat(firstWarnLogRecord(logRecordListener).getMessage()).startsWith(\n\t\t\t\"TestExecutionListener [\" + listenerClass.getName() + \"] threw exception for method: \" + methodName);\n\t}\n\n\tprivate static TestPlan anyTestPlan() {\n\t\treturn TestPlan.from(true, Set.of(anyTestDescriptor()), mock(), dummyOutputDirectoryCreator());\n\t}\n\n\tprivate static DemoMethodTestDescriptor anyTestDescriptor() {\n\t\tvar testClass = CompositeTestExecutionListenerTests.class;\n\t\tvar method = ReflectionUtils.findMethod(testClass, \"anyTestDescriptor\", new Class<?>[0]).orElseThrow();\n\t\treturn new DemoMethodTestDescriptor(UniqueId.root(\"method\", \"unique_id\"), method);\n\t}\n\n\tprivate static class ThrowingEagerTestExecutionListener extends ThrowingTestExecutionListener\n\t\t\timplements EagerTestExecutionListener {\n\t\t@Override\n\t\tpublic void executionJustStarted(TestIdentifier testIdentifier) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionJustFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\t}\n\n\tprivate static class ThrowingTestExecutionListener implements TestExecutionListener {\n\t\t@Override\n\t\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void testPlanExecutionFinished(TestPlan testPlan) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void dynamicTestRegistered(TestIdentifier testIdentifier) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionStarted(TestIdentifier testIdentifier) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {\n\t\t\tthrow new RuntimeException(\"failed to invoke listener\");\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherEngineFilterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.logging.Level.WARNING;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.EngineFilter.excludeEngines;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\n\n/**\n * @since 1.0\n */\nclass DefaultLauncherEngineFilterTests {\n\n\tprivate static final Runnable noOp = () -> {\n\t};\n\n\t@Test\n\tvoid launcherWillNotExecuteEnginesIfNotIncludedByAnEngineFilter() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"second\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest()\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId()))\n\t\t\t\t.filters(includeEngines(\"first\"))\n\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar rootIdentifier = testPlan.getRoots().iterator().next();\n\t\tassertThat(testPlan.getChildren(rootIdentifier.getUniqueIdObject())).hasSize(1);\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"first\"))).hasSize(1);\n\t}\n\n\t@Test\n\tvoid launcherWillExecuteAllEnginesExplicitlyIncludedViaSingleEngineFilter() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"second\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest()\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId()))\n\t\t\t\t.filters(includeEngines(\"first\", \"second\"))\n\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).hasSize(2);\n\t}\n\n\t@Test\n\tvoid launcherWillNotExecuteEnginesExplicitlyIncludedViaMultipleCompetingEngineFilters() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"second\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest()\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId()))\n\t\t\t\t.filters(includeEngines(\"first\"), includeEngines(\"second\"))\n\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).isEmpty();\n\t}\n\n\t@Test\n\tvoid launcherWillNotExecuteEnginesExplicitlyExcludedByAnEngineFilter() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"second\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest()\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId()))\n\t\t\t\t.filters(excludeEngines(\"second\"))\n\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar rootIdentifier = testPlan.getRoots().iterator().next();\n\t\tassertThat(testPlan.getChildren(rootIdentifier.getUniqueIdObject())).hasSize(1);\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"first\"))).hasSize(1);\n\t}\n\n\t@Test\n\tvoid launcherWillExecuteEnginesHonoringBothIncludeAndExcludeEngineFilters() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"second\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\t\tvar thirdEngine = new DemoHierarchicalTestEngine(\"third\");\n\t\tTestDescriptor test3 = thirdEngine.addTest(\"test3\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine, thirdEngine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest()\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId()), selectUniqueId(test3.getUniqueId()))\n\t\t\t\t.filters(includeEngines(\"first\", \"second\"), excludeEngines(\"second\"))\n\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar rootIdentifier = testPlan.getRoots().iterator().next();\n\t\tassertThat(testPlan.getChildren(rootIdentifier.getUniqueIdObject())).hasSize(1);\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"first\"))).hasSize(1);\n\t}\n\n\t@Test\n\tvoid launcherThrowsExceptionWhenNoEngineMatchesIncludeEngineFilter(@TrackLogRecords LogRecordListener log) {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = engine.addTest(\"test1\", noOp);\n\t\tLauncherDiscoveryRequest request = request() //\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId())) //\n\t\t\t\t.filters(includeEngines(\"second\")) //\n\t\t\t\t.build();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar exception = assertThrows(JUnitException.class, () -> launcher.discover(request));\n\n\t\tassertThat(exception.getMessage()) //\n\t\t\t\t.startsWith(\"No TestEngine ID matched the following include EngineFilters: [second].\") //\n\t\t\t\t.contains(\"Please fix/remove the filter or add the engine.\") //\n\t\t\t\t.contains(\"Registered TestEngines:\\n- first (\") //\n\t\t\t\t.endsWith(\"Registered EngineFilters:\\n- EngineFilter that includes engines with IDs [second]\");\n\t\tassertThat(log.stream(WARNING)).isEmpty();\n\t}\n\n\t@Test\n\tvoid launcherThrowsExceptionWhenNoEngineMatchesIdInIncludeEngineFilter(@TrackLogRecords LogRecordListener log) {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test1 = engine.addTest(\"test1\", noOp);\n\t\tLauncherDiscoveryRequest request = request() //\n\t\t\t\t.selectors(selectUniqueId(test1.getUniqueId())) //\n\t\t\t\t.filters(includeEngines(\"first\", \"second\")) //\n\t\t\t\t.build();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar exception = assertThrows(JUnitException.class, () -> launcher.discover(request));\n\n\t\tassertThat(exception.getMessage()) //\n\t\t\t\t.startsWith(\"No TestEngine ID matched the following include EngineFilters: [second].\") //\n\t\t\t\t.contains(\"Please fix/remove the filter or add the engine.\") //\n\t\t\t\t.contains(\"Registered TestEngines:\\n- first (\") //\n\t\t\t\t.endsWith(\"Registered EngineFilters:\\n- EngineFilter that includes engines with IDs [first, second]\");\n\t\tassertThat(log.stream(WARNING)).isEmpty();\n\t}\n\n\t@Test\n\tvoid launcherWillLogWarningWhenAllEnginesWereExcluded(@TrackLogRecords LogRecordListener log) {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"first\");\n\t\tTestDescriptor test = engine.addTest(\"test1\", noOp);\n\n\t\tvar launcher = createLauncher(engine);\n\n\t\t// @formatter:off\n\t\tvar testPlan = launcher.discover(\n\t\t\t\trequest()\n\t\t\t\t\t\t.selectors(selectUniqueId(test.getUniqueId()))\n\t\t\t\t\t\t.filters(excludeEngines(\"first\"))\n\t\t\t\t\t\t.build());\n\t\t// @formatter:on\n\n\t\tassertThat(testPlan.getRoots()).isEmpty();\n\t\tassertThat(log.stream(WARNING)).hasSize(1);\n\t\tassertThat(log.stream(WARNING).findAny().orElseThrow().getMessage()) //\n\t\t\t\t.startsWith(\"All TestEngines were excluded by EngineFilters.\") //\n\t\t\t\t.contains(\"Registered TestEngines:\\n- first (\") //\n\t\t\t\t.endsWith(\"Registered EngineFilters:\\n- EngineFilter that excludes engines with IDs [first]\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Objects.requireNonNull;\nimport static java.util.function.UnaryOperator.identity;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.SelectorResolutionResult.unresolved;\nimport static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.fakes.FaultyTestEngines.createEngineThatCannotResolveAnything;\nimport static org.junit.platform.fakes.FaultyTestEngines.createEngineThatFailsToResolveAnything;\nimport static org.junit.platform.launcher.LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.DRY_RUN_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.argThat;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.doAnswer;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.spy;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.time.Instant;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.function.UnaryOperator;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestExecutionResult.Status;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.fakes.TestEngineSpy;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.PostDiscoveryFilterStub;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.InOrder;\n\n/**\n * @since 1.0\n */\n@NullMarked\nclass DefaultLauncherTests {\n\n\tstatic final String FOO = \"DefaultLauncherTests.foo\";\n\tstatic final String BAR = \"DefaultLauncherTests.bar\";\n\n\tprivate static final Runnable noOp = () -> {\n\t};\n\n\t@Test\n\tvoid constructLauncherWithoutAnyEngines() {\n\t\tvar launcher = createLauncher();\n\n\t\tassertPreconditionViolationFor(() -> launcher.discover(request().build()))//\n\t\t\t\t.withMessageContaining(\"Cannot create Launcher without at least one TestEngine\");\n\t}\n\n\t@Test\n\tvoid constructLauncherWithMultipleTestEnginesWithDuplicateIds() {\n\t\tvar launcher = createLauncher(new DemoHierarchicalTestEngine(\"dummy id\"),\n\t\t\tnew DemoHierarchicalTestEngine(\"dummy id\"));\n\n\t\tvar exception = assertThrows(JUnitException.class, () -> launcher.discover(request().build()));\n\n\t\tassertThat(exception).hasMessageContaining(\"multiple engines with the same ID\");\n\t}\n\n\t@Test\n\tvoid discoverEmptyTestPlanWithEngineWithoutAnyTests() {\n\t\tvar launcher = createLauncher(new DemoHierarchicalTestEngine());\n\n\t\tvar testPlan = launcher.discover(request().build());\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t}\n\n\t@Test\n\tvoid discoverTestPlanForEngineThatReturnsNullForItsRootDescriptor() {\n\t\tTestEngine engine = new TestEngineStub(\"some-engine-id\") {\n\n\t\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\n\t\tvar discoveryListener = mock(LauncherDiscoveryListener.class);\n\t\tvar testPlan = createLauncher(engine).discover(request() //\n\t\t\t\t.listeners(discoveryListener) //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.build());\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tassertDiscoveryFailed(engine, inOrder(discoveryListener), discoveryListener);\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { Error.class, RuntimeException.class })\n\tvoid discoverErrorTestDescriptorForEngineThatThrowsInDiscoveryPhase(Class<? extends Throwable> throwableClass) {\n\t\tTestEngine engine = new TestEngineStub(\"my-engine-id\") {\n\n\t\t\t@SuppressWarnings(\"DataFlowIssue\")\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\ttry {\n\t\t\t\t\tvar constructor = throwableClass.getDeclaredConstructor(String.class);\n\t\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(constructor.newInstance(\"ignored\"));\n\t\t\t\t}\n\t\t\t\tcatch (Exception ignored) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar discoveryListener = mock(LauncherDiscoveryListener.class);\n\t\tvar request = request() //\n\t\t\t\t.listeners(discoveryListener) //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.build();\n\t\tvar testPlan = launcher.discover(request);\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar engineIdentifier = getOnlyElement(testPlan.getRoots());\n\t\tassertThat(getOnlyElement(testPlan.getRoots()).getDisplayName()).isEqualTo(\"my-engine-id\");\n\n\t\tInOrder inOrder = inOrder(discoveryListener);\n\t\tinOrder.verify(discoveryListener).launcherDiscoveryStarted(request);\n\t\tassertDiscoveryFailed(engine, inOrder, discoveryListener);\n\t\tinOrder.verify(discoveryListener).launcherDiscoveryFinished(request);\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tvar executionRequest = LauncherExecutionRequestBuilder.request(testPlan) //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\t\tlauncher.execute(executionRequest);\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(engineIdentifier);\n\t\tverify(listener).executionFinished(eq(engineIdentifier), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'my-engine-id' failed to discover tests\");\n\t}\n\n\tprivate void assertDiscoveryFailed(TestEngine testEngine, InOrder inOrder,\n\t\t\tLauncherDiscoveryListener discoveryListener) {\n\t\tvar engineId = testEngine.getId();\n\t\tvar failureCaptor = ArgumentCaptor.forClass(EngineDiscoveryResult.class);\n\t\tinOrder.verify(discoveryListener).engineDiscoveryStarted(UniqueId.forEngine(engineId));\n\t\tinOrder.verify(discoveryListener).engineDiscoveryFinished(eq(UniqueId.forEngine(engineId)),\n\t\t\tfailureCaptor.capture());\n\t\tvar result = failureCaptor.getValue();\n\t\tassertThat(result.getStatus()).isEqualTo(EngineDiscoveryResult.Status.FAILED);\n\t\tassertThat(result.getThrowable()).isPresent();\n\t\tassertThat(result.getThrowable().get()).hasMessage(\n\t\t\t\"TestEngine with ID '\" + engineId + \"' failed to discover tests\");\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailuresWithoutPriorEvents() {\n\t\tvar rootCause = new RuntimeException(\"something went horribly wrong\");\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'TestEngineStub' failed to execute tests\") //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailuresForSkippedEngine() {\n\t\tvar rootCause = new RuntimeException(\"something went horribly wrong\");\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\trequest.getEngineExecutionListener().executionSkipped(engineDescriptor, \"not today\");\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'TestEngineStub' failed to execute tests\") //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailuresForStartedEngine() {\n\t\tvar rootCause = new RuntimeException(\"something went horribly wrong\");\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\trequest.getEngineExecutionListener().executionStarted(engineDescriptor);\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'TestEngineStub' failed to execute tests\") //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailuresForSuccessfullyFinishedEngine() {\n\t\tvar rootCause = new RuntimeException(\"something went horribly wrong\");\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\trequest.getEngineExecutionListener().executionStarted(engineDescriptor);\n\t\t\t\trequest.getEngineExecutionListener().executionFinished(engineDescriptor, successful());\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'TestEngineStub' failed to execute tests\") //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailuresForFailedFinishedEngine() {\n\t\tvar rootCause = new RuntimeException(\"something went horribly wrong\");\n\t\tvar originalFailure = new RuntimeException(\"suppressed\");\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\tvar listener = request.getEngineExecutionListener();\n\t\t\t\tlistener.executionStarted(engineDescriptor);\n\t\t\t\tlistener.executionFinished(engineDescriptor, TestExecutionResult.failed(originalFailure));\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), testExecutionResult.capture());\n\t\tassertThat(testExecutionResult.getValue().getThrowable()).isPresent();\n\t\tassertThat(testExecutionResult.getValue().getThrowable().get()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'TestEngineStub' failed to execute tests\") //\n\t\t\t\t.hasSuppressedException(originalFailure) //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid reportsSkippedEngines() {\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\trequest.getEngineExecutionListener().executionSkipped(engineDescriptor, \"not today\");\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tverify(listener).executionSkipped(any(TestIdentifier.class), eq(\"not today\"));\n\t\tverify(listener, times(0)).executionStarted(any());\n\t\tverify(listener, times(0)).executionFinished(any(), any());\n\t}\n\n\t@Test\n\tvoid reportsFinishedEngines() {\n\t\tvar engine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\tvar listener = request.getEngineExecutionListener();\n\t\t\t\tlistener.executionStarted(engineDescriptor);\n\t\t\t\tlistener.executionFinished(engineDescriptor, successful());\n\t\t\t}\n\t\t};\n\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tcreateLauncher(engine).execute(request().forExecution().listeners(listener).build());\n\n\t\tverify(listener).executionStarted(any());\n\t\tverify(listener).executionFinished(any(), eq(successful()));\n\t}\n\n\t@Test\n\tvoid discoverTestPlanForSingleEngine() {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"myEngine\");\n\t\tengine.addTest(\"test1\", noOp);\n\t\tengine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(engine);\n\n\t\tvar testPlan = launcher.discover(request().selectors(selectPackage(\"any\")).build());\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar rootIdentifier = testPlan.getRoots().iterator().next();\n\t\tassertThat(testPlan.getChildren(rootIdentifier.getUniqueIdObject())).hasSize(2);\n\t\tassertThat(testPlan.getChildren(UniqueId.parse(\"[engine:myEngine]\"))).hasSize(2);\n\t}\n\n\t@Test\n\tvoid discoverTestPlanForMultipleEngines() {\n\t\tvar firstEngine = new DemoHierarchicalTestEngine(\"engine1\");\n\t\tTestDescriptor test1 = firstEngine.addTest(\"test1\", noOp);\n\t\tvar secondEngine = new DemoHierarchicalTestEngine(\"engine2\");\n\t\tTestDescriptor test2 = secondEngine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(firstEngine, secondEngine);\n\n\t\tvar testPlan = launcher.discover(\n\t\t\trequest().selectors(selectUniqueId(test1.getUniqueId()), selectUniqueId(test2.getUniqueId())).build());\n\n\t\tassertThat(testPlan.getRoots()).hasSize(2);\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"engine1\"))).hasSize(1);\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"engine2\"))).hasSize(1);\n\t}\n\n\t@Test\n\tvoid launcherAppliesPostDiscoveryFilters() {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"myEngine\");\n\t\tvar test1 = engine.addTest(\"test1\", noOp);\n\t\tengine.addTest(\"test2\", noOp);\n\n\t\tvar launcher = createLauncher(engine);\n\n\t\tPostDiscoveryFilter includeWithUniqueIdContainsTest = new PostDiscoveryFilterStub(\n\t\t\tdescriptor -> FilterResult.includedIf(descriptor.getUniqueId().toString().contains(\"test\")),\n\t\t\t() -> \"filter1\");\n\t\tPostDiscoveryFilter includeWithUniqueIdContains1 = new PostDiscoveryFilterStub(\n\t\t\tdescriptor -> FilterResult.includedIf(descriptor.getUniqueId().toString().contains(\"1\")), () -> \"filter2\");\n\n\t\tvar testPlan = launcher.discover( //\n\t\t\trequest() //\n\t\t\t\t\t.selectors(selectPackage(\"any\")) //\n\t\t\t\t\t.filters(includeWithUniqueIdContainsTest, includeWithUniqueIdContains1) //\n\t\t\t\t\t.build());\n\n\t\tassertThat(testPlan.getChildren(UniqueId.forEngine(\"myEngine\"))).hasSize(1);\n\t\tassertThat(testPlan.getTestIdentifier(test1.getUniqueId())).isNotNull();\n\t}\n\n\t@Test\n\tvoid withoutConfigurationParameters_LauncherPassesEmptyConfigurationParametersIntoTheExecutionRequest() {\n\t\tvar engine = new TestEngineSpy();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tlauncher.execute(request().forExecution().build());\n\n\t\tvar configurationParameters = requireNonNull(engine.requestForExecution).getConfigurationParameters();\n\t\tassertThat(configurationParameters.get(\"key\")).isNotPresent();\n\t}\n\n\t@Test\n\tvoid withConfigurationParameters_LauncherPassesPopulatedConfigurationParametersIntoTheExecutionRequest() {\n\t\tvar engine = new TestEngineSpy();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tlauncher.execute(request().configurationParameter(\"key\", \"value\").forExecution().build());\n\n\t\tvar configurationParameters = requireNonNull(engine.requestForExecution).getConfigurationParameters();\n\t\tassertThat(configurationParameters.get(\"key\")).isPresent();\n\t\tassertThat(configurationParameters.get(\"key\")).contains(\"value\");\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = DefaultLauncherTests.FOO, value = DefaultLauncherTests.BAR)\n\tvoid withoutConfigurationParameters_LookupFallsBackToSystemProperty() {\n\t\tvar engine = new TestEngineSpy();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tlauncher.execute(request().forExecution().build());\n\n\t\tvar configurationParameters = requireNonNull(engine.requestForExecution).getConfigurationParameters();\n\t\tvar optionalFoo = configurationParameters.get(FOO);\n\t\tassertTrue(optionalFoo.isPresent(), \"foo should have been picked up via system property\");\n\t\tassertEquals(BAR, optionalFoo.get(), \"foo property\");\n\t}\n\n\t@Test\n\tvoid withAdditionalListener() {\n\t\tvar engine = new TestEngineSpy();\n\t\tvar listener = new SummaryGeneratingListener();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tlauncher.execute(request().forExecution().listeners(listener).build());\n\n\t\tassertThat(listener.getSummary()).isNotNull();\n\t\tassertThat(listener.getSummary().getContainersFoundCount()).isEqualTo(1);\n\t\tassertThat(listener.getSummary().getTestsFoundCount()).isEqualTo(1);\n\t}\n\n\t@Test\n\tvoid prunesTestDescriptorsAfterApplyingPostDiscoveryFilters() {\n\t\tvar engine = new TestEngineSpy() {\n\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tsuper.discover(discoveryRequest, uniqueId);\n\t\t\t\tvar engineDescriptor = new TestDescriptorStub(uniqueId, uniqueId.toString());\n\t\t\t\tvar containerDescriptor = new TestDescriptorStub(uniqueId.append(\"container\", \"a\"), \"container\") {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Type getType() {\n\t\t\t\t\t\treturn Type.CONTAINER;\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tcontainerDescriptor.addChild(\n\t\t\t\t\tnew TestDescriptorStub(containerDescriptor.getUniqueId().append(\"test\", \"b\"), \"test\"));\n\t\t\t\tengineDescriptor.addChild(containerDescriptor);\n\t\t\t\treturn engineDescriptor;\n\t\t\t}\n\t\t};\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar testPlan = launcher.discover(request().filters(\n\t\t\t(PostDiscoveryFilter) testDescriptor -> FilterResult.includedIf(testDescriptor.isContainer())).build());\n\n\t\tassertThat(testPlan.getRoots()).hasSize(1);\n\t\tvar engineIdentifier = getOnlyElement(testPlan.getRoots());\n\t\tassertThat(testPlan.getChildren(engineIdentifier)).isEmpty();\n\t}\n\n\t@Test\n\tvoid reportsDynamicTestDescriptorsCorrectly() {\n\t\tvar engineId = UniqueId.forEngine(\"engine\");\n\t\tvar containerAndTestId = engineId.append(\"c&t\", \"c&t\");\n\t\tvar dynamicTestId = containerAndTestId.append(\"test\", \"test\");\n\n\t\tvar engine = new TestEngineSpy(engineId.getLastSegment().getValue()) {\n\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tsuper.discover(discoveryRequest, uniqueId);\n\t\t\t\tvar engineDescriptor = new TestDescriptorStub(uniqueId, uniqueId.toString());\n\t\t\t\tengineDescriptor.addChild(new TestDescriptorStub(containerAndTestId, \"c&t\") {\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Type getType() {\n\t\t\t\t\t\treturn Type.CONTAINER_AND_TEST;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn engineDescriptor;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tsuper.execute(request);\n\t\t\t\tvar listener = request.getEngineExecutionListener();\n\n\t\t\t\tlistener.executionStarted(request.getRootTestDescriptor());\n\t\t\t\tvar containerAndTest = getOnlyElement(request.getRootTestDescriptor().getChildren());\n\t\t\t\tlistener.executionStarted(containerAndTest);\n\n\t\t\t\tvar dynamicTest = new TestDescriptorStub(dynamicTestId, \"test\");\n\t\t\t\tdynamicTest.setParent(containerAndTest);\n\t\t\t\tlistener.dynamicTestRegistered(dynamicTest);\n\t\t\t\tlistener.executionStarted(dynamicTest);\n\t\t\t\tlistener.executionFinished(dynamicTest, successful());\n\n\t\t\t\tlistener.executionFinished(containerAndTest, successful());\n\t\t\t\tlistener.executionFinished(request.getRootTestDescriptor(), successful());\n\t\t\t}\n\t\t};\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tlauncher.execute(request().forExecution().listeners(listener).build());\n\n\t\tvar inOrder = inOrder(listener);\n\t\tvar testPlanArgumentCaptor = ArgumentCaptor.forClass(TestPlan.class);\n\t\tinOrder.verify(listener).testPlanExecutionStarted(testPlanArgumentCaptor.capture());\n\n\t\tvar testPlan = testPlanArgumentCaptor.getValue();\n\t\tvar engineTestIdentifier = testPlan.getTestIdentifier(engineId);\n\t\tvar containerAndTestIdentifier = testPlan.getTestIdentifier(containerAndTestId);\n\t\tvar dynamicTestIdentifier = testPlan.getTestIdentifier(dynamicTestId);\n\t\tassertThat(engineTestIdentifier.getParentIdObject()).isEmpty();\n\t\tassertThat(containerAndTestIdentifier.getParentIdObject()).contains(engineId);\n\t\tassertThat(dynamicTestIdentifier.getParentIdObject()).contains(containerAndTestId);\n\n\t\tinOrder.verify(listener).executionStarted(engineTestIdentifier);\n\t\tinOrder.verify(listener).executionStarted(containerAndTestIdentifier);\n\t\tinOrder.verify(listener).dynamicTestRegistered(dynamicTestIdentifier);\n\t\tinOrder.verify(listener).executionStarted(dynamicTestIdentifier);\n\t\tinOrder.verify(listener).executionFinished(dynamicTestIdentifier, successful());\n\t\tinOrder.verify(listener).executionFinished(containerAndTestIdentifier, successful());\n\t\tinOrder.verify(listener).executionFinished(engineTestIdentifier, successful());\n\t\tinOrder.verify(listener).testPlanExecutionFinished(same(testPlan));\n\t}\n\n\t@Test\n\tvoid launcherCanExecuteTestPlanExactlyOnce() {\n\t\tvar engine = mock(TestEngine.class);\n\t\twhen(engine.getId()).thenReturn(\"some-engine\");\n\t\twhen(engine.discover(any(), any())).thenAnswer(invocation -> {\n\t\t\tUniqueId uniqueId = invocation.getArgument(1);\n\t\t\treturn new EngineDescriptor(uniqueId, uniqueId.toString());\n\t\t});\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar testPlan = launcher.discover(request().build());\n\t\tverify(engine, times(1)).discover(any(), any());\n\n\t\tvar executionRequest = LauncherExecutionRequestBuilder.request(testPlan).build();\n\t\tlauncher.execute(executionRequest);\n\t\tverify(engine, times(1)).execute(any());\n\n\t\tassertPreconditionViolationFor(() -> launcher.execute(executionRequest))//\n\t\t\t\t.withMessage(\"TestPlan must only be executed once\");\n\t}\n\n\t@Test\n\tvoid thirdPartyEngineUsingReservedEngineIdPrefixEmitsWarning(@TrackLogRecords LogRecordListener listener) {\n\t\tvar id = \"junit-using-reserved-prefix\";\n\t\tvar launcher = createLauncher(new TestEngineStub(id));\n\t\tlauncher.discover(request().build());\n\t\tassertThat(listener.stream(EngineIdValidator.class, Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID: '\"\n\t\t\t\t\t\t\t+ id + \"'\");\n\t}\n\n\t@Test\n\tvoid thirdPartyEngineClaimingToBeJupiterResultsInException() {\n\t\tassertImposter(\"junit-jupiter\");\n\t}\n\n\t@Test\n\tvoid thirdPartyEngineClaimingToBeVintageResultsInException() {\n\t\tassertImposter(\"junit-vintage\");\n\t}\n\n\tprivate void assertImposter(String id) {\n\t\tTestEngine impostor = new TestEngineStub(id);\n\t\tvar launcher = createLauncher(impostor);\n\t\tException exception = assertThrows(JUnitException.class, () -> launcher.discover(request().build()));\n\t\tassertThat(exception).hasMessage(\n\t\t\t\"Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID.\",\n\t\t\timpostor.getClass().getName(), id);\n\t}\n\n\t@Test\n\tvoid dryRunModeReportsEventsForAllTestsButDoesNotExecuteThem() {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"engine\");\n\t\tvar container = engine.addContainer(\"container\", \"Container\", null);\n\t\tvar test = new DemoHierarchicalTestDescriptor(container.getUniqueId().append(\"test\", \"test\"), \"Test\",\n\t\t\t(__, ___) -> {\n\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t});\n\t\tcontainer.addChild(test);\n\n\t\tvar launcher = createLauncher(engine);\n\t\tTestExecutionListener listener = mock();\n\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DRY_RUN_PROPERTY_NAME, \"true\") //\n\t\t\t\t.forExecution() //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\t\tlauncher.execute(request);\n\n\t\tvar inOrder = inOrder(listener);\n\t\tinOrder.verify(listener).testPlanExecutionStarted(any());\n\t\tinOrder.verify(listener).executionStarted(TestIdentifier.from(engine.getEngineDescriptor()));\n\t\tinOrder.verify(listener).executionStarted(TestIdentifier.from(container));\n\t\tinOrder.verify(listener).executionSkipped(TestIdentifier.from(test), \"JUnit Platform dry-run mode is enabled\");\n\t\tinOrder.verify(listener).executionFinished(TestIdentifier.from(container), successful());\n\t\tinOrder.verify(listener).executionFinished(TestIdentifier.from(engine.getEngineDescriptor()), successful());\n\t\tinOrder.verify(listener).testPlanExecutionFinished(any());\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\t@Test\n\tvoid notifiesDiscoveryListenersOfProcessedSelectors() {\n\t\tTestEngine engine = new TestEngineStub(\"some-engine-id\") {\n\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\t//noinspection CodeBlock2Expr\n\t\t\t\tdiscoveryRequest.getSelectorsByType(DiscoverySelector.class).forEach(selector -> {\n\t\t\t\t\tdiscoveryRequest.getDiscoveryListener().selectorProcessed(uniqueId, selector, unresolved());\n\t\t\t\t});\n\t\t\t\treturn new EngineDescriptor(uniqueId, uniqueId.getLastSegment().getValue());\n\t\t\t}\n\t\t};\n\t\tvar engineId = UniqueId.forEngine(engine.getId());\n\n\t\tvar discoveryListenerOnConfig = mock(LauncherDiscoveryListener.class, \"discoveryListenerOnConfig\");\n\t\tvar discoveryListenerOnLauncher = mock(LauncherDiscoveryListener.class, \"discoveryListenerOnLauncher\");\n\t\tvar discoveryListenerOnRequest = mock(LauncherDiscoveryListener.class, \"discoveryListenerOnRequest\");\n\t\tvar selector = mock(DiscoverySelector.class);\n\n\t\tvar launcherConfig = LauncherFactoryForTestingPurposesOnly.createLauncherConfigBuilderWithDisabledServiceLoading() //\n\t\t\t\t.addTestEngines(engine) //\n\t\t\t\t.addLauncherDiscoveryListeners(discoveryListenerOnConfig) //\n\t\t\t\t.build();\n\n\t\tvar launcher = LauncherFactory.create(launcherConfig);\n\t\tlauncher.registerLauncherDiscoveryListeners(discoveryListenerOnLauncher);\n\n\t\tlauncher.discover(request() //\n\t\t\t\t.selectors(selector) //\n\t\t\t\t.listeners(discoveryListenerOnRequest) //\n\t\t\t\t.build());\n\n\t\tassertAll( //\n\t\t\t() -> verify(discoveryListenerOnConfig).selectorProcessed(engineId, selector, unresolved()), //\n\t\t\t() -> verify(discoveryListenerOnLauncher).selectorProcessed(engineId, selector, unresolved()), //\n\t\t\t() -> verify(discoveryListenerOnRequest).selectorProcessed(engineId, selector, unresolved()) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailureForCriticalDiscoveryIssuesAndLogsRemaining(\n\t\t\t@TrackLogRecords LogRecordListener listener) {\n\n\t\tvar result = execute(new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.ERROR, \"error\"));\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.WARNING, \"warning\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\") {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic Set<TestTag> getTags() {\n\t\t\t\t\t\treturn Set.of(TestTag.create(\"custom-tag\"));\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t});\n\n\t\tassertThat(result.testPlan().containsTests()).isTrue();\n\n\t\tassertThat(result.testIdentifier().getDisplayName()).isEqualTo(\"Engine\");\n\t\tassertThat(result.testIdentifier().getTags()).containsExactly(TestTag.create(\"custom-tag\"));\n\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(DiscoveryIssueException.class) //\n\t\t\t\t.hasMessageStartingWith(\n\t\t\t\t\t\"TestEngine with ID 'engine-id' encountered a critical issue during test discovery\") //\n\t\t\t\t.hasMessageContaining(\"(1) [ERROR] error\");\n\n\t\tvar logRecord = findFirstDiscoveryIssueLogRecord(listener, Level.WARNING);\n\t\tassertThat(logRecord.getMessage()) //\n\t\t\t\t.startsWith(\"TestEngine with ID 'engine-id' encountered a non-critical issue during test discovery\") //\n\t\t\t\t.contains(\"(1) [WARNING] warning\");\n\t\tassertThat(logRecord.getInstant()) //\n\t\t\t\t.isBetween(result.startTime(), result.finishTime());\n\t}\n\n\t@Test\n\tvoid logsNonCriticalIssuesForRegularEngineExecution(@TrackLogRecords LogRecordListener listener) {\n\n\t\tvar result = execute(new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.INFO, \"info\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar executionListener = request.getEngineExecutionListener();\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\texecutionListener.executionStarted(engineDescriptor);\n\t\t\t\texecutionListener.executionFinished(engineDescriptor, successful());\n\t\t\t}\n\t\t});\n\n\t\tassertThat(result.testIdentifier().getDisplayName()).isEqualTo(\"Engine\");\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.SUCCESSFUL);\n\n\t\tvar logRecord = findFirstDiscoveryIssueLogRecord(listener, Level.INFO);\n\t\tassertThat(logRecord.getMessage()) //\n\t\t\t\t.startsWith(\"TestEngine with ID 'engine-id' encountered a non-critical issue during test discovery\") //\n\t\t\t\t.contains(\"(1) [INFO] info\");\n\t\tassertThat(logRecord.getInstant()) //\n\t\t\t\t.isBetween(result.startTime(), result.finishTime());\n\t}\n\n\t@Test\n\tvoid logsAllIssuesForDiscoveryFailure(@TrackLogRecords LogRecordListener listener) {\n\n\t\tvar result = execute(new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.ERROR, \"error\"));\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.INFO, \"info\"));\n\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t}\n\t\t});\n\n\t\tassertThat(result.testPlan().containsTests()).isTrue();\n\n\t\tassertThat(result.testIdentifier().getDisplayName()).isEqualTo(\"engine-id\");\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'engine-id' failed to discover tests\") //\n\t\t\t\t.cause().hasMessage(\"boom\");\n\n\t\tvar logRecord = findFirstDiscoveryIssueLogRecord(listener, Level.SEVERE);\n\t\tassertThat(logRecord.getMessage()) //\n\t\t\t\t.startsWith(\"TestEngine with ID 'engine-id' encountered a critical issue during test discovery\") //\n\t\t\t\t.contains(\"(1) [ERROR] error\");\n\t\tassertThat(logRecord.getInstant()) //\n\t\t\t\t.isBetween(result.startTime(), result.finishTime());\n\n\t\tlogRecord = findFirstDiscoveryIssueLogRecord(listener, Level.INFO);\n\t\tassertThat(logRecord.getMessage()) //\n\t\t\t\t.startsWith(\"TestEngine with ID 'engine-id' encountered a non-critical issue during test discovery\") //\n\t\t\t\t.contains(\"(1) [INFO] info\");\n\t\tassertThat(logRecord.getInstant()) //\n\t\t\t\t.isBetween(result.startTime(), result.finishTime());\n\t}\n\n\t@Test\n\tvoid logsNonCriticalIssuesForExecutionFailure(@TrackLogRecords LogRecordListener listener) {\n\n\t\tvar result = execute(new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.INFO, \"info\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t}\n\t\t});\n\n\t\tassertThat(result.testIdentifier().getDisplayName()).isEqualTo(\"Engine\");\n\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'engine-id' failed to execute tests\") //\n\t\t\t\t.cause().hasMessage(\"boom\");\n\n\t\tvar logRecord = findFirstDiscoveryIssueLogRecord(listener, Level.INFO);\n\t\tassertThat(logRecord.getMessage()) //\n\t\t\t\t.startsWith(\"TestEngine with ID 'engine-id' encountered a non-critical issue during test discovery\") //\n\t\t\t\t.contains(\"(1) [INFO] info\");\n\t\tassertThat(logRecord.getInstant()) //\n\t\t\t\t.isBetween(result.startTime(), result.finishTime());\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailureOnUnresolvedUniqueIdSelectorWithEnginePrefix() {\n\t\tvar engine = createEngineThatCannotResolveAnything(\"some-engine\");\n\t\tvar selector = selectUniqueId(UniqueId.forEngine(engine.getId()));\n\t\tvar result = execute(engine, request -> request.selectors(selector));\n\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(DiscoveryIssueException.class) //\n\t\t\t\t.hasMessageStartingWith(\n\t\t\t\t\t\"TestEngine with ID 'some-engine' encountered a critical issue during test discovery\") //\n\t\t\t\t.hasMessageContaining(\"(1) [ERROR] %s could not be resolved\", selector);\n\t}\n\n\t@Test\n\tvoid ignoresUnresolvedUniqueIdSelectorWithoutEnginePrefix() {\n\t\tvar engine = createEngineThatCannotResolveAnything(\"some-engine\");\n\t\tvar selector = selectUniqueId(UniqueId.forEngine(\"some-other-engine\"));\n\t\tvar result = execute(engine, request -> request.selectors(selector));\n\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.SUCCESSFUL);\n\t}\n\n\t@Test\n\tvoid reportsEngineExecutionFailureForSelectorResolutionFailure() {\n\t\tvar engine = createEngineThatFailsToResolveAnything(\"some-engine\", new RuntimeException(\"boom\"));\n\t\tvar selector = selectClass(Object.class);\n\t\tvar result = execute(engine, request -> request.selectors(selector));\n\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(DiscoveryIssueException.class) //\n\t\t\t\t.hasMessageStartingWith(\n\t\t\t\t\t\"TestEngine with ID 'some-engine' encountered a critical issue during test discovery\") //\n\t\t\t\t.hasMessageContaining(\"(1) [ERROR] %s resolution failed\", selector) //\n\t\t\t\t.hasMessageContaining(\"Cause: java.lang.RuntimeException: boom\");\n\t}\n\n\t@Test\n\tvoid allowsConfiguringCriticalDiscoveryIssueSeverity() {\n\n\t\tvar engine = new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.INFO, \"info\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\t\t};\n\n\t\tvar result = execute(engine, request -> request //\n\t\t\t\t.configurationParameter(LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, \"info\"));\n\n\t\tassertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);\n\t\tassertThat(result.testExecutionResult().getThrowable().orElseThrow()) //\n\t\t\t\t.isInstanceOf(DiscoveryIssueException.class) //\n\t\t\t\t.hasMessageStartingWith(\n\t\t\t\t\t\"TestEngine with ID 'engine-id' encountered a critical issue during test discovery\") //\n\t\t\t\t.hasMessageContaining(\"(1) [INFO] info\");\n\t}\n\n\t@Test\n\tvoid failsIfCriticalSeverityIsConfiguredIncorrectly() {\n\n\t\tvar engine = new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.INFO, \"info\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar executionListener = request.getEngineExecutionListener();\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\texecutionListener.executionStarted(engineDescriptor);\n\t\t\t\texecutionListener.executionFinished(engineDescriptor, successful());\n\t\t\t}\n\t\t};\n\n\t\tvar exception = assertThrows(JUnitException.class, () -> execute(engine, request -> request //\n\t\t\t\t.configurationParameter(LauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME, \"wrong\")));\n\n\t\tassertThat(exception) //\n\t\t\t\t.hasRootCauseMessage(\n\t\t\t\t\t\"Invalid DiscoveryIssue.Severity 'wrong' set via the '%s' configuration parameter.\",\n\t\t\t\t\tLauncherConstants.CRITICAL_DISCOVERY_ISSUE_SEVERITY_PROPERTY_NAME);\n\t}\n\n\t@Test\n\tvoid failsDuringDiscoveryIfConfigurationParameterIsSetAccordingly() {\n\n\t\tvar engine = new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.ERROR, \"error\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\t\t};\n\n\t\tvar exception = assertThrows(DiscoveryIssueException.class, () -> execute(engine, request -> request //\n\t\t\t\t.configurationParameter(LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"discovery\")));\n\n\t\tassertThat(exception) //\n\t\t\t\t.isInstanceOf(DiscoveryIssueException.class) //\n\t\t\t\t.hasMessageStartingWith(\n\t\t\t\t\t\"TestEngine with ID 'engine-id' encountered a critical issue during test discovery\") //\n\t\t\t\t.hasMessageContaining(\"(1) [ERROR] error\");\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"discovery\", \"execution\" })\n\tvoid logsNonCriticalIssuesOnlyOnce(String phase, @TrackLogRecords LogRecordListener listener) {\n\n\t\tvar engine = new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.WARNING, \"warning\"));\n\t\t\t\treturn new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tvar executionListener = request.getEngineExecutionListener();\n\t\t\t\tvar engineDescriptor = request.getRootTestDescriptor();\n\t\t\t\texecutionListener.executionStarted(engineDescriptor);\n\t\t\t\texecutionListener.executionFinished(engineDescriptor, successful());\n\t\t\t}\n\t\t};\n\n\t\texecute(engine, request -> request //\n\t\t\t\t.configurationParameter(LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, phase));\n\n\t\tassertThat(listener.stream(DiscoveryIssueNotifier.class, Level.WARNING)).hasSize(1);\n\t}\n\n\t@Test\n\tvoid failsDuringDiscoveryIfConfigurationParameterValueIsInvalid() {\n\n\t\tvar engine = new TestEngineStub(\"engine-id\");\n\n\t\tvar exception = assertThrows(JUnitException.class, () -> execute(engine, request -> request //\n\t\t\t\t.configurationParameter(LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"wrong\")));\n\n\t\tassertThat(exception) //\n\t\t\t\t.hasRootCauseMessage(\"Invalid LauncherPhase 'wrong' set via the '%s' configuration parameter.\",\n\t\t\t\t\tLauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME);\n\t}\n\n\t@Test\n\tvoid reportsChildrenOfEngineDescriptorAsSkippedAfterCancellationWasRequested() {\n\t\tvar engine = spy(new TestEngineStub(\"engine-id\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar engineDescriptor = new EngineDescriptor(uniqueId, \"Engine\");\n\t\t\t\tvar container = new TestDescriptorStub(uniqueId.append(\"container\", \"1\"), \"Container\");\n\t\t\t\tvar test = new TestDescriptorStub(container.getUniqueId().append(\"test\", \"1\"), \"Test\");\n\t\t\t\tcontainer.addChild(test);\n\t\t\t\tengineDescriptor.addChild(container);\n\t\t\t\treturn engineDescriptor;\n\t\t\t}\n\t\t});\n\n\t\tvar executionListener = mock(TestExecutionListener.class);\n\n\t\tvar cancellationToken = CancellationToken.create();\n\t\tcancellationToken.cancel();\n\n\t\texecute(engine, identity(), executionRequest -> executionRequest //\n\t\t\t\t.listeners(executionListener) //\n\t\t\t\t.cancellationToken(cancellationToken));\n\n\t\tverify(engine, never()).execute(any());\n\n\t\tvar inOrder = inOrder(executionListener);\n\t\tinOrder.verify(executionListener).testPlanExecutionStarted(any());\n\t\tinOrder.verify(executionListener).executionStarted(\n\t\t\targThat(d -> d.getUniqueIdObject().equals(UniqueId.forEngine(\"engine-id\"))));\n\t\tinOrder.verify(executionListener).executionSkipped(\n\t\t\targThat(d -> d.getUniqueIdObject().getLastSegment().getType().equals(\"container\")),\n\t\t\teq(\"Execution cancelled\"));\n\t\tinOrder.verify(executionListener).executionFinished(\n\t\t\targThat(d -> d.getUniqueIdObject().equals(UniqueId.forEngine(\"engine-id\"))),\n\t\t\targThat(result -> result.getStatus() == ABORTED));\n\t\tinOrder.verify(executionListener).testPlanExecutionFinished(any());\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\tprivate static ReportedData execute(TestEngine engine) {\n\t\treturn execute(engine, identity());\n\t}\n\n\tprivate static ReportedData execute(TestEngine engine,\n\t\t\tUnaryOperator<LauncherDiscoveryRequestBuilder> discoveryConfigurer) {\n\t\treturn execute(engine, discoveryConfigurer, identity());\n\t}\n\n\tprivate static ReportedData execute(TestEngine engine,\n\t\t\tUnaryOperator<LauncherDiscoveryRequestBuilder> discoveryConfigurer,\n\t\t\tUnaryOperator<LauncherExecutionRequestBuilder> executionConfigurer) {\n\t\tvar executionListener = mock(TestExecutionListener.class);\n\n\t\tAtomicReference<@Nullable Instant> startTime = new AtomicReference<>();\n\t\tdoAnswer(invocation -> {\n\t\t\tstartTime.set(Instant.now());\n\t\t\treturn null;\n\t\t}).when(executionListener).executionStarted(any());\n\n\t\tAtomicReference<@Nullable Instant> finishTime = new AtomicReference<>();\n\t\tdoAnswer(invocation -> {\n\t\t\tfinishTime.set(Instant.now());\n\t\t\treturn null;\n\t\t}).when(executionListener).executionFinished(any(), any());\n\n\t\tvar launcher = createLauncher(engine);\n\n\t\tvar discoveryRequestBuilder = request() //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.configurationParameter(DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"execution\") //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\");\n\t\tvar discoveryRequest = discoveryConfigurer.apply(discoveryRequestBuilder).build();\n\t\tvar testPlan = launcher.discover(discoveryRequest);\n\n\t\tvar executionRequestBuilder = LauncherExecutionRequestBuilder.request(testPlan) //\n\t\t\t\t.listeners(executionListener);\n\t\tvar executionRequest = executionConfigurer.apply(executionRequestBuilder).build();\n\t\tlauncher.execute(executionRequest);\n\n\t\tvar inOrder = inOrder(executionListener);\n\t\tvar testIdentifier = ArgumentCaptor.forClass(TestIdentifier.class);\n\t\tvar testExecutionResult = ArgumentCaptor.forClass(TestExecutionResult.class);\n\t\tinOrder.verify(executionListener).testPlanExecutionStarted(any());\n\t\tinOrder.verify(executionListener).executionStarted(testIdentifier.capture());\n\t\tinOrder.verify(executionListener).executionFinished(any(), testExecutionResult.capture());\n\t\tinOrder.verify(executionListener).testPlanExecutionFinished(any());\n\t\tinOrder.verifyNoMoreInteractions();\n\n\t\treturn new ReportedData(testPlan, testIdentifier.getValue(), testExecutionResult.getValue(),\n\t\t\trequireNonNull(startTime.get()), requireNonNull(finishTime.get()));\n\t}\n\n\tprivate static LogRecord findFirstDiscoveryIssueLogRecord(LogRecordListener listener, Level level) {\n\t\treturn listener.stream(DiscoveryIssueNotifier.class, level) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.orElseThrow();\n\t}\n\n\tprivate record ReportedData(TestPlan testPlan, TestIdentifier testIdentifier,\n\t\t\tTestExecutionResult testExecutionResult, Instant startTime, Instant finishTime) {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/DiscoveryIssueCollectorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.UniqueId;\n\nclass DiscoveryIssueCollectorTests {\n\n\t@Test\n\tvoid reportsCollectedDiscoveryIssues() {\n\t\tvar collector = new DiscoveryIssueCollector(mock());\n\t\tvar issue = DiscoveryIssue.create(Severity.ERROR, \"hello\");\n\t\tcollector.issueEncountered(UniqueId.forEngine(\"dummy\"), issue);\n\t\tassertThat(collector.toNotifier().getAllIssues()).containsExactly(issue);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/DiscoveryIssueReportingDiscoveryListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasspathResource;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectDirectory;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectFile;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUri;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.jupiter.params.provider.RecordArguments;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.FilePosition;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.ClasspathResourceSource;\nimport org.junit.platform.engine.support.descriptor.DirectorySource;\nimport org.junit.platform.engine.support.descriptor.FileSource;\nimport org.junit.platform.engine.support.descriptor.PackageSource;\nimport org.junit.platform.engine.support.descriptor.UriSource;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\n\n@NullMarked\nclass DiscoveryIssueReportingDiscoveryListenerTests {\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"pairs\")\n\tvoid reportsFailedResolutionResultAsDiscoveryIssue(DiscoverySelector selector, TestSource source) {\n\t\tvar issues = new ArrayList<>();\n\t\tvar collector = new LauncherDiscoveryListener() {\n\n\t\t\t@Override\n\t\t\tpublic void issueEncountered(UniqueId engineId, DiscoveryIssue issue) {\n\t\t\t\tissues.add(issue);\n\t\t\t}\n\t\t};\n\n\t\tvar listener = new DiscoveryIssueReportingDiscoveryListener(collector);\n\t\tvar failure = SelectorResolutionResult.failed(new RuntimeException(\"boom\"));\n\t\tlistener.selectorProcessed(UniqueId.forEngine(\"dummy\"), selector, failure);\n\n\t\tvar expectedIssue = DiscoveryIssue.builder(Severity.ERROR, selector + \" resolution failed\") //\n\t\t\t\t.cause(failure.getThrowable()) //\n\t\t\t\t.source(source) //\n\t\t\t\t.build();\n\n\t\tassertThat(issues).containsExactly(expectedIssue);\n\t}\n\n\tpublic static Stream<Pair> pairs() {\n\t\treturn Stream.of( //\n\t\t\tnew Pair(selectClass(\"SomeClass\"), ClassSource.from(\"SomeClass\")), //\n\t\t\tnew Pair(selectMethod(\"SomeClass#someMethod(int,int)\"),\n\t\t\t\torg.junit.platform.engine.support.descriptor.MethodSource.from(\"SomeClass\", \"someMethod\", \"int,int\")), //\n\t\t\tnew Pair(selectClasspathResource(\"someResource\"), ClasspathResourceSource.from(\"someResource\")), //\n\t\t\tnew Pair(selectClasspathResource(\"someResource\", FilePosition.from(42)),\n\t\t\t\tClasspathResourceSource.from(\"someResource\",\n\t\t\t\t\torg.junit.platform.engine.support.descriptor.FilePosition.from(42))), //\n\t\t\tnew Pair(selectClasspathResource(\"someResource\", FilePosition.from(42, 23)),\n\t\t\t\tClasspathResourceSource.from(\"someResource\",\n\t\t\t\t\torg.junit.platform.engine.support.descriptor.FilePosition.from(42, 23))), //\n\t\t\tnew Pair(selectPackage(\"\"), PackageSource.from(\"\")), //\n\t\t\tnew Pair(selectPackage(\"some.package\"), PackageSource.from(\"some.package\")), //\n\t\t\tnew Pair(selectFile(\"someFile\"), FileSource.from(new File(\"someFile\"))), //\n\t\t\tnew Pair(selectFile(\"someFile\", FilePosition.from(42)),\n\t\t\t\tFileSource.from(new File(\"someFile\"),\n\t\t\t\t\torg.junit.platform.engine.support.descriptor.FilePosition.from(42))), //\n\t\t\tnew Pair(selectFile(\"someFile\", FilePosition.from(42, 23)),\n\t\t\t\tFileSource.from(new File(\"someFile\"),\n\t\t\t\t\torg.junit.platform.engine.support.descriptor.FilePosition.from(42, 23))), //\n\t\t\tnew Pair(selectDirectory(\"someDir\"), DirectorySource.from(new File(\"someDir\"))), //\n\t\t\tnew Pair(selectUri(\"some:uri\"), UriSource.from(URI.create(\"some:uri\"))) //\n\t\t);\n\t}\n\n\trecord Pair(DiscoverySelector selector, TestSource source) implements RecordArguments {\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/EngineDiscoveryResultValidatorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.fakes.TestEngineStub;\n\n/**\n * @since 1.3\n */\nclass EngineDiscoveryResultValidatorTests {\n\n\tprivate final TestEngine testEngine = new TestEngineStub(\"my-engine\");\n\tprivate final EngineDiscoveryResultValidator validator = new EngineDiscoveryResultValidator();\n\n\t@Test\n\tvoid detectCycleWithDoubleRoot() {\n\t\tvar root = new TestDescriptorStub(UniqueId.forEngine(\"root\"), \"root\");\n\t\tvalidator.validate(testEngine, root);\n\n\t\troot.addChild(root);\n\t\tassertPreconditionViolationFor(() -> validator.validate(testEngine, root)).withMessage(\"\"\"\n\t\t\t\tThe discover() method for TestEngine with ID 'my-engine' returned a cyclic graph; \\\n\t\t\t\t[engine:root] exists in at least two paths:\n\t\t\t\t(1) [engine:root]\n\t\t\t\t(2) [engine:root] -> [engine:root]\"\"\");\n\t}\n\n\t@Test\n\tvoid detectCycleWithDoubleGroup() {\n\t\tvar rootId = UniqueId.forEngine(\"root\");\n\t\tvar root = new TestDescriptorStub(rootId, \"root\");\n\t\tTestDescriptor group1 = new TestDescriptorStub(rootId.append(\"group\", \"1\"), \"1\");\n\t\tTestDescriptor group2 = new TestDescriptorStub(rootId.append(\"group\", \"2\"), \"2\");\n\t\troot.addChild(group1);\n\t\troot.addChild(group2);\n\t\tvalidator.validate(testEngine, root);\n\n\t\tgroup2.addChild(group1);\n\t\tassertPreconditionViolationFor(() -> validator.validate(testEngine, root)).withMessage(\"\"\"\n\t\t\t\tThe discover() method for TestEngine with ID 'my-engine' returned a cyclic graph; \\\n\t\t\t\t[engine:root]/[group:1] exists in at least two paths:\n\t\t\t\t(1) [engine:root] -> [engine:root]/[group:1]\n\t\t\t\t(2) [engine:root] -> [engine:root]/[group:2] -> [engine:root]/[group:1]\"\"\");\n\t}\n\n\t@Test\n\tvoid detectCycleWithDoubleTest() {\n\t\tvar rootId = UniqueId.forEngine(\"root\");\n\t\tvar root = new TestDescriptorStub(rootId, \"root\");\n\t\tTestDescriptor group1 = new TestDescriptorStub(rootId.append(\"group\", \"1\"), \"1\");\n\t\tTestDescriptor group2 = new TestDescriptorStub(rootId.append(\"group\", \"2\"), \"2\");\n\t\troot.addChild(group1);\n\t\troot.addChild(group2);\n\t\tTestDescriptor test1 = new TestDescriptorStub(group1.getUniqueId().append(\"test\", \"1\"), \"1-1\");\n\t\tTestDescriptor test2 = new TestDescriptorStub(group2.getUniqueId().append(\"test\", \"2\"), \"2-2\");\n\t\tgroup1.addChild(test1);\n\t\tgroup2.addChild(test2);\n\t\tvalidator.validate(testEngine, root);\n\n\t\tgroup2.addChild(test1);\n\t\tassertPreconditionViolationFor(() -> validator.validate(testEngine, root)).withMessage(\"\"\"\n\t\t\t\tThe discover() method for TestEngine with ID 'my-engine' returned a cyclic graph; \\\n\t\t\t\t[engine:root]/[group:1]/[test:1] exists in at least two paths:\n\t\t\t\t(1) [engine:root] -> [engine:root]/[group:1] -> [engine:root]/[group:1]/[test:1]\n\t\t\t\t(2) [engine:root] -> [engine:root]/[group:2] -> [engine:root]/[group:1]/[test:1]\"\"\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/ExecutionListenerAdapterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.quality.Strictness.STRICT_STUBS;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.util.ReflectionUtils;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.DemoMethodTestDescriptor;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n/**\n * @since 1.0\n */\n@MockitoSettings(strictness = STRICT_STUBS)\nclass ExecutionListenerAdapterTests {\n\n\tfinal UniqueId uniqueId = UniqueId.root(\"method\", \"demoTestMethod\");\n\tfinal TestDescriptor testDescriptor = createDemoMethodTestDescriptor();\n\tfinal TestIdentifier testIdentifier = TestIdentifier.from(testDescriptor);\n\n\t@Mock\n\tTestPlan testPlan;\n\n\t@Mock\n\tTestExecutionListener testExecutionListener;\n\n\t@AfterEach\n\tvoid verifyNoMoreInteractions() {\n\t\tMockito.verifyNoMoreInteractions(testPlan, testExecutionListener);\n\t}\n\n\t@Test\n\tvoid dynamicTestRegistered() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\n\t\texecutionListenerAdapter.dynamicTestRegistered(testDescriptor);\n\n\t\tverify(testExecutionListener).dynamicTestRegistered(testIdentifier);\n\t\tverify(testPlan).addInternal(testIdentifier);\n\t}\n\n\t@Test\n\tvoid executionStarted() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\n\t\twhen(testPlan.getTestIdentifier(uniqueId)).thenReturn(testIdentifier);\n\t\texecutionListenerAdapter.executionStarted(testDescriptor);\n\n\t\tverify(testExecutionListener).executionStarted(testIdentifier);\n\t}\n\n\t@Test\n\tvoid executionSkipped() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\t\tvar reason = \"skip reason\";\n\n\t\twhen(testPlan.getTestIdentifier(uniqueId)).thenReturn(testIdentifier);\n\t\texecutionListenerAdapter.executionSkipped(testDescriptor, reason);\n\n\t\tverify(testExecutionListener).executionSkipped(testIdentifier, reason);\n\t}\n\n\t@Test\n\tvoid executionFinished() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\t\tvar testExecutionResult = TestExecutionResult.successful();\n\n\t\twhen(testPlan.getTestIdentifier(uniqueId)).thenReturn(testIdentifier);\n\t\texecutionListenerAdapter.executionFinished(testDescriptor, testExecutionResult);\n\n\t\tverify(testExecutionListener).executionFinished(testIdentifier, testExecutionResult);\n\t}\n\n\t@Test\n\tvoid testReportingEntryPublished() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\t\tvar entry = ReportEntry.from(\"one\", \"two\");\n\n\t\twhen(testPlan.getTestIdentifier(uniqueId)).thenReturn(testIdentifier);\n\t\texecutionListenerAdapter.reportingEntryPublished(testDescriptor, entry);\n\n\t\tverify(testExecutionListener).reportingEntryPublished(testIdentifier, entry);\n\t}\n\n\t@Test\n\tvoid fileEntryPublished() {\n\t\tvar executionListenerAdapter = new ExecutionListenerAdapter(testPlan, testExecutionListener);\n\t\tvar entry = FileEntry.from(Path.of(\"entry.txt\"), \"application/txt\");\n\n\t\twhen(testPlan.getTestIdentifier(uniqueId)).thenReturn(testIdentifier);\n\t\texecutionListenerAdapter.fileEntryPublished(testDescriptor, entry);\n\n\t\tverify(testExecutionListener).fileEntryPublished(testIdentifier, entry);\n\t}\n\n\tprivate TestDescriptor createDemoMethodTestDescriptor() {\n\t\tvar demoTestMethod = ReflectionUtils.findMethod(ExecutionListenerAdapterTests.class,\n\t\t\t\"demoTestMethod\").orElseThrow();\n\t\treturn new DemoMethodTestDescriptor(uniqueId, demoTestMethod);\n\t}\n\n\t//for reflection purposes only\n\t@SuppressWarnings(\"unused\")\n\tvoid demoTestMethod() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/HierarchicalOutputDirectoryCreatorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.nio.file.Path;\nimport java.util.function.Supplier;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.mockito.InjectMocks;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoSettings;\n\n@MockitoSettings\nclass HierarchicalOutputDirectoryCreatorTests {\n\n\t@TempDir\n\tPath tempDir;\n\n\t@Mock\n\tSupplier<Path> rootDirSupplier;\n\n\t@Mock\n\tTestDescriptor testDescriptor;\n\n\t@InjectMocks\n\tHierarchicalOutputDirectoryCreator provider;\n\n\t@BeforeEach\n\tvoid prepareMock() {\n\t\twhen(rootDirSupplier.get()).thenReturn(tempDir);\n\t}\n\n\t@Test\n\tvoid returnsConfiguredRootDir() {\n\t\tassertThat(provider.getRootDirectory()).isEqualTo(tempDir);\n\t\tassertThat(provider.getRootDirectory()).isEqualTo(tempDir);\n\t\tverify(rootDirSupplier, times(1)).get();\n\t}\n\n\t@Test\n\tvoid createsSubDirectoriesBasedOnUniqueId() throws Exception {\n\t\tvar uniqueId = UniqueId.forEngine(\"engine\") //\n\t\t\t\t.append(\"irrelevant\", \"foo\") //\n\t\t\t\t.append(\"irrelevant\", \"bar\");\n\t\twhen(testDescriptor.getUniqueId()).thenReturn(uniqueId);\n\n\t\tvar outputDir = provider.createOutputDirectory(testDescriptor);\n\n\t\tassertThat(outputDir) //\n\t\t\t\t.isEqualTo(tempDir.resolve(Path.of(\"engine\", \"foo\", \"bar\"))) //\n\t\t\t\t.exists();\n\t}\n\n\t@Test\n\tvoid replacesForbiddenCharacters() throws Exception {\n\t\tvar uniqueId = UniqueId.forEngine(\"Engine<>\") //\n\t\t\t\t.append(\"irrelevant\", \"*/abc\");\n\t\twhen(testDescriptor.getUniqueId()).thenReturn(uniqueId);\n\n\t\tvar outputDir = provider.createOutputDirectory(testDescriptor);\n\n\t\tassertThat(outputDir) //\n\t\t\t\t.isEqualTo(tempDir.resolve(Path.of(\"Engine__\", \"__abc\"))) //\n\t\t\t\t.exists();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/InternalTestPlanTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;\n\nclass InternalTestPlanTests {\n\n\tprivate final ConfigurationParameters configParams = mock();\n\n\tprivate final EngineDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"foo\"), \"Foo\");\n\n\t@Test\n\tvoid doesNotContainTestsForEmptyContainers() {\n\t\tengineDescriptor.addChild(\n\t\t\tnew AbstractTestDescriptor(engineDescriptor.getUniqueId().append(\"test\", \"bar\"), \"Bar\") {\n\t\t\t\t@Override\n\t\t\t\tpublic Type getType() {\n\t\t\t\t\treturn Type.CONTAINER;\n\t\t\t\t}\n\t\t\t});\n\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(\n\t\t\tEngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isFalse();\n\t}\n\n\t@Test\n\tvoid containsTestsForTests() {\n\t\tengineDescriptor.addChild(\n\t\t\tnew AbstractTestDescriptor(engineDescriptor.getUniqueId().append(\"test\", \"bar\"), \"Bar\") {\n\t\t\t\t@Override\n\t\t\t\tpublic Type getType() {\n\t\t\t\t\treturn Type.TEST;\n\t\t\t\t}\n\t\t\t});\n\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(\n\t\t\tEngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isTrue();\n\t}\n\n\t@Test\n\tvoid containsTestsForContainersThatMayRegisterTests() {\n\t\tengineDescriptor.addChild(\n\t\t\tnew AbstractTestDescriptor(engineDescriptor.getUniqueId().append(\"test\", \"bar\"), \"Bar\") {\n\t\t\t\t@Override\n\t\t\t\tpublic Type getType() {\n\t\t\t\t\treturn Type.CONTAINER;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean mayRegisterTests() {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t});\n\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(\n\t\t\tEngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isTrue();\n\t}\n\n\t@Test\n\tvoid containsTestsForEnginesWithDiscoveryError() {\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(\n\t\t\tEngineResultInfo.errored(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES, new RuntimeException())));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isTrue();\n\t}\n\n\t@Test\n\tvoid containsTestsForEnginesWithCriticalDiscoveryIssues() {\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(EngineResultInfo.completed(engineDescriptor,\n\t\t\tDiscoveryIssueNotifier.from(Severity.ERROR, List.of(DiscoveryIssue.create(Severity.ERROR, \"error\"))))));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isTrue();\n\t}\n\n\t@Test\n\tvoid doesNotContainTestsForEnginesWithNonCriticalDiscoveryIssues() {\n\t\tvar testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(EngineResultInfo.completed(engineDescriptor,\n\t\t\tDiscoveryIssueNotifier.from(Severity.ERROR, List.of(DiscoveryIssue.create(Severity.WARNING, \"warning\"))))));\n\n\t\tassertThat(testPlan.containsTests()).as(\"contains tests\").isFalse();\n\t}\n\n\tprivate LauncherDiscoveryResult createLauncherDiscoveryResult(EngineResultInfo result) {\n\t\tvar testEngineResults = Map.of(mock(TestEngine.class), result);\n\t\treturn new LauncherDiscoveryResult(testEngineResults, configParams, dummyOutputDirectoryCreator());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TestExecutionListener;\n\n/**\n * Unit tests for {@link LauncherConfig} and {@link LauncherConfig.Builder}.\n *\n * @since 1.3\n */\nclass LauncherConfigTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> LauncherConfig.builder().addTestEngines((TestEngine[]) null));\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> LauncherConfig.builder().addTestExecutionListeners((TestExecutionListener[]) null));\n\n\t\tTestEngine engine = new TestEngineStub();\n\t\tvar listener = new TestExecutionListener() {\n\t\t};\n\t\tassertPreconditionViolationFor(() -> LauncherConfig.builder().addTestEngines(engine, engine, null));\n\t\tassertPreconditionViolationFor(\n\t\t\t() -> LauncherConfig.builder().addTestExecutionListeners(listener, listener, null));\n\t}\n\n\t@Test\n\tvoid defaultConfig() {\n\t\tvar config = LauncherConfig.DEFAULT;\n\n\t\tassertTrue(config.isTestEngineAutoRegistrationEnabled(),\n\t\t\t\"Test engine auto-registration should be enabled by default\");\n\t\tassertTrue(config.isLauncherDiscoveryListenerAutoRegistrationEnabled(),\n\t\t\t\"Launcher discovery listener auto-registration should be enabled by default\");\n\t\tassertTrue(config.isTestExecutionListenerAutoRegistrationEnabled(),\n\t\t\t\"Test execution listener auto-registration should be enabled by default\");\n\t\tassertTrue(config.isPostDiscoveryFilterAutoRegistrationEnabled(),\n\t\t\t\"Post-discovery filter auto-registration should be enabled by default\");\n\n\t\tassertThat(config.getAdditionalTestEngines()).isEmpty();\n\n\t\tassertThat(config.getAdditionalTestExecutionListeners()).isEmpty();\n\t}\n\n\t@Test\n\tvoid disableTestEngineAutoRegistration() {\n\t\tvar config = LauncherConfig.builder().enableTestEngineAutoRegistration(false).build();\n\n\t\tassertFalse(config.isTestEngineAutoRegistrationEnabled());\n\t}\n\n\t@Test\n\tvoid disableLauncherDiscoveryListenerAutoRegistration() {\n\t\tvar config = LauncherConfig.builder().enableLauncherDiscoveryListenerAutoRegistration(false).build();\n\n\t\tassertFalse(config.isLauncherDiscoveryListenerAutoRegistrationEnabled());\n\t}\n\n\t@Test\n\tvoid disableTestExecutionListenerAutoRegistration() {\n\t\tvar config = LauncherConfig.builder().enableTestExecutionListenerAutoRegistration(false).build();\n\n\t\tassertFalse(config.isTestExecutionListenerAutoRegistrationEnabled());\n\t}\n\n\t@Test\n\tvoid disablePostDiscoveryFilterAutoRegistration() {\n\t\tvar config = LauncherConfig.builder().enablePostDiscoveryFilterAutoRegistration(false).build();\n\n\t\tassertFalse(config.isPostDiscoveryFilterAutoRegistrationEnabled());\n\t}\n\n\t@Test\n\tvoid addTestEngines() {\n\t\tTestEngine first = new TestEngineStub();\n\t\tTestEngine second = new TestEngineStub();\n\n\t\tvar config = LauncherConfig.builder().addTestEngines(first, second).build();\n\n\t\tassertThat(config.getAdditionalTestEngines()).containsOnly(first, second);\n\t}\n\n\t@Test\n\tvoid addLauncherSessionListeners() {\n\t\tvar first = new LauncherSessionListener() {\n\t\t};\n\t\tvar second = new LauncherSessionListener() {\n\t\t};\n\n\t\tvar config = LauncherConfig.builder().addLauncherSessionListeners(first, second).build();\n\n\t\tassertThat(config.getAdditionalLauncherSessionListeners()).containsOnly(first, second);\n\t}\n\n\t@Test\n\tvoid addLauncherDiscoveryListeners() {\n\t\tvar first = new LauncherDiscoveryListener() {\n\t\t};\n\t\tvar second = new LauncherDiscoveryListener() {\n\t\t};\n\n\t\tvar config = LauncherConfig.builder().addLauncherDiscoveryListeners(first, second).build();\n\n\t\tassertThat(config.getAdditionalLauncherDiscoveryListeners()).containsOnly(first, second);\n\t}\n\n\t@Test\n\tvoid addTestExecutionListeners() {\n\t\tvar first = new TestExecutionListener() {\n\t\t};\n\t\tvar second = new TestExecutionListener() {\n\t\t};\n\n\t\tvar config = LauncherConfig.builder().addTestExecutionListeners(first, second).build();\n\n\t\tassertThat(config.getAdditionalTestExecutionListeners()).containsOnly(first, second);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherConfigurationParametersTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.as;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\n\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.assertj.core.api.InstanceOfAssertFactories;\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.launcher.listeners.SummaryGeneratingListener;\n\n/**\n * Unit tests for {@link LauncherConfigurationParameters}.\n *\n * @since 1.0\n */\n@NullMarked\nclass LauncherConfigurationParametersTests {\n\n\tprivate static final String CONFIG_FILE_NAME = \"test-junit-platform.properties\";\n\tprivate static final String KEY = \"org.junit.platform.launcher.core.LauncherConfigurationParametersTests\";\n\tprivate static final String INHERITED_PARAM = \"parent config param\";\n\tprivate static final String CONFIG_PARAM = \"explicit config param\";\n\tprivate static final String CONFIG_FILE = \"from config file\";\n\tprivate static final String SYSTEM_PROPERTY = \"system property\";\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid constructorPreconditions() {\n\t\tassertPreconditionViolationFor(() -> fromMap(null));\n\t\tassertPreconditionViolationFor(() -> fromMapAndFile(Map.of(), null));\n\t\tassertPreconditionViolationFor(() -> fromMapAndFile(Map.of(), \"\"));\n\t\tassertPreconditionViolationFor(() -> fromMapAndFile(Map.of(), \"  \"));\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid getPreconditions() {\n\t\tConfigurationParameters configParams = fromMap(Map.of());\n\t\tassertPreconditionViolationFor(() -> configParams.get(null));\n\t\tassertPreconditionViolationFor(() -> configParams.get(\"\"));\n\t\tassertPreconditionViolationFor(() -> configParams.get(\"  \"));\n\t}\n\n\t@Test\n\tvoid noConfigParams() {\n\t\tConfigurationParameters configParams = fromMap(Map.of());\n\t\tassertThat(configParams.get(KEY)).isEmpty();\n\t\tassertThat(configParams.keySet()).doesNotContain(KEY);\n\t\tassertThat(configParams.toString()).doesNotContain(KEY);\n\t}\n\n\t@Test\n\tvoid explicitConfigParam() {\n\t\tConfigurationParameters configParams = fromMap(Map.of(KEY, CONFIG_PARAM));\n\t\tassertThat(configParams.get(KEY)).contains(CONFIG_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_PARAM);\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid systemProperty() {\n\t\tConfigurationParameters configParams = fromMap(Map.of());\n\t\tassertThat(configParams.get(KEY)).contains(SYSTEM_PROPERTY);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).doesNotContain(KEY);\n\t}\n\n\t@Test\n\tvoid configFile() {\n\t\tConfigurationParameters configParams = fromMapAndFile(Map.of(), CONFIG_FILE_NAME);\n\t\tassertThat(configParams.get(KEY)).contains(CONFIG_FILE);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_FILE);\n\t}\n\n\t@Test\n\tvoid inherited() {\n\t\tConfigurationParameters configParams = fromMapAndParent( //\n\t\t\tMap.of(), //\n\t\t\tMap.of(KEY, INHERITED_PARAM));\n\t\tassertThat(configParams.get(KEY)).contains(INHERITED_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(KEY);\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid explicitConfigParamOverridesSystemProperty() {\n\t\tConfigurationParameters configParams = fromMap(Map.of(KEY, CONFIG_PARAM));\n\t\tassertThat(configParams.get(KEY)).contains(CONFIG_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_PARAM);\n\t}\n\n\t@Test\n\tvoid explicitConfigParamOverridesConfigFile() {\n\t\tConfigurationParameters configParams = fromMapAndFile(Map.of(KEY, CONFIG_PARAM), CONFIG_FILE_NAME);\n\t\tassertThat(configParams.get(KEY)).contains(CONFIG_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_PARAM);\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid explicitConfigParamOverridesInheritedProperty() {\n\t\tConfigurationParameters configParams = fromMapAndParent( //\n\t\t\tMap.of(KEY, CONFIG_PARAM), //\n\t\t\tMap.of(KEY, INHERITED_PARAM));\n\t\tassertThat(configParams.get(KEY)).contains(CONFIG_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_PARAM);\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid systemPropertyOverridesConfigFile() {\n\t\tConfigurationParameters configParams = fromMapAndFile(Map.of(), CONFIG_FILE_NAME);\n\t\tassertThat(configParams.get(KEY)).contains(SYSTEM_PROPERTY);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(CONFIG_FILE);\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid inheritedPropertyOverridesSystemProperty() {\n\t\tConfigurationParameters configParams = fromMapAndParent(Map.of(), Map.of(KEY, INHERITED_PARAM));\n\t\tassertThat(configParams.get(KEY)).contains(INHERITED_PARAM);\n\t\tassertThat(configParams.keySet()).contains(KEY);\n\t\tassertThat(configParams.toString()).contains(KEY);\n\t}\n\n\t@Test\n\tvoid getValueInExtensionContext() {\n\t\tvar summary = new SummaryGeneratingListener();\n\t\tvar request = LauncherDiscoveryRequestBuilder.request() //\n\t\t\t\t.configurationParameter(\"thing\", \"one else!\") //\n\t\t\t\t.selectors(DiscoverySelectors.selectClass(Something.class)) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.listeners(summary) //\n\t\t\t\t.build();\n\t\tLauncherFactory.create().execute(request);\n\t\tassertEquals(0, summary.getSummary().getTestsFailedCount());\n\t}\n\n\t@Test\n\tvoid getWithSuccessfulTransformer() {\n\t\tConfigurationParameters configParams = fromMap(Map.of(KEY, \"42\"));\n\t\tassertThat(configParams.get(KEY, Integer::valueOf)).contains(42);\n\t}\n\n\t@Test\n\tvoid getWithErroneousTransformer() {\n\t\tConfigurationParameters configParams = fromMap(Map.of(KEY, \"42\"));\n\t\tvar exception = assertThrows(JUnitException.class, () -> configParams.get(KEY, input -> {\n\t\t\tthrow new RuntimeException(\"foo\");\n\t\t}));\n\t\tassertThat(exception).hasMessageContaining(\n\t\t\t\"Failed to transform configuration parameter with key '\" + KEY + \"' and initial value '42'\");\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = KEY, value = SYSTEM_PROPERTY)\n\tvoid ignoresSystemPropertyAndConfigFileWhenImplicitLookupsAreDisabled() {\n\t\tConfigurationParameters configParams = LauncherConfigurationParameters.builder() //\n\t\t\t\t.enableImplicitProviders(false) //\n\t\t\t\t.build();\n\t\tassertThat(configParams.get(KEY)).isEmpty();\n\t}\n\n\t@Test\n\tvoid warnsOnMultiplePropertyResources(@TempDir Path tempDir, @TrackLogRecords LogRecordListener logRecordListener)\n\t\t\tthrows Exception {\n\n\t\tProperties properties = new Properties();\n\t\tproperties.setProperty(KEY, \"from second config file\");\n\t\ttry (var out = Files.newOutputStream(tempDir.resolve(CONFIG_FILE_NAME))) {\n\t\t\tproperties.store(out, \"\");\n\t\t}\n\n\t\tClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();\n\t\tURL originalResource = originalClassLoader.getResource(CONFIG_FILE_NAME);\n\n\t\ttry (var customClassLoader = new URLClassLoader(new URL[] { tempDir.toUri().toURL() }, originalClassLoader)) {\n\t\t\tThread.currentThread().setContextClassLoader(customClassLoader);\n\t\t\tConfigurationParameters configParams = fromMapAndFile(Map.of(), CONFIG_FILE_NAME);\n\n\t\t\tassertThat(configParams.get(KEY)).contains(CONFIG_FILE);\n\n\t\t\tassertThat(logRecordListener.stream(Level.WARNING).map(LogRecord::getMessage)) //\n\t\t\t\t\t.hasSize(1) //\n\t\t\t\t\t.first(as(InstanceOfAssertFactories.STRING)) //\n\t\t\t\t\t.contains(\"\"\"\n\t\t\t\t\t\t\tDiscovered 2 '%s' configuration files on the classpath (see below); \\\n\t\t\t\t\t\t\tonly the first (*) will be used.\n\t\t\t\t\t\t\t- %s (*)\n\t\t\t\t\t\t\t- %s\"\"\"//\n\t\t\t\t\t\t\t.formatted(CONFIG_FILE_NAME, originalResource,\n\t\t\t\t\t\t\t\ttempDir.resolve(CONFIG_FILE_NAME).toUri().toURL()));\n\t\t}\n\t\tfinally {\n\t\t\tThread.currentThread().setContextClassLoader(originalClassLoader);\n\t\t}\n\t}\n\n\tprivate static LauncherConfigurationParameters fromMap(Map<String, String> map) {\n\t\treturn LauncherConfigurationParameters.builder().explicitParameters(map).build();\n\t}\n\n\tprivate static LauncherConfigurationParameters fromMapAndFile(Map<String, String> map, String configFileName) {\n\t\treturn LauncherConfigurationParameters.builder() //\n\t\t\t\t.explicitParameters(map) //\n\t\t\t\t.configFileName(configFileName) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static LauncherConfigurationParameters fromMapAndParent(Map<String, String> map,\n\t\t\tMap<String, String> inherited) {\n\t\tvar parameters = LauncherConfigurationParameters.builder() //\n\t\t\t\t.explicitParameters(inherited) //\n\t\t\t\t.build();\n\n\t\treturn LauncherConfigurationParameters.builder() //\n\t\t\t\t.explicitParameters(map) //\n\t\t\t\t.parentConfigurationParameters(parameters) //\n\t\t\t\t.build();\n\t}\n\n\tprivate static class Mutator implements TestInstancePostProcessor {\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {\n\t\t\tvar value = context.getConfigurationParameter(\"thing\").orElse(\"thing\");\n\t\t\tSomething.class.getField(\"thing\").set(testInstance, value);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\t@ExtendWith(Mutator.class)\n\tstatic class Something {\n\n\t\t// `public` is needed for simple \"Class#getField(String)\" to work\n\t\tpublic String thing = \"body.\";\n\n\t\t@Test\n\t\tvoid some() {\n\t\t\tassertEquals(\"Someone else!\", \"Some\" + thing);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryRequestBuilderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.FilterResult.excluded;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectModule;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectPackage;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\nimport static org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners.abortOnFailure;\nimport static org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners.logging;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryFilter;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UniqueIdSelector;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.DiscoveryFilterStub;\nimport org.junit.platform.launcher.PostDiscoveryFilterStub;\n\n/**\n * @since 1.0\n */\nclass LauncherDiscoveryRequestBuilderTests {\n\n\t@Nested\n\tclass DiscoverySelectionTests {\n\n\t\t@Test\n\t\tvoid modulesAreStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = LauncherDiscoveryRequestBuilder.request()\n\t\t\t\t\t.selectors(\n\t\t\t\t\t\t\tselectModule(\"java.base\")\n\t\t\t\t\t).build();\n\t\t\t// @formatter:on\n\n\t\t\tvar packageSelectors = discoveryRequest.getSelectorsByType(ModuleSelector.class).stream().map(\n\t\t\t\tModuleSelector::getModuleName).toList();\n\t\t\tassertThat(packageSelectors).contains(\"java.base\");\n\t\t}\n\n\t\t@Test\n\t\tvoid packagesAreStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.selectors(\n\t\t\t\t\t\t\tselectPackage(\"org.junit.platform.engine\")\n\t\t\t\t\t).build();\n\t\t\t// @formatter:on\n\n\t\t\tvar packageSelectors = discoveryRequest.getSelectorsByType(PackageSelector.class).stream().map(\n\t\t\t\tPackageSelector::getPackageName).toList();\n\t\t\tassertThat(packageSelectors).contains(\"org.junit.platform.engine\");\n\t\t}\n\n\t\t@Test\n\t\tvoid classesAreStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.selectors(\n\t\t\t\t\t\t\tselectClass(LauncherDiscoveryRequestBuilderTests.class.getName()),\n\t\t\t\t\t\t\tselectClass(SampleTestClass.class)\n\t\t\t\t\t)\n\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\t@SuppressWarnings(\"rawtypes\")\n\t\t\tList<Class> classes = discoveryRequest.getSelectorsByType(ClassSelector.class).stream()//\n\t\t\t\t\t.map(ClassSelector::getJavaClass).map(Class.class::cast).toList();\n\t\t\tassertThat(classes).contains(SampleTestClass.class, LauncherDiscoveryRequestBuilderTests.class);\n\t\t}\n\n\t\t@Test\n\t\tvoid methodsByFullyQualifiedNameAreStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.selectors(selectMethod(fullyQualifiedMethodName()))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar methodSelectors = discoveryRequest.getSelectorsByType(MethodSelector.class);\n\t\t\tassertThat(methodSelectors).hasSize(1);\n\n\t\t\tvar methodSelector = methodSelectors.getFirst();\n\t\t\tassertThat(methodSelector.getJavaClass()).isEqualTo(LauncherDiscoveryRequestBuilderTests.class);\n\t\t\tassertThat(methodSelector.getJavaMethod()).isEqualTo(fullyQualifiedMethod());\n\t\t}\n\n\t\t@Test\n\t\tvoid methodsByNameAreStoredInDiscoveryRequest() throws Exception {\n\t\t\tClass<?> testClass = SampleTestClass.class;\n\t\t\tvar testMethod = testClass.getDeclaredMethod(\"test\");\n\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.selectors(selectMethod(SampleTestClass.class.getName(), \"test\"))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar methodSelectors = discoveryRequest.getSelectorsByType(MethodSelector.class);\n\t\t\tassertThat(methodSelectors).hasSize(1);\n\n\t\t\tvar methodSelector = methodSelectors.getFirst();\n\t\t\tassertThat(methodSelector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(methodSelector.getJavaMethod()).isEqualTo(testMethod);\n\t\t}\n\n\t\t@Test\n\t\tvoid methodsByClassAreStoredInDiscoveryRequest() throws Exception {\n\t\t\tClass<?> testClass = SampleTestClass.class;\n\t\t\tvar testMethod = testClass.getDeclaredMethod(\"test\");\n\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = (DefaultDiscoveryRequest) discoveryRequest()\n\t\t\t\t\t.selectors(\n\t\t\t\t\t\t\tselectMethod(testClass, \"test\")\n\t\t\t\t\t).build();\n\t\t\t// @formatter:on\n\n\t\t\tvar methodSelectors = discoveryRequest.getSelectorsByType(MethodSelector.class);\n\t\t\tassertThat(methodSelectors).hasSize(1);\n\n\t\t\tvar methodSelector = methodSelectors.getFirst();\n\t\t\tassertThat(methodSelector.getJavaClass()).isEqualTo(testClass);\n\t\t\tassertThat(methodSelector.getJavaMethod()).isEqualTo(testMethod);\n\t\t}\n\n\t\t@Test\n\t\tvoid uniqueIdsAreStoredInDiscoveryRequest() {\n\t\t\tvar id1 = UniqueId.forEngine(\"engine\").append(\"foo\", \"id1\");\n\t\t\tvar id2 = UniqueId.forEngine(\"engine\").append(\"foo\", \"id2\");\n\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.selectors(\n\t\t\t\t\t\t\tselectUniqueId(id1),\n\t\t\t\t\t\t\tselectUniqueId(id2)\n\t\t\t\t\t).build();\n\t\t\t// @formatter:on\n\n\t\t\tvar uniqueIds = discoveryRequest.getSelectorsByType(UniqueIdSelector.class).stream().map(\n\t\t\t\tUniqueIdSelector::getUniqueId).map(Object::toString).toList();\n\n\t\t\tassertThat(uniqueIds).contains(id1.toString(), id2.toString());\n\t\t}\n\t}\n\n\t@Nested\n\tclass DiscoveryFilterTests {\n\n\t\t@Test\n\t\tvoid engineFiltersAreStoredInDiscoveryRequest() {\n\t\t\tTestEngine engine1 = new TestEngineStub(\"engine1\");\n\t\t\tTestEngine engine2 = new TestEngineStub(\"engine2\");\n\t\t\tTestEngine engine3 = new TestEngineStub(\"engine3\");\n\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.filters(includeEngines(engine1.getId(), engine2.getId()))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar filters = discoveryRequest.getEngineFilters();\n\t\t\tassertThat(filters).hasSize(1);\n\t\t\tvar engineFilter = filters.getFirst();\n\t\t\tassertTrue(engineFilter.apply(engine1).included());\n\t\t\tassertTrue(engineFilter.apply(engine2).included());\n\t\t\tassertTrue(engineFilter.apply(engine3).excluded());\n\t\t}\n\n\t\t@Test\n\t\tvoid discoveryFiltersAreStoredInDiscoveryRequest() {\n\t\t\tvar filter1 = new DiscoveryFilterStub<>(\"filter1\");\n\t\t\tvar filter2 = new DiscoveryFilterStub<>(\"filter2\");\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.filters(filter1, filter2)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar filters = discoveryRequest.getFiltersByType(DiscoveryFilter.class);\n\t\t\tassertThat(filters).containsOnly(filter1, filter2);\n\t\t}\n\n\t\t@Test\n\t\tvoid postDiscoveryFiltersAreStoredInDiscoveryRequest() {\n\t\t\tvar postFilter1 = new PostDiscoveryFilterStub(\"postFilter1\");\n\t\t\tvar postFilter2 = new PostDiscoveryFilterStub(\"postFilter2\");\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.filters(postFilter1, postFilter2)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar filters = discoveryRequest.getPostDiscoveryFilters();\n\t\t\tassertThat(filters).containsOnly(postFilter1, postFilter2);\n\t\t}\n\n\t\t@Test\n\t\tvoid exceptionForIllegalFilterClass() {\n\t\t\tassertPreconditionViolationFor(() -> discoveryRequest().filters(o -> excluded(\"reason\")))//\n\t\t\t\t\t.withMessageStartingWith(\"Filter\")//\n\t\t\t\t\t.withMessageEndingWith(\"must implement EngineFilter, PostDiscoveryFilter, or DiscoveryFilter.\");\n\t\t}\n\t}\n\n\t@Nested\n\tclass DiscoveryConfigurationParameterTests {\n\n\t\t@Test\n\t\tvoid withoutConfigurationParametersSet_NoConfigurationParametersAreStoredInDiscoveryRequest() {\n\t\t\tvar discoveryRequest = discoveryRequest().build();\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key\")).isNotPresent();\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParameterAddedDirectly_isStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParameter(\"key\", \"value\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key\")).contains(\"value\");\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParameterAddedDirectlyTwice_overridesPreviousValueInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParameter(\"key\", \"value\")\n\t\t\t\t\t.configurationParameter(\"key\", \"value-new\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key\")).contains(\"value-new\");\n\t\t}\n\n\t\t@Test\n\t\tvoid multipleConfigurationParametersAddedDirectly_areStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParameter(\"key1\", \"value1\")\n\t\t\t\t\t.configurationParameter(\"key2\", \"value2\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key1\")).contains(\"value1\");\n\t\t\tassertThat(configParams.get(\"key2\")).contains(\"value2\");\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParameterAddedByMap_isStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParameters(Map.of(\"key\", \"value\"))\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key\")).contains(\"value\");\n\t\t}\n\n\t\t@Test\n\t\tvoid multipleConfigurationParametersAddedByMap_areStoredInDiscoveryRequest() {\n\t\t\tMap<String, String> configurationParams = new HashMap<>();\n\t\t\tconfigurationParams.put(\"key1\", \"value1\");\n\t\t\tconfigurationParams.put(\"key2\", \"value2\");\n\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParameters(configurationParams)\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"key1\")).contains(\"value1\");\n\t\t\tassertThat(configParams.get(\"key2\")).contains(\"value2\");\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParametersResource_areStoredInDiscoveryRequest() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParametersResources(\"config-test.properties\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"com.example.prop.first\")).contains(\"first value\");\n\t\t\tassertThat(configParams.get(\"com.example.prop.second\")).contains(\"second value\");\n\t\t\tassertThat(configParams.get(\"com.example.prop.third\")).contains(\"third value\");\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParametersResource_explicitConfigParametersOverrideResource() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParametersResources(\"config-test.properties\")\n\t\t\t\t\t.configurationParameter(\"com.example.prop.first\", \"first value override\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"com.example.prop.first\")).contains(\"first value override\");\n\t\t\tassertThat(configParams.get(\"com.example.prop.second\")).contains(\"second value\");\n\t\t}\n\n\t\t@Test\n\t\tvoid configurationParametersResource_lastDeclaredResourceFileWins() {\n\t\t\t// @formatter:off\n\t\t\tvar discoveryRequest = discoveryRequest()\n\t\t\t\t\t.configurationParametersResources(\"config-test.properties\")\n\t\t\t\t\t.configurationParametersResources(\"config-test-override.properties\")\n\t\t\t\t\t.build();\n\t\t\t// @formatter:on\n\n\t\t\tvar configParams = discoveryRequest.getConfigurationParameters();\n\t\t\tassertThat(configParams.get(\"com.example.prop.first\")).contains(\"first value from override file\");\n\t\t\tassertThat(configParams.get(\"com.example.prop.second\")).contains(\"second value\");\n\t\t}\n\t}\n\n\t@Nested\n\tclass DiscoveryListenerTests {\n\n\t\t@Test\n\t\tvoid usesAbortOnFailureByDefault() {\n\t\t\tvar request = discoveryRequest().build();\n\n\t\t\tassertThat(request.getDiscoveryListener()).isEqualTo(abortOnFailure());\n\t\t}\n\n\t\t@Test\n\t\tvoid onlyAddsAbortOnFailureOnce() {\n\t\t\tvar request = discoveryRequest() //\n\t\t\t\t\t.listeners(abortOnFailure()) //\n\t\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"abortOnFailure\") //\n\t\t\t\t\t.build();\n\n\t\t\tassertThat(request.getDiscoveryListener()).isEqualTo(abortOnFailure());\n\t\t}\n\n\t\t@Test\n\t\tvoid onlyAddsLoggingOnce() {\n\t\t\tvar request = discoveryRequest() //\n\t\t\t\t\t.listeners(logging()) //\n\t\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t\t.build();\n\n\t\t\tassertThat(request.getDiscoveryListener()).isEqualTo(logging());\n\t\t}\n\n\t\t@Test\n\t\tvoid createsCompositeForMultipleListeners() {\n\t\t\tvar request = discoveryRequest() //\n\t\t\t\t\t.listeners(logging(), abortOnFailure()) //\n\t\t\t\t\t.build();\n\n\t\t\tassertThat(request.getDiscoveryListener().getClass().getSimpleName()).startsWith(\"Composite\");\n\t\t}\n\n\t}\n\n\tprivate static class SampleTestClass {\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tprivate static String fullyQualifiedMethodName() {\n\t\treturn LauncherDiscoveryRequestBuilderTests.class.getName() + \"#\" + fullyQualifiedMethod().getName();\n\t}\n\n\tprivate static Method fullyQualifiedMethod() {\n\t\ttry {\n\t\t\treturn LauncherDiscoveryRequestBuilderTests.class.getDeclaredMethod(\"myTest\");\n\t\t}\n\t\tcatch (Exception ex) {\n\t\t\tthrow new IllegalStateException(ex);\n\t\t}\n\t}\n\n\tvoid myTest() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherDiscoveryResultTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;\n\n/**\n * Unit tests for {@link LauncherDiscoveryResult}.\n */\nclass LauncherDiscoveryResultTests {\n\n\t/**\n\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/4862\">GitHub - Issue #4862</a>\n\t */\n\t@Test\n\tvoid withRetainedEnginesMaintainsOriginalTestEngineRegistrationOrder() {\n\t\tvar engine1 = new DemoEngine(\"Engine 1\");\n\t\tvar engine2 = new DemoEngine(\"Engine 2\");\n\t\tvar engine3 = new DemoEngine(\"Engine 3\");\n\t\tvar engine4 = new DemoEngine(\"Engine 4\");\n\n\t\t@SuppressWarnings(\"serial\")\n\t\tMap<TestEngine, EngineResultInfo> engineResults = new LinkedHashMap<>() {\n\t\t\t{\n\t\t\t\tput(engine1, new DemoEngineResultInfo(true));\n\t\t\t\tput(engine2, new DemoEngineResultInfo(false));\n\t\t\t\tput(engine3, new DemoEngineResultInfo(false));\n\t\t\t\tput(engine4, new DemoEngineResultInfo(true));\n\t\t\t}\n\t\t};\n\t\tassertThat(engineResults.keySet()).containsExactly(engine1, engine2, engine3, engine4);\n\n\t\tLauncherDiscoveryResult discoveryResult = new LauncherDiscoveryResult(engineResults, mock(), mock());\n\t\tassertThat(discoveryResult.getTestEngines()).containsExactly(engine1, engine2, engine3, engine4);\n\n\t\tLauncherDiscoveryResult prunedDiscoveryResult = discoveryResult.withRetainedEngines(TestDescriptor::isTest);\n\t\tassertThat(prunedDiscoveryResult.getTestEngines()).containsExactly(engine1, engine4);\n\t}\n\n\tprivate record DemoEngine(String id) implements TestEngine {\n\n\t\t@Override\n\t\tpublic String getId() {\n\t\t\treturn this.id;\n\t\t}\n\n\t\t@Override\n\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\tthrow new UnsupportedOperationException(\"discover\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void execute(ExecutionRequest request) {\n\t\t\tthrow new UnsupportedOperationException(\"execute\");\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn getId();\n\t\t}\n\t}\n\n\tprivate static class DemoEngineResultInfo extends EngineResultInfo {\n\n\t\tDemoEngineResultInfo(boolean isTest) {\n\t\t\tsuper(createRootDescriptor(isTest), mock(), null);\n\t\t}\n\n\t\tprivate static TestDescriptor createRootDescriptor(boolean isTest) {\n\t\t\tTestDescriptor rootDescriptor = mock();\n\t\t\twhen(rootDescriptor.isTest()).thenReturn(isTest);\n\t\t\treturn rootDescriptor;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherFactoryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.TemporaryClasspathExecutor.withAdditionalClasspathRoot;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClasses;\nimport static org.junit.platform.launcher.LauncherConstants.DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.ENABLE_LAUNCHER_INTERCEPTORS;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.logging.LogRecord;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeAllCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.StoreScope;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.fakes.TestEngineSpy;\nimport org.junit.platform.launcher.InterceptedTestEngine;\nimport org.junit.platform.launcher.InterceptorInjectedLauncherSessionListener;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.junit.platform.launcher.TagFilter;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestLauncherDiscoveryListener;\nimport org.junit.platform.launcher.TestLauncherInterceptor1;\nimport org.junit.platform.launcher.TestLauncherInterceptor2;\nimport org.junit.platform.launcher.TestLauncherSessionListener;\nimport org.junit.platform.launcher.listeners.AnotherUnusedTestExecutionListener;\nimport org.junit.platform.launcher.listeners.NoopTestExecutionListener;\nimport org.junit.platform.launcher.listeners.UnusedTestExecutionListener;\n\n/**\n * @since 1.0\n */\n@NullMarked\nclass LauncherFactoryTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationFor(() -> LauncherFactory.create(null));\n\t}\n\n\t@Test\n\tvoid testExecutionListenerIsLoadedViaServiceApi() {\n\t\twithTestServices(() -> {\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.addTestEngines(new TestEngineSpy()) //\n\t\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t\t.build();\n\t\t\tvar launcher = LauncherFactory.create(config);\n\n\t\t\tNoopTestExecutionListener.called = false;\n\n\t\t\tlauncher.execute(request().forExecution().build());\n\n\t\t\tassertTrue(NoopTestExecutionListener.called);\n\t\t});\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = DEACTIVATE_LISTENERS_PATTERN_PROPERTY_NAME, value = \"org.junit.*.launcher.listeners.Unused*,org.junit.*.launcher.listeners.AnotherUnused*\")\n\tvoid testExecutionListenersExcludedViaConfigParametersIsNotLoadedViaServiceApi(\n\t\t\t@TrackLogRecords LogRecordListener listener) {\n\t\twithTestServices(() -> {\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.addTestEngines(new TestEngineSpy()) //\n\t\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t\t.build();\n\t\t\tvar launcher = LauncherFactory.create(config);\n\n\t\t\tUnusedTestExecutionListener.called = false;\n\t\t\tAnotherUnusedTestExecutionListener.called = false;\n\n\t\t\tlauncher.execute(request().forExecution().build());\n\n\t\t\tvar logMessage = listener.stream(ServiceLoaderRegistry.class) //\n\t\t\t\t\t.map(LogRecord::getMessage) //\n\t\t\t\t\t.filter(it -> it.startsWith(\"Loaded TestExecutionListener instances\")) //\n\t\t\t\t\t.findAny();\n\t\t\tassertThat(logMessage).isPresent();\n\t\t\tassertThat(logMessage.get()) //\n\t\t\t\t\t.contains(\"NoopTestExecutionListener@\") //\n\t\t\t\t\t.endsWith(\" (excluded classes: [\" + UnusedTestExecutionListener.class.getName() + \", \"\n\t\t\t\t\t\t\t+ AnotherUnusedTestExecutionListener.class.getName() + \"])\");\n\n\t\t\tassertFalse(UnusedTestExecutionListener.called);\n\t\t\tassertFalse(AnotherUnusedTestExecutionListener.called);\n\t\t});\n\t}\n\n\t@Test\n\tvoid create() {\n\t\tvar discoveryRequest = createLauncherDiscoveryRequestForBothStandardEngineExampleClasses();\n\n\t\tvar testPlan = LauncherFactory.create().discover(discoveryRequest);\n\t\tvar roots = testPlan.getRoots();\n\t\tassertThat(roots).hasSize(3);\n\n\t\t// @formatter:off\n\t\tvar ids = roots.stream()\n\t\t\t\t.map(TestIdentifier::getUniqueId)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\n\t\tassertThat(ids).containsOnly(\"[engine:junit-vintage]\", \"[engine:junit-jupiter]\",\n\t\t\t\"[engine:junit-platform-suite]\");\n\t}\n\n\t@Test\n\tvoid createWithConfig() {\n\t\tvar discoveryRequest = createLauncherDiscoveryRequestForBothStandardEngineExampleClasses();\n\n\t\tvar config = LauncherConfig.builder()//\n\t\t\t\t.enableTestEngineAutoRegistration(false)//\n\t\t\t\t.addTestEngines(new JupiterTestEngine())//\n\t\t\t\t.build();\n\n\t\tvar testPlan = LauncherFactory.create(config).discover(discoveryRequest);\n\t\tvar roots = testPlan.getRoots();\n\t\tassertThat(roots).hasSize(1);\n\n\t\t// @formatter:off\n\t\tvar ids = roots.stream()\n\t\t\t\t.map(TestIdentifier::getUniqueId)\n\t\t\t\t.toList();\n\t\t// @formatter:on\n\n\t\tassertThat(ids).containsOnly(\"[engine:junit-jupiter]\");\n\t}\n\n\t@Test\n\tvoid createWithPostDiscoveryFilters() {\n\t\tvar discoveryRequest = createLauncherDiscoveryRequestForBothStandardEngineExampleClasses();\n\n\t\tvar config = LauncherConfig.builder()//\n\t\t\t\t.addPostDiscoveryFilters(TagFilter.includeTags(\"test-post-discovery\")).build();\n\n\t\tvar testPlan = LauncherFactory.create(config).discover(discoveryRequest);\n\t\tfinal var vintage = testPlan.getChildren(UniqueId.parse(\"[engine:junit-vintage]\"));\n\t\tassertThat(vintage).isEmpty();\n\n\t\tfinal var jupiter = testPlan.getChildren(UniqueId.parse(\"[engine:junit-jupiter]\"));\n\t\tassertThat(jupiter).hasSize(1);\n\t}\n\n\t@Test\n\tvoid applyPostDiscoveryFiltersViaServiceApi() {\n\t\twithTestServices(() -> {\n\t\t\tvar discoveryRequest = createLauncherDiscoveryRequestForBothStandardEngineExampleClasses();\n\n\t\t\tvar config = LauncherConfig.builder()//\n\t\t\t\t\t.build();\n\n\t\t\tvar testPlan = LauncherFactory.create(config).discover(discoveryRequest);\n\t\t\tfinal var vintage = testPlan.getChildren(UniqueId.parse(\"[engine:junit-vintage]\"));\n\t\t\tassertThat(vintage).isEmpty();\n\n\t\t\tfinal var jupiter = testPlan.getChildren(UniqueId.parse(\"[engine:junit-jupiter]\"));\n\t\t\tassertThat(jupiter).hasSize(1);\n\t\t});\n\t}\n\n\t@Test\n\tvoid notApplyIfDisabledPostDiscoveryFiltersViaServiceApi() {\n\t\twithTestServices(() -> {\n\t\t\tvar discoveryRequest = createLauncherDiscoveryRequestForBothStandardEngineExampleClasses();\n\n\t\t\tvar config = LauncherConfig.builder()//\n\t\t\t\t\t.enablePostDiscoveryFilterAutoRegistration(false).build();\n\n\t\t\tvar testPlan = LauncherFactory.create(config).discover(discoveryRequest);\n\t\t\tfinal var vintage = testPlan.getChildren(UniqueId.parse(\"[engine:junit-vintage]\"));\n\t\t\tassertThat(vintage).hasSize(1);\n\n\t\t\tfinal var jupiter = testPlan.getChildren(UniqueId.parse(\"[engine:junit-jupiter]\"));\n\t\t\tassertThat(jupiter).hasSize(1);\n\t\t});\n\t}\n\n\t@Test\n\tvoid doesNotDiscoverLauncherDiscoverRequestListenerViaServiceApiWhenDisabled() {\n\t\twithTestServices(() -> {\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.enableLauncherDiscoveryListenerAutoRegistration(false) //\n\t\t\t\t\t.build();\n\t\t\tvar launcher = LauncherFactory.create(config);\n\t\t\tTestLauncherDiscoveryListener.called = false;\n\n\t\t\tlauncher.discover(request().build());\n\n\t\t\tassertFalse(TestLauncherDiscoveryListener.called);\n\t\t});\n\t}\n\n\t@Test\n\tvoid discoversLauncherDiscoverRequestListenerViaServiceApiByDefault() {\n\t\twithTestServices(() -> {\n\t\t\tvar launcher = LauncherFactory.create();\n\t\t\tTestLauncherDiscoveryListener.called = false;\n\n\t\t\tlauncher.discover(request().build());\n\n\t\t\tassertTrue(TestLauncherDiscoveryListener.called);\n\t\t});\n\t}\n\n\t@Test\n\tvoid doesNotDiscoverLauncherSessionListenerViaServiceApiWhenDisabled() {\n\t\twithTestServices(() -> {\n\t\t\ttry (var session = (DefaultLauncherSession) LauncherFactory.openSession(\n\t\t\t\tLauncherConfig.builder().enableLauncherSessionListenerAutoRegistration(false).build())) {\n\n\t\t\t\tassertThat(session.getListener()).isSameAs(LauncherSessionListener.NOOP);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Test\n\tvoid discoversLauncherSessionListenerViaServiceApiByDefault() {\n\t\twithTestServices(() -> {\n\t\t\ttry (var session = LauncherFactory.openSession()) {\n\t\t\t\tassertThat(TestLauncherSessionListener.session).isSameAs(session);\n\t\t\t}\n\t\t\tassertThat(TestLauncherSessionListener.session).isNull();\n\t\t});\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = ENABLE_LAUNCHER_INTERCEPTORS, value = \"true\")\n\tvoid createsLauncherInterceptorsBeforeDiscoveringTestEngines() {\n\t\twithTestServices(() -> {\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.enableTestEngineAutoRegistration(true) //\n\t\t\t\t\t.build();\n\t\t\tvar request = request().build();\n\n\t\t\tvar testPlan = LauncherFactory.create(config).discover(request);\n\n\t\t\tassertThat(testPlan.getRoots()) //\n\t\t\t\t\t.map(TestIdentifier::getUniqueIdObject) //\n\t\t\t\t\t.map(UniqueId::getLastSegment) //\n\t\t\t\t\t.map(UniqueId.Segment::getValue) //\n\t\t\t\t\t.describedAs(\n\t\t\t\t\t\t\"Intercepted test engine is added by class loader created by TestLauncherInterceptor1\").contains(\n\t\t\t\t\t\t\tInterceptedTestEngine.ID);\n\t\t});\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = ENABLE_LAUNCHER_INTERCEPTORS, value = \"true\")\n\tvoid appliesLauncherInterceptorsToTestDiscovery() {\n\t\tInterceptorInjectedLauncherSessionListener.CALLS = 0;\n\t\twithTestServices(() -> {\n\t\t\tvar engine = new TestEngineSpy() {\n\t\t\t\t@Override\n\t\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\t\tthrow new RuntimeException(\"from discovery\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t\t.addTestEngines(engine) //\n\t\t\t\t\t.build();\n\t\t\tvar launcher = LauncherFactory.create(config);\n\t\t\tvar request = request().build();\n\n\t\t\tvar exception = assertThrows(RuntimeException.class, () -> launcher.discover(request));\n\n\t\t\tassertThat(exception) //\n\t\t\t\t\t.hasRootCauseMessage(\"from discovery\") //\n\t\t\t\t\t.hasStackTraceContaining(TestLauncherInterceptor1.class.getName() + \".intercept(\") //\n\t\t\t\t\t.hasStackTraceContaining(TestLauncherInterceptor2.class.getName() + \".intercept(\");\n\t\t\tassertThat(InterceptorInjectedLauncherSessionListener.CALLS).isEqualTo(1);\n\t\t});\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = ENABLE_LAUNCHER_INTERCEPTORS, value = \"true\")\n\tvoid appliesLauncherInterceptorsToTestExecution() {\n\t\tInterceptorInjectedLauncherSessionListener.CALLS = 0;\n\t\twithTestServices(() -> {\n\t\t\tvar engine = new TestEngineSpy() {\n\t\t\t\t@Override\n\t\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\t\tthrow new RuntimeException(\"from execution\");\n\t\t\t\t}\n\t\t\t};\n\t\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t\t.addTestEngines(engine) //\n\t\t\t\t\t.build();\n\n\t\t\tAtomicReference<@Nullable TestExecutionResult> result = new AtomicReference<>();\n\t\t\tvar listener = new TestExecutionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\t\t\tif (testIdentifier.getParentId().isEmpty()) {\n\t\t\t\t\t\tresult.set(testExecutionResult);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar request = request() //\n\t\t\t\t\t.configurationParameter(LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, \"false\") //\n\t\t\t\t\t.forExecution() //\n\t\t\t\t\t.listeners(listener) //\n\t\t\t\t\t.build();\n\n\t\t\tvar launcher = LauncherFactory.create(config);\n\t\t\tlauncher.execute(request);\n\n\t\t\tassertThat(requireNonNull(result.get()).getThrowable().orElseThrow()) //\n\t\t\t\t\t.hasRootCauseMessage(\"from execution\") //\n\t\t\t\t\t.hasStackTraceContaining(TestLauncherInterceptor1.class.getName() + \".intercept(\") //\n\t\t\t\t\t.hasStackTraceContaining(TestLauncherInterceptor2.class.getName() + \".intercept(\");\n\t\t\tassertThat(InterceptorInjectedLauncherSessionListener.CALLS).isEqualTo(1);\n\t\t});\n\t}\n\n\t@Test\n\tvoid extensionCanReadValueFromSessionStoreAndReadByLauncherSessionListenerOnOpened() {\n\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t.addLauncherSessionListeners(new LauncherSessionListenerOpenedExample()) //\n\t\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession(config)) {\n\n\t\t\tAtomicReference<@Nullable Throwable> errorRef = new AtomicReference<>();\n\t\t\tvar listener = new TestExecutionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\t\t\ttestExecutionResult.getThrowable().ifPresent(errorRef::set);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar request = request() //\n\t\t\t\t\t.selectors(selectClass(SessionTrackingTestCase.class)) //\n\t\t\t\t\t.forExecution() //\n\t\t\t\t\t.listeners(listener) //\n\t\t\t\t\t.build();\n\n\t\t\tsession.getLauncher().execute(request);\n\n\t\t\tassertThat(errorRef.get()).isNull();\n\t\t}\n\t}\n\n\t@Test\n\tvoid extensionCanReadValueFromSessionStoreAndReadByLauncherSessionListenerOnClose() {\n\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t.addLauncherSessionListeners(new LauncherSessionListenerClosedExample()) //\n\t\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession(config)) {\n\n\t\t\tAtomicReference<@Nullable Throwable> errorRef = new AtomicReference<>();\n\t\t\tvar listener = new TestExecutionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\t\t\ttestExecutionResult.getThrowable().ifPresent(errorRef::set);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar request = request() //\n\t\t\t\t\t.selectors(selectClass(SessionStoringTestCase.class)) //\n\t\t\t\t\t.forExecution() //\n\t\t\t\t\t.listeners(listener) //\n\t\t\t\t\t.build();\n\n\t\t\tsession.getLauncher().execute(request);\n\n\t\t\tassertThat(errorRef.get()).isNull();\n\t\t}\n\t}\n\n\t@Test\n\tvoid sessionResourceClosedOnSessionClose() {\n\t\tCloseTrackingResource.closed = false;\n\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t.addLauncherSessionListeners(new AutoCloseCheckListener()) //\n\t\t\t\t.build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession(config)) {\n\t\t\tvar request = request() //\n\t\t\t\t\t.selectors(selectClass(SessionResourceAutoCloseTestCase.class)) //\n\t\t\t\t\t.forExecution() //\n\t\t\t\t\t.build();\n\n\t\t\tsession.getLauncher().execute(request);\n\t\t\tassertThat(CloseTrackingResource.closed).isFalse();\n\t\t}\n\n\t\tassertThat(CloseTrackingResource.closed).isTrue();\n\t}\n\n\t@Test\n\tvoid requestResourceClosedOnExecutionClose() {\n\t\tCloseTrackingResource.closed = false;\n\t\tvar config = LauncherConfig.builder().build();\n\n\t\ttry (LauncherSession session = LauncherFactory.openSession(config)) {\n\t\t\tvar request = request() //\n\t\t\t\t\t.selectors(selectClass(RequestResourceAutoCloseTestCase.class)) //\n\t\t\t\t\t.forExecution() //\n\t\t\t\t\t.build();\n\n\t\t\tsession.getLauncher().execute(request);\n\n\t\t\tassertThat(CloseTrackingResource.closed).isTrue();\n\t\t}\n\t}\n\n\tprivate static void withTestServices(Runnable runnable) {\n\t\twithAdditionalClasspathRoot(\"testservices/\", runnable);\n\t}\n\n\tprivate LauncherDiscoveryRequest createLauncherDiscoveryRequestForBothStandardEngineExampleClasses() {\n\t\t// @formatter:off\n\t\treturn request()\n\t\t\t\t.selectors(selectClasses(JUnit4Example.class, JUnit5Example.class))\n\t\t\t\t.enableImplicitConfigurationParameters(false)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class JUnit4Example {\n\n\t\t@org.junit.Test\n\t\tpublic void testJ4() {\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tstatic class JUnit5Example {\n\n\t\t@Tag(\"test-post-discovery\")\n\t\t@Test\n\t\tvoid testJ5() {\n\t\t}\n\t}\n\n\t@ExtendWith(SessionTrackingExtension.class)\n\tstatic class SessionTrackingTestCase {\n\n\t\t@Test\n\t\tvoid dummyTest() {\n\t\t\t// Just a placeholder to trigger the extension\n\t\t}\n\t}\n\n\t@ExtendWith(SessionStoringExtension.class)\n\tstatic class SessionStoringTestCase {\n\n\t\t@Test\n\t\tvoid dummyTest() {\n\t\t\t// Just a placeholder to trigger the extension\n\t\t}\n\t}\n\n\tstatic class LauncherSessionListenerOpenedExample implements LauncherSessionListener {\n\t\t@Override\n\t\tpublic void launcherSessionOpened(LauncherSession session) {\n\t\t\tsession.getStore().put(Namespace.GLOBAL, \"testKey\", \"testValue\");\n\t\t}\n\t}\n\n\tstatic class LauncherSessionListenerClosedExample implements LauncherSessionListener {\n\t\t@Override\n\t\tpublic void launcherSessionClosed(LauncherSession session) {\n\t\t\tObject storedValue = session.getStore().get(Namespace.GLOBAL, \"testKey\");\n\t\t\tassertThat(storedValue).isEqualTo(\"testValue\");\n\t\t}\n\t}\n\n\tstatic class SessionTrackingExtension implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tvar value = context.getStore(ExtensionContext.Namespace.GLOBAL).get(\"testKey\");\n\t\t\tif (!\"testValue\".equals(value)) {\n\t\t\t\tthrow new IllegalStateException(\"Expected 'testValue' but got: \" + value);\n\t\t\t}\n\n\t\t\tvalue = context.getStore(StoreScope.LAUNCHER_SESSION, ExtensionContext.Namespace.GLOBAL).get(\"testKey\");\n\t\t\tif (!\"testValue\".equals(value)) {\n\t\t\t\tthrow new IllegalStateException(\"Expected 'testValue' but got: \" + value);\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic class SessionStoringExtension implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tcontext.getStore(StoreScope.LAUNCHER_SESSION, ExtensionContext.Namespace.GLOBAL).put(\"testKey\",\n\t\t\t\t\"testValue\");\n\t\t}\n\t}\n\n\tprivate static class CloseTrackingResource implements AutoCloseable {\n\t\tprivate static boolean closed = false;\n\n\t\t@Override\n\t\tpublic void close() {\n\t\t\tclosed = true;\n\t\t}\n\n\t\tpublic boolean isClosed() {\n\t\t\treturn closed;\n\t\t}\n\t}\n\n\tprivate static class SessionResourceStoreUsingExtension implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tCloseTrackingResource sessionResource = new CloseTrackingResource();\n\t\t\tcontext.getStore(StoreScope.LAUNCHER_SESSION, ExtensionContext.Namespace.GLOBAL).put(\"sessionResource\",\n\t\t\t\tsessionResource);\n\t\t}\n\t}\n\n\tprivate static class RequestResourceStoreUsingExtension implements BeforeAllCallback {\n\t\t@Override\n\t\tpublic void beforeAll(ExtensionContext context) {\n\t\t\tCloseTrackingResource requestResource = new CloseTrackingResource();\n\t\t\tcontext.getStore(StoreScope.EXECUTION_REQUEST, ExtensionContext.Namespace.GLOBAL).put(\"requestResource\",\n\t\t\t\trequestResource);\n\t\t}\n\t}\n\n\t@ExtendWith(SessionResourceStoreUsingExtension.class)\n\tstatic class SessionResourceAutoCloseTestCase {\n\n\t\t@Test\n\t\tvoid dummyTest() {\n\t\t\t// Just a placeholder to trigger the extension\n\t\t}\n\t}\n\n\t@ExtendWith(RequestResourceStoreUsingExtension.class)\n\tstatic class RequestResourceAutoCloseTestCase {\n\n\t\t@Test\n\t\tvoid dummyTest() {\n\t\t\t// Just a placeholder to trigger the extension\n\t\t}\n\t}\n\n\tprivate static class AutoCloseCheckListener implements LauncherSessionListener {\n\t\t@Override\n\t\tpublic void launcherSessionClosed(LauncherSession session) {\n\t\t\tCloseTrackingResource sessionResource = session //\n\t\t\t\t\t.getStore() //\n\t\t\t\t\t.get(Namespace.GLOBAL, \"sessionResource\", CloseTrackingResource.class);\n\n\t\t\tassertThat(sessionResource).isNotNull();\n\t\t\tassertThat(sessionResource.isClosed()).isFalse();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherPreconditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.jupiter.api.Assertions.assertInstanceOf;\nimport static org.junit.jupiter.params.provider.Arguments.argumentSet;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationContainsNoNullElementsFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.engine.support.store.NamespacedHierarchicalStore.CloseAction.closeAutoCloseables;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherInterceptor;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n@ParameterizedClass\n@MethodSource(\"launchers\")\n@SuppressWarnings({ \"NullAway\", \"DataFlowIssue\" })\nclass LauncherPreconditionTests {\n\n\tprivate final Launcher launcher;\n\n\tLauncherPreconditionTests(Launcher launcher) {\n\t\tthis.launcher = launcher;\n\t}\n\n\t@Test\n\tvoid discoverRejectsNullDiscoveryRequest() {\n\t\tassertPreconditionViolationNotNullFor(\"discoveryRequest\", () -> launcher.discover(null));\n\t}\n\n\t@Test\n\tvoid executeRejectsNullDiscoveryRequest() {\n\t\tassertPreconditionViolationNotNullFor(\"discoveryRequest\",\n\t\t\t() -> launcher.execute((LauncherDiscoveryRequest) null));\n\t}\n\n\t@Test\n\tvoid executeRejectsNullTestPlan() {\n\t\tassertPreconditionViolationNotNullFor(\"testPlan\", () -> launcher.execute((TestPlan) null));\n\t}\n\n\t@Test\n\tvoid executeRejectsNullExecutionRequest() {\n\t\tassertPreconditionViolationNotNullFor(\"executionRequest\", () -> launcher.execute(null));\n\t}\n\n\t@Test\n\tvoid rejectNullLauncherDiscoveryListenersArray() {\n\t\tassertPreconditionViolationNotNullFor(\"listeners\",\n\t\t\t() -> launcher.registerLauncherDiscoveryListeners((LauncherDiscoveryListener[]) null));\n\t}\n\n\t@Test\n\tvoid rejectNullTestExecutionListenersArray() {\n\t\tassertPreconditionViolationNotNullFor(\"listeners\",\n\t\t\t() -> launcher.registerTestExecutionListeners((TestExecutionListener[]) null));\n\t}\n\n\t@Test\n\tvoid rejectNullListenersArrayWhenExecutingDiscoveryRequest() {\n\t\tvar request = mock(LauncherDiscoveryRequest.class);\n\t\tassertPreconditionViolationNotNullFor(\"listeners\",\n\t\t\t() -> launcher.execute(request, (TestExecutionListener[]) null));\n\t}\n\n\t@Test\n\tvoid rejectNullListenersArrayWhenExecutingTestPlan() {\n\t\tvar testPlan = mock(TestPlan.class);\n\t\tassertPreconditionViolationNotNullFor(\"listeners\",\n\t\t\t() -> launcher.execute(testPlan, (TestExecutionListener[]) null));\n\t}\n\n\t@Test\n\tvoid rejectNullElementsInLauncherDiscoveryListeners() {\n\t\tvar listener = mock(LauncherDiscoveryListener.class);\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"listener array\",\n\t\t\t() -> launcher.registerLauncherDiscoveryListeners(listener, null));\n\t}\n\n\t@Test\n\tvoid rejectNullElementsInTestExecutionListeners() {\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"listener array\",\n\t\t\t() -> launcher.registerTestExecutionListeners(listener, null));\n\t}\n\n\t@Test\n\tvoid rejectNullElementsInListenersWhenExecutingDiscoveryRequest() {\n\t\tvar request = mock(LauncherDiscoveryRequest.class);\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"listener array\",\n\t\t\t() -> launcher.execute(request, listener, null));\n\t}\n\n\t@Test\n\tvoid rejectNullElementsInListenersWhenExecutingTestPlan() {\n\t\tvar testPlan = mock(TestPlan.class);\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tassertPreconditionViolationContainsNoNullElementsFor(\"listener array\",\n\t\t\t() -> launcher.execute(testPlan, listener, null));\n\t}\n\n\tstatic Stream<Arguments> launchers() {\n\t\treturn Stream.of( //\n\t\t\targumentSet(\"SessionPerRequestLauncher\", createSessionPerRequestLauncher()),\n\t\t\targumentSet(\"DefaultLauncher\", createDefaultLauncher()),\n\t\t\targumentSet(\"DelegatingLauncher\", createDelegatingLauncher()),\n\t\t\targumentSet(\"InterceptingLauncher\", createInterceptingLauncher()) //\n\t\t);\n\t}\n\n\tprivate static SessionPerRequestLauncher createSessionPerRequestLauncher() {\n\t\tLauncherConfig config = LauncherConfig.builder() //\n\t\t\t\t.enableTestEngineAutoRegistration(false) //\n\t\t\t\t.enableLauncherDiscoveryListenerAutoRegistration(false) //\n\t\t\t\t.enableTestExecutionListenerAutoRegistration(false) //\n\t\t\t\t.enablePostDiscoveryFilterAutoRegistration(false) //\n\t\t\t\t.enableLauncherSessionListenerAutoRegistration(false) //\n\t\t\t\t.addTestEngines(new TestEngineStub()) //\n\t\t\t\t.build(); //\n\t\tLauncher launcher = LauncherFactory.create(config);\n\t\treturn assertInstanceOf(SessionPerRequestLauncher.class, launcher);\n\t}\n\n\tprivate static DefaultLauncher createDefaultLauncher() {\n\t\treturn new DefaultLauncher( //\n\t\t\tList.of(new TestEngineStub()), //\n\t\t\tList.of(), //\n\t\t\tnew NamespacedHierarchicalStore<>(null, closeAutoCloseables()) //\n\t\t);\n\t}\n\n\tprivate static DelegatingLauncher createDelegatingLauncher() {\n\t\treturn new DelegatingLauncher(mock(Launcher.class));\n\t}\n\n\tprivate static InterceptingLauncher createInterceptingLauncher() {\n\t\treturn new InterceptingLauncher(mock(Launcher.class), mock(LauncherInterceptor.class));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/LauncherSessionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.discoveryRequest;\nimport static org.junit.platform.launcher.core.LauncherExecutionRequestBuilder.executionRequest;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncherConfigBuilderWithDisabledServiceLoading;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verifyNoMoreInteractions;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.mockito.ArgumentCaptor;\n\nclass LauncherSessionTests {\n\n\tLauncherSessionListener firstSessionListener = mock(LauncherSessionListener.class, \"firstSessionListener\");\n\tLauncherSessionListener secondSessionListener = mock(LauncherSessionListener.class, \"secondSessionListener\");\n\tLauncherConfig launcherConfig = createLauncherConfigBuilderWithDisabledServiceLoading() //\n\t\t\t.addLauncherSessionListeners(firstSessionListener, secondSessionListener) //\n\t\t\t.addTestEngines(new TestEngineStub()) //\n\t\t\t.build();\n\tLauncherDiscoveryRequest discoveryRequest = discoveryRequest().build();\n\n\t@Test\n\tvoid callsRegisteredListenersWhenLauncherIsUsedDirectly() {\n\t\tvar launcher = LauncherFactory.create(launcherConfig);\n\n\t\tvar testPlan = launcher.discover(discoveryRequest);\n\n\t\tvar inOrder = inOrder(firstSessionListener, secondSessionListener);\n\t\tvar launcherSession = ArgumentCaptor.forClass(LauncherSession.class);\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\n\t\tlauncher.execute(testPlan);\n\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\n\t\tlauncher.execute(discoveryRequest);\n\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\n\t\ttestPlan = launcher.discover(discoveryRequest);\n\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\n\t\tlauncher.execute(executionRequest(testPlan).build());\n\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\n\t\tlauncher.execute(executionRequest(discoveryRequest).build());\n\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(launcherSession.capture());\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(launcherSession.getValue());\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(launcherSession.getValue());\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid callsRegisteredListenersWhenLauncherIsUsedViaSession() {\n\t\tvar session = LauncherFactory.openSession(launcherConfig);\n\t\tvar launcher = session.getLauncher();\n\n\t\tvar inOrder = inOrder(firstSessionListener, secondSessionListener);\n\t\tinOrder.verify(firstSessionListener).launcherSessionOpened(session);\n\t\tinOrder.verify(secondSessionListener).launcherSessionOpened(session);\n\t\tverifyNoMoreInteractions(firstSessionListener, secondSessionListener);\n\n\t\tvar testPlan = launcher.discover(discoveryRequest);\n\t\tlauncher.execute(testPlan);\n\t\tlauncher.execute(discoveryRequest);\n\n\t\ttestPlan = launcher.discover(discoveryRequest);\n\t\tlauncher.execute(executionRequest(testPlan).build());\n\t\tlauncher.execute(executionRequest(discoveryRequest).build());\n\n\t\tverifyNoMoreInteractions(firstSessionListener, secondSessionListener);\n\n\t\tsession.close();\n\n\t\tinOrder.verify(secondSessionListener).launcherSessionClosed(session);\n\t\tinOrder.verify(firstSessionListener).launcherSessionClosed(session);\n\t\tverifyNoMoreInteractions(firstSessionListener, secondSessionListener);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"resource\")\n\tvoid closedSessionCannotBeUsed() {\n\t\tvar session = LauncherFactory.openSession(launcherConfig);\n\t\tvar launcher = session.getLauncher();\n\t\tvar testPlan = launcher.discover(discoveryRequest);\n\n\t\tsession.close();\n\n\t\tassertPreconditionViolationFor(() -> launcher.discover(discoveryRequest));\n\t\tassertPreconditionViolationFor(() -> launcher.execute(testPlan));\n\t\tassertPreconditionViolationFor(() -> launcher.execute(discoveryRequest));\n\t\tassertPreconditionViolationFor(() -> launcher.execute(executionRequest(testPlan).build()));\n\t\tassertPreconditionViolationFor(() -> launcher.execute(executionRequest(discoveryRequest).build()));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/ListenerRegistryTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\nclass ListenerRegistryTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid registerWithNullArray() {\n\t\tvar registry = ListenerRegistry.create(List::getFirst);\n\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"listeners array\", () -> registry.addAll((Object[]) null));\n\t}\n\n\t@Test\n\tvoid registerWithEmptyArray() {\n\t\tvar registry = ListenerRegistry.create(List::getFirst);\n\n\t\tassertPreconditionViolationNotNullOrEmptyFor(\"listeners array\", registry::addAll);\n\t}\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid registerWithArrayContainingNullElements() {\n\t\tvar registry = ListenerRegistry.create(List::getFirst);\n\n\t\tassertPreconditionViolationNotNullFor(\"individual listeners\", () -> registry.addAll(new Object[] { null }));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/StoreSharingTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.fakes.TestEngineSpy;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.Launcher;\nimport org.junit.platform.launcher.LauncherExecutionRequest;\n\n/**\n * @since 5.13\n */\nclass StoreSharingTests {\n\n\t@Test\n\tvoid twoDummyEnginesUseRequestLevelStore() {\n\t\tTestEngineSpy engineWriter = new TestEngineSpy(\"Writer\") {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\trequest.getStore().put(Namespace.GLOBAL, \"sharedKey\", \"Hello from Writer\");\n\t\t\t\tsuper.execute(request);\n\t\t\t}\n\t\t};\n\n\t\tTestEngineStub engineReader = new TestEngineStub(\"Reader\") {\n\t\t\t@Override\n\t\t\tpublic void execute(ExecutionRequest request) {\n\t\t\t\tObject value = request.getStore().get(Namespace.GLOBAL, \"sharedKey\");\n\t\t\t\tassertEquals(\"Hello from Writer\", value);\n\t\t\t\tsuper.execute(request);\n\t\t\t}\n\t\t};\n\n\t\tExecutionRequest request = mock(ExecutionRequest.class);\n\t\twhen(request.getStore()).thenReturn(NamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStore());\n\n\t\tLauncher launcher = LauncherFactory.create( //\n\t\t\tLauncherConfig.builder() //\n\t\t\t\t\t.addTestEngines(engineWriter, engineReader) //\n\t\t\t\t\t.build());\n\n\t\tLauncherExecutionRequest discoveryRequest = LauncherDiscoveryRequestBuilder //\n\t\t\t\t.request() //\n\t\t\t\t.forExecution() //\n\t\t\t\t.build();\n\n\t\tlauncher.execute(discoveryRequest);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptingTestExecutionListenerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.entry;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.doAnswer;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.never;\nimport static org.mockito.Mockito.verify;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.util.function.Supplier;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.BeforeTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.mockito.ArgumentCaptor;\n\n/**\n * @since 1.3\n */\nclass StreamInterceptingTestExecutionListenerIntegrationTests {\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"systemStreams\")\n\t@ExtendWith(HiddenSystemOutAndErr.class)\n\tvoid interceptsStream(String configParam, Supplier<PrintStream> printStreamSupplier, String reportKey) {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"engine\");\n\t\tTestDescriptor test = engine.addTest(\"test\", () -> printStreamSupplier.get().print(\"4567890\"));\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tdoAnswer(invocation -> {\n\t\t\tTestIdentifier testIdentifier = invocation.getArgument(0);\n\t\t\tif (testIdentifier.getUniqueIdObject().equals(test.getUniqueId())) {\n\t\t\t\tprintStreamSupplier.get().print(\"123\");\n\t\t\t}\n\t\t\treturn null;\n\t\t}).when(listener).executionStarted(any());\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar executionRequest = request()//\n\t\t\t\t.selectors(selectUniqueId(test.getUniqueId()))//\n\t\t\t\t.configurationParameter(configParam, String.valueOf(true))//\n\t\t\t\t.configurationParameter(LauncherConstants.CAPTURE_MAX_BUFFER_PROPERTY_NAME, String.valueOf(5))//\n\t\t\t\t.forExecution()//\n\t\t\t\t.listeners(listener)//\n\t\t\t\t.build();\n\t\tlauncher.execute(executionRequest);\n\n\t\tvar testPlanArgumentCaptor = ArgumentCaptor.forClass(TestPlan.class);\n\t\tvar inOrder = inOrder(listener);\n\t\tinOrder.verify(listener).testPlanExecutionStarted(testPlanArgumentCaptor.capture());\n\t\tvar testPlan = testPlanArgumentCaptor.getValue();\n\t\tvar testIdentifier = testPlan.getTestIdentifier(test.getUniqueId());\n\n\t\tvar reportEntryArgumentCaptor = ArgumentCaptor.forClass(ReportEntry.class);\n\t\tinOrder.verify(listener).reportingEntryPublished(same(testIdentifier), reportEntryArgumentCaptor.capture());\n\t\tinOrder.verify(listener).executionFinished(testIdentifier, successful());\n\t\tvar reportEntry = reportEntryArgumentCaptor.getValue();\n\n\t\tassertThat(reportEntry.getKeyValuePairs()).containsExactly(entry(reportKey, \"12345\"));\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource(\"systemStreams\")\n\t@ExtendWith(HiddenSystemOutAndErr.class)\n\tvoid doesNotInterceptStreamWhenAlreadyBeingIntercepted(String configParam,\n\t\t\tSupplier<PrintStream> printStreamSupplier) {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"engine\");\n\t\tTestDescriptor test = engine.addTest(\"test\", () -> printStreamSupplier.get().print(\"1234567890\"));\n\n\t\tassertThat(StreamInterceptor.registerStdout(1)).isPresent();\n\t\tassertThat(StreamInterceptor.registerStderr(1)).isPresent();\n\n\t\tvar launcher = createLauncher(engine);\n\t\tvar listener = mock(TestExecutionListener.class);\n\t\tvar executionRequest = request()//\n\t\t\t\t.selectors(selectUniqueId(test.getUniqueId()))//\n\t\t\t\t.configurationParameter(configParam, String.valueOf(true))//\n\t\t\t\t.forExecution()//\n\t\t\t\t.listeners(listener)//\n\t\t\t\t.build();\n\t\tlauncher.execute(executionRequest);\n\n\t\tverify(listener, never()).reportingEntryPublished(any(), any());\n\t}\n\n\t@SuppressWarnings(\"unused\") // used via @MethodSource(\"systemStreams\")\n\tprivate static Stream<Arguments> systemStreams() {\n\t\treturn Stream.of(//\n\t\t\tstreamType(CAPTURE_STDOUT_PROPERTY_NAME, () -> System.out, STDOUT_REPORT_ENTRY_KEY), //\n\t\t\tstreamType(CAPTURE_STDERR_PROPERTY_NAME, () -> System.err, STDERR_REPORT_ENTRY_KEY));\n\t}\n\n\tprivate static Arguments streamType(String configParam, Supplier<PrintStream> printStreamSupplier,\n\t\t\tString reportKey) {\n\t\treturn arguments(configParam, printStreamSupplier, reportKey);\n\t}\n\n\tstatic class HiddenSystemOutAndErr implements BeforeTestExecutionCallback, AfterTestExecutionCallback {\n\n\t\tprivate static final Namespace NAMESPACE = Namespace.create(HiddenSystemOutAndErr.class);\n\n\t\t@Override\n\t\tpublic void beforeTestExecution(ExtensionContext context) {\n\t\t\tvar store = context.getStore(NAMESPACE);\n\t\t\tstore.put(\"out\", System.out);\n\t\t\tstore.put(\"err\", System.err);\n\t\t\tSystem.setOut(new PrintStream(new ByteArrayOutputStream()));\n\t\t\tSystem.setErr(new PrintStream(new ByteArrayOutputStream()));\n\t\t}\n\n\t\t@Override\n\t\tpublic void afterTestExecution(ExtensionContext context) {\n\t\t\tvar store = context.getStore(NAMESPACE);\n\t\t\tSystem.setOut(store.get(\"out\", PrintStream.class));\n\t\t\tSystem.setErr(store.get(\"err\", PrintStream.class));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/core/StreamInterceptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.core;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotSame;\nimport static org.junit.jupiter.api.Assertions.assertSame;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.PrintStream;\nimport java.util.stream.IntStream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.AutoClose;\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.3\n */\nclass StreamInterceptorTests {\n\n\tfinal ByteArrayOutputStream originalOut = new ByteArrayOutputStream();\n\tPrintStream targetStream = new PrintStream(originalOut);\n\n\t@AutoClose\n\t@Nullable\n\tStreamInterceptor streamInterceptor;\n\n\t@Test\n\tvoid interceptsWriteOperationsToStreamPerThread() {\n\t\tstreamInterceptor = StreamInterceptor.register(targetStream, newStream -> this.targetStream = newStream,\n\t\t\t3).orElseThrow(RuntimeException::new);\n\t\t// @formatter:off\n\t\tIntStream.range(0, 1000)\n\t\t\t\t.parallel()\n\t\t\t\t.mapToObj(String::valueOf)\n\t\t\t\t.peek(i -> streamInterceptor.capture())\n\t\t\t\t.peek(i -> targetStream.println(i))\n\t\t\t\t.forEach(i -> assertEquals(i, streamInterceptor.consume().strip()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid unregisterRestoresOriginalStream() {\n\t\tvar originalStream = targetStream;\n\n\t\tstreamInterceptor = StreamInterceptor.register(targetStream, newStream -> this.targetStream = newStream,\n\t\t\t3).orElseThrow(RuntimeException::new);\n\t\tassertSame(streamInterceptor, targetStream);\n\n\t\tstreamInterceptor.unregister();\n\t\tassertSame(originalStream, targetStream);\n\t}\n\n\t@Test\n\tvoid writeForwardsOperationsToOriginalStream() throws Exception {\n\t\tvar originalStream = targetStream;\n\n\t\tstreamInterceptor = StreamInterceptor.register(targetStream, newStream -> this.targetStream = newStream,\n\t\t\t2).orElseThrow(RuntimeException::new);\n\t\tassertNotSame(originalStream, targetStream);\n\n\t\ttargetStream.write('a');\n\t\ttargetStream.write(\"b\".getBytes());\n\t\ttargetStream.write(\"c\".getBytes(), 0, 1);\n\t\tassertEquals(\"abc\", originalOut.toString());\n\t}\n\n\t@Test\n\tvoid handlesNestedCaptures() {\n\t\tstreamInterceptor = StreamInterceptor.register(targetStream, newStream -> this.targetStream = newStream,\n\t\t\t100).orElseThrow(RuntimeException::new);\n\n\t\tString outermost, inner, innermost;\n\n\t\tstreamInterceptor.capture();\n\t\tstreamInterceptor.print(\"before outermost - \");\n\t\t{\n\t\t\tstreamInterceptor.capture();\n\t\t\tstreamInterceptor.print(\"before inner - \");\n\t\t\t{\n\t\t\t\tstreamInterceptor.capture();\n\t\t\t\tstreamInterceptor.print(\"innermost\");\n\t\t\t\tinnermost = streamInterceptor.consume();\n\t\t\t}\n\t\t\tstreamInterceptor.print(\"after inner\");\n\t\t\tinner = streamInterceptor.consume();\n\t\t}\n\t\tstreamInterceptor.print(\"after outermost\");\n\t\toutermost = streamInterceptor.consume();\n\n\t\tassertAll(//\n\t\t\t() -> assertEquals(\"before outermost - after outermost\", outermost), //\n\t\t\t() -> assertEquals(\"before inner - after inner\", inner), //\n\t\t\t() -> assertEquals(\"innermost\", innermost) //\n\t\t);\n\t}\n\n\t@Test\n\tvoid capturesOutputFromNonTestThreads() throws Exception {\n\t\tstreamInterceptor = StreamInterceptor.register(targetStream, newStream -> this.targetStream = newStream,\n\t\t\t100).orElseThrow(RuntimeException::new);\n\n\t\tstreamInterceptor.capture();\n\t\tvar thread = new Thread(() -> targetStream.println(\"from non-test thread\"));\n\t\tthread.start();\n\t\tthread.join();\n\n\t\tassertEquals(\"from non-test thread\", streamInterceptor.consume().strip());\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/jfr/FlightRecordingDiscoveryListenerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport static org.junit.platform.commons.util.ExceptionUtils.readStackTrace;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.moditect.jfrunit.ExpectedEvent.event;\nimport static org.moditect.jfrunit.JfrEventsAssert.assertThat;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.moditect.jfrunit.EnableEvent;\nimport org.moditect.jfrunit.JfrEventTest;\nimport org.moditect.jfrunit.JfrEvents;\n\n@JfrEventTest\n@DisabledOnOpenJ9\npublic class FlightRecordingDiscoveryListenerIntegrationTests {\n\n\tpublic JfrEvents jfrEvents = new JfrEvents();\n\n\t@Test\n\t@EnableEvent(\"org.junit.*\")\n\tvoid reportsEvents() {\n\t\tvar source = ClassSource.from(FlightRecordingDiscoveryListenerIntegrationTests.class);\n\t\tvar cause = new RuntimeException(\"boom\");\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, \"some message\") //\n\t\t\t\t.source(source) //\n\t\t\t\t.cause(cause) //\n\t\t\t\t.build();\n\n\t\tvar testEngine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tdiscoveryRequest.getDiscoveryListener().issueEncountered(uniqueId, issue);\n\t\t\t\treturn super.discover(discoveryRequest, uniqueId);\n\t\t\t}\n\t\t};\n\n\t\tEngineTestKit.discover(testEngine, request() //\n\t\t\t\t.selectors(selectClass(FlightRecordingDiscoveryListenerIntegrationTests.class)) //\n\t\t\t\t.listeners(new FlightRecordingDiscoveryListener()) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build());\n\n\t\tjfrEvents.awaitEvents();\n\n\t\tassertThat(jfrEvents) //\n\t\t\t\t.contains(event(\"org.junit.LauncherDiscovery\") //\n\t\t\t\t\t\t.with(\"selectors\", 1) //\n\t\t\t\t\t\t.with(\"filters\", 0)) //\n\t\t\t\t.contains(event(\"org.junit.EngineDiscovery\") //\n\t\t\t\t\t\t.with(\"uniqueId\", \"[engine:TestEngineStub]\")) //\n\t\t\t\t.contains(event(\"org.junit.DiscoveryIssue\") //\n\t\t\t\t\t\t.with(\"engineId\", \"[engine:TestEngineStub]\") //\n\t\t\t\t\t\t.with(\"severity\", \"WARNING\") //\n\t\t\t\t\t\t.with(\"message\", \"some message\") //\n\t\t\t\t\t\t.with(\"source\", source.toString()) //\n\t\t\t\t\t\t.with(\"cause\", readStackTrace(cause)));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/jfr/FlightRecordingExecutionListenerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.jfr;\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.hierarchicalOutputDirectoryCreator;\nimport static org.junit.platform.reporting.testutil.FileUtils.findPath;\nimport static org.moditect.jfrunit.ExpectedEvent.event;\nimport static org.moditect.jfrunit.JfrEventsAssert.assertThat;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly;\nimport org.moditect.jfrunit.EnableEvent;\nimport org.moditect.jfrunit.JfrEventTest;\nimport org.moditect.jfrunit.JfrEvents;\n\n@JfrEventTest\n@DisabledOnOpenJ9\npublic class FlightRecordingExecutionListenerIntegrationTests {\n\n\tpublic JfrEvents jfrEvents = new JfrEvents();\n\n\t@Test\n\t@EnableEvent(\"org.junit.*\")\n\tvoid reportsEvents(@TempDir Path tempDir) {\n\t\tvar launcher = LauncherFactoryForTestingPurposesOnly.createLauncher(new JupiterTestEngine());\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(TestCase.class)) //\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(tempDir)) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.build();\n\n\t\tlauncher.execute(request);\n\t\tjfrEvents.awaitEvents();\n\n\t\tvar testFile = findPath(tempDir, \"glob:**/test.txt\");\n\n\t\tassertThat(jfrEvents) //\n\t\t\t\t.contains(event(\"org.junit.TestPlanExecution\") //\n\t\t\t\t\t\t.with(\"engineNames\", \"JUnit Jupiter\")) //\n\t\t\t\t.contains(event(\"org.junit.TestExecution\") //\n\t\t\t\t\t\t.with(\"displayName\", \"JUnit Jupiter\") //\n\t\t\t\t\t\t.with(\"type\", \"CONTAINER\")) //\n\t\t\t\t.contains(event(\"org.junit.TestExecution\") //\n\t\t\t\t\t\t.with(\"displayName\", FlightRecordingExecutionListenerIntegrationTests.class.getSimpleName()\n\t\t\t\t\t\t\t\t+ \"$\" + TestCase.class.getSimpleName()) //\n\t\t\t\t\t\t.with(\"type\", \"CONTAINER\")) //\n\t\t\t\t.contains(event(\"org.junit.TestExecution\") //\n\t\t\t\t\t\t.with(\"displayName\", \"test(TestReporter)\") //\n\t\t\t\t\t\t.with(\"type\", \"TEST\") //\n\t\t\t\t\t\t.with(\"result\", \"SUCCESSFUL\")) //\n\t\t\t\t.contains(event(\"org.junit.ReportEntry\") //\n\t\t\t\t\t\t.with(\"key\", \"message\") //\n\t\t\t\t\t\t.with(\"value\", \"Hello JFR!\")) //\n\t\t\t\t.contains(event(\"org.junit.FileEntry\") //\n\t\t\t\t\t\t.with(\"path\", testFile.toAbsolutePath().toString())) //\n\t\t\t\t.contains(event(\"org.junit.SkippedTest\") //\n\t\t\t\t\t\t.with(\"displayName\", \"skipped()\") //\n\t\t\t\t\t\t.with(\"type\", \"TEST\") //\n\t\t\t\t\t\t.with(\"reason\", \"for demonstration purposes\"));\n\t}\n\n\tstatic class TestCase {\n\t\t@Test\n\t\tvoid test(TestReporter reporter) {\n\t\t\treporter.publishEntry(\"message\", \"Hello JFR!\");\n\t\t\treporter.publishFile(\"test.txt\", MediaType.TEXT_PLAIN_UTF_8, file -> Files.writeString(file, \"test\"));\n\t\t}\n\n\t\t@Test\n\t\t@Disabled(\"for demonstration purposes\")\n\t\tvoid skipped() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/AnotherUnusedTestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\npublic class AnotherUnusedTestExecutionListener implements TestExecutionListener {\n\tpublic static boolean called;\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tcalled = true;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/LoggingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.ArgumentMatchers.isNull;\nimport static org.mockito.ArgumentMatchers.startsWith;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.function.BiConsumer;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.platform.launcher.core.LauncherConfig;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.mockito.ArgumentMatchers;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.TestAbortedException;\n\nclass LoggingListenerTests {\n\n\t@Test\n\tvoid logsExecutionEvents() {\n\t\tBiConsumer<Throwable, String> logger = mock();\n\n\t\texecuteTestCase(LoggingListener.forBiConsumer((t, m) -> {\n\t\t\tSystem.out.println(m.get());\n\t\t\tlogger.accept(t, m.get());\n\t\t}));\n\n\t\tvar inOrder = inOrder(logger);\n\t\tinOrder.verify(logger).accept(isNull(),\n\t\t\tstartsWith(\"TestPlan Execution Started: org.junit.platform.launcher.TestPlan@\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\"Execution Started: JUnit Jupiter - [engine:junit-jupiter]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Started: LoggingListenerTests$TestCase - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Started: success() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Dynamic Test Registered: dynamic - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]/[dynamic-test:#1]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Started: dynamic - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]/[dynamic-test:#1]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Finished: dynamic - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()]/[dynamic-test:#1] - TestExecutionResult [status = SUCCESSFUL, throwable = null]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Finished: success() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[test-factory:success()] - TestExecutionResult [status = SUCCESSFUL, throwable = null]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Skipped: skipped() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:skipped()] - void org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase.skipped() is @Disabled\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Started: failed() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:failed()]\"));\n\t\tinOrder.verify(logger).accept(ArgumentMatchers.notNull(AssertionFailedError.class), eq(\n\t\t\t\"Execution Finished: failed() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:failed()] - TestExecutionResult [status = FAILED, throwable = org.opentest4j.AssertionFailedError]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Started: aborted() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:aborted()]\"));\n\t\tinOrder.verify(logger).accept(ArgumentMatchers.notNull(TestAbortedException.class), eq(\n\t\t\t\"Execution Finished: aborted() - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase]/[method:aborted()] - TestExecutionResult [status = ABORTED, throwable = org.opentest4j.TestAbortedException]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Finished: LoggingListenerTests$TestCase - [engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.LoggingListenerTests$TestCase] - TestExecutionResult [status = SUCCESSFUL, throwable = null]\"));\n\t\tinOrder.verify(logger).accept(isNull(), eq(\n\t\t\t\"Execution Finished: JUnit Jupiter - [engine:junit-jupiter] - TestExecutionResult [status = SUCCESSFUL, throwable = null]\"));\n\t\tinOrder.verify(logger).accept(isNull(),\n\t\t\tstartsWith(\"TestPlan Execution Finished: org.junit.platform.launcher.TestPlan@\"));\n\t\tinOrder.verifyNoMoreInteractions();\n\t}\n\n\tprivate static void executeTestCase(LoggingListener listener) {\n\t\tvar config = LauncherConfig.builder() //\n\t\t\t\t.enableTestExecutionListenerAutoRegistration(false) //\n\t\t\t\t.addTestExecutionListeners() //\n\t\t\t\t.build();\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectClass(TestCase.class)) //\n\t\t\t\t.filters(includeEngines(\"junit-jupiter\")) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.listeners(listener) //\n\t\t\t\t.build();\n\t\tLauncherFactory.create(config) //\n\t\t\t\t.execute(request);\n\t}\n\n\t@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n\tstatic class TestCase {\n\t\t@TestFactory\n\t\t@Order(1)\n\t\tStream<DynamicTest> success() {\n\t\t\treturn Stream.of(dynamicTest(\"dynamic\", () -> {\n\t\t\t}));\n\t\t}\n\n\t\t@Test\n\t\t@Disabled\n\t\t@Order(2)\n\t\tvoid skipped() {\n\t\t}\n\n\t\t@Test\n\t\t@Order(3)\n\t\tvoid failed() {\n\t\t\tfail();\n\t\t}\n\n\t\t@Test\n\t\t@Order(4)\n\t\tvoid aborted() {\n\t\t\tabort();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/NoopTestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\npublic class NoopTestExecutionListener implements TestExecutionListener {\n\tpublic static boolean called;\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tcalled = true;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/OutputDirTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.assertj.core.api.Assertions.as;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.InstanceOfAssertFactories.STRING;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.launcher.LauncherConstants;\n\nclass OutputDirTests {\n\n\t@TempDir\n\tPath cwd;\n\n\t@Test\n\tvoid getOutputDirUsesCustomOutputDir() throws Exception {\n\t\tvar customDir = cwd.resolve(\"custom-dir\");\n\t\tvar outputDir = OutputDir.create(Optional.of(customDir.toAbsolutePath().toString())).toPath();\n\t\tassertThat(Files.isSameFile(customDir, outputDir)).isTrue();\n\t\tassertThat(outputDir).exists();\n\t}\n\n\t@Test\n\tvoid getOutputDirUsesCustomOutputDirWithPlaceholder() {\n\t\tvar customDir = cwd.resolve(\"build\").resolve(\"junit-\" + LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER);\n\t\tvar outputDir = OutputDir.create(Optional.of(customDir.toAbsolutePath().toString())).toPath();\n\t\tassertThat(outputDir).exists() //\n\t\t\t\t.hasParent(cwd.resolve(\"build\")) //\n\t\t\t\t.extracting(it -> it.getFileName().toString(), as(STRING)) //\n\t\t\t\t.matches(\"junit-\\\\d+\");\n\t}\n\n\t@Test\n\tvoid getOutputDirFallsBackToCurrentWorkingDir() throws Exception {\n\t\tvar expected = cwd;\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\t@Test\n\tvoid getOutputDirDetectsMavenPom() throws Exception {\n\t\tFiles.createFile(cwd.resolve(\"pom.xml\"));\n\t\tvar expected = cwd.resolve(\"target\");\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\t@Test\n\tvoid getOutputDirDetectsGradleGroovyDefaultBuildScript() throws Exception {\n\t\tFiles.createFile(cwd.resolve(\"build.gradle\"));\n\t\tvar expected = cwd.resolve(\"build\");\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\t@Test\n\tvoid getOutputDirDetectsGradleGroovyCustomBuildScript() throws Exception {\n\t\tFiles.createFile(cwd.resolve(\"sub-project.gradle\"));\n\t\tvar expected = cwd.resolve(\"build\");\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\t@Test\n\tvoid getOutputDirDetectsGradleKotlinDefaultBuildScript() throws Exception {\n\t\tFiles.createFile(cwd.resolve(\"build.gradle.kts\"));\n\t\tvar expected = cwd.resolve(\"build\");\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\t@Test\n\tvoid getOutputDirDetectsGradleKotlinCustomBuildScript() throws Exception {\n\t\tFiles.createFile(cwd.resolve(\"sub-project.gradle.kts\"));\n\t\tvar expected = cwd.resolve(\"build\");\n\n\t\tassertOutputDirIsDetected(expected);\n\t}\n\n\tprivate void assertOutputDirIsDetected(Path expected) throws IOException {\n\t\tvar outputDir = OutputDir.createSafely(Optional.empty(), () -> cwd).toPath();\n\t\tassertThat(Files.isSameFile(expected, outputDir)).isTrue();\n\t\tassertThat(outputDir).exists();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.platform.commons.test.ConcurrencyTestingUtils.executeConcurrently;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.RepeatedTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.TestSource;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\nclass SummaryGenerationTests {\n\n\tprivate final SummaryGeneratingListener listener = new SummaryGeneratingListener();\n\tprivate final TestPlan testPlan = TestPlan.from(true, List.of(), mock(), dummyOutputDirectoryCreator());\n\n\t@Test\n\tvoid emptyReport() {\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\n\t\tassertEquals(0, listener.getSummary().getTestsFailedCount());\n\n\t\tvar summaryString = summaryAsString();\n\t\tassertAll(\"summary\", //\n\t\t\t() -> assertTrue(summaryString.contains(\"Test run finished after\"), \"test run\"), //\n\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers found\"), \"containers found\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers skipped\"), \"containers skipped\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers started\"), \"containers started\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers aborted\"), \"containers aborted\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers successful\"), \"containers successful\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 containers failed\"), \"containers failed\"), //\n\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests found\"), \"tests found\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests skipped\"), \"tests skipped\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests started\"), \"tests started\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests aborted\"), \"tests aborted\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests successful\"), \"tests successful\"), //\n\t\t\t() -> assertTrue(summaryString.contains(\"0 tests failed\"), \"tests failed\") //\n\t\t);\n\n\t\tassertEquals(\"\", failuresAsString());\n\t}\n\n\t@Test\n\tvoid reportingCorrectCounts() {\n\t\tvar successfulContainer = createContainerIdentifier(\"c1\");\n\t\tvar failedContainer = createContainerIdentifier(\"c2\");\n\t\tvar abortedContainer = createContainerIdentifier(\"c3\");\n\t\tvar skippedContainer = createContainerIdentifier(\"c4\");\n\n\t\tvar successfulTest = createTestIdentifier(\"t1\");\n\t\tvar failedTest = createTestIdentifier(\"t2\");\n\t\tvar abortedTest = createTestIdentifier(\"t3\");\n\t\tvar skippedTest = createTestIdentifier(\"t4\");\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\n\t\tlistener.executionSkipped(skippedContainer, \"skipped\");\n\t\tlistener.executionSkipped(skippedTest, \"skipped\");\n\n\t\tlistener.executionStarted(successfulContainer);\n\t\tlistener.executionFinished(successfulContainer, TestExecutionResult.successful());\n\n\t\tlistener.executionStarted(successfulTest);\n\t\tlistener.executionFinished(successfulTest, TestExecutionResult.successful());\n\n\t\tlistener.executionStarted(failedContainer);\n\t\tlistener.executionFinished(failedContainer, TestExecutionResult.failed(new RuntimeException(\"failed\")));\n\n\t\tlistener.executionStarted(failedTest);\n\t\tlistener.executionFinished(failedTest, TestExecutionResult.failed(new RuntimeException(\"failed\")));\n\n\t\tlistener.executionStarted(abortedContainer);\n\t\tlistener.executionFinished(abortedContainer, TestExecutionResult.aborted(new RuntimeException(\"aborted\")));\n\n\t\tlistener.executionStarted(abortedTest);\n\t\tlistener.executionFinished(abortedTest, TestExecutionResult.aborted(new RuntimeException(\"aborted\")));\n\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\n\t\tvar summaryString = summaryAsString();\n\t\ttry {\n\t\t\tassertAll(\"summary\", //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"4 containers found\"), \"containers found\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 containers skipped\"), \"containers skipped\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"3 containers started\"), \"containers started\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 containers aborted\"), \"containers aborted\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 containers successful\"), \"containers successful\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 containers failed\"), \"containers failed\"), //\n\n\t\t\t\t() -> assertTrue(summaryString.contains(\"4 tests found\"), \"tests found\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 tests skipped\"), \"tests skipped\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"3 tests started\"), \"tests started\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 tests aborted\"), \"tests aborted\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 tests successful\"), \"tests successful\"), //\n\t\t\t\t() -> assertTrue(summaryString.contains(\"1 tests failed\"), \"tests failed\") //\n\t\t\t);\n\t\t}\n\t\tcatch (AssertionError error) {\n\t\t\tSystem.err.println(summaryString);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t@Test\n\tvoid canGetListOfFailures() {\n\t\tvar failedException = new RuntimeException(\"Pow!\");\n\t\tvar testDescriptor = new TestDescriptorStub(UniqueId.root(\"root\", \"1\"), \"failingTest\") {\n\n\t\t\t@Override\n\t\t\tpublic Optional<TestSource> getSource() {\n\t\t\t\treturn Optional.of(ClassSource.from(Object.class));\n\t\t\t}\n\t\t};\n\t\tvar failingTest = TestIdentifier.from(testDescriptor);\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\tlistener.executionStarted(failingTest);\n\t\tlistener.executionFinished(failingTest, TestExecutionResult.failed(failedException));\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\t\tfinal var failures = listener.getSummary().getFailures();\n\t\tassertThat(failures).hasSize(1);\n\t\tassertThat(failures.getFirst().getException()).isEqualTo(failedException);\n\t\tassertThat(failures.getFirst().getTestIdentifier()).isEqualTo(failingTest);\n\t}\n\n\t@Test\n\tvoid reportingCorrectFailures() {\n\t\tvar iaeCausedBy = new IllegalArgumentException(\"Illegal Argument Exception\");\n\t\tvar failedException = new RuntimeException(\"Runtime Exception\", iaeCausedBy);\n\t\tvar npeSuppressed = new NullPointerException(\"Null Pointer Exception\");\n\t\tfailedException.addSuppressed(npeSuppressed);\n\n\t\tvar testDescriptor = new TestDescriptorStub(UniqueId.root(\"root\", \"2\"), \"failingTest\") {\n\n\t\t\t@Override\n\t\t\tpublic Optional<TestSource> getSource() {\n\t\t\t\treturn Optional.of(ClassSource.from(Object.class));\n\t\t\t}\n\t\t};\n\t\tvar failed = TestIdentifier.from(testDescriptor);\n\t\tvar aborted = TestIdentifier.from(new TestDescriptorStub(UniqueId.root(\"root\", \"3\"), \"abortedTest\"));\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\tlistener.executionStarted(failed);\n\t\tlistener.executionFinished(failed, TestExecutionResult.failed(failedException));\n\t\tlistener.executionStarted(aborted);\n\t\tlistener.executionFinished(aborted, TestExecutionResult.aborted(new RuntimeException(\"aborted\")));\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\n\t\t// An aborted test is not a failure\n\t\tassertEquals(1, listener.getSummary().getTestsFailedCount());\n\n\t\tvar failuresString = failuresAsString();\n\t\tassertAll(\"failures\", //\n\t\t\t() -> assertTrue(failuresString.contains(\"Failures (1)\"), \"test failures\"), //\n\t\t\t() -> assertTrue(failuresString.contains(Object.class.getName()), \"source\"), //\n\t\t\t() -> assertTrue(failuresString.contains(\"failingTest\"), \"display name\"), //\n\t\t\t() -> assertTrue(failuresString.contains(\"=> \" + failedException), \"main exception\"), //\n\t\t\t() -> assertTrue(failuresString.contains(\"Caused by: \" + iaeCausedBy), \"Caused by exception\"), //\n\t\t\t() -> assertTrue(failuresString.contains(\"Suppressed: \" + npeSuppressed), \"Suppressed exception\") //\n\t\t);\n\t}\n\n\t@Test\n\tpublic void reportingCircularFailure() {\n\t\tvar iaeCausedBy = new IllegalArgumentException(\"Illegal Argument Exception\");\n\t\tvar failedException = new RuntimeException(\"Runtime Exception\", iaeCausedBy);\n\t\tvar npeSuppressed = new NullPointerException(\"Null Pointer Exception\");\n\t\tfailedException.addSuppressed(npeSuppressed);\n\t\tnpeSuppressed.addSuppressed(iaeCausedBy);\n\n\t\tvar testDescriptor = new TestDescriptorStub(UniqueId.root(\"root\", \"2\"), \"failingTest\") {\n\n\t\t\t@Override\n\t\t\tpublic Optional<TestSource> getSource() {\n\t\t\t\treturn Optional.of(ClassSource.from(Object.class));\n\t\t\t}\n\t\t};\n\t\tvar failed = TestIdentifier.from(testDescriptor);\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\tlistener.executionStarted(failed);\n\t\tlistener.executionFinished(failed, TestExecutionResult.failed(failedException));\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\n\t\tassertEquals(1, listener.getSummary().getTestsFailedCount());\n\n\t\tvar failuresString = failuresAsString();\n\t\tassertAll(\"failures\", //\n\t\t\t() -> assertTrue(failuresString.contains(\"Suppressed: \" + npeSuppressed), \"Suppressed exception\"), //\n\t\t\t() -> assertTrue(failuresString.contains(\"Circular reference: \" + iaeCausedBy), \"Circular reference\"), //\n\t\t\t() -> assertFalse(failuresString.contains(\"Caused by: \"),\n\t\t\t\t\"'Caused by: ' omitted because of Circular reference\") //\n\t\t);\n\t}\n\n\t@RepeatedTest(10)\n\tvoid reportingConcurrentlyFinishedTests() throws Exception {\n\t\tvar numThreads = 250;\n\t\tvar testIdentifier = TestIdentifier.from(new TestDescriptorStub(UniqueId.root(\"root\", \"2\"), \"failingTest\") {\n\t\t\t@Override\n\t\t\tpublic Optional<TestSource> getSource() {\n\t\t\t\treturn Optional.of(ClassSource.from(Object.class));\n\t\t\t}\n\t\t});\n\t\tvar result = TestExecutionResult.failed(new RuntimeException());\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\texecuteConcurrently(numThreads, () -> {\n\t\t\tlistener.executionStarted(testIdentifier);\n\t\t\tlistener.executionFinished(testIdentifier, result);\n\t\t});\n\t\tlistener.testPlanExecutionFinished(testPlan);\n\n\t\tassertThat(listener.getSummary().getFailures()).hasSize(numThreads);\n\t}\n\n\tprivate TestIdentifier createTestIdentifier(String uniqueId) {\n\t\tvar identifier = TestIdentifier.from(new TestDescriptorStub(UniqueId.root(\"test\", uniqueId), uniqueId));\n\t\ttestPlan.addInternal(identifier);\n\t\treturn identifier;\n\t}\n\n\tprivate TestIdentifier createContainerIdentifier(String uniqueId) {\n\t\tvar identifier = TestIdentifier.from(new TestDescriptorStub(UniqueId.root(\"container\", uniqueId), uniqueId) {\n\n\t\t\t@Override\n\t\t\tpublic Type getType() {\n\t\t\t\treturn Type.CONTAINER;\n\t\t\t}\n\t\t});\n\t\ttestPlan.addInternal(identifier);\n\t\treturn identifier;\n\t}\n\n\tprivate String summaryAsString() {\n\t\tvar summaryWriter = new StringWriter();\n\t\tlistener.getSummary().printTo(new PrintWriter(summaryWriter));\n\t\treturn summaryWriter.toString();\n\t}\n\n\tprivate String failuresAsString() {\n\t\tvar failuresWriter = new StringWriter();\n\t\tlistener.getSummary().printFailuresTo(new PrintWriter(failuresWriter));\n\t\treturn failuresWriter.toString();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/UniqueIdTrackingListenerIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.util.FunctionUtils.where;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.EngineFilter.includeEngines;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.listeners.UniqueIdTrackingListener.DEFAULT_OUTPUT_FILE_PREFIX;\nimport static org.junit.platform.launcher.listeners.UniqueIdTrackingListener.LISTENER_ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.launcher.listeners.UniqueIdTrackingListener.OUTPUT_DIR_PROPERTY_NAME;\nimport static org.junit.platform.launcher.listeners.UniqueIdTrackingListener.OUTPUT_FILE_PREFIX_PROPERTY_NAME;\nimport static org.junit.platform.launcher.listeners.UniqueIdTrackingListener.WORKING_DIR_PROPERTY_NAME;\nimport static org.junit.platform.testkit.engine.Event.byTestDescriptor;\nimport static org.junit.platform.testkit.engine.EventConditions.abortedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.ServiceLoader;\nimport java.util.stream.Stream;\n\nimport org.assertj.core.api.Condition;\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.junit.platform.launcher.core.LauncherFactory;\nimport org.junit.platform.testkit.engine.EngineTestKit;\nimport org.junit.platform.testkit.engine.Event;\nimport org.opentest4j.AssertionFailedError;\nimport org.opentest4j.TestAbortedException;\n\n/**\n * Integration tests for the {@link UniqueIdTrackingListener}.\n *\n * @since 1.8\n */\nclass UniqueIdTrackingListenerIntegrationTests {\n\n\tprivate static final String passingTest = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[method:passingTest()]\";\n\tprivate static final String skippedTest = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[method:skippedTest()]\";\n\tprivate static final String abortedTest = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[method:abortedTest()]\";\n\tprivate static final String failingTest = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[method:failingTest()]\";\n\tprivate static final String dynamicTest1 = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[test-factory:dynamicTests()]/[dynamic-test:#1]\";\n\tprivate static final String dynamicTest2 = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase1]/[test-factory:dynamicTests()]/[dynamic-test:#2]\";\n\tprivate static final String testA = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase2]/[method:testA()]\";\n\tprivate static final String testB = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase2]/[method:testB()]\";\n\tprivate static final String testC = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase3]/[method:testC()]\";\n\tprivate static final String testD = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase3]/[method:testD()]\";\n\tprivate static final String testE = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testE()]\";\n\tprivate static final String testF = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testF()]\";\n\tprivate static final String testG = \"[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$DisabledTestCase]/[nested-class:Inner]/[method:testG()]\";\n\n\tprivate static final String[] expectedUniqueIds = { passingTest, skippedTest, abortedTest, failingTest,\n\t\t\tdynamicTest1, dynamicTest2, testA, testB, testG };\n\n\tprivate static final String[] expectedConcurrentUniqueIds = { testA, testB, testC, testD, testE, testF };\n\n\t@TempDir\n\tPath workingDir;\n\n\t@BeforeEach\n\tvoid createFakeGradleFiles() throws Exception {\n\t\tFiles.createFile(workingDir.resolve(\"build.gradle\"));\n\t\tFiles.createDirectory(workingDir.resolve(\"build\"));\n\t}\n\n\t@Test\n\tvoid confirmExpectedUniqueIdsViaEngineTestKit() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(\"junit-jupiter\")\n\t\t\t.selectors(selectClasses())\n\t\t\t.execute()\n\t\t\t.testEvents()\n\t\t\t.assertStatistics(stats -> stats.started(7).skipped(1).aborted(1).succeeded(5).failed(1))\n\t\t\t.assertEventsMatchLoosely(\n\t\t\t\tevent(test(uniqueId(passingTest)), finishedSuccessfully()),\n\t\t\t\tevent(test(uniqueId(abortedTest)), abortedWithReason(instanceOf(TestAbortedException.class))),\n\t\t\t\tevent(test(uniqueId(failingTest)), finishedWithFailure(instanceOf(AssertionFailedError.class))),\n\t\t\t\tevent(test(uniqueId(dynamicTest1)), finishedSuccessfully()),\n\t\t\t\tevent(test(uniqueId(dynamicTest2)), finishedSuccessfully()),\n\t\t\t\tevent(test(uniqueId(testA)), finishedSuccessfully()),\n\t\t\t\tevent(test(uniqueId(testB)), finishedSuccessfully())\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate Condition<Event> uniqueId(String uniqueId) {\n\t\treturn new Condition<>(\n\t\t\tbyTestDescriptor(where(TestDescriptor::getUniqueId, uid -> uid.toString().equals(uniqueId))),\n\t\t\t\"descriptor with uniqueId '%s'\", uniqueId);\n\t}\n\n\t@Test\n\tvoid listenerIsRegisteredButDisabledByDefault() throws Exception {\n\t\tvar numListenersRegistered = ServiceLoader.load(TestExecutionListener.class).stream()//\n\t\t\t\t.filter(provider -> UniqueIdTrackingListener.class.equals(provider.type()))//\n\t\t\t\t.count();\n\t\tassertThat(numListenersRegistered).isEqualTo(1);\n\n\t\tvar actualUniqueIds = executeTests(Map.of());\n\n\t\t// Sanity check using the results of our local TestExecutionListener\n\t\tassertThat(actualUniqueIds).containsExactlyInAnyOrder(expectedUniqueIds);\n\n\t\t// Check that files were not generated by the UniqueIdTrackingListener\n\t\tassertThat(findFiles(workingDir, DEFAULT_OUTPUT_FILE_PREFIX)).isEmpty();\n\t}\n\n\t@Test\n\tvoid verifyUniqueIdsAreTrackedWithDefaults() throws Exception {\n\t\tverifyUniqueIdsAreTracked(\"build\", DEFAULT_OUTPUT_FILE_PREFIX, Map.of());\n\t}\n\n\t@Test\n\tvoid verifyUniqueIdsAreTrackedWithCustomOutputFile() throws Exception {\n\t\tvar customPrefix = \"test_ids\";\n\t\tverifyUniqueIdsAreTracked(\"build\", customPrefix, Map.of(OUTPUT_FILE_PREFIX_PROPERTY_NAME, customPrefix));\n\t}\n\n\t@Test\n\tvoid verifyUniqueIdsAreTrackedWithCustomOutputDir() throws Exception {\n\t\tvar customDir = \"build/UniqueIdTrackingListenerIntegrationTests\";\n\t\tverifyUniqueIdsAreTracked(customDir, DEFAULT_OUTPUT_FILE_PREFIX, Map.of(OUTPUT_DIR_PROPERTY_NAME, customDir));\n\t}\n\n\t@Test\n\tvoid verifyUniqueIdsAreTrackedWithCustomOutputFileAndCustomOutputDir() throws Exception {\n\t\tvar customPrefix = \"test_ids\";\n\t\tvar customDir = \"build/UniqueIdTrackingListenerIntegrationTests\";\n\n\t\tverifyUniqueIdsAreTracked(customDir, customPrefix,\n\t\t\tMap.of(OUTPUT_DIR_PROPERTY_NAME, customDir, OUTPUT_FILE_PREFIX_PROPERTY_NAME, customPrefix));\n\t}\n\n\tprivate void verifyUniqueIdsAreTracked(String outputDir, String prefix, Map<String, String> configurationParameters)\n\t\t\tthrows IOException {\n\n\t\tconfigurationParameters = new HashMap<>(configurationParameters);\n\t\tconfigurationParameters.put(LISTENER_ENABLED_PROPERTY_NAME, \"true\");\n\n\t\tvar actualUniqueIds = executeTests(configurationParameters);\n\n\t\t// Sanity check using the results of our local TestExecutionListener\n\t\tassertThat(actualUniqueIds).containsExactlyInAnyOrder(expectedUniqueIds);\n\n\t\t// Check contents of the file (or files) generated by the UniqueIdTrackingListener\n\t\tassertThat(readAllFiles(workingDir.resolve(outputDir), prefix)).containsExactlyInAnyOrder(expectedUniqueIds);\n\t}\n\n\t@Test\n\tvoid verifyUniqueIdsAreTrackedWithConcurrentlyExecutingTestPlans() throws Exception {\n\t\tvar customDir = workingDir.resolve(\"build/UniqueIdTrackingListenerIntegrationTests\");\n\t\tvar prefix = DEFAULT_OUTPUT_FILE_PREFIX;\n\n\t\tMap<String, String> configurationParameters = new HashMap<>();\n\t\tconfigurationParameters.put(LISTENER_ENABLED_PROPERTY_NAME, \"true\");\n\t\tconfigurationParameters.put(OUTPUT_DIR_PROPERTY_NAME, customDir.toAbsolutePath().toString());\n\n\t\tStream.of(TestCase2.class, TestCase3.class, TestCase4.class).parallel()//\n\t\t\t\t.forEach(clazz -> executeTests(configurationParameters, selectClass(clazz)));\n\n\t\t// 3 output files should have been generated.\n\t\tassertThat(findFiles(customDir, prefix)).hasSize(3);\n\n\t\t// Check contents of the file (or files) generated by the UniqueIdTrackingListener\n\t\tassertThat(readAllFiles(customDir, prefix)).containsExactlyInAnyOrder(expectedConcurrentUniqueIds);\n\t}\n\n\tprivate List<String> executeTests(Map<String, String> configurationParameters) {\n\t\treturn executeTests(configurationParameters, selectClasses());\n\t}\n\n\tprivate List<String> executeTests(Map<String, String> configurationParameters, ClassSelector... classSelectors) {\n\t\treturn executeTests(configurationParameters, List.of(classSelectors));\n\t}\n\n\tprivate List<String> executeTests(Map<String, String> configurationParameters, List<ClassSelector> classSelectors) {\n\t\tList<String> uniqueIds = new ArrayList<>();\n\t\tvar listener = new TestExecutionListener() {\n\n\t\t\t@Nullable\n\t\t\tprivate TestPlan testPlan;\n\n\t\t\t@Override\n\t\t\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\t\t\tthis.testPlan = testPlan;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void executionSkipped(TestIdentifier testIdentifier, String reason) {\n\t\t\t\tif (testIdentifier.isTest()) {\n\t\t\t\t\tuniqueIds.add(testIdentifier.getUniqueId());\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\trequireNonNull(this.testPlan).getChildren(testIdentifier).forEach(\n\t\t\t\t\t\tchild -> executionSkipped(child, reason));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {\n\t\t\t\tif (testIdentifier.isTest()) {\n\t\t\t\t\tuniqueIds.add(testIdentifier.getUniqueId());\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tvar request = request()//\n\t\t\t\t.selectors(classSelectors)//\n\t\t\t\t.filters(includeEngines(\"junit-jupiter\"))//\n\t\t\t\t.configurationParameters(configurationParameters)//\n\t\t\t\t.configurationParameter(WORKING_DIR_PROPERTY_NAME, workingDir.toAbsolutePath().toString())//\n\t\t\t\t.forExecution()//\n\t\t\t\t.listeners(listener)//\n\t\t\t\t.build();\n\t\tLauncherFactory.create().execute(request);\n\t\treturn uniqueIds;\n\t}\n\n\tprivate static List<ClassSelector> selectClasses() {\n\t\treturn DiscoverySelectors.selectClasses(TestCase1.class, TestCase2.class, DisabledTestCase.class);\n\t}\n\n\tprivate static Stream<Path> findFiles(Path outputDir, String prefix) throws IOException {\n\t\tif (!Files.exists(outputDir)) {\n\t\t\treturn Stream.empty();\n\t\t}\n\t\treturn Files.find(outputDir, 1, //\n\t\t\t(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()\n\t\t\t\t\t&& path.getFileName().toString().startsWith(prefix)));\n\t}\n\n\tprivate Stream<String> readAllFiles(Path outputDir, String prefix) throws IOException {\n\t\treturn findFiles(outputDir, prefix).map(outputFile -> {\n\t\t\ttry {\n\t\t\t\treturn Files.readAllLines(outputFile);\n\t\t\t}\n\t\t\tcatch (IOException ex) {\n\t\t\t\tthrow new UncheckedIOException(ex);\n\t\t\t}\n\t\t}).flatMap(List::stream);\n\t}\n\n\t// -------------------------------------------------------------------------\n\n\tstatic class TestCase1 {\n\n\t\t@Test\n\t\tvoid passingTest() {\n\t\t}\n\n\t\t@Test\n\t\t@Disabled(\"testing\")\n\t\tvoid skippedTest() {\n\t\t}\n\n\t\t@Test\n\t\tvoid abortedTest() {\n\t\t\tabort();\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail();\n\t\t}\n\n\t\t@TestFactory\n\t\tStream<DynamicTest> dynamicTests() {\n\t\t\treturn Stream.of(\"cat\", \"dog\").map(text -> dynamicTest(text, () -> assertEquals(3, text.length())));\n\t\t}\n\t}\n\n\tstatic class TestCase2 {\n\n\t\t@Test\n\t\tvoid testA() {\n\t\t}\n\n\t\t@Test\n\t\tvoid testB() {\n\t\t}\n\t}\n\n\tstatic class TestCase3 {\n\n\t\t@Test\n\t\tvoid testC() {\n\t\t}\n\n\t\t@Test\n\t\tvoid testD() {\n\t\t}\n\t}\n\n\tstatic class TestCase4 {\n\n\t\t@Test\n\t\tvoid testE() {\n\t\t}\n\n\t\t@Test\n\t\tvoid testF() {\n\t\t}\n\t}\n\n\t@Disabled\n\tstatic class DisabledTestCase {\n\n\t\t@Nested\n\t\tclass Inner {\n\n\t\t\t@Test\n\t\t\tvoid testG() {\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/UnusedTestExecutionListener.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners;\n\nimport org.junit.platform.launcher.TestExecutionListener;\nimport org.junit.platform.launcher.TestPlan;\n\npublic class UnusedTestExecutionListener implements TestExecutionListener {\n\tpublic static boolean called;\n\n\t@Override\n\tpublic void testPlanExecutionStarted(TestPlan testPlan) {\n\t\tcalled = true;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/AbortOnFailureLauncherDiscoveryListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\nimport static org.junit.platform.launcher.listeners.discovery.LauncherDiscoveryListeners.abortOnFailure;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.commons.JUnitException;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.fakes.TestEngineStub;\n\nclass AbortOnFailureLauncherDiscoveryListenerTests {\n\n\t@Test\n\tvoid abortsDiscoveryOnEngineDiscoveryFailure() {\n\t\tvar rootCause = new RuntimeException();\n\t\tvar engine = new TestEngineStub(\"some-engine\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\t\tvar request = request() //\n\t\t\t\t.listeners(abortOnFailure()) //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tvar exception = assertThrows(JUnitException.class, () -> launcher.discover(request));\n\t\tassertThat(exception) //\n\t\t\t\t.hasMessage(\"TestEngine with ID 'some-engine' failed to discover tests\") //\n\t\t\t\t.cause().isSameAs(rootCause);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/CompositeLauncherDiscoveryListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.launcher.EngineDiscoveryResult;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.mockito.InOrder;\n\nclass CompositeLauncherDiscoveryListenerTests {\n\n\t@Test\n\tvoid callsListenersInReverseOrderForFinishedEvents() {\n\t\tvar firstListener = mock(LauncherDiscoveryListener.class, \"firstListener\");\n\t\tvar secondListener = mock(LauncherDiscoveryListener.class, \"secondListener\");\n\n\t\tvar launcherDiscoveryRequest = mock(LauncherDiscoveryRequest.class);\n\t\tvar engineId = UniqueId.forEngine(\"engine\");\n\t\tvar engineDiscoveryResult = EngineDiscoveryResult.successful();\n\t\tvar selector = selectUniqueId(engineId);\n\t\tvar selectorResolutionResult = SelectorResolutionResult.resolved();\n\t\tvar discoveryIssue = DiscoveryIssue.create(Severity.WARNING, \"message\");\n\n\t\tvar composite = new CompositeLauncherDiscoveryListener(List.of(firstListener, secondListener));\n\t\tcomposite.launcherDiscoveryStarted(launcherDiscoveryRequest);\n\t\tcomposite.engineDiscoveryStarted(engineId);\n\t\tcomposite.selectorProcessed(engineId, selector, selectorResolutionResult);\n\t\tcomposite.issueEncountered(engineId, discoveryIssue);\n\t\tcomposite.engineDiscoveryFinished(engineId, engineDiscoveryResult);\n\t\tcomposite.launcherDiscoveryFinished(launcherDiscoveryRequest);\n\n\t\tInOrder inOrder = inOrder(firstListener, secondListener);\n\n\t\tinOrder.verify(firstListener).launcherDiscoveryStarted(launcherDiscoveryRequest);\n\t\tinOrder.verify(secondListener).launcherDiscoveryStarted(launcherDiscoveryRequest);\n\t\tinOrder.verify(firstListener).engineDiscoveryStarted(engineId);\n\t\tinOrder.verify(secondListener).engineDiscoveryStarted(engineId);\n\n\t\tinOrder.verify(firstListener).selectorProcessed(engineId, selector, selectorResolutionResult);\n\t\tinOrder.verify(secondListener).selectorProcessed(engineId, selector, selectorResolutionResult);\n\t\tinOrder.verify(firstListener).issueEncountered(engineId, discoveryIssue);\n\t\tinOrder.verify(secondListener).issueEncountered(engineId, discoveryIssue);\n\n\t\tinOrder.verify(secondListener).engineDiscoveryFinished(engineId, engineDiscoveryResult);\n\t\tinOrder.verify(firstListener).engineDiscoveryFinished(engineId, engineDiscoveryResult);\n\t\tinOrder.verify(secondListener).launcherDiscoveryFinished(launcherDiscoveryRequest);\n\t\tinOrder.verify(firstListener).launcherDiscoveryFinished(launcherDiscoveryRequest);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/discovery/LoggingLauncherDiscoveryListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.discovery;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.fakes.FaultyTestEngines.createEngineThatCannotResolveAnything;\nimport static org.junit.platform.fakes.FaultyTestEngines.createEngineThatFailsToResolveAnything;\nimport static org.junit.platform.launcher.LauncherConstants.DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\n\nimport java.util.logging.Level;\nimport java.util.logging.LogRecord;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.fixtures.TrackLogRecords;\nimport org.junit.platform.commons.logging.LogRecordListener;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.fakes.TestEngineStub;\n\n@TrackLogRecords\nclass LoggingLauncherDiscoveryListenerTests {\n\n\t@Test\n\tvoid logsWarningOnUnresolvedUniqueIdSelectorWithEnginePrefix(LogRecordListener log) {\n\t\tvar engine = createEngineThatCannotResolveAnything(\"some-engine\");\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"execution\") //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tlauncher.discover(request);\n\n\t\tassertThat(log.stream(LoggingLauncherDiscoveryListener.class, Level.WARNING)) //\n\t\t\t\t.extracting(LogRecord::getMessage) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"UniqueIdSelector [uniqueId = [engine:some-engine]] could not be resolved by [engine:some-engine]\");\n\t}\n\n\t@Test\n\tvoid logsDebugMessageOnUnresolvedUniqueIdSelectorWithoutEnginePrefix(LogRecordListener log) {\n\t\tvar engine = createEngineThatCannotResolveAnything(\"some-engine\");\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(\"some-other-engine\"))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tlauncher.discover(request);\n\n\t\tassertThat(log.stream(LoggingLauncherDiscoveryListener.class, Level.FINE)) //\n\t\t\t\t.extracting(LogRecord::getMessage) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"UniqueIdSelector [uniqueId = [engine:some-other-engine]] could not be resolved by [engine:some-engine]\");\n\t}\n\n\t@Test\n\tvoid logsErrorOnSelectorResolutionFailure(LogRecordListener log) {\n\t\tvar rootCause = new RuntimeException();\n\t\tvar engine = createEngineThatFailsToResolveAnything(\"some-engine\", rootCause);\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DISCOVERY_ISSUE_FAILURE_PHASE_PROPERTY_NAME, \"execution\") //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.selectors(selectClass(Object.class)) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tlauncher.discover(request);\n\n\t\tassertThat(log.stream(LoggingLauncherDiscoveryListener.class, Level.SEVERE)) //\n\t\t\t\t.extracting(LogRecord::getMessage) //\n\t\t\t\t.containsExactly(\n\t\t\t\t\t\"Resolution of ClassSelector [className = 'java.lang.Object', classLoader = null] by [engine:some-engine] failed\");\n\t}\n\n\t@Test\n\tvoid logsErrorOnEngineDiscoveryFailure(LogRecordListener log) {\n\t\tvar rootCause = new RuntimeException();\n\t\tvar engine = new TestEngineStub(\"some-engine\") {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tthrow rootCause;\n\t\t\t}\n\t\t};\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tlauncher.discover(request);\n\n\t\tvar logRecord = log.stream(LoggingLauncherDiscoveryListener.class, Level.SEVERE).findFirst().get();\n\t\tassertThat(logRecord.getMessage()).isEqualTo(\"TestEngine with ID 'some-engine' failed to discover tests\");\n\t\tassertThat(logRecord.getThrown()).isSameAs(rootCause);\n\t}\n\n\t@Test\n\tvoid logsTraceMessageOnStartAndEnd(LogRecordListener log) {\n\t\tvar engine = new TestEngineStub(\"some-engine\");\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(DEFAULT_DISCOVERY_LISTENER_CONFIGURATION_PROPERTY_NAME, \"logging\") //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.build();\n\t\tvar launcher = createLauncher(engine);\n\n\t\tlauncher.discover(request);\n\n\t\tassertThat(log.stream(LoggingLauncherDiscoveryListener.class, Level.FINER)) //\n\t\t\t\t.extracting(LogRecord::getMessage) //\n\t\t\t\t.containsExactly( //\n\t\t\t\t\t\"Test discovery started\", //\n\t\t\t\t\t\"Engine [engine:some-engine] has started discovering tests\", //\n\t\t\t\t\t\"Engine [engine:some-engine] has finished discovering tests\", //\n\t\t\t\t\t\"Test discovery finished\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/listeners/session/CompositeLauncherSessionListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.listeners.session;\n\nimport static org.mockito.Mockito.inOrder;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.launcher.LauncherSession;\nimport org.junit.platform.launcher.LauncherSessionListener;\nimport org.mockito.InOrder;\n\nclass CompositeLauncherSessionListenerTests {\n\n\t@Test\n\tvoid callsListenersInReverseOrderForClosedEvents() {\n\t\tvar firstListener = mock(LauncherSessionListener.class, \"firstListener\");\n\t\tvar secondListener = mock(LauncherSessionListener.class, \"secondListener\");\n\n\t\tvar launcherSession = mock(LauncherSession.class);\n\n\t\tvar composite = new CompositeLauncherSessionListener(List.of(firstListener, secondListener));\n\t\tcomposite.launcherSessionOpened(launcherSession);\n\t\tcomposite.launcherSessionClosed(launcherSession);\n\n\t\tInOrder inOrder = inOrder(firstListener, secondListener);\n\t\tinOrder.verify(firstListener).launcherSessionOpened(launcherSession);\n\t\tinOrder.verify(secondListener).launcherSessionOpened(launcherSession);\n\t\tinOrder.verify(secondListener).launcherSessionClosed(launcherSession);\n\t\tinOrder.verify(firstListener).launcherSessionClosed(launcherSession);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserErrorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nclass ParserErrorTests {\n\n\tprivate final Parser parser = new Parser();\n\n\t@Test\n\tvoid emptyExpression() {\n\t\tassertThat(parseErrorFromParsing(\"\")).contains(\"empty tag expression\");\n\t}\n\n\t@Test\n\tvoid missingClosingParenthesis() {\n\t\tassertThat(parseErrorFromParsing(\"(\")).contains(\"missing closing parenthesis for '(' at index <0>\");\n\t\tassertThat(parseErrorFromParsing(\"( foo & bar\")).contains(\"missing closing parenthesis for '(' at index <0>\");\n\t}\n\n\t@Test\n\tvoid missingOpeningParenthesis() {\n\t\tassertThat(parseErrorFromParsing(\")\")).contains(\"missing opening parenthesis for ')' at index <0>\");\n\t\tassertThat(parseErrorFromParsing(\" foo | bar)\")).contains(\"missing opening parenthesis for ')' at index <10>\");\n\t}\n\n\t@Test\n\tvoid partialUnaryOperator() {\n\t\tassertThat(parseErrorFromParsing(\"!\")).contains(\"missing rhs operand for '!' at index <0>\");\n\t}\n\n\t@Test\n\tvoid partialBinaryOperator() {\n\t\tassertThat(parseErrorFromParsing(\"& foo\")).contains(\"missing lhs operand for '&' at index <0>\");\n\t\tassertThat(parseErrorFromParsing(\"foo |\")).contains(\"missing rhs operand for '|' at index <4>\");\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"data\")\n\tvoid acceptanceTests(String tagExpression, String parseError) {\n\t\tassertThat(parseErrorFromParsing(tagExpression)).contains(parseError);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static Stream<Arguments> data() {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\t\targuments(\"&\", \"missing lhs and rhs operand for '&' at index <0>\"),\n\t\t\t\targuments(\"|\", \"missing lhs and rhs operand for '|' at index <0>\"),\n\t\t\t\targuments(\"| |\", \"missing lhs and rhs operand for '|' at index <0>\"),\n\t\t\t\targuments(\"!\", \"missing rhs operand for '!' at index <0>\"),\n\t\t\t\targuments(\"foo bar\", \"missing operator between 'foo' at index <2> and 'bar' at index <4>\"),\n\t\t\t\targuments(\"foo bar |\", \"missing rhs operand for '|' at index <8>\"),\n\t\t\t\targuments(\"foo bar | baz\", \"missing operator between 'foo' at index <2> and '(bar | baz)' at index <4>\"),\n\t\t\t\targuments(\"foo bar &\", \"missing rhs operand for '&' at index <8>\"),\n\t\t\t\targuments(\"foo & (bar !)\", \"missing rhs operand for '!' at index <11>\"),\n\t\t\t\targuments(\"( foo & bar ) )\", \"missing opening parenthesis for ')' at index <14>\"),\n\t\t\t\targuments(\"( ( foo & bar )\", \"missing closing parenthesis for '(' at index <0>\"),\n\n\t\t\t\targuments(\"foo & (bar baz) |\", \"missing operator between 'bar' at index <9> and 'baz' at index <11>\"),\n\n\t\t\t\targuments(\"foo & (bar baz) &\", \"missing operator between 'bar' at index <9> and 'baz' at index <11>\"),\n\t\t\t\targuments(\"foo & (bar |baz) &\", \"missing rhs operand for '&' at index <17>\"),\n\n\t\t\t\targuments(\"foo | (bar baz) &\", \"missing rhs operand for '&' at index <16>\"),\n\t\t\t\targuments(\"foo | (bar baz) &quux\", \"missing operator between 'bar' at index <9> and '(baz & quux)' at index <11>\"),\n\n\t\t\t\targuments(\"foo & |\", \"missing rhs operand for '&' at index <4>\"),\n\t\t\t\targuments(\"foo !& bar\", \"missing rhs operand for '!' at index <4>\"),\n\t\t\t\targuments(\"foo !| bar\", \"missing rhs operand for '!' at index <4>\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate @Nullable String parseErrorFromParsing(String tagExpression) {\n\t\ttry {\n\t\t\tvar parseResult = parser.parse(tagExpression);\n\t\t\tparseResult.tagExpressionOrThrow(RuntimeException::new);\n\t\t\treturn null;\n\t\t}\n\t\tcatch (RuntimeException ex) {\n\t\t\treturn ex.getMessage();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/ParserTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nclass ParserTests {\n\n\tprivate final Parser parser = new Parser();\n\n\t@Test\n\tvoid notHasHigherPrecedenceThanAnd() {\n\t\tassertThat(tagExpressionParsedFrom(\"! foo & bar\")).hasToString(\"(!foo & bar)\");\n\t}\n\n\t@Test\n\tvoid andHasHigherPrecedenceThanOr() {\n\t\tassertThat(tagExpressionParsedFrom(\"foo | bar & baz\")).hasToString(\"(foo | (bar & baz))\");\n\t}\n\n\t@Test\n\tvoid notIsRightAssociative() {\n\t\tassertThat(tagExpressionParsedFrom(\"foo &! bar\")).hasToString(\"(foo & !bar)\");\n\t}\n\n\t@Test\n\tvoid andIsLeftAssociative() {\n\t\tassertThat(tagExpressionParsedFrom(\"foo & bar & baz\")).hasToString(\"((foo & bar) & baz)\");\n\t}\n\n\t@Test\n\tvoid orIsLeftAssociative() {\n\t\tassertThat(tagExpressionParsedFrom(\"foo | bar | baz\")).hasToString(\"((foo | bar) | baz)\");\n\t}\n\n\t@ParameterizedTest\n\t@MethodSource(\"data\")\n\tvoid acceptanceTests(String tagExpression, String expression) {\n\t\tassertThat(tagExpressionParsedFrom(tagExpression)).hasToString(expression);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tprivate static Stream<Arguments> data() {\n\t\t// @formatter:off\n\t\treturn Stream.of(\n\t\t\t\targuments(\"foo\", \"foo\"),\n\t\t\t\targuments(\"! foo\", \"!foo\"),\n\t\t\t\targuments(\"foo & bar\", \"(foo & bar)\"),\n\t\t\t\targuments(\"foo | bar\", \"(foo | bar)\"),\n\t\t\t\targuments(\"( ! foo & bar | baz)\", \"((!foo & bar) | baz)\"),\n\t\t\t\targuments(\"(foo & bar ) | baz & quux\", \"((foo & bar) | (baz & quux))\"),\n\t\t\t\targuments(\"! foo | bar & ! baz | ! quux | quuz & corge\", \"(((!foo | (bar & !baz)) | !quux) | (quuz & corge))\"),\n\t\t\t\targuments(\"(foo & bar ) | baz & quux\", \"((foo & bar) | (baz & quux))\"),\n\t\t\t\targuments(\"foo | bar & baz|quux\", \"((foo | (bar & baz)) | quux)\"),\n\t\t\t\targuments(\"any()\", \"any()\"),\n\t\t\t\targuments(\"! none()\", \"!none()\")\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\tprivate TagExpression tagExpressionParsedFrom(String tagExpression) {\n\t\treturn parser.parse(tagExpression).tagExpressionOrThrow(\n\t\t\terror -> new RuntimeException(\"[\" + tagExpression + \"] should be parsable\"));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TagExpressionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.engine.TestTag.create;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.and;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.any;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.none;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.not;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.or;\nimport static org.junit.platform.launcher.tagexpression.TagExpressions.tag;\n\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestTag;\n\nclass TagExpressionsTests {\n\n\tprivate static final TagExpression True = tags -> true;\n\tprivate static final TagExpression False = tags -> false;\n\n\t@Test\n\tvoid tagIsJustATestTag() {\n\t\tassertThat(tag(\"foo\")).hasToString(\"foo\");\n\t}\n\n\t@Test\n\tvoid rejectInvalidTestTags() {\n\t\tassertPreconditionViolationFor(() -> tag(\"tags with spaces are not allowed\"))//\n\t\t\t\t.withMessageContaining(\"tags with spaces are not allowed\");\n\t}\n\n\t@Test\n\tvoid tagEvaluation() {\n\t\tvar tagExpression = tag(\"foo\");\n\n\t\tassertThat(tagExpression.evaluate(Set.of(create(\"foo\")))).isTrue();\n\t\tassertThat(tagExpression.evaluate(Set.of(create(\"not_foo\")))).isFalse();\n\t}\n\n\t@Test\n\tvoid justConcatenateNot() {\n\t\tassertThat(not(tag(\"foo\"))).hasToString(\"!foo\");\n\t\tassertThat(not(and(tag(\"foo\"), tag(\"bar\")))).hasToString(\"!(foo & bar)\");\n\t\tassertThat(not(or(tag(\"foo\"), tag(\"bar\")))).hasToString(\"!(foo | bar)\");\n\t}\n\n\t@Test\n\tvoid notEvaluation() {\n\t\tassertThat(not(True).evaluate(Set.of())).isFalse();\n\t\tassertThat(not(False).evaluate(Set.of())).isTrue();\n\t}\n\n\t@Test\n\tvoid encloseAndWithParenthesis() {\n\t\tassertThat(and(tag(\"foo\"), tag(\"bar\"))).hasToString(\"(foo & bar)\");\n\t}\n\n\t@Test\n\tvoid andEvaluation() {\n\t\tassertThat(and(True, True).evaluate(Set.of())).isTrue();\n\t\tassertThat(and(True, False).evaluate(Set.of())).isFalse();\n\t\tassertThat(and(False, onEvaluateThrow()).evaluate(Set.of())).isFalse();\n\t}\n\n\t@Test\n\tvoid encloseOrWithParenthesis() {\n\t\tassertThat(or(tag(\"foo\"), tag(\"bar\"))).hasToString(\"(foo | bar)\");\n\t}\n\n\t@Test\n\tvoid orEvaluation() {\n\t\tassertThat(or(False, False).evaluate(Set.of())).isFalse();\n\t\tassertThat(or(True, onEvaluateThrow()).evaluate(Set.of())).isTrue();\n\t\tassertThat(or(False, True).evaluate(Set.of())).isTrue();\n\t}\n\n\t@Test\n\tvoid anyEvaluation() {\n\t\tassertThat(any().evaluate(Set.of())).isFalse();\n\t\tassertThat(any().evaluate(Set.of(TestTag.create(\"foo\")))).isTrue();\n\t}\n\n\t@Test\n\tvoid noneEvaluation() {\n\t\tassertThat(none().evaluate(Set.of())).isTrue();\n\t\tassertThat(none().evaluate(Set.of(TestTag.create(\"foo\")))).isFalse();\n\t}\n\n\tprivate TagExpression onEvaluateThrow() {\n\t\treturn tags -> {\n\t\t\tthrow new RuntimeException(\"should not be evaluated\");\n\t\t};\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.assertj.core.api.AssertionsForClassTypes.assertThat;\n\nimport org.junit.jupiter.api.Test;\n\nclass TokenTests {\n\n\t@Test\n\tvoid startIndexOfTokenString() {\n\t\tassertThat(new Token(0, \"!\").strippedTokenStartIndex()).isEqualTo(0);\n\t\tassertThat(new Token(0, \"  !\").strippedTokenStartIndex()).isEqualTo(2);\n\t\tassertThat(new Token(7, \"!\").strippedTokenStartIndex()).isEqualTo(7);\n\t}\n\n\t@Test\n\tvoid endIndexExclusive() {\n\t\tassertThat(new Token(0, \"!\").endIndexExclusive()).isEqualTo(1);\n\t\tassertThat(new Token(0, \"  !\").endIndexExclusive()).isEqualTo(3);\n\t\tassertThat(new Token(7, \"!\").endIndexExclusive()).isEqualTo(8);\n\t}\n\n\t@Test\n\tvoid lastCharacterIndex() {\n\t\tassertThat(new Token(0, \"!\").lastCharacterIndex()).isEqualTo(0);\n\t\tassertThat(new Token(0, \"  !\").lastCharacterIndex()).isEqualTo(2);\n\t\tassertThat(new Token(7, \"!\").lastCharacterIndex()).isEqualTo(7);\n\t}\n\n\t@Test\n\tvoid concatenateTwoTokens() {\n\t\tvar tokens = new Tokenizer().tokenize(\" ! foo\");\n\t\tvar one = tokens.get(0);\n\t\tvar two = tokens.get(1);\n\t\tvar joined = one.concatenate(two);\n\t\tassertThat(joined.rawString()).isEqualTo(\" ! foo\");\n\t\tassertThat(joined.startIndex()).isEqualTo(0);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/launcher/tagexpression/TokenizerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.launcher.tagexpression;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\n\nclass TokenizerTests {\n\n\t@Test\n\tvoid nullContainsNoTokens() {\n\t\tassertThat(tokenStringsExtractedFrom(null)).isEmpty();\n\t}\n\n\t@Test\n\tvoid removeLeadingAndTrailingSpaces() {\n\t\tassertThat(tokenStringsExtractedFrom(\" tag \")).containsExactly(\"tag\");\n\t}\n\n\t@Test\n\tvoid notIsAReservedKeyword() {\n\t\tassertThat(tokenStringsExtractedFrom(\"! tag\")).containsExactly(\"!\", \"tag\");\n\t\tassertThat(tokenStringsExtractedFrom(\"!tag\")).containsExactly(\"!\", \"tag\");\n\t}\n\n\t@Test\n\tvoid andIsAReservedKeyword() {\n\t\tassertThat(tokenStringsExtractedFrom(\"one & two\")).containsExactly(\"one\", \"&\", \"two\");\n\t\tassertThat(tokenStringsExtractedFrom(\"one&two\")).containsExactly(\"one\", \"&\", \"two\");\n\t}\n\n\t@Test\n\tvoid orIsAReservedKeyword() {\n\t\tassertThat(tokenStringsExtractedFrom(\"one | two\")).containsExactly(\"one\", \"|\", \"two\");\n\t\tassertThat(tokenStringsExtractedFrom(\"one|two\")).containsExactly(\"one\", \"|\", \"two\");\n\t}\n\n\t@Test\n\tvoid anyAndNoneAreReservedKeywords() {\n\t\tassertThat(tokenStringsExtractedFrom(\"!(any())\")).containsExactly(\"!\", \"(\", \"any()\", \")\");\n\t\tassertThat(tokenStringsExtractedFrom(\"!(none())\")).containsExactly(\"!\", \"(\", \"none()\", \")\");\n\t}\n\n\t@Test\n\tvoid discoverBrackets() {\n\t\tassertThat(tokenStringsExtractedFrom(\"()\")).containsExactly(\"(\", \")\");\n\t\tassertThat(tokenStringsExtractedFrom(\"(tag)\")).containsExactly(\"(\", \"tag\", \")\");\n\t\tassertThat(tokenStringsExtractedFrom(\"( tag )\")).containsExactly(\"(\", \"tag\", \")\");\n\t\tassertThat(tokenStringsExtractedFrom(\"( foo &bar)| (baz& qux )\")).containsExactly(\"(\", \"foo\", \"&\", \"bar\", \")\",\n\t\t\t\"|\", \"(\", \"baz\", \"&\", \"qux\", \")\");\n\t}\n\n\t@Test\n\tvoid extractRawStringWithSpaceCharactersBeforeTheToken() {\n\t\tassertThat(rawStringsExtractedFrom(\"(\")).containsExactly(\"(\");\n\t\tassertThat(rawStringsExtractedFrom(\"  (\")).containsExactly(\"  (\");\n\t\tassertThat(rawStringsExtractedFrom(\"  ( foo \")).containsExactly(\"  (\", \" foo\");\n\t\tassertThat(rawStringsExtractedFrom(\"(( ((   (\")).containsExactly(\"(\", \"(\", \" (\", \"(\", \"   (\");\n\t}\n\n\t@Test\n\tvoid extractStartPositionOfRawString() {\n\t\tassertThat(startIndicesExtractedFrom(\"(\")).containsExactly(0);\n\t\tassertThat(startIndicesExtractedFrom(\"  (  (\")).containsExactly(0, 3);\n\t\tassertThat(startIndicesExtractedFrom(\"foo &!bar\")).containsExactly(0, 3, 5, 6);\n\t}\n\n\tprivate Stream<Integer> startIndicesExtractedFrom(String expression) {\n\t\treturn tokensExtractedFrom(expression).map(Token::startIndex);\n\t}\n\n\tprivate Stream<String> rawStringsExtractedFrom(String expression) {\n\t\treturn tokensExtractedFrom(expression).map(Token::rawString);\n\t}\n\n\tprivate List<String> tokenStringsExtractedFrom(@Nullable String expression) {\n\t\treturn tokensExtractedFrom(expression).map(Token::string).toList();\n\t}\n\n\tprivate Stream<Token> tokensExtractedFrom(@Nullable String expression) {\n\t\treturn new Tokenizer().tokenize(expression).stream();\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/IncrementingClock.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.ZoneId;\n\n/**\n * @since 1.0\n */\nfinal class IncrementingClock extends Clock {\n\n\tprivate final Duration duration;\n\tprivate final ZoneId zone;\n\n\tprivate int counter;\n\n\tIncrementingClock(int start, Duration duration) {\n\t\tthis(start, duration, ZoneId.systemDefault());\n\t}\n\n\tprivate IncrementingClock(int start, Duration duration, ZoneId zone) {\n\t\tthis.counter = start;\n\t\tthis.duration = duration;\n\t\tthis.zone = zone;\n\t}\n\n\t@Override\n\tpublic Instant instant() {\n\t\treturn Instant.EPOCH.plus(duration.multipliedBy(counter++));\n\t}\n\n\t@Override\n\tpublic Clock withZone(ZoneId zone) {\n\t\treturn new IncrementingClock(counter, duration, zone);\n\t}\n\n\t@Override\n\tpublic ZoneId getZone() {\n\t\treturn zone;\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/LegacyXmlReportGeneratingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.joox.JOOX.$;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeFalse;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportAssertions.assertValidAccordingToJenkinsSchema;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.net.InetAddress;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Clock;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.time.LocalDateTime;\nimport java.time.Year;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.joox.Match;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.hierarchical.DemoEngineExecutionContext;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalContainerDescriptor;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestDescriptor;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\nimport org.opentest4j.AssertionFailedError;\n\n/**\n * Tests for {@link LegacyXmlReportGeneratingListener}.\n *\n * @since 1.0\n */\nclass LegacyXmlReportGeneratingListenerTests {\n\n\t@TempDir\n\tPath tempDirectory;\n\n\t@Test\n\tvoid writesFileForSingleSucceedingTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"succeedingTest\", \"display<-->Name 😎\", () -> {\n\t\t});\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"name\")).isEqualTo(\"dummy\");\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.child(\"system-out\").text()) //\n\t\t\t\t.containsSubsequence(\"unique-id: [engine:dummy]\", \"display-name: dummy\");\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"display<-->Name 😎\");\n\t\tassertThat(testcase.attr(\"classname\")).isEqualTo(\"dummy\");\n\t\tassertThat(testcase.child(\"system-out\").text()) //\n\t\t\t\t.containsSubsequence(\"unique-id: [engine:dummy]/[test:succeedingTest]\",\n\t\t\t\t\t\"display-name: dummy > display<-->Name 😎\");\n\n\t\tassertThat(testsuite.find(\"skipped\")).isEmpty();\n\t\tassertThat(testsuite.find(\"failure\")).isEmpty();\n\t\tassertThat(testsuite.find(\"error\")).isEmpty();\n\t}\n\n\t@Test\n\tvoid writesFileForSingleFailingTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"failingTest\", () -> fail(\"expected to <b>fail</b>\"));\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(0);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"failingTest\");\n\n\t\tvar failure = testcase.child(\"failure\");\n\t\tassertThat(failure.attr(\"message\")).isEqualTo(\"expected to <b>fail</b>\");\n\t\tassertThat(failure.attr(\"type\")).isEqualTo(AssertionFailedError.class.getName());\n\t\tassertThat(failure.text()).containsSubsequence(\"AssertionFailedError: expected to <b>fail</b>\", \"\\tat\");\n\n\t\tassertThat(testsuite.find(\"skipped\")).isEmpty();\n\t\tassertThat(testsuite.find(\"error\")).isEmpty();\n\t}\n\n\t@Test\n\tvoid writesFileForSingleErroneousTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"failingTest\", () -> {\n\t\t\tthrow new RuntimeException(\"error occurred\");\n\t\t});\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(1);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"failingTest\");\n\n\t\tvar error = testcase.child(\"error\");\n\t\tassertThat(error.attr(\"message\")).isEqualTo(\"error occurred\");\n\t\tassertThat(error.attr(\"type\")).isEqualTo(RuntimeException.class.getName());\n\t\tassertThat(error.text()).containsSubsequence(\"RuntimeException: error occurred\", \"\\tat\");\n\n\t\tassertThat(testsuite.find(\"skipped\")).isEmpty();\n\t\tassertThat(testsuite.find(\"failure\")).isEmpty();\n\t}\n\n\t@Test\n\tvoid writesFileForSingleSkippedTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tvar testDescriptor = engine.addTest(\"skippedTest\", () -> fail(\"never called\"));\n\t\ttestDescriptor.markSkipped(\"should be skipped\");\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(0);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"skippedTest\");\n\t\tassertThat(testcase.child(\"skipped\").text()).isEqualTo(\"should be skipped\");\n\n\t\tassertThat(testsuite.find(\"failure\")).isEmpty();\n\t\tassertThat(testsuite.find(\"error\")).isEmpty();\n\t}\n\n\t@SuppressWarnings(\"ConstantConditions\")\n\t@Test\n\tvoid writesFileForSingleAbortedTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"abortedTest\", () -> assumeFalse(true, \"deliberately aborted\"));\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(0);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"abortedTest\");\n\t\tassertThat(testcase.child(\"skipped\").text()) //\n\t\t\t\t.containsSubsequence(\"TestAbortedException: \", \"deliberately aborted\", \"at \");\n\n\t\tassertThat(testsuite.find(\"failure\")).isEmpty();\n\t\tassertThat(testsuite.find(\"error\")).isEmpty();\n\t}\n\n\t@Test\n\tvoid measuresTimesInSeconds() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"firstTest\", () -> {\n\t\t});\n\t\tengine.addTest(\"secondTest\", () -> {\n\t\t});\n\n\t\texecuteTests(engine, new IncrementingClock(0, Duration.ofMillis(333)));\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\t//               start        end\n\t\t// ----------- ---------- -----------\n\t\t// engine          0 (1)    1,665 (6)\n\t\t// firstTest     333 (2)      666 (3)\n\t\t// secondTest    999 (4)    1,332 (5)\n\n\t\tassertThat(testsuite.attr(\"time\", double.class)) //\n\t\t\t\t.isEqualTo(1.665);\n\t\tassertThat(testsuite.children(\"testcase\").matchAttr(\"name\", \"firstTest\").attr(\"time\", double.class)) //\n\t\t\t\t.isEqualTo(0.333);\n\t\tassertThat(testsuite.children(\"testcase\").matchAttr(\"name\", \"secondTest\").attr(\"time\", double.class)) //\n\t\t\t\t.isEqualTo(0.333);\n\t}\n\n\t@Test\n\tvoid testWithImmeasurableTimeIsOutputCorrectly() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test\", () -> {\n\t\t});\n\n\t\texecuteTests(engine, Clock.fixed(Instant.EPOCH, ZoneId.systemDefault()));\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.child(\"testcase\").attr(\"time\")).isEqualTo(\"0\");\n\t}\n\n\t@Test\n\tvoid writesFileForSkippedContainer() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test\", () -> fail(\"never called\"));\n\t\tengine.getEngineDescriptor().markSkipped(\"should be skipped\");\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"skipped\", int.class)).isEqualTo(1);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"test\");\n\t\tassertThat(testcase.child(\"skipped\").text()).isEqualTo(\"parent was skipped: should be skipped\");\n\t}\n\n\t@Test\n\tvoid writesFileForFailingContainer() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test\", () -> fail(\"never called\"));\n\t\tengine.getEngineDescriptor().setBeforeAllBehavior(() -> fail(\"failure before all tests\"));\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"failures\", int.class)).isEqualTo(1);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"test\");\n\n\t\tvar failure = testcase.child(\"failure\");\n\t\tassertThat(failure.attr(\"message\")).isEqualTo(\"failure before all tests\");\n\t\tassertThat(failure.attr(\"type\")).isEqualTo(AssertionFailedError.class.getName());\n\t\tassertThat(failure.text()).containsSubsequence(\"AssertionFailedError: failure before all tests\", \"\\tat\");\n\t}\n\n\t@Test\n\tvoid writesFileForFailingContainerWithoutTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addContainer(\"failingContainer\", () -> {\n\t\t\tthrow new RuntimeException(\"boom\");\n\t\t});\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(1);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"failingContainer\");\n\t\tassertThat(testcase.attr(\"classname\")).isEqualTo(\"dummy\");\n\n\t\tvar error = testcase.child(\"error\");\n\t\tassertThat(error.attr(\"message\")).isEqualTo(\"boom\");\n\t\tassertThat(error.attr(\"type\")).isEqualTo(RuntimeException.class.getName());\n\t\tassertThat(error.text()).containsSubsequence(\"RuntimeException: boom\", \"\\tat\");\n\t}\n\n\t@Test\n\tvoid writesFileForContainerFailingAfterTest() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\n\t\tvar container = engine.addChild(\"failingContainer\",\n\t\t\tuniqueId -> new DemoHierarchicalContainerDescriptor(uniqueId, \"failingContainer\", null, null) {\n\t\t\t\t@Override\n\t\t\t\tpublic void after(DemoEngineExecutionContext context) {\n\t\t\t\t\tthrow new RuntimeException(\"boom\");\n\t\t\t\t}\n\t\t\t}, \"child\");\n\t\tcontainer.addChild(new DemoHierarchicalTestDescriptor(container.getUniqueId().append(\"test\", \"someTest\"),\n\t\t\t\"someTest\", (_, _) -> {\n\t\t\t}));\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(1);\n\t\tassertThat(testsuite.attr(\"errors\", int.class)).isEqualTo(1);\n\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"someTest\");\n\t\tassertThat(testcase.attr(\"classname\")).isEqualTo(\"failingContainer\");\n\n\t\tvar error = testcase.child(\"error\");\n\t\tassertThat(error.attr(\"message\")).isEqualTo(\"boom\");\n\t\tassertThat(error.attr(\"type\")).isEqualTo(RuntimeException.class.getName());\n\t\tassertThat(error.text()).containsSubsequence(\"RuntimeException: boom\", \"\\tat\");\n\t}\n\n\t@Test\n\tvoid writesSystemProperties() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test\", () -> {\n\t\t});\n\n\t\texecuteTests(engine);\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\t\tvar properties = testsuite.child(\"properties\").children(\"property\");\n\t\tassertThat(properties.matchAttr(\"name\", \"file\\\\.separator\").attr(\"value\")).isEqualTo(File.separator);\n\t\tassertThat(properties.matchAttr(\"name\", \"path\\\\.separator\").attr(\"value\")).isEqualTo(File.pathSeparator);\n\t}\n\n\t@Test\n\tvoid writesHostNameAndTimestamp() throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test\", () -> {\n\t\t});\n\n\t\tvar now = LocalDateTime.parse(\"2016-01-28T14:02:59.123\");\n\t\tvar zone = ZoneId.systemDefault();\n\n\t\texecuteTests(engine, Clock.fixed(ZonedDateTime.of(now, zone).toInstant(), zone));\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-dummy.xml\"));\n\t\tassertThat(testsuite.attr(\"hostname\")).isEqualTo(InetAddress.getLocalHost().getHostName());\n\t\tassertThat(testsuite.attr(\"timestamp\")).isEqualTo(\"2016-01-28T14:02:59\");\n\t}\n\n\t@Test\n\tvoid printsExceptionWhenReportsDirCannotBeCreated() throws Exception {\n\t\tvar reportsDir = tempDirectory.resolve(\"dummy.txt\");\n\t\tFiles.write(reportsDir, Set.of(\"content\"));\n\n\t\tvar out = new StringWriter();\n\t\tvar listener = new LegacyXmlReportGeneratingListener(reportsDir, new PrintWriter(out));\n\n\t\tlistener.testPlanExecutionStarted(TestPlan.from(true, Set.of(), mock(), dummyOutputDirectoryCreator()));\n\n\t\tassertThat(out.toString()).containsSubsequence(\"Could not create reports directory\",\n\t\t\t\"FileAlreadyExistsException\", \"at \");\n\t}\n\n\t@Test\n\tvoid printsExceptionWhenReportCouldNotBeWritten() throws Exception {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\n\t\tvar xmlFile = tempDirectory.resolve(\"TEST-engine.xml\");\n\t\tFiles.createDirectories(xmlFile);\n\n\t\tvar out = new StringWriter();\n\t\tvar listener = new LegacyXmlReportGeneratingListener(tempDirectory, new PrintWriter(out));\n\n\t\tlistener.testPlanExecutionStarted(\n\t\t\tTestPlan.from(true, Set.of(engineDescriptor), mock(), dummyOutputDirectoryCreator()));\n\t\tlistener.executionFinished(TestIdentifier.from(engineDescriptor), successful());\n\n\t\tassertThat(out.toString()).containsSubsequence(\"Could not write XML report\", \"Exception\", \"at \");\n\t}\n\n\t@Test\n\tvoid writesReportEntriesToSystemOutElement() throws Exception {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\t\tvar childUniqueId = UniqueId.root(\"child\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(childUniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), mock(), dummyOutputDirectoryCreator());\n\n\t\tvar out = new StringWriter();\n\t\tvar listener = new LegacyXmlReportGeneratingListener(tempDirectory, new PrintWriter(out));\n\n\t\tlistener.testPlanExecutionStarted(testPlan);\n\t\tvar testIdentifier = testPlan.getTestIdentifier(childUniqueId);\n\t\tlistener.executionStarted(testIdentifier);\n\t\tlistener.reportingEntryPublished(testIdentifier, ReportEntry.from(\"foo\", \"bar\"));\n\t\tMap<String, String> map = new LinkedHashMap<>();\n\t\tmap.put(\"bar\", \"baz\");\n\t\tmap.put(\"qux\", \"foo\");\n\t\tlistener.reportingEntryPublished(testIdentifier, ReportEntry.from(map));\n\t\tlistener.executionFinished(testIdentifier, successful());\n\t\tlistener.executionFinished(testPlan.getTestIdentifier(engineDescriptor.getUniqueId()), successful());\n\n\t\tvar testsuite = readValidXmlFile(tempDirectory.resolve(\"TEST-engine.xml\"));\n\n\t\tassertThat(String.join(\"\\n\", testsuite.child(\"testcase\").children(\"system-out\").texts())) //\n\t\t\t\t.containsSubsequence( //\n\t\t\t\t\t\"Report Entry #1 (timestamp: \" + Year.now(), \"- foo: bar\\n\",\n\t\t\t\t\t\"Report Entry #2 (timestamp: \" + Year.now(), \"- bar: baz\\n\", \"- qux: foo\\n\");\n\t}\n\n\tprivate void executeTests(TestEngine engine) {\n\t\texecuteTests(engine, Clock.systemDefaultZone());\n\t}\n\n\tprivate void executeTests(TestEngine engine, Clock clock) {\n\t\tvar out = new PrintWriter(new StringWriter());\n\t\tvar reportListener = new LegacyXmlReportGeneratingListener(tempDirectory.toString(), out, clock);\n\t\tvar launcher = createLauncher(engine);\n\t\tlauncher.registerTestExecutionListeners(reportListener);\n\t\tvar request = request() //\n\t\t\t\t.configurationParameter(LauncherConstants.STACKTRACE_PRUNING_ENABLED_PROPERTY_NAME, \"false\") //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.build();\n\t\tlauncher.execute(request);\n\t}\n\n\tprivate Match readValidXmlFile(Path xmlFile) throws Exception {\n\t\tassertTrue(Files.exists(xmlFile), () -> \"File does not exist: \" + xmlFile);\n\t\ttry (var reader = Files.newBufferedReader(xmlFile)) {\n\t\t\tvar xml = $(reader);\n\t\t\tassertValidAccordingToJenkinsSchema(xml.document());\n\t\t\treturn xml;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportAssertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport javax.xml.XMLConstants;\nimport javax.xml.transform.dom.DOMSource;\nimport javax.xml.validation.Schema;\nimport javax.xml.validation.SchemaFactory;\nimport javax.xml.validation.Validator;\n\nimport org.w3c.dom.Document;\nimport org.xml.sax.SAXException;\n\n/**\n * @since 1.0\n */\nclass XmlReportAssertions {\n\n\tstatic void assertValidAccordingToJenkinsSchema(Document document) throws Exception {\n\t\ttry {\n\t\t\t// Schema is thread-safe, Validator is not\n\t\t\tvar validator = CachedSchema.JENKINS.newValidator();\n\t\t\tvalidator.validate(new DOMSource(document));\n\t\t}\n\t\tcatch (SAXException e) {\n\t\t\tfail(\"Invalid XML document: \" + document, e);\n\t\t}\n\t}\n\n\tprivate enum CachedSchema {\n\n\t\tJENKINS(\"/jenkins-junit.xsd\");\n\n\t\tprivate final Schema schema;\n\n\t\tCachedSchema(String resourcePath) {\n\t\t\tvar schemaFile = LegacyXmlReportGeneratingListener.class.getResource(resourcePath);\n\t\t\tvar schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);\n\t\t\ttry {\n\t\t\t\tthis.schema = schemaFactory.newSchema(schemaFile);\n\t\t\t}\n\t\t\tcatch (SAXException e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to create schema using \" + schemaFile, e);\n\t\t\t}\n\t\t}\n\n\t\tValidator newValidator() {\n\t\t\treturn schema.newValidator();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportDataTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.mockito.Mockito.mock;\n\nimport java.time.Clock;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\nclass XmlReportDataTests {\n\n\tprivate final ConfigurationParameters configParams = mock();\n\n\t@Test\n\tvoid resultsOfTestIdentifierWithoutAnyReportedEventsAreEmpty() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\t\tvar childUniqueId = UniqueId.root(\"child\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(childUniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar results = reportData.getResults(testPlan.getTestIdentifier(childUniqueId));\n\n\t\tassertThat(results).isEmpty();\n\t}\n\n\t@Test\n\tvoid resultsOfTestIdentifierWithoutReportedEventsContainsOnlyFailureOfAncestor() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\t\tvar childUniqueId = UniqueId.root(\"child\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(childUniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar failureOfAncestor = failed(new RuntimeException(\"failed!\"));\n\t\treportData.markFinished(testPlan.getTestIdentifier(engineDescriptor.getUniqueId()), failureOfAncestor);\n\n\t\tvar results = reportData.getResults(testPlan.getTestIdentifier(childUniqueId));\n\n\t\tassertThat(results).containsExactly(failureOfAncestor);\n\t}\n\n\t@Test\n\tvoid resultsOfTestIdentifierWithoutReportedEventsContainsOnlySuccessOfAncestor() {\n\t\tvar engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\t\tvar childUniqueId = UniqueId.root(\"child\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(childUniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\treportData.markFinished(testPlan.getTestIdentifier(engineDescriptor.getUniqueId()), successful());\n\n\t\tvar results = reportData.getResults(testPlan.getTestIdentifier(childUniqueId));\n\n\t\tassertThat(results).containsExactly(successful());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/legacy/xml/XmlReportWriterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.legacy.xml;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.joox.JOOX.$;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.TestExecutionResult.failed;\nimport static org.junit.platform.engine.TestExecutionResult.successful;\nimport static org.junit.platform.launcher.LauncherConstants.STDERR_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.LauncherConstants.STDOUT_REPORT_ENTRY_KEY;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.dummyOutputDirectoryCreator;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportAssertions.assertValidAccordingToJenkinsSchema;\nimport static org.junit.platform.reporting.legacy.xml.XmlReportWriter.ILLEGAL_CHARACTER_REPLACEMENT;\nimport static org.mockito.Mockito.mock;\n\nimport java.io.StringReader;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.time.Clock;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.joox.Match;\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.fakes.TestDescriptorStub;\nimport org.junit.platform.launcher.TestIdentifier;\nimport org.junit.platform.launcher.TestPlan;\n\n/**\n * @since 1.0\n */\nclass XmlReportWriterTests {\n\n\tprivate final ConfigurationParameters configParams = mock();\n\n\tprivate EngineDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"engine\"), \"Engine\");\n\n\t@Test\n\tvoid writesTestsuiteElementsWithoutTestcaseElementsWithoutAnyTests() throws Exception {\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tassertThat(testsuite.document().getDocumentElement().getTagName()).isEqualTo(\"testsuite\");\n\t\tassertThat(testsuite.attr(\"name\")).isEqualTo(\"Engine\");\n\t\tassertThat(testsuite.attr(\"tests\", int.class)).isEqualTo(0);\n\t\tassertThat(testsuite.find(\"testcase\")).isEmpty();\n\t}\n\n\t@Test\n\tvoid writesReportEntry() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tvar testDescriptor = new TestDescriptorStub(uniqueId, \"successfulTest\");\n\t\tengineDescriptor.addChild(testDescriptor);\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\treportData.addReportEntry(TestIdentifier.from(testDescriptor), ReportEntry.from(\"myKey\", \"myValue\"));\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), successful());\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tassertThat(String.join(\"\\n\", testsuite.find(\"system-out\").texts())) //\n\t\t\t\t.containsSubsequence(\"Report Entry #1 (timestamp: \", \"- myKey: myValue\");\n\t}\n\n\t@Test\n\tvoid writesCapturedOutput() throws Exception {\n\t\tvar classDescriptor = new TestDescriptorStub(engineDescriptor.getUniqueId().append(\"class\", \"SomeClass\"),\n\t\t\t\"SomeClass\");\n\t\tengineDescriptor.addChild(classDescriptor);\n\n\t\tvar testDescriptor = new TestDescriptorStub(classDescriptor.getUniqueId().append(\"test\", \"successfulTest\"),\n\t\t\t\"successfulTest\");\n\t\tclassDescriptor.addChild(testDescriptor);\n\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar reportEntry = ReportEntry.from(Map.of( //\n\t\t\tSTDOUT_REPORT_ENTRY_KEY, \"normal output\", //\n\t\t\tSTDERR_REPORT_ENTRY_KEY, \"error output\", //\n\t\t\t\"foo\", \"bar\"));\n\t\treportData.addReportEntry(TestIdentifier.from(testDescriptor), reportEntry);\n\t\treportData.addReportEntry(TestIdentifier.from(testDescriptor), ReportEntry.from(Map.of(\"baz\", \"qux\")));\n\t\treportData.markFinished(testPlan.getTestIdentifier(testDescriptor.getUniqueId()), successful());\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tassertThat(testsuite.find(\"system-out\").text(0)) //\n\t\t\t\t.containsSubsequence(\"unique-id: [engine:engine]/[class:SomeClass]/[test:successfulTest]\",\n\t\t\t\t\t\"display-name: Engine > SomeClass > successfulTest\");\n\t\tassertThat(testsuite.find(\"system-out\").text(1)) //\n\t\t\t\t.containsSubsequence(\"Report Entry #1 (timestamp: \", \"- foo: bar\", \"Report Entry #2 (timestamp: \",\n\t\t\t\t\t\"- baz: qux\");\n\t\tassertThat(testsuite.find(\"system-out\").text(2).strip()) //\n\t\t\t\t.isEqualTo(\"normal output\");\n\t\tassertThat(testsuite.find(\"system-err\").text().strip()) //\n\t\t\t\t.isEqualTo(\"error output\");\n\t}\n\n\t@Test\n\tvoid writesEmptySkippedElementForSkippedTestWithoutReason() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"skippedTest\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\treportData.markSkipped(testPlan.getTestIdentifier(uniqueId), null);\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"skippedTest\");\n\t\tvar skipped = testcase.child(\"skipped\");\n\t\tassertThat(skipped.size()).isEqualTo(1);\n\t\tassertThat(skipped.children()).isEmpty();\n\t}\n\n\t@Test\n\t@NullMarked\n\tvoid writesEmptyErrorElementForFailedTestWithoutCause() throws Exception {\n\t\tengineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"myEngineId\"), \"Fancy Engine\") {\n\t\t\t@Override\n\t\t\tpublic String getLegacyReportingName() {\n\t\t\t\treturn \"myEngine\";\n\t\t\t}\n\t\t};\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"some fancy name\") {\n\t\t\t@Override\n\t\t\tpublic String getLegacyReportingName() {\n\t\t\t\treturn \"failedTest\";\n\t\t\t}\n\t\t});\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(null));\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tvar testcase = testsuite.child(\"testcase\");\n\t\tassertThat(testcase.attr(\"name\")).isEqualTo(\"failedTest\");\n\t\tassertThat(testcase.attr(\"classname\")).isEqualTo(\"myEngine\");\n\t\tvar error = testcase.child(\"error\");\n\t\tassertThat(error.size()).isEqualTo(1);\n\t\tassertThat(error.children()).isEmpty();\n\t}\n\n\t@Test\n\tvoid omitsMessageAttributeForFailedTestWithThrowableWithoutMessage() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"failedTest\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(new NullPointerException()));\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tvar error = testsuite.find(\"error\");\n\t\tassertThat(error.attr(\"type\")).isEqualTo(\"java.lang.NullPointerException\");\n\t\tassertThat(error.attr(\"message\")).isNull();\n\t}\n\n\t@Test\n\tvoid writesValidXmlEvenIfExceptionMessageContainsCData() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar assertionError = new AssertionError(\"<foo><![CDATA[bar]]></foo>\");\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(assertionError));\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tassertThat(testsuite.find(\"failure\").attr(\"message\")).isEqualTo(\"<foo><![CDATA[bar]]></foo>\");\n\t}\n\n\t@Test\n\t@SetSystemProperty(key = \"foo.bar\", value = \"\\1\")\n\tvoid escapesInvalidCharactersInSystemPropertiesAndExceptionMessages() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar assertionError = new AssertionError(\"expected: <A> but was: <B\\0>\");\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(assertionError));\n\n\t\tMatch testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\t\tassertThat(testsuite.find(\"property\").matchAttr(\"name\", \"foo\\\\.bar\").attr(\"value\")) //\n\t\t\t\t.isEqualTo(String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT));\n\t\tvar failure = testsuite.find(\"failure\");\n\t\tassertThat(failure.attr(\"message\")) //\n\t\t\t\t.isEqualTo(\"expected: <A> but was: <B\" + ILLEGAL_CHARACTER_REPLACEMENT + \">\");\n\t\tassertThat(failure.text()) //\n\t\t\t\t.contains(\"AssertionError: expected: <A> but was: <B\" + ILLEGAL_CHARACTER_REPLACEMENT + \">\");\n\t}\n\n\t@ParameterizedTest(name = \"[{index}]\")\n\t@MethodSource(\"stringPairs\")\n\tvoid replacesIllegalCharacters(String input, String output) {\n\t\tassertEquals(output, XmlReportWriter.replaceIllegalCharacters(input));\n\t}\n\n\t@Test\n\tvoid writesValidXmlForExceptionMessagesContainingLineBreaks() throws Exception {\n\t\tvar uniqueId = engineDescriptor.getUniqueId().append(\"test\", \"test\");\n\t\tengineDescriptor.addChild(new TestDescriptorStub(uniqueId, \"test\"));\n\t\tvar testPlan = TestPlan.from(true, Set.of(engineDescriptor), configParams, dummyOutputDirectoryCreator());\n\n\t\tvar allWhitespaceCharacters = IntStream.range(0, 0x10000) //\n\t\t\t\t.filter(Character::isWhitespace) //\n\t\t\t\t.filter(XmlReportWriter::isAllowedXmlCharacter) //\n\t\t\t\t.mapToObj(Character::toString) //\n\t\t\t\t.collect(joining());\n\n\t\tvar message = \"a\" + allWhitespaceCharacters + \" b<&>\";\n\t\tvar reportData = new XmlReportData(testPlan, Clock.systemDefaultZone());\n\t\tvar assertionError = new AssertionError(message);\n\t\treportData.markFinished(testPlan.getTestIdentifier(uniqueId), failed(assertionError));\n\n\t\tvar testsuite = writeXmlReport(testPlan, reportData);\n\n\t\tassertValidAccordingToJenkinsSchema(testsuite.document());\n\n\t\tvar attributeValue = testsuite.find(\"failure\").attr(\"message\");\n\t\tassertThat(attributeValue).isEqualTo(message);\n\t}\n\n\tstatic Stream<Arguments> stringPairs() {\n\t\treturn Stream.of( //\n\t\t\targuments(\"\\0\", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), //\n\t\t\targuments(\"\\1\", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), //\n\t\t\targuments(\"\\t\", \"\\t\"), //\n\t\t\targuments(\"\\r\", \"\\r\"), //\n\t\t\targuments(\"\\n\", \"\\n\"), //\n\t\t\targuments(\"\\u001f\", String.valueOf(ILLEGAL_CHARACTER_REPLACEMENT)), //\n\t\t\targuments(\"✅\", \"✅\"), //\n\t\t\targuments(\" \", \" \"), //\n\t\t\targuments(\"foo!\", \"foo!\"), //\n\t\t\targuments(\"\\uD801\\uDC00\", \"\\uD801\\uDC00\") //\n\t\t);\n\t}\n\n\tprivate Match writeXmlReport(TestPlan testPlan, XmlReportData reportData) throws Exception {\n\t\tvar out = new StringWriter();\n\t\twriteXmlReport(testPlan, reportData, out);\n\t\treturn $(new StringReader(out.toString()));\n\t}\n\n\tprivate void writeXmlReport(TestPlan testPlan, XmlReportData reportData, Writer out) throws Exception {\n\t\tnew XmlReportWriter(reportData).writeXmlReport(getOnlyElement(testPlan.getRoots()), out);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/open/xml/JUnitContributorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.opentest4j.reporting.tooling.core.htmlreport.DefaultHtmlReportWriter;\n\nclass JUnitContributorTests {\n\n\t@Test\n\tvoid contributesJUnitSpecificMetadata(@TempDir Path tempDir) throws Exception {\n\t\tvar xmlFile = Files.writeString(tempDir.resolve(\"report.xml\"),\n\t\t\t\"\"\"\n\t\t\t\t\t<e:events xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\" xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\" xmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\t\t\t          xmlns:junit=\"https://schemas.junit.org/open-test-reporting\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t\t\t          xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n\t\t\t\t\t    <e:started id=\"1\" name=\"dummy\" time=\"2024-11-10T16:31:35.000Z\">\n\t\t\t\t\t        <metadata>\n\t\t\t\t\t            <junit:uniqueId>[engine:dummy]</junit:uniqueId>\n\t\t\t\t\t            <junit:legacyReportingName>dummy</junit:legacyReportingName>\n\t\t\t\t\t            <junit:type>CONTAINER</junit:type>\n\t\t\t\t\t        </metadata>\n\t\t\t\t\t    </e:started>\n\t\t\t\t\t    <e:started id=\"2\" name=\"method\" parentId=\"1\" time=\"2024-11-10T16:31:35.001Z\">\n\t\t\t\t\t        <metadata>\n\t\t\t\t\t            <junit:uniqueId>[engine:dummy]/[test:method]</junit:uniqueId>\n\t\t\t\t\t            <junit:legacyReportingName>method()</junit:legacyReportingName>\n\t\t\t\t\t            <junit:type>TEST</junit:type>\n\t\t\t\t\t        </metadata>\n\t\t\t\t\t    </e:started>\n\t\t\t\t\t    <e:finished id=\"2\" time=\"2024-11-10T16:31:35.002Z\">\n\t\t\t\t\t        <result status=\"SUCCESSFUL\"/>\n\t\t\t\t\t    </e:finished>\n\t\t\t\t\t    <e:finished id=\"1\" time=\"2024-11-10T16:31:35.003Z\">\n\t\t\t\t\t        <result status=\"SUCCESSFUL\"/>\n\t\t\t\t\t    </e:finished>\n\t\t\t\t\t</e:events>\n\t\t\t\t\t\"\"\");\n\t\tvar htmlReport = tempDir.resolve(\"report.html\");\n\n\t\tnew DefaultHtmlReportWriter().writeHtmlReport(List.of(xmlFile), htmlReport);\n\n\t\tassertThat(htmlReport).content() //\n\t\t\t\t.contains(\"JUnit metadata\") //\n\t\t\t\t.contains(\"Type\").contains(\"CONTAINER\") //\n\t\t\t\t.contains(\"Unique ID\").contains(\"[engine:dummy]\") //\n\t\t\t\t.contains(\"Legacy reporting name\").contains(\"dummy\") //\n\t\t\t\t.contains(\"Type\").contains(\"TEST\") //\n\t\t\t\t.contains(\"Unique ID\").contains(\"[engine:dummy]/[test:method]\") //\n\t\t\t\t.contains(\"Legacy reporting name\").contains(\"method()\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/reporting/open/xml/OpenTestReportGeneratingListenerTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.reporting.open.xml;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_PROPERTY_NAME;\nimport static org.junit.platform.launcher.LauncherConstants.OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\nimport static org.junit.platform.launcher.core.LauncherFactoryForTestingPurposesOnly.createLauncher;\nimport static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.GIT_ENABLED_PROPERTY_NAME;\nimport static org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener.SOCKET_PROPERTY_NAME;\nimport static org.junit.platform.reporting.testutil.FileUtils.findPath;\n\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\nimport java.io.PrintStream;\nimport java.net.InetAddress;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.time.Duration;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.commons.util.ExceptionUtils;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.FileEntry;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.hierarchical.DemoHierarchicalTestEngine;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.junit.platform.tests.process.ProcessStarter;\nimport org.opentest4j.reporting.schema.Namespace;\nimport org.opentest4j.reporting.tooling.core.validator.DefaultValidator;\nimport org.opentest4j.reporting.tooling.core.validator.ValidationResult;\nimport org.xmlunit.assertj3.XmlAssert;\nimport org.xmlunit.placeholder.PlaceholderDifferenceEvaluator;\n\n/**\n * Tests for {@link OpenTestReportGeneratingListener}.\n *\n * @since 1.9\n */\nclass OpenTestReportGeneratingListenerTests {\n\n\tprivate static final Map<String, String> NAMESPACE_CONTEXT = Map.of( //\n\t\t\"core\", Namespace.REPORTING_CORE.getUri(), //\n\t\t\"e\", Namespace.REPORTING_EVENTS.getUri(), //\n\t\t\"git\", Namespace.REPORTING_GIT.getUri() //\n\t);\n\n\tprivate PrintStream originalOut;\n\tprivate PrintStream originalErr;\n\n\t@BeforeEach\n\tvoid wrapSystemPrintStreams() {\n\t\t// Work around nesting check in org.junit.platform.launcher.core.StreamInterceptor\n\t\toriginalOut = System.out;\n\t\tSystem.setOut(new PrintStream(System.out));\n\t\toriginalErr = System.err;\n\t\tSystem.setErr(new PrintStream(System.err));\n\t}\n\n\t@AfterEach\n\tvoid restoreSystemPrintStreams() {\n\t\tSystem.setOut(originalOut);\n\t\tSystem.setErr(originalErr);\n\t}\n\n\t@Test\n\tvoid writesValidXmlReport(@TempDir Path tempDirectory) throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"failingTest\", \"display<-->Name 😎\", (context, descriptor) -> {\n\t\t\ttry {\n\t\t\t\tvar listener = context.request().getEngineExecutionListener();\n\t\t\t\tlistener.reportingEntryPublished(descriptor, ReportEntry.from(\"key\", \"value\"));\n\t\t\t\tlistener.fileEntryPublished(descriptor, FileEntry.from(\n\t\t\t\t\tFiles.writeString(tempDirectory.resolve(\"test.txt\"), \"Hello, world!\"), \"text/plain\"));\n\t\t\t\tSystem.out.println(\"Hello, stdout!\");\n\t\t\t\tSystem.err.println(\"Hello, stderr!\");\n\t\t\t}\n\t\t\tcatch (Throwable t) {\n\t\t\t\tthrow ExceptionUtils.throwAsUncheckedException(t);\n\t\t\t}\n\t\t\tfail(\"failure message\");\n\t\t});\n\n\t\texecuteTests(tempDirectory, engine, tempDirectory.resolve(\"junit-\" + OUTPUT_DIR_UNIQUE_NUMBER_PLACEHOLDER));\n\n\t\tvar xmlFile = findPath(tempDirectory, \"glob:**/open-test-report.xml\");\n\t\tassertThat(tempDirectory.relativize(xmlFile).toString()) //\n\t\t\t\t.matches(\"junit-\\\\d+[/\\\\\\\\]open-test-report.xml\");\n\t\tassertThat(validate(xmlFile)).isEmpty();\n\n\t\tvar expected = \"\"\"\n\t\t\t\t<e:events xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\"\n\t\t\t\t          xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\"\n\t\t\t\t          xmlns:git=\"https://schemas.opentest4j.org/reporting/git/0.2.0\"\n\t\t\t\t          xmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\t\t          xmlns:junit=\"https://schemas.junit.org/open-test-reporting\"\n\t\t\t\t          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t\t          xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n\t\t\t\t    <infrastructure>\n\t\t\t\t        <hostName>${xmlunit.ignore}</hostName>\n\t\t\t\t        <userName>${xmlunit.ignore}</userName>\n\t\t\t\t        <operatingSystem>${xmlunit.ignore}</operatingSystem>\n\t\t\t\t        <cpuCores>${xmlunit.ignore}</cpuCores>\n\t\t\t\t        <java:javaVersion>${xmlunit.ignore}</java:javaVersion>\n\t\t\t\t        <java:fileEncoding>${xmlunit.ignore}</java:fileEncoding>\n\t\t\t\t        <java:heapSize max=\"${xmlunit.isNumber}\"/>\n\t\t\t\t    </infrastructure>\n\t\t\t\t    <e:started id=\"1\" name=\"dummy\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <metadata>\n\t\t\t\t            <junit:uniqueId>[engine:dummy]</junit:uniqueId>\n\t\t\t\t            <junit:legacyReportingName>dummy</junit:legacyReportingName>\n\t\t\t\t            <junit:type>CONTAINER</junit:type>\n\t\t\t\t        </metadata>\n\t\t\t\t    </e:started>\n\t\t\t\t    <e:started id=\"2\" name=\"display&lt;--&gt;Name 😎\" parentId=\"1\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <metadata>\n\t\t\t\t            <junit:uniqueId>[engine:dummy]/[test:failingTest]</junit:uniqueId>\n\t\t\t\t            <junit:legacyReportingName>display&lt;--&gt;Name 😎</junit:legacyReportingName>\n\t\t\t\t            <junit:type>TEST</junit:type>\n\t\t\t\t        </metadata>\n\t\t\t\t    </e:started>\n\t\t\t\t    <e:reported id=\"2\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <attachments>\n\t\t\t\t            <data time=\"${xmlunit.isDateTime}\">\n\t\t\t\t                <entry key=\"key\">value</entry>\n\t\t\t\t            </data>\n\t\t\t\t        </attachments>\n\t\t\t\t    </e:reported>\n\t\t\t\t    <e:reported id=\"2\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <attachments>\n\t\t\t\t            <file time=\"${xmlunit.isDateTime}\" path=\"${xmlunit.matchesRegex(\\\\.\\\\.[/\\\\\\\\]test.txt)}\" mediaType=\"text/plain\" />\n\t\t\t\t        </attachments>\n\t\t\t\t    </e:reported>\n\t\t\t\t    <e:reported id=\"2\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <attachments>\n\t\t\t\t            <output time=\"${xmlunit.isDateTime}\" source=\"stdout\"><![CDATA[Hello, stdout!]]></output>\n\t\t\t\t            <output time=\"${xmlunit.isDateTime}\" source=\"stderr\"><![CDATA[Hello, stderr!]]></output>\n\t\t\t\t        </attachments>\n\t\t\t\t    </e:reported>\n\t\t\t\t    <e:finished id=\"2\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <result status=\"FAILED\">\n\t\t\t\t            <java:throwable assertionError=\"true\" type=\"org.opentest4j.AssertionFailedError\">\n\t\t\t\t                ${xmlunit.matchesRegex(org\\\\.opentest4j\\\\.AssertionFailedError: failure message)}\n\t\t\t\t            </java:throwable>\n\t\t\t\t        </result>\n\t\t\t\t    </e:finished>\n\t\t\t\t    <e:finished id=\"1\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t        <result status=\"SUCCESSFUL\"/>\n\t\t\t\t    </e:finished>\n\t\t\t\t</e:events>\n\t\t\t\t\"\"\";\n\n\t\tXmlAssert.assertThat(xmlFile).and(expected) //\n\t\t\t\t.withDifferenceEvaluator(new PlaceholderDifferenceEvaluator()) //\n\t\t\t\t.ignoreWhitespace() //\n\t\t\t\t.areIdentical();\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(strings = { \"https://github.com/junit-team/junit-framework.git\",\n\t\t\t\"git@github.com:junit-team/junit-framework.git\" })\n\tvoid includesGitInfoWhenEnabled(String originUrl, @TempDir Path tempDirectory) throws Exception {\n\n\t\tassumeTrue(tryExecGit(tempDirectory, \"--version\").exitCode() == 0, \"git not installed\");\n\t\texecGit(tempDirectory, \"init\", \"--initial-branch=my_branch\");\n\t\texecGit(tempDirectory, \"remote\", \"add\", \"origin\", originUrl);\n\n\t\tFiles.writeString(tempDirectory.resolve(\"README.md\"), \"Hello, world!\");\n\t\texecGit(tempDirectory, \"add\", \".\");\n\n\t\texecGit(tempDirectory, \"config\", \"user.name\", \"Alice\");\n\t\texecGit(tempDirectory, \"config\", \"user.email\", \"alice@example.org\");\n\t\texecGit(tempDirectory, \"commit\", \"--no-gpg-sign\", \"-m\", \"Initial commit\");\n\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\n\t\texecuteTests(tempDirectory, engine, tempDirectory.resolve(\"junit-reports\"));\n\n\t\tvar xmlFile = findPath(tempDirectory, \"glob:**/open-test-report.xml\");\n\t\tassertThat(validate(xmlFile)).isEmpty();\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.doesNotHaveXPath(\"/e:events/core:infrastructure/git:repository\");\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.doesNotHaveXPath(\"/e:events/core:infrastructure/git:branch\");\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.doesNotHaveXPath(\"/e:events/core:infrastructure/git:commit\");\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.doesNotHaveXPath(\"/e:events/core:infrastructure/git:status\");\n\n\t\texecuteTests(tempDirectory, engine, tempDirectory.resolve(\"junit-reports\"),\n\t\t\tMap.of(GIT_ENABLED_PROPERTY_NAME, \"true\"));\n\n\t\tassertThat(validate(xmlFile)).isEmpty();\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:repository/@originUrl\") //\n\t\t\t\t.isEqualTo(originUrl);\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:branch\") //\n\t\t\t\t.isEqualTo(\"my_branch\");\n\n\t\tvar commitHash = execGit(tempDirectory, \"rev-parse\", \"--verify\", \"HEAD\").stdOut().strip();\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:commit\") //\n\t\t\t\t.isEqualTo(commitHash);\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:status/@clean\") //\n\t\t\t\t.isEqualTo(false);\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:status\") //\n\t\t\t\t.startsWith(\"?? junit-reports\");\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource(textBlock = \"\"\"\n\t\t\t\thttps://foo:bar@github.com/junit-team/junit-framework.git, https://***@github.com/junit-team/junit-framework.git\n\t\t\t\thttps://token@github.com/junit-team/junit-framework.git,   https://***@github.com/junit-team/junit-framework.git\n\t\t\t\tfoo@github.com:junit-team/junit-framework.git,             ***@github.com:junit-team/junit-framework.git\n\t\t\t\tssh://foo@github.com:junit-team/junit-framework.git,       ssh://***@github.com:junit-team/junit-framework.git\n\t\t\t\tgit@github.com:junit-team/junit-framework.git,             git@github.com:junit-team/junit-framework.git\n\t\t\t\tssh://git@github.com:junit-team/junit-framework.git,       ssh://git@github.com:junit-team/junit-framework.git\n\t\t\t\"\"\")\n\tvoid stripsCredentialsFromOriginUrl(String configuredUrl, String reportedUrl, @TempDir Path tempDirectory)\n\t\t\tthrows Exception {\n\n\t\tassumeTrue(tryExecGit(tempDirectory, \"--version\").exitCode() == 0, \"git not installed\");\n\t\texecGit(tempDirectory, \"init\", \"--initial-branch=my_branch\");\n\t\texecGit(tempDirectory, \"remote\", \"add\", \"origin\", configuredUrl);\n\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\n\t\texecuteTests(tempDirectory, engine, tempDirectory.resolve(\"junit-reports\"),\n\t\t\tMap.of(GIT_ENABLED_PROPERTY_NAME, \"true\"));\n\n\t\tvar xmlFile = findPath(tempDirectory, \"glob:**/open-test-report.xml\");\n\t\tassertThat(validate(xmlFile)).isEmpty();\n\n\t\tassertThatXml(xmlFile) //\n\t\t\t\t.valueByXPath(\"/e:events/core:infrastructure/git:repository/@originUrl\") //\n\t\t\t\t.isEqualTo(reportedUrl);\n\t}\n\n\t@Test\n\tvoid writesXmlReportToSocket(@TempDir Path tempDirectory) throws Exception {\n\t\tvar engine = new DemoHierarchicalTestEngine(\"dummy\");\n\t\tengine.addTest(\"test1\", \"Test 1\", (context, descriptor) -> {\n\t\t\t// Simple test\n\t\t});\n\n\t\t// Start a server socket to receive the XML\n\t\tvar builder = new StringBuilder();\n\n\t\ttry (var serverSocket = new ServerSocket(0, 50, InetAddress.getLoopbackAddress())) { // Use any available port\n\t\t\tint port = serverSocket.getLocalPort();\n\n\t\t\t// Start a daemon thread to accept the connection and read the XML\n\t\t\tThread serverThread = new Thread(() -> {\n\t\t\t\ttry (Socket clientSocket = serverSocket.accept();\n\t\t\t\t\t\tvar reader = new BufferedReader(\n\t\t\t\t\t\t\tnew InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8))) {\n\t\t\t\t\tString line;\n\t\t\t\t\twhile ((line = reader.readLine()) != null) {\n\t\t\t\t\t\tbuilder.append(line).append(\"\\n\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch (Exception e) {\n\t\t\t\t\tfail(e);\n\t\t\t\t}\n\t\t\t});\n\t\t\tserverThread.setDaemon(true);\n\t\t\tserverThread.start();\n\n\t\t\t// Execute tests with socket configuration\n\t\t\texecuteTests(tempDirectory, engine, tempDirectory.resolve(\"junit-reports\"),\n\t\t\t\tMap.of(SOCKET_PROPERTY_NAME, String.valueOf(port)));\n\n\t\t\t// Wait for the server to receive the data\n\t\t\tassertThat(serverThread.join(Duration.ofSeconds(10))).isTrue();\n\n\t\t\t// Verify XML was received\n\t\t\tvar expected = \"\"\"\n\t\t\t\t\t<e:events xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\"\n\t\t\t\t\t          xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\"\n\t\t\t\t\t          xmlns:git=\"https://schemas.opentest4j.org/reporting/git/0.2.0\"\n\t\t\t\t\t          xmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\t\t\t          xmlns:junit=\"https://schemas.junit.org/open-test-reporting\"\n\t\t\t\t\t          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t\t\t          xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n\t\t\t\t\t    <infrastructure>\n\t\t\t\t\t        <hostName>${xmlunit.ignore}</hostName>\n\t\t\t\t\t        <userName>${xmlunit.ignore}</userName>\n\t\t\t\t\t        <operatingSystem>${xmlunit.ignore}</operatingSystem>\n\t\t\t\t\t        <cpuCores>${xmlunit.ignore}</cpuCores>\n\t\t\t\t\t        <java:javaVersion>${xmlunit.ignore}</java:javaVersion>\n\t\t\t\t\t        <java:fileEncoding>${xmlunit.ignore}</java:fileEncoding>\n\t\t\t\t\t        <java:heapSize max=\"${xmlunit.isNumber}\"/>\n\t\t\t\t\t    </infrastructure>\n\t\t\t\t\t    <e:started id=\"1\" name=\"dummy\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t\t        <metadata>\n\t\t\t\t\t            <junit:uniqueId>[engine:dummy]</junit:uniqueId>\n\t\t\t\t\t            <junit:legacyReportingName>dummy</junit:legacyReportingName>\n\t\t\t\t\t            <junit:type>CONTAINER</junit:type>\n\t\t\t\t\t        </metadata>\n\t\t\t\t\t    </e:started>\n\t\t\t\t\t    <e:started id=\"2\" name=\"Test 1\" parentId=\"1\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t\t        <metadata>\n\t\t\t\t\t            <junit:uniqueId>[engine:dummy]/[test:test1]</junit:uniqueId>\n\t\t\t\t\t            <junit:legacyReportingName>Test 1</junit:legacyReportingName>\n\t\t\t\t\t            <junit:type>TEST</junit:type>\n\t\t\t\t\t        </metadata>\n\t\t\t\t\t    </e:started>\n\t\t\t\t\t    <e:finished id=\"2\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t\t        <result status=\"SUCCESSFUL\"/>\n\t\t\t\t\t    </e:finished>\n\t\t\t\t\t    <e:finished id=\"1\" time=\"${xmlunit.isDateTime}\">\n\t\t\t\t\t        <result status=\"SUCCESSFUL\"/>\n\t\t\t\t\t    </e:finished>\n\t\t\t\t\t</e:events>\n\t\t\t\t\t\"\"\";\n\t\t\tXmlAssert.assertThat(builder.toString()).and(expected) //\n\t\t\t\t\t.withDifferenceEvaluator(new PlaceholderDifferenceEvaluator()) //\n\t\t\t\t\t.ignoreWhitespace() //\n\t\t\t\t\t.areIdentical();\n\t\t}\n\t}\n\n\tprivate static XmlAssert assertThatXml(Path xmlFile) {\n\t\treturn XmlAssert.assertThat(xmlFile) //\n\t\t\t\t.withNamespaceContext(NAMESPACE_CONTEXT);\n\t}\n\n\tprivate static ProcessResult execGit(Path workingDir, String... arguments) throws InterruptedException {\n\t\tvar result = tryExecGit(workingDir, arguments);\n\t\tassertEquals(0, result.exitCode(), \"git \" + String.join(\" \", arguments) + \" failed\");\n\t\treturn result;\n\t}\n\n\tprivate static ProcessResult tryExecGit(Path workingDir, String... arguments) throws InterruptedException {\n\t\tSystem.out.println(\"$ git \" + String.join(\" \", arguments));\n\t\treturn new ProcessStarter() //\n\t\t\t\t.executable(Path.of(\"git\")) //\n\t\t\t\t.putEnvironment(\"GIT_CONFIG_GLOBAL\", \"/dev/null\") // https://git-scm.com/docs/git#Documentation/git.txt-GITCONFIGGLOBAL\n\t\t\t\t.workingDir(workingDir) //\n\t\t\t\t.addArguments(arguments) //\n\t\t\t\t.startAndWait();\n\t}\n\n\tprivate ValidationResult validate(Path xmlFile) throws URISyntaxException {\n\t\tvar catalogUri = requireNonNull(getClass().getResource(\"catalog.xml\")).toURI();\n\t\treturn new DefaultValidator(catalogUri).validate(xmlFile);\n\t}\n\n\tprivate static void executeTests(Path tempDirectory, TestEngine engine, Path outputDir) {\n\t\texecuteTests(tempDirectory, engine, outputDir, Map.of());\n\t}\n\n\tprivate static void executeTests(Path tempDirectory, TestEngine engine, Path outputDir,\n\t\t\tMap<String, String> extraConfigurationParameters) {\n\t\tvar request = request() //\n\t\t\t\t.selectors(selectUniqueId(UniqueId.forEngine(engine.getId()))) //\n\t\t\t\t.enableImplicitConfigurationParameters(false) //\n\t\t\t\t.configurationParameter(ENABLED_PROPERTY_NAME, String.valueOf(true)) //\n\t\t\t\t.configurationParameter(CAPTURE_STDOUT_PROPERTY_NAME, String.valueOf(true)) //\n\t\t\t\t.configurationParameter(CAPTURE_STDERR_PROPERTY_NAME, String.valueOf(true)) //\n\t\t\t\t.configurationParameter(OUTPUT_DIR_PROPERTY_NAME, outputDir.toString()) //\n\t\t\t\t.configurationParameters(extraConfigurationParameters) //\n\t\t\t\t.forExecution() //\n\t\t\t\t.listeners(new OpenTestReportGeneratingListener(tempDirectory)) //\n\t\t\t\t.build();\n\t\tcreateLauncher(engine).execute(request);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/BeforeAndAfterSuiteTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Named.named;\nimport static org.junit.jupiter.params.provider.Arguments.arguments;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.suite.engine.SuiteEngineDescriptor.ENGINE_ID;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.suppressed;\n\nimport java.util.ArrayList;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.suite.api.AfterSuite;\nimport org.junit.platform.suite.api.BeforeSuite;\nimport org.junit.platform.suite.engine.testcases.StatefulTestCase;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingBeforeAndAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.FailingBeforeSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonStaticAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonStaticBeforeSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonVoidAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.NonVoidBeforeSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.ParameterAcceptingAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.ParameterAcceptingBeforeSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.PrivateAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.PrivateBeforeSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SeveralFailingBeforeAndAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SubclassWithBeforeAndAfterSuite;\nimport org.junit.platform.suite.engine.testsuites.LifecycleMethodsSuites.SuccessfulBeforeAndAfterSuite;\nimport org.junit.platform.testkit.engine.EngineExecutionResults;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n/**\n * Integration tests that verify support for {@link BeforeSuite} and {@link AfterSuite},\n * in the {@link SuiteTestEngine}.\n *\n * @since 1.11\n */\npublic class BeforeAndAfterSuiteTests {\n\n\t@BeforeEach\n\tvoid setUp() {\n\t\tStatefulTestCase.callSequence = new ArrayList<>();\n\t}\n\n\t@Test\n\tvoid successfulBeforeAndAfterSuite() {\n\t\t// @formatter:off\n\t\texecuteSuite(SuccessfulBeforeAndAfterSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(7).finished(7).succeeded(6).failed(1))\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(StatefulTestCase.Test1.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(StatefulTestCase.Test2.class.getName()), finishedWithFailure()));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"beforeSuiteMethod\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"afterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid beforeAndAfterSuiteInheritance() {\n\t\t// @formatter:off\n\t\texecuteSuite(SubclassWithBeforeAndAfterSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(7).finished(7).succeeded(6).failed(1));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"superclassBeforeSuiteMethod\",\n\t\t\t\t\"subclassBeforeSuiteMethod\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"subclassAfterSuiteMethod\",\n\t\t\t\t\"superclassAfterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid failingBeforeSuite() {\n\t\t// @formatter:off\n\t\texecuteSuite(FailingBeforeSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1))\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(\n\t\t\t\t\t\tcontainer(FailingBeforeSuite.class),\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @BeforeSuite method\"))));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"beforeSuiteMethod\",\n\t\t\t\t\"afterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid failingAfterSuite() {\n\t\t// @formatter:off\n\t\texecuteSuite(FailingAfterSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(7).finished(7).succeeded(5).failed(2))\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(\n\t\t\t\t\t\tcontainer(FailingAfterSuite.class),\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @AfterSuite method\"))));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"beforeSuiteMethod\",\n\t\t\t\t\"test1\",\n\t\t\t\t\"test2\",\n\t\t\t\t\"afterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid failingBeforeAndAfterSuite() {\n\t\t// @formatter:off\n\t\texecuteSuite(FailingBeforeAndAfterSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1))\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(\n\t\t\t\t\t\tcontainer(FailingBeforeAndAfterSuite.class),\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @BeforeSuite method\"),\n\t\t\t\t\t\t\t\tsuppressed(0, instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @AfterSuite method\")))));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"beforeSuiteMethod\",\n\t\t\t\t\"afterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid severalFailingBeforeAndAfterSuite() {\n\t\t// @formatter:off\n\t\texecuteSuite(SeveralFailingBeforeAndAfterSuite.class)\n\t\t\t\t.allEvents()\n\t\t\t\t.assertStatistics(stats -> stats.started(2).finished(2).succeeded(1).failed(1))\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(\n\t\t\t\t\t\tcontainer(SeveralFailingBeforeAndAfterSuite.class),\n\t\t\t\t\t\tfinishedWithFailure(instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @BeforeSuite method\"),\n\t\t\t\t\t\t\t\tsuppressed(0, instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @AfterSuite method\")),\n\t\t\t\t\t\t\t\tsuppressed(1, instanceOf(RuntimeException.class),\n\t\t\t\t\t\t\t\t\t\tmessage(\"Exception thrown by @AfterSuite method\")))));\n\n\t\tassertThat(StatefulTestCase.callSequence).containsExactly(\n\t\t\t\t\"beforeSuiteMethod\",\n\t\t\t\t\"afterSuiteMethod\",\n\t\t\t\t\"afterSuiteMethod\"\n\t\t);\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest(name = \"{0}\")\n\t@MethodSource\n\tvoid invalidBeforeOrAfterSuiteMethod(Class<?> testSuiteClass, Predicate<String> failureMessagePredicate) {\n\t\tvar results = engineWithSelectedSuite(testSuiteClass).discover();\n\n\t\tvar issue = getOnlyElement(results.getDiscoveryIssues());\n\t\tassertThat(issue.severity()).isEqualTo(Severity.ERROR);\n\t\tassertThat(issue.message()).matches(failureMessagePredicate);\n\t\tassertThat(issue.source()).containsInstanceOf(org.junit.platform.engine.support.descriptor.MethodSource.class);\n\t}\n\n\tprivate static Stream<Arguments> invalidBeforeOrAfterSuiteMethod() {\n\t\treturn Stream.of(\n\t\t\tinvalidBeforeOrAfterSuiteCase(NonVoidBeforeSuite.class, \"@BeforeSuite method\", \"must not return a value.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(ParameterAcceptingBeforeSuite.class, \"@BeforeSuite method\",\n\t\t\t\t\"must not accept parameters.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(NonStaticBeforeSuite.class, \"@BeforeSuite method\", \"must be static.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(PrivateBeforeSuite.class, \"@BeforeSuite method\", \"must not be private.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(NonVoidAfterSuite.class, \"@AfterSuite method\", \"must not return a value.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(ParameterAcceptingAfterSuite.class, \"@AfterSuite method\",\n\t\t\t\t\"must not accept parameters.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(NonStaticAfterSuite.class, \"@AfterSuite method\", \"must be static.\"),\n\t\t\tinvalidBeforeOrAfterSuiteCase(PrivateAfterSuite.class, \"@AfterSuite method\", \"must not be private.\"));\n\t}\n\n\tprivate static Arguments invalidBeforeOrAfterSuiteCase(Class<?> suiteClass, String failureMessageStart,\n\t\t\tString failureMessageEnd) {\n\t\treturn arguments(named(suiteClass.getSimpleName(), suiteClass),\n\t\t\texpectedMessage(failureMessageStart, failureMessageEnd));\n\t}\n\n\tprivate static Predicate<String> expectedMessage(String failureMessageStart, String failureMessageEnd) {\n\t\treturn message -> message.startsWith(failureMessageStart) && message.endsWith(failureMessageEnd);\n\t}\n\n\tprivate static EngineExecutionResults executeSuite(Class<?> suiteClass) {\n\t\treturn engineWithSelectedSuite(suiteClass).execute();\n\t}\n\n\tprivate static EngineTestKit.Builder engineWithSelectedSuite(Class<?> suiteClass) {\n\t\treturn EngineTestKit.engine(ENGINE_ID).selectors(selectClass(suiteClass));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteEngineTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.TemporaryClasspathExecutor.withAdditionalClasspathRoot;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;\nimport static org.junit.platform.launcher.TagFilter.excludeTags;\nimport static org.junit.platform.launcher.core.OutputDirectoryCreators.hierarchicalOutputDirectoryCreator;\nimport static org.junit.platform.suite.engine.SuiteEngineDescriptor.ENGINE_ID;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.instanceOf;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.mockito.ArgumentMatchers.same;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.nio.file.Path;\n\nimport org.jspecify.annotations.Nullable;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.EngineExecutionListener;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.FilterResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.engine.support.descriptor.MethodSource;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.launcher.core.NamespacedHierarchicalStoreProviders;\nimport org.junit.platform.suite.api.AfterSuite;\nimport org.junit.platform.suite.api.BeforeSuite;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.ConfigurationSensitiveTestCase;\nimport org.junit.platform.suite.engine.testcases.DynamicTestsTestCase;\nimport org.junit.platform.suite.engine.testcases.ErroneousTestCase;\nimport org.junit.platform.suite.engine.testcases.JUnit4TestsTestCase;\nimport org.junit.platform.suite.engine.testcases.MultipleTestsTestCase;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\nimport org.junit.platform.suite.engine.testcases.TaggedTestTestCase;\nimport org.junit.platform.suite.engine.testsuites.AbstractSuite;\nimport org.junit.platform.suite.engine.testsuites.BlankSuiteDisplayNameSuite;\nimport org.junit.platform.suite.engine.testsuites.ConfigurationSuite;\nimport org.junit.platform.suite.engine.testsuites.CyclicSuite;\nimport org.junit.platform.suite.engine.testsuites.DynamicSuite;\nimport org.junit.platform.suite.engine.testsuites.EmptyCyclicSuite;\nimport org.junit.platform.suite.engine.testsuites.EmptyDynamicTestSuite;\nimport org.junit.platform.suite.engine.testsuites.EmptyDynamicTestWithFailIfNoTestFalseSuite;\nimport org.junit.platform.suite.engine.testsuites.EmptyTestCaseSuite;\nimport org.junit.platform.suite.engine.testsuites.EmptyTestCaseWithFailIfNoTestFalseSuite;\nimport org.junit.platform.suite.engine.testsuites.ErroneousTestSuite;\nimport org.junit.platform.suite.engine.testsuites.InheritedSuite;\nimport org.junit.platform.suite.engine.testsuites.MultiEngineSuite;\nimport org.junit.platform.suite.engine.testsuites.MultipleSuite;\nimport org.junit.platform.suite.engine.testsuites.NestedSuite;\nimport org.junit.platform.suite.engine.testsuites.SelectByIdentifierSuite;\nimport org.junit.platform.suite.engine.testsuites.SelectClassesSuite;\nimport org.junit.platform.suite.engine.testsuites.SelectMethodsSuite;\nimport org.junit.platform.suite.engine.testsuites.SelectorProcessingErrorTestSuite;\nimport org.junit.platform.suite.engine.testsuites.SuiteDisplayNameSuite;\nimport org.junit.platform.suite.engine.testsuites.SuiteSuite;\nimport org.junit.platform.suite.engine.testsuites.SuiteWithErroneousTestSuite;\nimport org.junit.platform.suite.engine.testsuites.ThreePartCyclicSuite;\nimport org.junit.platform.suite.engine.testsuites.WhitespaceSuiteDisplayNameSuite;\nimport org.junit.platform.testkit.engine.EngineTestKit;\n\n/**\n * @since 1.8\n */\nclass SuiteEngineTests {\n\n\t@TempDir\n\tprivate Path outputDir;\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { SelectClassesSuite.class, InheritedSuite.class })\n\tvoid selectClasses(Class<?> suiteClass) {\n\t\t// @formatter:off\n\t\tEngineTestKit.Builder testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(suiteClass))\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(suiteClass.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectMethods() {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SelectMethodsSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(MultipleTestsTestCase.class.getName(), \"test()\"), finishedSuccessfully()))\n\t\t\t\t.doNotHave(event(test(MultipleTestsTestCase.class.getName(), \"test2()\")));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid suiteDisplayName() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SuiteDisplayNameSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(container(displayName(\"Suite Display Name\")), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid abstractSuiteIsNotExecuted() {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(AbstractSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid privateSuiteIsNotExecuted() {\n\t\t// @formatter:off\n\t\tvar message = \"@Suite class '%s' must not be private. It will not be executed.\"\n\t\t\t\t.formatted(PrivateSuite.class.getName());\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, message)\n\t\t\t\t.source(ClassSource.from(PrivateSuite.class))\n\t\t\t\t.build();\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(PrivateSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.containsExactly(issue);\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid abstractPrivateSuiteIsNotExecuted() {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(AbstractPrivateSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(classes = { InnerSuite.class, AbstractInnerSuite.class })\n\tvoid innerSuiteIsNotExecuted(Class<?> suiteClass) {\n\t\t// @formatter:off\n\t\tvar message = \"@Suite class '%s' must not be an inner class. Did you forget to declare it static? It will not be executed.\"\n\t\t\t\t.formatted(suiteClass.getName());\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, message)\n\t\t\t\t.source(ClassSource.from(suiteClass))\n\t\t\t\t.build();\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(suiteClass));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.containsExactly(issue);\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid localSuiteIsNotExecuted() {\n\n\t\t@Suite\n\t\t@SelectClasses(names = \"org.junit.platform.suite.engine.testcases.SingleTestTestCase\")\n\t\tclass LocalSuite {\n\t\t}\n\n\t\t// @formatter:off\n\t\tvar message = \"@Suite class '%s' must not be a local class. It will not be executed.\"\n\t\t\t\t.formatted(LocalSuite.class.getName());\n\t\tvar issue = DiscoveryIssue.builder(Severity.WARNING, message)\n\t\t\t\t.source(ClassSource.from(LocalSuite.class))\n\t\t\t\t.build();\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(LocalSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.containsExactly(issue);\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid anonymousSuiteIsNotExecuted() {\n\t\tvar object = new Object() {\n\t\t};\n\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(object.getClass()));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid nestedSuiteIsNotExecuted() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(NestedSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid dynamicSuite() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(DynamicSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(2, event(test(DynamicTestsTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid suiteSuite() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SuiteSuite.class))\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SuiteSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SelectClassesSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid multipleSuites() {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(\n\t\t\t\t\t\tselectClass(SelectClassesSuite.class),\n\t\t\t\t\t\tselectClass(MultipleSuite.class)\n\t\t\t\t)\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SelectClassesSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(2, event(test(MultipleSuite.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid multipleSuitesWithMemoryCleanupEnabled() {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.configurationParameter(LauncherConstants.MEMORY_CLEANUP_ENABLED_PROPERTY_NAME, \"true\") //\n\t\t\t\t.selectors(\n\t\t\t\t\t\tselectClass(SelectClassesSuite.class),\n\t\t\t\t\t\tselectClass(MultipleSuite.class)\n\t\t\t\t)\n\t\t\t\t.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.isEmpty();\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SelectClassesSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(2, event(test(MultipleSuite.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectClassesByUniqueId() {\n\t\t// @formatter:off\n\t\tUniqueId uniqId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, SelectClassesSuite.class.getName());\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectUniqueId(uniqId));\n\t\t\tbuilder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SelectClassesSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectMethodInTestPlanByUniqueId() {\n\t\t// @formatter:off\n\t\tUniqueId uniqueId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, MultipleSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, MultipleTestsTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectUniqueId(uniqueId))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(MultipleSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(MultipleTestsTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectSuiteByUniqueId() {\n\t\t// @formatter:off\n\t\tUniqueId uniqueId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, MultipleSuite.class.getName());\n\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectUniqueId(uniqueId))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(2, event(test(MultipleSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(2, event(test(MultipleTestsTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectMethodAndSuiteInTestPlanByUniqueId() {\n\t\t// @formatter:off\n\t\tUniqueId uniqueId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, MultipleSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, MultipleTestsTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectUniqueId(uniqueId))\n\t\t\t\t.selectors(selectClass(SelectClassesSuite.class));\n\t\t\tbuilder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SelectClassesSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(MultipleSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(MultipleTestsTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectMethodsInTestPlanByUniqueId() {\n\t\t// @formatter:off\n\t\tUniqueId uniqueId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, MultipleSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, MultipleTestsTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test()\");\n\n\t\tUniqueId uniqueId2 = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, MultipleSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, MultipleTestsTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test2()\");\n\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectUniqueId(uniqueId))\n\t\t\t\t.selectors(selectUniqueId(uniqueId2))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(2, event(test(MultipleSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(2, event(test(MultipleTestsTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectConfigurationSensitiveMethodsInTestPlanByUniqueId() {\n\t\t// @formatter:off\n\t\tvar uniqueId1 = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, ConfigurationSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, ConfigurationSensitiveTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test1()\");\n\n\t\tvar uniqueId2 = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, ConfigurationSuite.class.getName())\n\t\t\t\t.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID)\n\t\t\t\t.append(ClassTestDescriptor.SEGMENT_TYPE, ConfigurationSensitiveTestCase.class.getName())\n\t\t\t\t.append(TestMethodTestDescriptor.SEGMENT_TYPE, \"test2()\");\n\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(\n\t\t\t\t\tselectUniqueId(uniqueId1),\n\t\t\t\t\tselectUniqueId(uniqueId2)\n\t\t\t\t)\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(ConfigurationSuite.class.getName(), \"test1()\"), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(ConfigurationSuite.class.getName(), \"test2()\"), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid postDiscoveryCanRemoveTestDescriptorsInSuite() {\n\t\t// @formatter:off\n\t\tPostDiscoveryFilter postDiscoveryFilter = testDescriptor -> testDescriptor.getSource()\n\t\t\t\t.filter(MethodSource.class::isInstance)\n\t\t\t\t.map(MethodSource.class::cast)\n\t\t\t\t.filter(classSource -> SingleTestTestCase.class.equals(classSource.getJavaClass()))\n\t\t\t\t.map(classSource -> FilterResult.excluded(\"Was a test in SimpleTest\"))\n\t\t\t\t.orElseGet(() -> FilterResult.included(\"Was not a test in SimpleTest\"));\n\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SelectClassesSuite.class))\n\t\t\t\t.filters(postDiscoveryFilter)\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid emptySuiteFails() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(EmptyTestCaseSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.containerEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(container(EmptyTestCaseSuite.class), finishedWithFailure(instanceOf(NoTestsDiscoveredException.class))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid emptySuitePassesWhenFailIfNoTestIsFalse() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(EmptyTestCaseWithFailIfNoTestFalseSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.containerEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(engine(), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid emptyDynamicSuiteFails() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(EmptyDynamicTestSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.containerEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(container(EmptyDynamicTestSuite.class), finishedWithFailure(instanceOf(NoTestsDiscoveredException.class))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid emptyDynamicSuitePassesWhenFailIfNoTestIsFalse() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(EmptyDynamicTestWithFailIfNoTestFalseSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveAtLeastOne(event(container(EmptyDynamicTestWithFailIfNoTestFalseSuite.class), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid pruneAfterPostDiscoveryFilters() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(MultiEngineSuite.class))\n\t\t\t\t.filters(excludeTags(\"excluded\"))\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(JUnit4TestsTestCase.class.getName()), finishedSuccessfully()))\n\t\t\t\t.doNotHave(test(TaggedTestTestCase.class.getName()))\n\t\t\t\t.doNotHave(container(\"junit-jupiter\"));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid cyclicSuite() {\n\t\t// @formatter:off\n\t\tvar expectedUniqueId = UniqueId.forEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, CyclicSuite.class.getName())\n\t\t\t\t.appendEngine(ENGINE_ID)\n\t\t\t\t.append(SuiteTestDescriptor.SEGMENT_TYPE, CyclicSuite.class.getName());\n\t\tvar message = \"The suite configuration of [%s] resulted in a cycle [%s] and will not be discovered a second time.\"\n\t\t\t\t.formatted(CyclicSuite.class.getName(), expectedUniqueId);\n\t\tvar issue = DiscoveryIssue.builder(Severity.INFO, message)\n\t\t\t\t.source(ClassSource.from(CyclicSuite.class))\n\t\t\t\t.build();\n\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(CyclicSuite.class));\n\t\t\tvar testKit = builder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues())\n\t\t\t\t.containsExactly(issue);\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid emptyCyclicSuite() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(EmptyCyclicSuite.class))\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(container(EmptyCyclicSuite.class), finishedWithFailure(message(\n\t\t\t\t\t\t\"Suite [org.junit.platform.suite.engine.testsuites.EmptyCyclicSuite] did not discover any tests\"\n\t\t\t\t))));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid threePartCyclicSuite() {\n\t\t// @formatter:off\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(ThreePartCyclicSuite.PartA.class));\n\t\t\tbuilder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.allEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectByIdentifier() {\n\t\t// @formatter:off\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SelectByIdentifierSuite.class));\n\t\t\tbuilder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SelectByIdentifierSuite.class.getName()), finishedSuccessfully()))\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid passesOutputDirectoryCreatorToEnginesInSuite() {\n\t\t// @formatter:off\n\t\tEngineTestKit.Builder builder = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SelectClassesSuite.class));\n\t\t\tbuilder.outputDirectoryCreator(hierarchicalOutputDirectoryCreator(outputDir))\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.haveExactly(1, event(test(SingleTestTestCase.class.getName()), finishedSuccessfully()));\n\t\t// @formatter:on\n\n\t\tassertThat(outputDir).isDirectoryRecursivelyContaining(\"glob:**/test.txt\");\n\t}\n\n\t@Test\n\tvoid discoveryIssueOfNestedTestEnginesAreReported() throws Exception {\n\t\t// @formatter:off\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID)\n\t\t\t\t.selectors(selectClass(SuiteWithErroneousTestSuite.class));\n\n\t\tvar discoveryIssues = testKit.discover().getDiscoveryIssues();\n\t\tassertThat(discoveryIssues).hasSize(1);\n\n\t\tvar issue = discoveryIssues.getFirst();\n\t\tassertThat(issue.message()) //\n\t\t\t\t.startsWith(\"[junit-jupiter] @BeforeAll method\") //\n\t\t\t\t.endsWith(\" (via @Suite %s > %s).\".formatted(SuiteWithErroneousTestSuite.class.getName(),\n\t\t\t\t\t\tErroneousTestSuite.class.getName()));\n\n\t\tvar method = ErroneousTestCase.class.getDeclaredMethod(\"nonStaticLifecycleMethod\");\n\t\tassertThat(issue.source()).contains(MethodSource.from(method));\n\n\t\ttestKit\n\t\t\t\t.execute()\n\t\t\t\t.testEvents()\n\t\t\t\t.assertThatEvents()\n\t\t\t\t.isEmpty();\n\t\t// @formatter:on\n\t}\n\n\t@Test\n\tvoid selectorProcessingFailuresAreReported() {\n\t\twithAdditionalClasspathRoot(\"error-engine/\", () -> {\n\n\t\t\tvar testKit = EngineTestKit.engine(ENGINE_ID).selectors(\n\t\t\t\tselectClass(SelectorProcessingErrorTestSuite.class));\n\n\t\t\tvar discoveryIssues = testKit.discover().getDiscoveryIssues();\n\t\t\tassertThat(discoveryIssues).hasSize(1);\n\n\t\t\tvar issue = discoveryIssues.getFirst();\n\t\t\tassertThat(issue.message()) //\n\t\t\t\t\t.startsWith(\"[selector-error-engine] ErrorSelector[message=simulatedError] resolution failed\") //\n\t\t\t\t\t.endsWith(\" (via @Suite %s).\".formatted(SelectorProcessingErrorTestSuite.class.getName()));\n\t\t});\n\t}\n\n\t@Test\n\tvoid suiteEnginePassesRequestLevelStoreToSuiteTestDescriptors() {\n\t\tUniqueId engineId = UniqueId.forEngine(SuiteEngineDescriptor.ENGINE_ID);\n\t\tSuiteEngineDescriptor engineDescriptor = new SuiteEngineDescriptor(engineId);\n\n\t\tSuiteTestDescriptor mockDescriptor = mock(SuiteTestDescriptor.class);\n\t\tengineDescriptor.addChild(mockDescriptor);\n\n\t\tEngineExecutionListener listener = mock(EngineExecutionListener.class);\n\t\tNamespacedHierarchicalStore<Namespace> requestLevelStore = NamespacedHierarchicalStoreProviders.dummyNamespacedHierarchicalStore();\n\t\tvar cancellationToken = CancellationToken.create();\n\n\t\tExecutionRequest request = mock();\n\t\twhen(request.getRootTestDescriptor()).thenReturn(engineDescriptor);\n\t\twhen(request.getEngineExecutionListener()).thenReturn(listener);\n\t\twhen(request.getStore()).thenReturn(requestLevelStore);\n\t\twhen(request.getCancellationToken()).thenReturn(cancellationToken);\n\n\t\tnew SuiteTestEngine().execute(request);\n\n\t\tverify(mockDescriptor).execute(same(listener), same(requestLevelStore), same(cancellationToken));\n\t}\n\n\t@Test\n\tvoid reportsSuiteClassAsSkippedWhenCancelledBeforeExecution() {\n\t\tCancellingSuite.cancellationToken = CancellationToken.create();\n\t\ttry {\n\t\t\tvar testKit = EngineTestKit.engine(ENGINE_ID) //\n\t\t\t\t\t.selectors(selectClass(CancellingSuite.class), selectClass(SelectMethodsSuite.class)) //\n\t\t\t\t\t.cancellationToken(CancellingSuite.cancellationToken);\n\n\t\t\tvar results = testKit.execute();\n\n\t\t\tresults.allEvents() //\n\t\t\t\t\t.assertStatistics(stats -> stats.started(3).succeeded(2).aborted(1).skipped(2)) //\n\t\t\t\t\t.assertEventsMatchLooselyInOrder( //\n\t\t\t\t\t\tevent(container(CancellingSuite.class), started()), //\n\t\t\t\t\t\tevent(container(SingleTestTestCase.class), skippedWithReason(\"Execution cancelled\")), //\n\t\t\t\t\t\tevent(container(CancellingSuite.class), finishedSuccessfully()), //\n\t\t\t\t\t\tevent(container(SelectMethodsSuite.class), skippedWithReason(\"Execution cancelled\")) //\n\t\t\t\t\t);\n\t\t}\n\t\tfinally {\n\t\t\tCancellingSuite.cancellationToken = null;\n\t\t}\n\t}\n\n\t@Test\n\tvoid reportsChildrenOfEnginesInSuiteAsSkippedWhenCancelledDuringExecution() {\n\t\tCancellingSuite.cancellationToken = CancellationToken.create();\n\t\ttry {\n\t\t\tvar testKit = EngineTestKit.engine(ENGINE_ID) //\n\t\t\t\t\t.selectors(selectClass(CancellingSuite.class)) //\n\t\t\t\t\t.cancellationToken(CancellingSuite.cancellationToken);\n\n\t\t\tvar results = testKit.execute();\n\n\t\t\tresults.allEvents().assertThatEvents() //\n\t\t\t\t\t.haveExactly(1, event(container(SingleTestTestCase.class),\n\t\t\t\t\t\tskippedWithReason(\"Execution cancelled\"))).haveExactly(0, event(test(), started()));\n\n\t\t\tassertThat(CancellingSuite.afterCalled) //\n\t\t\t\t\t.describedAs(\"@AfterSuite method was called\") //\n\t\t\t\t\t.isTrue();\n\t\t}\n\t\tfinally {\n\t\t\tCancellingSuite.cancellationToken = null;\n\t\t}\n\t}\n\n\t@Test\n\tvoid blankSuiteDisplayNameGeneratesWarning() {\n\t\tvar expectedMessage = \"@SuiteDisplayName on %s must be declared with a non-blank value.\".formatted(\n\t\t\tBlankSuiteDisplayNameSuite.class.getName());\n\t\tvar expectedIssue = DiscoveryIssue.builder(Severity.WARNING, expectedMessage).source(\n\t\t\tClassSource.from(BlankSuiteDisplayNameSuite.class)).build();\n\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID).selectors(selectClass(BlankSuiteDisplayNameSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues()).contains(expectedIssue);\n\t}\n\n\t@Test\n\tvoid whitespaceSuiteDisplayNameGeneratesWarning() {\n\t\tvar expectedMessage = \"@SuiteDisplayName on %s must be declared with a non-blank value.\".formatted(\n\t\t\tWhitespaceSuiteDisplayNameSuite.class.getName());\n\t\tvar expectedIssue = DiscoveryIssue.builder(Severity.WARNING, expectedMessage).source(\n\t\t\tClassSource.from(WhitespaceSuiteDisplayNameSuite.class)).build();\n\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID).selectors(selectClass(WhitespaceSuiteDisplayNameSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues()).contains(expectedIssue);\n\t}\n\n\t@Test\n\tvoid validSuiteDisplayNameGeneratesNoWarning() {\n\t\tvar testKit = EngineTestKit.engine(ENGINE_ID).selectors(selectClass(SuiteDisplayNameSuite.class));\n\n\t\tassertThat(testKit.discover().getDiscoveryIssues()) //\n\t\t\t\t.noneMatch(issue -> issue.message().contains(\"@SuiteDisplayName\"));\n\t}\n\n\t// -----------------------------------------------------------------------------------------------------------------\n\n\tstatic class CancellingSuite extends SelectClassesSuite {\n\n\t\tstatic @Nullable CancellationToken cancellationToken;\n\t\tstatic boolean afterCalled;\n\n\t\t@BeforeSuite\n\t\tstatic void beforeSuite() {\n\t\t\tCancellingSuite.afterCalled = false;\n\t\t\trequireNonNull(cancellationToken).cancel();\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void afterSuite() {\n\t\t\tafterCalled = true;\n\t\t}\n\t}\n\n\t@Suite\n\t@SelectClasses(SingleTestTestCase.class)\n\tabstract private static class AbstractPrivateSuite {\n\t}\n\n\t@Suite\n\t@SelectClasses(SingleTestTestCase.class)\n\tprivate static class PrivateSuite {\n\t}\n\n\t@Suite\n\t@SelectClasses(names = \"org.junit.platform.suite.engine.testcases.SingleTestTestCase\")\n\tabstract class AbstractInnerSuite {\n\t}\n\n\t@Suite\n\t@SelectClasses(names = \"org.junit.platform.suite.engine.testcases.SingleTestTestCase\")\n\tclass InnerSuite {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteLauncherDiscoveryRequestBuilderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static java.util.Map.entry;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertFalse;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilder.request;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.engine.JupiterTestEngine;\nimport org.junit.platform.commons.util.CollectionUtils;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestTag;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.discovery.ClassNameFilter;\nimport org.junit.platform.engine.discovery.ClassSelector;\nimport org.junit.platform.engine.discovery.ClasspathResourceSelector;\nimport org.junit.platform.engine.discovery.DirectorySelector;\nimport org.junit.platform.engine.discovery.DiscoverySelectors;\nimport org.junit.platform.engine.discovery.FilePosition;\nimport org.junit.platform.engine.discovery.FileSelector;\nimport org.junit.platform.engine.discovery.MethodSelector;\nimport org.junit.platform.engine.discovery.ModuleSelector;\nimport org.junit.platform.engine.discovery.PackageNameFilter;\nimport org.junit.platform.engine.discovery.PackageSelector;\nimport org.junit.platform.engine.discovery.UriSelector;\nimport org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;\nimport org.junit.platform.launcher.EngineFilter;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.PostDiscoveryFilter;\nimport org.junit.platform.suite.api.ConfigurationParameter;\nimport org.junit.platform.suite.api.ConfigurationParametersResource;\nimport org.junit.platform.suite.api.DisableParentConfigurationParameters;\nimport org.junit.platform.suite.api.ExcludeClassNamePatterns;\nimport org.junit.platform.suite.api.ExcludeEngines;\nimport org.junit.platform.suite.api.ExcludePackages;\nimport org.junit.platform.suite.api.ExcludeTags;\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.IncludePackages;\nimport org.junit.platform.suite.api.IncludeTags;\nimport org.junit.platform.suite.api.Select;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.SelectClasspathResource;\nimport org.junit.platform.suite.api.SelectDirectories;\nimport org.junit.platform.suite.api.SelectFile;\nimport org.junit.platform.suite.api.SelectMethod;\nimport org.junit.platform.suite.api.SelectModules;\nimport org.junit.platform.suite.api.SelectPackages;\nimport org.junit.platform.suite.api.SelectUris;\n\nclass SuiteLauncherDiscoveryRequestBuilderTests {\n\n\tSuiteLauncherDiscoveryRequestBuilder builder = request();\n\n\t@Test\n\tvoid configurationParameter() {\n\t\t@ConfigurationParameter(key = \"com.example\", value = \"*\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build();\n\t\tConfigurationParameters configuration = request.getConfigurationParameters();\n\t\tOptional<String> parameter = configuration.get(\"com.example\");\n\t\tassertEquals(Optional.of(\"*\"), parameter);\n\t}\n\n\t@Test\n\tvoid configurationParametersResource() {\n\t\t@ConfigurationParametersResource(\"config-test.properties\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build();\n\t\tConfigurationParameters configuration = request.getConfigurationParameters();\n\t\tOptional<String> parameter = configuration.get(\"com.example.prop.first\");\n\t\tassertEquals(Optional.of(\"first value\"), parameter);\n\t}\n\n\t@Test\n\tvoid configurationParametersResources() {\n\t\t@ConfigurationParametersResource(\"config-test.properties\")\n\t\t@ConfigurationParametersResource(\"config-test-override.properties\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build();\n\t\tConfigurationParameters configuration = request.getConfigurationParameters();\n\t\tOptional<String> parameterOne = configuration.get(\"com.example.prop.first\");\n\t\tassertEquals(Optional.of(\"first value from override file\"), parameterOne);\n\t\tOptional<String> parameterTwo = configuration.get(\"com.example.prop.second\");\n\t\tassertEquals(Optional.of(\"second value\"), parameterTwo);\n\t}\n\n\t@Test\n\tvoid configurationParametersResource_explicitParametersTakePrecedence() {\n\t\t@ConfigurationParametersResource(\"config-test.properties\")\n\t\t@ConfigurationParameter(key = \"com.example.prop.first\", value = \"first value from explicit parameter\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applyConfigurationParametersFromSuite(Suite.class).build();\n\t\tConfigurationParameters configuration = request.getConfigurationParameters();\n\t\tOptional<String> parameterOne = configuration.get(\"com.example.prop.first\");\n\t\tassertEquals(Optional.of(\"first value from explicit parameter\"), parameterOne);\n\t\tOptional<String> parameterTwo = configuration.get(\"com.example.prop.second\");\n\t\tassertEquals(Optional.of(\"second value\"), parameterTwo);\n\t}\n\n\t@Test\n\tvoid excludeClassNamePatterns() {\n\t\tclass TestCase {\n\t\t}\n\t\t@ExcludeClassNamePatterns(\"^.*TestCase$\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClassNameFilter> filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertTrue(exactlyOne(filters).apply(TestCase.class.getName()).excluded());\n\t}\n\n\t@Test\n\tvoid excludeEngines() {\n\t\t@ExcludeEngines(\"junit-jupiter\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<EngineFilter> filters = request.getEngineFilters();\n\t\tassertTrue(exactlyOne(filters).apply(new JupiterTestEngine()).excluded());\n\t}\n\n\t@Test\n\tvoid excludePackages() {\n\t\t@ExcludePackages(\"com.example.testcases\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PackageNameFilter> filters = request.getFiltersByType(PackageNameFilter.class);\n\t\tassertTrue(exactlyOne(filters).apply(\"com.example.testcases\").excluded());\n\t}\n\n\t@Test\n\tvoid excludeTags() {\n\t\t@ExcludeTags(\"test-tag\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PostDiscoveryFilter> filters = request.getPostDiscoveryFilters();\n\t\tTestDescriptor testDescriptor = new StubAbstractTestDescriptor();\n\t\tassertTrue(exactlyOne(filters).apply(testDescriptor).excluded());\n\t}\n\n\t@Test\n\tvoid includeClassNamePatterns() {\n\t\tclass TestCase {\n\t\t}\n\t\t@IncludeClassNamePatterns(\"^.*TestCase$\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClassNameFilter> filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertTrue(exactlyOne(filters).apply(TestCase.class.getName()).included());\n\t\tassertTrue(exactlyOne(filters).apply(Suite.class.getName()).excluded());\n\t}\n\n\t@Test\n\tvoid filtersOnStandardClassNamePatternsWhenIncludeClassNamePatternsIsOmitted() {\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tassertTrue(request.getFiltersByType(ClassNameFilter.class).isEmpty());\n\t}\n\n\t@Test\n\tvoid filtersOnStandardClassNamePatternsWhenIncludeClassNamePatternsIsOmittedUnlessDisabled() {\n\t\tclass ExampleTest {\n\t\t}\n\t\tclass Suite {\n\t\t}\n\n\t\t// @formatter:off\n\t\tLauncherDiscoveryRequest request = builder\n\t\t\t\t.filterStandardClassNamePatterns()\n\t\t\t\t.applySelectorsAndFiltersFromSuite(Suite.class)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tList<ClassNameFilter> filters = request.getFiltersByType(ClassNameFilter.class);\n\t\tassertTrue(exactlyOne(filters).apply(ExampleTest.class.getName()).included());\n\t\tassertTrue(exactlyOne(filters).apply(Suite.class.getName()).excluded());\n\t}\n\n\t@Test\n\tvoid includeEngines() {\n\t\t@IncludeEngines(\"junit-jupiter\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<EngineFilter> filters = request.getEngineFilters();\n\t\tassertTrue(exactlyOne(filters).apply(new JupiterTestEngine()).included());\n\t}\n\n\t@Test\n\tvoid includePackages() {\n\t\t@IncludePackages(\"com.example.testcases\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PackageNameFilter> filters = request.getFiltersByType(PackageNameFilter.class);\n\t\tassertTrue(exactlyOne(filters).apply(\"com.example.testcases\").included());\n\t}\n\n\t@Test\n\tvoid includeTags() {\n\t\t@IncludeTags(\"test-tag\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PostDiscoveryFilter> filters = request.getPostDiscoveryFilters();\n\t\tTestDescriptor testDescriptor = new StubAbstractTestDescriptor();\n\t\tassertTrue(exactlyOne(filters).apply(testDescriptor).included());\n\t}\n\n\t@Test\n\tvoid selectClassesByReference() {\n\t\tclass TestCase {\n\t\t}\n\t\t@SelectClasses(TestCase.class)\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClassSelector> selectors = request.getSelectorsByType(ClassSelector.class);\n\t\tassertFalse(selectors.isEmpty());\n\t\tassertEquals(TestCase.class, exactlyOne(selectors).getJavaClass());\n\t}\n\n\t@Test\n\tvoid selectClassesByName() {\n\t\t@SelectClasses(names = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NonLocalTestCase\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClassSelector> selectors = request.getSelectorsByType(ClassSelector.class);\n\t\tassertEquals(NonLocalTestCase.class, exactlyOne(selectors).getJavaClass());\n\t}\n\n\t@Test\n\tvoid selectClassesWithoutReferencesOrNames() {\n\t\t@SelectClasses\n\t\tclass Suite {\n\t\t}\n\n\t\tassertPreconditionViolationFor(() -> builder.applySelectorsAndFiltersFromSuite(Suite.class))//\n\t\t\t\t.withMessageMatching(\"@SelectClasses on class \\\\[\"\n\t\t\t\t\t\t+ Pattern.quote(SuiteLauncherDiscoveryRequestBuilderTests.class.getName())\n\t\t\t\t\t\t+ \"\\\\$\\\\d+Suite] must declare at least one class reference or name\");\n\t}\n\n\tstatic class NonLocalTestCase {\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> selectOneMethodWithNoParameters() {\n\t\t@SelectMethod(\"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase#testMethod\")\n\t\tclass SuiteA {\n\t\t}\n\t\t@SelectMethod(type = NoParameterTestCase.class, name = \"testMethod\")\n\t\tclass SuiteB {\n\t\t}\n\t\t@SelectMethod(typeName = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase\", name = \"testMethod\")\n\t\tclass SuiteC {\n\t\t}\n\n\t\treturn Stream.of(SuiteA.class, SuiteB.class, SuiteC.class) //\n\t\t\t\t.map(suiteClass -> dynamicTest(suiteClass.getSimpleName(), () -> {\n\t\t\t\t\tLauncherDiscoveryRequest request = request().applySelectorsAndFiltersFromSuite(suiteClass).build();\n\t\t\t\t\tList<MethodSelector> selectors = request.getSelectorsByType(MethodSelector.class);\n\t\t\t\t\tassertEquals(DiscoverySelectors.selectMethod(NoParameterTestCase.class, \"testMethod\"),\n\t\t\t\t\t\texactlyOne(selectors));\n\t\t\t\t}));\n\t}\n\n\tstatic class NoParameterTestCase {\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid testMethod() {\n\t\t}\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> selectOneMethodWithOneParameter() {\n\t\t@SelectMethod(\"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$OneParameterTestCase#testMethod(int)\")\n\t\tclass SuiteA {\n\t\t}\n\t\t@SelectMethod(type = OneParameterTestCase.class, name = \"testMethod\", parameterTypeNames = \"int\")\n\t\tclass SuiteB {\n\t\t}\n\t\t@SelectMethod(type = OneParameterTestCase.class, name = \"testMethod\", parameterTypes = int.class)\n\t\tclass SuiteC {\n\t\t}\n\t\t@SelectMethod(typeName = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$OneParameterTestCase\", name = \"testMethod\", parameterTypeNames = \"int\")\n\t\tclass SuiteD {\n\t\t}\n\t\t@SelectMethod(typeName = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$OneParameterTestCase\", name = \"testMethod\", parameterTypes = int.class)\n\t\tclass SuiteE {\n\t\t}\n\n\t\treturn Stream.of(SuiteA.class, SuiteB.class, SuiteC.class, SuiteD.class, SuiteE.class) //\n\t\t\t\t.map(suiteClass -> dynamicTest(suiteClass.getSimpleName(), () -> {\n\t\t\t\t\tLauncherDiscoveryRequest request = request().applySelectorsAndFiltersFromSuite(suiteClass).build();\n\t\t\t\t\tList<MethodSelector> selectors = request.getSelectorsByType(MethodSelector.class);\n\t\t\t\t\tassertEquals(DiscoverySelectors.selectMethod(OneParameterTestCase.class, \"testMethod\", \"int\"),\n\t\t\t\t\t\texactlyOne(selectors));\n\t\t\t\t}));\n\t}\n\n\tstatic class OneParameterTestCase {\n\t\t@SuppressWarnings(\"unused\")\n\t\tvoid testMethod(int i) {\n\t\t}\n\t}\n\n\t@Test\n\tvoid selectTwoMethodsWithTwoParameters() {\n\t\t@SuppressWarnings(\"unused\")\n\t\tclass TestClass {\n\t\t\tvoid firstTestMethod(int i, String j) {\n\t\t\t}\n\n\t\t\tvoid secondTestMethod(boolean i, float j) {\n\t\t\t}\n\t\t}\n\t\t@SelectMethod(type = TestClass.class, name = \"firstTestMethod\", parameterTypeNames = \"int, java.lang.String\")\n\t\t@SelectMethod(type = TestClass.class, name = \"secondTestMethod\", parameterTypes = { boolean.class,\n\t\t\t\tfloat.class })\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<MethodSelector> selectors = request.getSelectorsByType(MethodSelector.class);\n\t\tassertEquals(2, selectors.size());\n\t\tassertEquals(DiscoverySelectors.selectMethod(TestClass.class, \"firstTestMethod\", \"int, java.lang.String\"),\n\t\t\tselectors.get(0));\n\t\tassertEquals(DiscoverySelectors.selectMethod(TestClass.class, \"secondTestMethod\", \"boolean, float\"),\n\t\t\tselectors.get(1));\n\t}\n\n\t@TestFactory\n\tStream<DynamicTest> selectMethodCausesExceptionOnInvalidUsage() {\n\t\t@SelectMethod(value = \"irrelevant\", type = NoParameterTestCase.class)\n\t\tclass ValueAndType {\n\t\t}\n\t\t@SelectMethod(value = \"SomeClass#someMethod\", typeName = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase\")\n\t\tclass ValueAndTypeName {\n\t\t}\n\t\t@SelectMethod(value = \"SomeClass#someMethod\", name = \"testMethod\")\n\t\tclass ValueAndMethodName {\n\t\t}\n\t\t@SelectMethod(value = \"SomeClass#someMethod\", parameterTypes = int.class)\n\t\tclass ValueAndParameterTypes {\n\t\t}\n\t\t@SelectMethod(value = \"SomeClass#someMethod\", parameterTypeNames = \"int\")\n\t\tclass ValueAndParameterTypeNames {\n\t\t}\n\t\t@SelectMethod(type = NoParameterTestCase.class)\n\t\tclass MissingMethodName {\n\t\t}\n\t\t@SelectMethod(name = \"testMethod\", type = NoParameterTestCase.class, typeName = \"org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase\")\n\t\tclass TypeAndTypeName {\n\t\t}\n\t\t@SelectMethod(name = \"testMethod\", parameterTypes = int.class, parameterTypeNames = \"int\")\n\t\tclass ParameterTypesAndParameterTypeNames {\n\t\t}\n\n\t\tvar expectedFailureMessages = Map.ofEntries( //\n\t\t\tentry(ValueAndType.class, \"type must not be set in conjunction with fully qualified method name\"), //\n\t\t\tentry(ValueAndTypeName.class, \"type name must not be set in conjunction with fully qualified method name\"), //\n\t\t\tentry(ValueAndMethodName.class,\n\t\t\t\t\"method name must not be set in conjunction with fully qualified method name\"), //\n\t\t\tentry(ValueAndParameterTypes.class,\n\t\t\t\t\"parameter types must not be set in conjunction with fully qualified method name\"), //\n\t\t\tentry(ValueAndParameterTypeNames.class,\n\t\t\t\t\"parameter type names must not be set in conjunction with fully qualified method name\"), //\n\t\t\tentry(MissingMethodName.class, \"method name must not be blank\"), //\n\t\t\tentry(TypeAndTypeName.class, \"either type name or type must be set but not both\"), //\n\t\t\tentry(ParameterTypesAndParameterTypeNames.class,\n\t\t\t\t\"either parameter type names or parameter types must be set but not both\") //\n\t\t);\n\t\treturn expectedFailureMessages.entrySet().stream() //\n\t\t\t\t.map(entry -> {\n\t\t\t\t\tClass<?> suiteClassName = entry.getKey();\n\t\t\t\t\tvar expectedFailureMessage = entry.getValue();\n\t\t\t\t\treturn dynamicTest(suiteClassName.getSimpleName(), () -> {\n\t\t\t\t\t\tassertPreconditionViolationFor(\n\t\t\t\t\t\t\t() -> request().applySelectorsAndFiltersFromSuite(suiteClassName))//\n\t\t\t\t\t\t\t\t\t.withMessage(\"@SelectMethod on class [\" + suiteClassName.getName() + \"]: \"\n\t\t\t\t\t\t\t\t\t\t\t+ expectedFailureMessage);\n\t\t\t\t\t});\n\t\t\t\t});\n\t}\n\n\t@Test\n\tvoid selectClasspathResource() {\n\t\t@SelectClasspathResource(\"com.example.testcases\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClasspathResourceSelector> selectors = request.getSelectorsByType(ClasspathResourceSelector.class);\n\t\tassertEquals(\"com.example.testcases\", exactlyOne(selectors).getClasspathResourceName());\n\t}\n\n\t@Test\n\tvoid selectClasspathResourcePosition() {\n\t\t@SelectClasspathResource(value = \"com.example.testcases\", line = 42)\n\t\t@SelectClasspathResource(value = \"com.example.testcases\", line = 14, column = 15)\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClasspathResourceSelector> selectors = request.getSelectorsByType(ClasspathResourceSelector.class);\n\t\tassertEquals(Optional.of(FilePosition.from(42)), selectors.get(0).getPosition());\n\t\tassertEquals(Optional.of(FilePosition.from(14, 15)), selectors.get(1).getPosition());\n\t}\n\n\t@Test\n\tvoid ignoreClasspathResourcePosition() {\n\t\t@SelectClasspathResource(value = \"com.example.testcases\", line = -1)\n\t\t@SelectClasspathResource(value = \"com.example.testcases\", column = 12)\n\t\t@SelectClasspathResource(value = \"com.example.testcases\", line = 42, column = -12)\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClasspathResourceSelector> selectors = request.getSelectorsByType(ClasspathResourceSelector.class);\n\t\tassertEquals(Optional.empty(), selectors.get(0).getPosition());\n\t\tassertEquals(Optional.empty(), selectors.get(1).getPosition());\n\t\tassertEquals(Optional.of(FilePosition.from(42)), selectors.get(2).getPosition());\n\t}\n\n\t@Test\n\tvoid selectDirectories() {\n\t\t@SelectDirectories(\"path/to/root\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<DirectorySelector> selectors = request.getSelectorsByType(DirectorySelector.class);\n\t\tassertEquals(Path.of(\"path/to/root\"), exactlyOne(selectors).getPath());\n\t}\n\n\t@Test\n\tvoid selectDirectoriesFiltersEmptyPaths() {\n\t\t@SelectDirectories(\"\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tassertTrue(request.getSelectorsByType(DirectorySelector.class).isEmpty());\n\t}\n\n\t@Test\n\tvoid selectFile() {\n\t\t@SelectFile(\"path/to/root\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<FileSelector> selectors = request.getSelectorsByType(FileSelector.class);\n\t\tassertEquals(Path.of(\"path/to/root\"), exactlyOne(selectors).getPath());\n\t}\n\n\t@Test\n\tvoid selectFilePosition() {\n\t\t@SelectFile(value = \"path/to/root\", line = 42)\n\t\t@SelectFile(value = \"path/to/root\", line = 14, column = 15)\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<FileSelector> selectors = request.getSelectorsByType(FileSelector.class);\n\t\tassertEquals(Optional.of(FilePosition.from(42)), selectors.get(0).getPosition());\n\t\tassertEquals(Optional.of(FilePosition.from(14, 15)), selectors.get(1).getPosition());\n\t}\n\n\t@Test\n\tvoid ignoreInvalidFilePosition() {\n\t\t@SelectFile(value = \"path/to/root\", line = -1)\n\t\t@SelectFile(value = \"path/to/root\", column = 12)\n\t\t@SelectFile(value = \"path/to/root\", line = 42, column = -12)\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<FileSelector> selectors = request.getSelectorsByType(FileSelector.class);\n\t\tassertEquals(Optional.empty(), selectors.get(0).getPosition());\n\t\tassertEquals(Optional.empty(), selectors.get(1).getPosition());\n\t\tassertEquals(Optional.of(FilePosition.from(42)), selectors.get(2).getPosition());\n\t}\n\n\t@Test\n\tvoid selectModules() {\n\t\t@SelectModules(\"com.example.testcases\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ModuleSelector> selectors = request.getSelectorsByType(ModuleSelector.class);\n\t\tassertEquals(\"com.example.testcases\", exactlyOne(selectors).getModuleName());\n\t}\n\n\t@Test\n\tvoid selectUris() {\n\t\t@SelectUris(\"path/to/root\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<UriSelector> selectors = request.getSelectorsByType(UriSelector.class);\n\t\tassertEquals(URI.create(\"path/to/root\"), exactlyOne(selectors).getUri());\n\t}\n\n\t@Test\n\tvoid selectUrisFiltersEmptyUris() {\n\t\t@SelectUris(\"\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tassertTrue(request.getSelectorsByType(UriSelector.class).isEmpty());\n\t}\n\n\t@Test\n\tvoid selectPackages() {\n\t\t@SelectPackages(\"com.example.testcases\")\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PackageSelector> selectors = request.getSelectorsByType(PackageSelector.class);\n\t\tassertEquals(\"com.example.testcases\", exactlyOne(selectors).getPackageName());\n\t}\n\n\t@SelectPackages(\"com.example.testcases\")\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@interface Meta {\n\t}\n\n\t@Test\n\tvoid metaAnnotations() {\n\t\t@Meta\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<PackageSelector> pSelectors = request.getSelectorsByType(PackageSelector.class);\n\t\tassertEquals(\"com.example.testcases\", exactlyOne(pSelectors).getPackageName());\n\t}\n\n\t@Test\n\tvoid enableParentConfigurationParametersByDefault() {\n\t\tclass Suite {\n\n\t\t}\n\t\t// @formatter:off\n\t\tvar configuration = new ParentConfigurationParameters(\"parent\", \"parent parameters were used\");\n\t\tvar request = builder.applyConfigurationParametersFromSuite(Suite.class)\n\t\t\t\t.parentConfigurationParameters(configuration)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tvar configurationParameters = request.getConfigurationParameters();\n\t\tassertEquals(Optional.of(\"parent parameters were used\"), configurationParameters.get(\"parent\"));\n\t}\n\n\t@Test\n\tvoid disableParentConfigurationParameters() {\n\t\t@DisableParentConfigurationParameters\n\t\tclass Suite {\n\n\t\t}\n\t\t// @formatter:off\n\t\tvar configuration = new ParentConfigurationParameters(\"parent\", \"parent parameters were used\");\n\t\tvar request = builder.applyConfigurationParametersFromSuite(Suite.class)\n\t\t\t\t.parentConfigurationParameters(configuration)\n\t\t\t\t.build();\n\t\t// @formatter:on\n\t\tvar configurationParameters = request.getConfigurationParameters();\n\t\tassertEquals(Optional.empty(), configurationParameters.get(\"parent\"));\n\t}\n\n\t@Test\n\tvoid selectByIdentifier() {\n\t\t// @formatter:off\n\t\t@Select({\n\t\t\t\t\"class:org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NonLocalTestCase\",\n\t\t\t\t\"method:org.junit.platform.suite.engine.SuiteLauncherDiscoveryRequestBuilderTests$NoParameterTestCase#testMethod\"\n\t\t})\n\t\t// @formatter:on\n\t\tclass Suite {\n\t\t}\n\n\t\tLauncherDiscoveryRequest request = builder.applySelectorsAndFiltersFromSuite(Suite.class).build();\n\t\tList<ClassSelector> classSelectors = request.getSelectorsByType(ClassSelector.class);\n\t\tassertEquals(NonLocalTestCase.class, exactlyOne(classSelectors).getJavaClass());\n\t\tList<MethodSelector> methodSelectors = request.getSelectorsByType(MethodSelector.class);\n\t\t// @formatter:off\n\t\tassertThat(exactlyOne(methodSelectors))\n\t\t\t\t.extracting(MethodSelector::getJavaClass, MethodSelector::getMethodName)\n\t\t\t\t.containsExactly(NoParameterTestCase.class, \"testMethod\");\n\t\t// @formatter:on\n\t}\n\n\tprivate static <T> T exactlyOne(List<T> list) {\n\t\treturn CollectionUtils.getOnlyElement(list);\n\t}\n\n\tprivate static class StubAbstractTestDescriptor extends AbstractTestDescriptor {\n\n\t\tStubAbstractTestDescriptor() {\n\t\t\tsuper(UniqueId.forEngine(\"test\"), \"stub\");\n\t\t}\n\n\t\t@Override\n\t\tpublic Type getType() {\n\t\t\treturn Type.CONTAINER;\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<TestTag> getTags() {\n\t\t\treturn Set.of(TestTag.create(\"test-tag\"));\n\t\t}\n\n\t}\n\n\tprivate static class ParentConfigurationParameters implements ConfigurationParameters {\n\n\t\tprivate final Map<String, String> map;\n\n\t\tParentConfigurationParameters(String key, String value) {\n\t\t\tthis.map = Map.of(key, value);\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<String> get(String key) {\n\t\t\treturn Optional.ofNullable(map.get(key));\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Boolean> getBoolean(String key) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> keySet() {\n\t\t\treturn Set.of();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/SuiteTestDescriptorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.mockito.Mockito.mock;\n\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.Set;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.platform.engine.ConfigurationParameters;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.discovery.DiscoveryIssueReporter;\nimport org.junit.platform.launcher.core.OutputDirectoryCreators;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\nimport org.junit.platform.suite.engine.testsuites.SelectClassesSuite;\n\n/**\n * @since 1.8\n */\nclass SuiteTestDescriptorTests {\n\n\tfinal UniqueId engineId = UniqueId.forEngine(SuiteEngineDescriptor.ENGINE_ID);\n\tfinal UniqueId suiteId = engineId.append(SuiteTestDescriptor.SEGMENT_TYPE, \"test\");\n\tfinal UniqueId jupiterEngineId = suiteId.append(\"engine\", JupiterEngineDescriptor.ENGINE_ID);\n\tfinal UniqueId testClassId = jupiterEngineId.append(ClassTestDescriptor.SEGMENT_TYPE,\n\t\tSingleTestTestCase.class.getName());\n\tfinal UniqueId methodId = testClassId.append(TestMethodTestDescriptor.SEGMENT_TYPE,\n\t\t\"test(%s)\".formatted(TestReporter.class.getName()));\n\n\tfinal ConfigurationParameters configurationParameters = new EmptyConfigurationParameters();\n\tfinal OutputDirectoryCreator outputDirectoryCreator = OutputDirectoryCreators.dummyOutputDirectoryCreator();\n\tfinal DiscoveryIssueReporter discoveryIssueReporter = DiscoveryIssueReporter.forwarding(mock(), engineId);\n\tfinal SuiteTestDescriptor suite = new SuiteTestDescriptor(suiteId, TestSuite.class, configurationParameters,\n\t\toutputDirectoryCreator, mock(), discoveryIssueReporter);\n\n\t@Test\n\tvoid suiteIsEmptyBeforeDiscovery() {\n\t\tsuite.addDiscoveryRequestFrom(SelectClassesSuite.class);\n\n\t\tassertThat(suite.getChildren()).isEmpty();\n\t}\n\n\t@Test\n\tvoid suiteDiscoversTestsFromClass() {\n\t\tsuite.addDiscoveryRequestFrom(SelectClassesSuite.class);\n\t\tsuite.discover();\n\n\t\tassertThat(suite.getDescendants()).map(TestDescriptor::getUniqueId)//\n\t\t\t\t.containsExactly(jupiterEngineId, testClassId, methodId);\n\t}\n\n\t@Test\n\tvoid suiteDiscoversTestsFromUniqueId() {\n\t\tsuite.addDiscoveryRequestFrom(methodId);\n\t\tsuite.discover();\n\n\t\tassertThat(suite.getDescendants()).map(TestDescriptor::getUniqueId)//\n\t\t\t\t.containsExactly(jupiterEngineId, testClassId, methodId);\n\t}\n\n\t@Test\n\tvoid discoveryPlanCanNotBeModifiedAfterDiscovery() {\n\t\tsuite.addDiscoveryRequestFrom(SelectClassesSuite.class);\n\t\tsuite.discover();\n\n\t\tassertAll(//\n\t\t\t() -> assertPreconditionViolationFor(() -> suite.addDiscoveryRequestFrom(SelectClassesSuite.class))//\n\t\t\t\t\t.withMessage(\"discovery request cannot be modified after discovery\"),\n\t\t\t() -> assertPreconditionViolationFor(() -> suite.addDiscoveryRequestFrom(methodId))//\n\t\t\t\t\t.withMessage(\"discovery request cannot be modified after discovery\"));\n\t}\n\n\t@Test\n\tvoid suiteMayRegisterTests() {\n\t\tassertThat(suite.mayRegisterTests()).isTrue();\n\t}\n\n\t@Suite\n\tstatic class TestSuite {\n\t}\n\n\tprivate static class EmptyConfigurationParameters implements ConfigurationParameters {\n\t\t@Override\n\t\tpublic Optional<String> get(String key) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Optional<Boolean> getBoolean(String key) {\n\t\t\treturn Optional.empty();\n\t\t}\n\n\t\t@Override\n\t\tpublic Set<String> keySet() {\n\t\t\treturn Collections.emptySet();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/error/ErrorSelector.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.error;\n\nimport org.junit.platform.engine.DiscoverySelector;\n\nrecord ErrorSelector(String message) implements DiscoverySelector {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/error/ErrorSelectorIdentifierParser.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.error;\n\nimport java.util.Optional;\n\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.DiscoverySelectorIdentifier;\nimport org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser;\n\npublic class ErrorSelectorIdentifierParser implements DiscoverySelectorIdentifierParser {\n\n\t@Override\n\tpublic String getPrefix() {\n\t\treturn \"error\";\n\t}\n\n\t@Override\n\tpublic Optional<? extends DiscoverySelector> parse(DiscoverySelectorIdentifier identifier, Context context) {\n\t\treturn Optional.of(new ErrorSelector(identifier.getValue()));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/error/SelectorProcessingErrorCausingEngine.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.error;\n\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.SelectorResolutionResult;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.fakes.TestEngineStub;\n\npublic class SelectorProcessingErrorCausingEngine extends TestEngineStub {\n\n\t@Override\n\tpublic String getId() {\n\t\treturn \"selector-error-engine\";\n\t}\n\n\t@Override\n\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\tvar engineDescriptor = super.discover(discoveryRequest, uniqueId);\n\t\tvar errorSelectors = discoveryRequest.getSelectorsByType(ErrorSelector.class);\n\t\tif (!errorSelectors.isEmpty()) {\n\t\t\tvar selector = errorSelectors.getFirst();\n\t\t\tvar failure = SelectorResolutionResult.failed(new RuntimeException(selector.message()));\n\t\t\tdiscoveryRequest.getDiscoveryListener().selectorProcessed(engineDescriptor.getUniqueId(), selector,\n\t\t\t\tfailure);\n\t\t}\n\t\treturn engineDescriptor;\n\t}\n\n\t@Override\n\tpublic void execute(ExecutionRequest request) {\n\t\tthrow new RuntimeException(\"should not be called\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/error/package-info.java",
    "content": "@NullMarked\npackage org.junit.platform.suite.engine.error;\n\nimport org.jspecify.annotations.NullMarked;\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/ConfigurationSensitiveTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport org.junit.jupiter.api.MethodOrderer.MethodName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\n\n/**\n * @since 1.11\n */\n@TestMethodOrder(MethodName.class)\npublic class ConfigurationSensitiveTestCase {\n\n\tboolean shared;\n\n\t@Test\n\tvoid test1() {\n\t\tshared = true;\n\t}\n\n\t@Test\n\tvoid test2() {\n\t\t// This will fail unless the test instance lifecycle is set to per_class,\n\t\t// which is configured via @ConfigurationParameter in ConfigurationSuite.\n\t\tassertTrue(shared);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/DynamicTestsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\n/**\n * @since 1.8\n */\npublic class DynamicTestsTestCase {\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTests() {\n\t\treturn Stream.of(//\n\t\t\tdynamicTest(\"Add test\", () -> assertEquals(2, Math.addExact(1, 1))),\n\t\t\tdynamicTest(\"Multiply Test\", () -> assertEquals(4, Math.multiplyExact(2, 2)))//\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyDynamicTestsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\n/**\n * @since 1.9\n */\npublic class EmptyDynamicTestsTestCase {\n\n\t@TestFactory\n\tStream<DynamicTest> dynamicTests() {\n\t\treturn Stream.empty();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/EmptyTestTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\n/**\n * @since 1.9\n */\npublic class EmptyTestTestCase {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/ErroneousTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\n\npublic class ErroneousTestCase {\n\n\t@SuppressWarnings({ \"JUnitMalformedDeclaration\", \"unused\" })\n\t@BeforeAll\n\tvoid nonStaticLifecycleMethod() {\n\t\tfail(\"should not be called\");\n\t}\n\n\t@Test\n\tvoid name() {\n\t\tfail(\"should not be called\");\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/JUnit4TestsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport org.junit.Test;\n\npublic class JUnit4TestsTestCase {\n\t@Test\n\tpublic void aTest() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/MultipleTestsTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.8\n */\npublic class MultipleTestsTestCase {\n\n\t@Test\n\tvoid test() {\n\t}\n\n\t@Test\n\tvoid test2() {\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/SingleTestTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport java.nio.file.Files;\n\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestReporter;\n\n/**\n * @since 1.8\n */\npublic class SingleTestTestCase {\n\n\t@Test\n\tvoid test(TestReporter testReporter) {\n\t\ttestReporter.publishFile(\"test.txt\", MediaType.TEXT_PLAIN_UTF_8, file -> Files.writeString(file, \"test\"));\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/StatefulTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Test;\n\n/**\n * @since 1.11\n */\npublic class StatefulTestCase {\n\n\tpublic static List<String> callSequence = new ArrayList<>();\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class Test1 {\n\n\t\t@Test\n\t\tvoid statefulTest() {\n\t\t\tcallSequence.add(\"test1\");\n\t\t}\n\n\t}\n\n\t@SuppressWarnings(\"NewClassNamingConvention\")\n\tpublic static class Test2 {\n\n\t\t@Test\n\t\tvoid statefulTest() {\n\t\t\tcallSequence.add(\"test2\");\n\t\t\tfail(\"This is a failing test\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testcases/TaggedTestTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testcases;\n\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\n\npublic class TaggedTestTestCase {\n\t@Test\n\t@Tag(\"excluded\")\n\tpublic void test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/AbstractSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@SelectClasses(SingleTestTestCase.class)\npublic abstract class AbstractSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/BlankSuiteDisplayNameSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.api.SuiteDisplayName;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * Test suite with blank @SuiteDisplayName to verify validation.\n *\n * @since 5.14\n */\n@Suite\n@SelectClasses(SingleTestTestCase.class)\n@SuiteDisplayName(\"\")\npublic class BlankSuiteDisplayNameSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ConfigurationSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport static org.junit.jupiter.api.Constants.DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME;\n\nimport org.junit.platform.suite.api.ConfigurationParameter;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.ConfigurationSensitiveTestCase;\n\n@Suite\n@ConfigurationParameter(key = DEFAULT_TEST_INSTANCE_LIFECYCLE_PROPERTY_NAME, value = \"per_class\")\n@SelectClasses(ConfigurationSensitiveTestCase.class)\npublic class ConfigurationSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/CyclicSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@IncludeClassNamePatterns(\".*\")\n@SelectClasses({ CyclicSuite.class, SingleTestTestCase.class })\npublic class CyclicSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/DynamicSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.DynamicTestsTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@SelectClasses(DynamicTestsTestCase.class)\npublic class DynamicSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyCyclicSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * @since 1.9.2\n */\n@Suite\n@IncludeClassNamePatterns(\".*\")\n@SelectClasses(EmptyCyclicSuite.class)\npublic class EmptyCyclicSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.EmptyDynamicTestsTestCase;\n\n/**\n * @since 1.9\n */\n@Suite\n@SelectClasses(EmptyDynamicTestsTestCase.class)\npublic class EmptyDynamicTestSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyDynamicTestWithFailIfNoTestFalseSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.EmptyDynamicTestsTestCase;\n\n/**\n * @since 1.9\n */\n@Suite(failIfNoTests = false)\n@SelectClasses(EmptyDynamicTestsTestCase.class)\npublic class EmptyDynamicTestWithFailIfNoTestFalseSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.EmptyTestTestCase;\n\n/**\n * @since 1.9\n */\n@Suite\n@SelectClasses(EmptyTestTestCase.class)\npublic class EmptyTestCaseSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/EmptyTestCaseWithFailIfNoTestFalseSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.EmptyTestTestCase;\n\n/**\n * @since 1.9\n */\n@Suite(failIfNoTests = false)\n@SelectClasses(EmptyTestTestCase.class)\npublic class EmptyTestCaseWithFailIfNoTestFalseSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ErroneousTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.ErroneousTestCase;\n\n@Suite\n@SelectClasses(ErroneousTestCase.class)\npublic class ErroneousTestSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/InheritedSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\npublic class InheritedSuite extends AbstractSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/LifecycleMethodsSuites.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.platform.suite.api.AfterSuite;\nimport org.junit.platform.suite.api.BeforeSuite;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.BeforeAndAfterSuiteTests;\nimport org.junit.platform.suite.engine.testcases.StatefulTestCase;\n\n/**\n * Test suites used in {@link BeforeAndAfterSuiteTests}.\n *\n * @since 1.11\n */\n@SuppressWarnings(\"NewClassNamingConvention\")\npublic class LifecycleMethodsSuites {\n\n\t@Retention(RetentionPolicy.RUNTIME)\n\t@Target(ElementType.TYPE)\n\t@Suite\n\t@SelectClasses({ StatefulTestCase.Test1.class, StatefulTestCase.Test2.class })\n\tprivate @interface TestSuite {\n\t}\n\n\t@TestSuite\n\tpublic static class SuccessfulBeforeAndAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class FailingBeforeSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @BeforeSuite method\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class FailingAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @AfterSuite method\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class FailingBeforeAndAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @BeforeSuite method\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @AfterSuite method\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class SeveralFailingBeforeAndAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp1() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @BeforeSuite method\");\n\t\t}\n\n\t\t@BeforeSuite\n\t\tstatic void setUp2() {\n\t\t\tStatefulTestCase.callSequence.add(\"beforeSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @BeforeSuite method\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown1() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @AfterSuite method\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown2() {\n\t\t\tStatefulTestCase.callSequence.add(\"afterSuiteMethod\");\n\t\t\tthrow new RuntimeException(\"Exception thrown by @AfterSuite method\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class SuperclassWithBeforeAndAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"superclassBeforeSuiteMethod\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"superclassAfterSuiteMethod\");\n\t\t}\n\n\t}\n\n\tpublic static class SubclassWithBeforeAndAfterSuite extends SuperclassWithBeforeAndAfterSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void setUp() {\n\t\t\tStatefulTestCase.callSequence.add(\"subclassBeforeSuiteMethod\");\n\t\t}\n\n\t\t@AfterSuite\n\t\tstatic void tearDown() {\n\t\t\tStatefulTestCase.callSequence.add(\"subclassAfterSuiteMethod\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class NonVoidBeforeSuite {\n\n\t\t@BeforeSuite\n\t\tstatic String nonVoidBeforeSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t\treturn \"\";\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class ParameterAcceptingBeforeSuite {\n\n\t\t@BeforeSuite\n\t\tstatic void parameterAcceptingBeforeSuite(String param) {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class NonStaticBeforeSuite {\n\n\t\t@BeforeSuite\n\t\tvoid nonStaticBeforeSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class PrivateBeforeSuite {\n\n\t\t@BeforeSuite\n\t\tprivate static void privateBeforeSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class NonVoidAfterSuite {\n\n\t\t@AfterSuite\n\t\tstatic String nonVoidAfterSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t\treturn \"\";\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class ParameterAcceptingAfterSuite {\n\n\t\t@AfterSuite\n\t\tstatic void parameterAcceptingAfterSuite(String param) {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class NonStaticAfterSuite {\n\n\t\t@AfterSuite\n\t\tvoid nonStaticAfterSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n\t@TestSuite\n\tpublic static class PrivateAfterSuite {\n\n\t\t@AfterSuite\n\t\tprivate static void privateAfterSuite() {\n\t\t\tfail(\"Should not be called\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultiEngineSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.JUnit4TestsTestCase;\nimport org.junit.platform.suite.engine.testcases.TaggedTestTestCase;\n\n@Suite\n@SelectClasses({ JUnit4TestsTestCase.class, TaggedTestTestCase.class })\npublic class MultiEngineSuite {\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/MultipleSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.MultipleTestsTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@SelectClasses(MultipleTestsTestCase.class)\npublic class MultipleSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/NestedSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.MultipleTestsTestCase;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.8\n */\npublic class NestedSuite {\n\n\t@Suite\n\t@SelectClasses(SingleTestTestCase.class)\n\tstatic class Jupiter {\n\t}\n\n\t@Suite\n\t@SelectClasses(MultipleTestsTestCase.class)\n\tstatic class Tagged {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectByIdentifierSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.Select;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * @since 1.11\n */\n@Suite\n@IncludeClassNamePatterns(\".*\")\n@Select(\"class:org.junit.platform.suite.engine.testcases.SingleTestTestCase\")\npublic class SelectByIdentifierSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectClassesSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@SelectClasses(SingleTestTestCase.class)\npublic class SelectClassesSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectMethodsSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectMethod;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.MultipleTestsTestCase;\n\n/**\n * @since 1.10\n */\n@Suite\n@SelectMethod(type = MultipleTestsTestCase.class, name = \"test\")\npublic class SelectMethodsSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SelectorProcessingErrorTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeEngines;\nimport org.junit.platform.suite.api.Select;\nimport org.junit.platform.suite.api.Suite;\n\n@Suite\n@IncludeEngines(\"selector-error-engine\")\n@Select(\"error:simulatedError\")\npublic class SelectorProcessingErrorTestSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteDisplayNameSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.api.SuiteDisplayName;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.8\n */\n@Suite\n@SelectClasses(SingleTestTestCase.class)\n@SuiteDisplayName(\"Suite Display Name\")\npublic class SuiteDisplayNameSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\n\n/**\n * @since 1.8\n */\n@Suite\n@IncludeClassNamePatterns(\".*\")\n@SelectClasses(SelectClassesSuite.class)\npublic class SuiteSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/SuiteWithErroneousTestSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\n\n@Suite\n@SelectClasses(ErroneousTestSuite.class)\npublic class SuiteWithErroneousTestSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/ThreePartCyclicSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.IncludeClassNamePatterns;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * @since 1.9.2\n */\npublic class ThreePartCyclicSuite {\n\n\t@Suite\n\t@IncludeClassNamePatterns(\".*\")\n\t@SelectClasses({ PartB.class })\n\tpublic static class PartA {\n\n\t}\n\n\t@Suite\n\t@IncludeClassNamePatterns(\".*\")\n\t@SelectClasses({ PartC.class })\n\tpublic static class PartB {\n\n\t}\n\n\t@Suite\n\t@IncludeClassNamePatterns(\".*\")\n\t@SelectClasses({ PartB.class, SingleTestTestCase.class })\n\tpublic static class PartC {\n\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/suite/engine/testsuites/WhitespaceSuiteDisplayNameSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.suite.engine.testsuites;\n\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\nimport org.junit.platform.suite.api.SuiteDisplayName;\nimport org.junit.platform.suite.engine.testcases.SingleTestTestCase;\n\n/**\n * Test suite with whitespace-only @SuiteDisplayName to verify validation.\n *\n * @since 5.14\n */\n@Suite\n@SelectClasses(SingleTestTestCase.class)\n@SuiteDisplayName(\"   \")\npublic class WhitespaceSuiteDisplayNameSuite {\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineDiscoveryResultsIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.util.CollectionUtils.getOnlyElement;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;\n\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.provider.EnumSource;\nimport org.junit.platform.engine.DiscoveryIssue;\nimport org.junit.platform.engine.DiscoveryIssue.Severity;\nimport org.junit.platform.engine.DiscoverySelector;\nimport org.junit.platform.engine.EngineDiscoveryRequest;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.ClassSource;\nimport org.junit.platform.fakes.TestEngineStub;\nimport org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;\n\n@ParameterizedClass\n@EnumSource\nrecord EngineDiscoveryResultsIntegrationTests(TestKitApi testKit) {\n\n\t@Test\n\tvoid returnsEngineDescriptor() {\n\t\tvar results = testKit.discover(\"junit-jupiter\", selectClass(TestCase.class));\n\n\t\tassertThat(results.getEngineDescriptor().getDisplayName()).isEqualTo(\"JUnit Jupiter\");\n\t\tassertThat(getOnlyElement(results.getEngineDescriptor().getChildren()).getSource()) //\n\t\t\t\t.contains(ClassSource.from(TestCase.class));\n\t}\n\n\t@Test\n\t@NullMarked\n\tvoid collectsDiscoveryIssues() {\n\t\tvar issue = DiscoveryIssue.create(Severity.WARNING, \"warning\");\n\t\tvar testEngine = new TestEngineStub() {\n\t\t\t@Override\n\t\t\tpublic TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {\n\t\t\t\tvar listener = discoveryRequest.getDiscoveryListener();\n\t\t\t\tlistener.issueEncountered(uniqueId, issue);\n\t\t\t\treturn super.discover(discoveryRequest, uniqueId);\n\t\t\t}\n\t\t};\n\n\t\tvar results = testKit.discover(testEngine);\n\n\t\tassertThat(results.getDiscoveryIssues()).containsExactly(issue);\n\t}\n\n\tstatic class TestCase {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n\n\tenum TestKitApi {\n\n\t\tSTATIC_METHOD {\n\t\t\t@Override\n\t\t\tEngineDiscoveryResults discover(String engineId, DiscoverySelector selector) {\n\t\t\t\treturn EngineTestKit.discover(engineId, newRequest().selectors(selector).build());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tEngineDiscoveryResults discover(TestEngine testEngine) {\n\t\t\t\treturn EngineTestKit.discover(testEngine, newRequest().build());\n\t\t\t}\n\n\t\t\tprivate static LauncherDiscoveryRequestBuilder newRequest() {\n\t\t\t\treturn request().enableImplicitConfigurationParameters(false);\n\t\t\t}\n\t\t},\n\n\t\tFLUENT_API {\n\t\t\t@Override\n\t\t\tEngineDiscoveryResults discover(String engineId, DiscoverySelector selector) {\n\t\t\t\treturn EngineTestKit.engine(engineId).selectors(selector).discover();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tEngineDiscoveryResults discover(TestEngine testEngine) {\n\t\t\t\treturn EngineTestKit.engine(testEngine).discover();\n\t\t\t}\n\t\t};\n\n\t\t@SuppressWarnings(\"SameParameterValue\")\n\t\tabstract EngineDiscoveryResults discover(String engineId, DiscoverySelector selector);\n\n\t\tabstract EngineDiscoveryResults discover(TestEngine testEngine);\n\t}\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/EngineTestKitTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.mockito.ArgumentCaptor.forClass;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.mockConstruction;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport java.util.Optional;\nimport java.util.function.UnaryOperator;\n\nimport org.junit.jupiter.api.AfterEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.BeforeEachCallback;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.junit.jupiter.api.util.SetSystemProperty;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\nimport org.junit.platform.engine.CancellationToken;\nimport org.junit.platform.engine.ExecutionRequest;\nimport org.junit.platform.engine.TestEngine;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.reporting.ReportEntry;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.junit.platform.engine.support.store.Namespace;\nimport org.junit.platform.engine.support.store.NamespacedHierarchicalStore;\nimport org.junit.platform.launcher.LauncherDiscoveryListener;\nimport org.junit.platform.launcher.LauncherDiscoveryRequest;\nimport org.junit.platform.launcher.core.EngineExecutionOrchestrator;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.MockedConstruction;\n\n@SetSystemProperty(key = EngineTestKitTests.KEY, value = \"from system property\")\nclass EngineTestKitTests {\n\n\tstatic final String KEY = \"org.junit.platform.testkit.engine.EngineTestKitTests\";\n\n\t@AfterEach\n\tvoid resetSystemProperty() {\n\t\tSystem.clearProperty(KEY);\n\t}\n\n\t@Test\n\tvoid ignoresImplicitConfigurationParametersByDefault() {\n\t\tvar value = executeExampleTestCaseAndCollectValue(builder -> builder);\n\n\t\tassertThat(value).isEmpty();\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"unchecked\")\n\tvoid verifyRequestLevelStoreIsUsedInExecution() {\n\t\tTestEngine testEngine = mock(TestEngine.class);\n\t\twhen(testEngine.getId()).thenReturn(\"test-engine\");\n\n\t\tLauncherDiscoveryRequest request = mock(LauncherDiscoveryRequest.class);\n\t\twhen(request.getConfigurationParameters()).thenReturn(mock());\n\t\twhen(request.getDiscoveryListener()).thenReturn(LauncherDiscoveryListener.NOOP);\n\n\t\ttry (MockedConstruction<EngineExecutionOrchestrator> mockedConstruction = mockConstruction(\n\t\t\tEngineExecutionOrchestrator.class)) {\n\t\t\tEngineTestKit.execute(testEngine, request);\n\t\t\tassertThat(mockedConstruction.constructed()).isNotEmpty();\n\n\t\t\tEngineExecutionOrchestrator mockOrchestrator = mockedConstruction.constructed().getFirst();\n\t\t\tArgumentCaptor<NamespacedHierarchicalStore<Namespace>> storeCaptor = forClass(\n\t\t\t\tNamespacedHierarchicalStore.class);\n\n\t\t\tverify(mockOrchestrator).execute(any(), any(), any(), storeCaptor.capture(),\n\t\t\t\teq(CancellationToken.disabled()));\n\t\t\tassertNotNull(storeCaptor.getValue(), \"Request level store should be passed to execute\");\n\t\t}\n\t}\n\n\t@ParameterizedTest\n\t@CsvSource({ \"true, from system property\", \"false,\" })\n\tvoid usesImplicitConfigurationParametersWhenEnabled(boolean enabled, String expectedValue) {\n\t\tvar value = executeExampleTestCaseAndCollectValue(\n\t\t\tbuilder -> builder.enableImplicitConfigurationParameters(enabled));\n\n\t\tassertThat(value).isEqualTo(Optional.ofNullable(expectedValue));\n\t}\n\n\t@Test\n\tvoid cancellationTokenIsPassedToEngines() {\n\t\tTestEngine testEngine = mock(TestEngine.class);\n\t\twhen(testEngine.getId()).thenReturn(\"test-engine\");\n\t\twhen(testEngine.discover(any(), any())).thenReturn(\n\t\t\tnew EngineDescriptor(UniqueId.forEngine(\"test-engine\"), \"Engine\"));\n\n\t\tvar cancellationToken = CancellationToken.create();\n\n\t\tEngineTestKit.engine(testEngine).cancellationToken(cancellationToken).execute();\n\n\t\tvar executionRequest = forClass(ExecutionRequest.class);\n\t\tverify(testEngine).execute(executionRequest.capture());\n\n\t\tassertThat(executionRequest.getValue().getCancellationToken()).isSameAs(cancellationToken);\n\t}\n\n\tprivate Optional<String> executeExampleTestCaseAndCollectValue(UnaryOperator<EngineTestKit.Builder> configuration) {\n\t\treturn configuration.apply(EngineTestKit.engine(\"junit-jupiter\")) //\n\t\t\t\t.selectors(selectClass(ExampleTestCase.class)) //\n\t\t\t\t.execute() //\n\t\t\t\t.allEvents() //\n\t\t\t\t.reportingEntryPublished() //\n\t\t\t\t.map(event -> event.getPayload(ReportEntry.class).orElseThrow()) //\n\t\t\t\t.map(ReportEntry::getKeyValuePairs) //\n\t\t\t\t.map(entries -> entries.get(KEY)) //\n\t\t\t\t.findFirst();\n\t}\n\n\tstatic class ExampleTestCase {\n\n\t\t@RegisterExtension\n\t\tBeforeEachCallback callback = context -> context.getConfigurationParameter(KEY) //\n\t\t\t\t.ifPresent(value -> context.publishReportEntry(KEY, value));\n\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/EventsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure;\nimport static org.junit.platform.testkit.engine.EventConditions.skippedWithReason;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\n\nimport java.util.List;\n\nimport org.assertj.core.error.AssertJMultipleFailuresError;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.function.Executable;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.TestExecutionResult;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.engine.support.descriptor.EngineDescriptor;\nimport org.opentest4j.AssertionFailedError;\n\nclass EventsTests {\n\n\tTestDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine(\"e1\"), \"engine\");\n\n\tList<Event> list = List.of(Event.executionStarted(engineDescriptor),\n\t\tEvent.executionSkipped(engineDescriptor, \"reason1\"), Event.executionSkipped(engineDescriptor, \"reason2\"),\n\t\tEvent.executionFinished(engineDescriptor, TestExecutionResult.successful()));\n\n\tEvents events = new Events(list, \"test\");\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchExactly: all events in order -> match\")\n\tvoid assertEventsMatchExactlyMatchesAllEventsInOrder() {\n\t\tevents.assertEventsMatchExactly( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), skippedWithReason(\"reason2\")), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: all events in order -> match\")\n\tvoid assertEventsMatchLooselyMatchesAllEventsInOrder() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), skippedWithReason(\"reason2\")), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: all events in wrong order -> match\")\n\tvoid assertEventsMatchLooselyMatchesAllEventsInWrongOrder() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), skippedWithReason(\"reason2\")), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: tailing subset -> match\")\n\tvoid assertEventsMatchLooselyMatchesATailingSubset() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: starting subset -> match\")\n\tvoid assertEventsMatchLooselyMatchesAStartingSubset() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: subset in wrong order -> match\")\n\tvoid assertEventsMatchLooselyMatchesASubsetInWrongOrder() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: only last event -> match\")\n\tvoid assertEventsMatchLooselyMatchesTheLastEventAlone() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: only first event -> match\")\n\tvoid assertEventsMatchLooselyMatchesTheFirstEventAlone() {\n\t\tevents.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: only bad events -> fails\")\n\tvoid assertEventsMatchLooselyWithBadConditionsOnlyFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), finishedWithFailure()), //\n\t\t\tevent(engine(), skippedWithReason(\"other\")) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertJMultipleFailuresError.class, willFail);\n\n\t\tvar failures = error.getFailures();\n\t\tassertEquals(2, failures.size());\n\t\tassertEquals(AssertionError.class, failures.get(0).getClass());\n\t\tassertEquals(AssertionError.class, failures.get(1).getClass());\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLoosely: one matching and one bad event -> fails\")\n\tvoid assertEventsMatchLooselyWithOneMatchingAndOneBadConditionFailsPartly() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLoosely( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), finishedWithFailure()) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertJMultipleFailuresError.class, willFail);\n\n\t\tvar failures = error.getFailures();\n\t\tassertEquals(1, failures.size());\n\t\tassertEquals(AssertionError.class, failures.getFirst().getClass());\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: all events in order -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesAllEventsInOrder() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), skippedWithReason(\"reason2\")), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: all events in wrong order -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithAllEventsInWrongOrderFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), skippedWithReason(\"reason2\")), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertionFailedError.class, willFail);\n\t\tassertThat(error).hasMessageContaining(\"Conditions are not in the correct order.\");\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: tailing subset in order -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesATailingSubset() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: starting subset in order -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesAStartingSubset() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: subset in wrong order -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithASubsetInWrongOrderFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")), //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertionFailedError.class, willFail);\n\t\tassertThat(error).hasMessageContaining(\"Conditions are not in the correct order.\");\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: last event alone -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesTheLastEventAlone() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: first event alone -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesTheFirstEventAlone() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), started()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: bad events only -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithBadConditionsOnlyFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), finishedWithFailure()), //\n\t\t\tevent(engine(), skippedWithReason(\"other\")) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertJMultipleFailuresError.class, willFail);\n\n\t\tvar failures = error.getFailures();\n\t\tassertEquals(2, failures.size());\n\t\tassertEquals(AssertionError.class, failures.get(0).getClass());\n\t\tassertEquals(AssertionError.class, failures.get(1).getClass());\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: one matching event and one bad event -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithOneMatchingAndOneBadConditionFailsPartly() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), finishedWithFailure()) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertJMultipleFailuresError.class, willFail);\n\t\tassertEquals(1, error.getFailures().size());\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: first and last event in order -> match\")\n\tvoid assertEventsMatchLooselyInOrderMatchesFirstAndLastEventInOrder() {\n\t\tevents.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), started()), //\n\t\t\tevent(engine(), finishedSuccessfully()) //\n\t\t);\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: second and last event in bad order -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithSecondAndLastEventInBadOrderFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), skippedWithReason(\"reason1\")) //\n\t\t);\n\n\t\tvar error = assertThrows(AssertionFailedError.class, willFail);\n\t\tassertThat(error).hasMessageContaining(\"Conditions are not in the correct order.\");\n\t}\n\n\t@Test\n\t@DisplayName(\"assertEventsMatchLooselyInOrder: too many events -> fail\")\n\tvoid assertEventsMatchLooselyInOrderWithTooManyEventsFails() {\n\t\tExecutable willFail = () -> events.assertEventsMatchLooselyInOrder( //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()), //\n\t\t\tevent(engine(), finishedSuccessfully()));\n\n\t\tvar error = assertThrows(AssertionError.class, willFail);\n\t\tassertThat(error).hasMessageEndingWith(\"to be less than or equal to 4 but was 6\");\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/ExecutionsIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.assertj.core.api.Assertions.fail;\nimport static org.junit.jupiter.api.Assumptions.abort;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Integration tests for {@link Executions}.\n *\n * @since 1.4\n */\nclass ExecutionsIntegrationTests {\n\n\t@Test\n\tvoid executionsFromSkippedTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 1 for both of the following cases, since an Execution can\n\t\t// be created for a \"skipped event even if \"started\" and \"finished\" events\n\t\t// are filtered out.\n\t\tassertThat(testEvents.executions().skipped().count()).isEqualTo(1);\n\t\tassertThat(testEvents.skipped().executions().count()).isEqualTo(1);\n\t}\n\n\t@Test\n\t@SuppressWarnings(\"removal\")\n\tvoid executionsFromStartedTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 3 if the executions are created BEFORE filtering out \"finished\" events.\n\t\tassertThat(testEvents.executions().started().count()).isEqualTo(3);\n\t\t// We expect 0 if the executions are created AFTER filtering out \"finished\" events.\n\t\tassertThat(testEvents.started().executions().count()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid executionsFromFinishedTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 3 if the executions are created BEFORE filtering out \"started\" events.\n\t\tassertThat(testEvents.executions().finished().count()).isEqualTo(3);\n\t\t// We expect 0 if the executions are created AFTER filtering out \"started\" events.\n\t\tassertThat(testEvents.finished().executions().count()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid executionsFromSucceededTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 1 if the executions are created BEFORE filtering out \"finished\" events.\n\t\tassertThat(testEvents.executions().succeeded().count()).isEqualTo(1);\n\t\t// We expect 0 if the executions are created AFTER filtering out \"finished\" events.\n\t\tassertThat(testEvents.succeeded().executions().count()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid executionsFromAbortedTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 1 if the executions are created BEFORE filtering out \"started\" events.\n\t\tassertThat(testEvents.executions().aborted().count()).isEqualTo(1);\n\t\t// We expect 0 if the executions are created AFTER filtering out \"started\" events.\n\t\tassertThat(testEvents.aborted().executions().count()).isEqualTo(0);\n\t}\n\n\t@Test\n\tvoid executionsFromFailedTestEvents() {\n\t\tvar testEvents = getTestEvents();\n\n\t\t// We expect 1 if the executions are created BEFORE filtering out \"started\" events.\n\t\tassertThat(testEvents.executions().failed().count()).isEqualTo(1);\n\t\t// We expect 0 if the executions are created AFTER filtering out \"started\" events.\n\t\tassertThat(testEvents.failed().executions().count()).isEqualTo(0);\n\t}\n\n\tprivate Events getTestEvents() {\n\t\treturn EngineTestKit.engine(\"junit-jupiter\")//\n\t\t\t\t.selectors(selectClass(ExampleTestCase.class))//\n\t\t\t\t.execute()//\n\t\t\t\t.testEvents()//\n\t\t\t\t.assertStatistics(stats -> stats.skipped(1).started(3).succeeded(1).aborted(1).failed(1));\n\t}\n\n\tstatic class ExampleTestCase {\n\n\t\t@Test\n\t\t@Disabled\n\t\tvoid skippedTest() {\n\t\t}\n\n\t\t@Test\n\t\tvoid succeedingTest() {\n\t\t}\n\n\t\t@Test\n\t\tvoid abortedTest() {\n\t\t\tabort();\n\t\t}\n\n\t\t@Test\n\t\tvoid failingTest() {\n\t\t\tfail(\"Boom!\");\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/NestedContainerEventConditionTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;\nimport static org.junit.platform.testkit.engine.EventConditions.container;\nimport static org.junit.platform.testkit.engine.EventConditions.displayName;\nimport static org.junit.platform.testkit.engine.EventConditions.engine;\nimport static org.junit.platform.testkit.engine.EventConditions.event;\nimport static org.junit.platform.testkit.engine.EventConditions.finishedSuccessfully;\nimport static org.junit.platform.testkit.engine.EventConditions.nestedContainer;\nimport static org.junit.platform.testkit.engine.EventConditions.started;\nimport static org.junit.platform.testkit.engine.EventConditions.test;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport java.util.HashMap;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.engine.TestDescriptor;\nimport org.junit.platform.engine.UniqueId;\nimport org.junit.platform.testkit.engine.NestedContainerEventConditionTests.ATestCase.BTestCase;\nimport org.junit.platform.testkit.engine.NestedContainerEventConditionTests.ATestCase.BTestCase.CTestCase;\n\n/**\n * @since 1.6\n */\nclass NestedContainerEventConditionTests {\n\n\t@SuppressWarnings(\"DataFlowIssue\")\n\t@Test\n\tvoid preconditions() {\n\t\tassertPreconditionViolationNotNullFor(\"Class\", () -> nestedContainer(null));\n\n\t\tassertPreconditionViolationFor(() -> nestedContainer(NestedContainerEventConditionTests.class))//\n\t\t\t\t.withMessage(NestedContainerEventConditionTests.class.getName() + \" must be a nested class\");\n\t}\n\n\t@Test\n\tvoid nestedContainerChecksSuppliedClassAndAllEnclosingClasses() {\n\t\tvar uniqueId = UniqueId.root(\"top-level\", getClass().getName())//\n\t\t\t\t.append(\"nested\", ATestCase.class.getSimpleName())//\n\t\t\t\t.append(\"nested\", BTestCase.class.getSimpleName())//\n\t\t\t\t.append(\"nested\", CTestCase.class.getSimpleName());\n\t\tvar event = createEvent(uniqueId);\n\n\t\tvar condition = nestedContainer(HashMap.Entry.class);\n\t\tassertThat(condition.matches(event)).isFalse();\n\t\tassertThat(condition.toString()).contains(//\n\t\t\t\"is a container\", \"with uniqueId substring 'Map'\", \"with uniqueId substring 'Entry'\");\n\n\t\tcondition = nestedContainer(ATestCase.BTestCase.CTestCase.class);\n\t\tassertThat(condition.matches(event)).isTrue();\n\t\tassertThat(condition.toString()).contains(//\n\t\t\t\"is a container\", \"with uniqueId substring 'NestedContainerEventConditionTests'\",\n\t\t\t\"with uniqueId substring 'ATestCase'\", \"with uniqueId substring 'BTestCase'\",\n\t\t\t\"with uniqueId substring 'CTestCase'\");\n\t}\n\n\tprivate Event createEvent(UniqueId uniqueId) {\n\t\tvar testDescriptor = mock(TestDescriptor.class);\n\t\twhen(testDescriptor.isContainer()).thenReturn(true);\n\t\twhen(testDescriptor.getUniqueId()).thenReturn(uniqueId);\n\n\t\tvar event = mock(Event.class);\n\t\twhen(event.getTestDescriptor()).thenReturn(testDescriptor);\n\t\treturn event;\n\t}\n\n\t@Test\n\tvoid usingNestedContainerCorrectly() {\n\t\tassertDoesNotThrow(() -> container(ATestCase.class));\n\t\tassertDoesNotThrow(() -> nestedContainer(ATestCase.class));\n\n\t\tassertDoesNotThrow(() -> container(ATestCase.BTestCase.class));\n\t\tassertDoesNotThrow(() -> nestedContainer(ATestCase.BTestCase.class));\n\n\t\tassertDoesNotThrow(() -> container(ATestCase.BTestCase.CTestCase.class));\n\t\tassertDoesNotThrow(() -> nestedContainer(ATestCase.BTestCase.CTestCase.class));\n\n\t\tassertDoesNotThrow(() -> container(NestedContainerEventConditionTests.class));\n\t}\n\n\t@Test\n\tvoid eventConditionsForMultipleLevelsOfNestedClasses() {\n\t\t// @formatter:off\n\t\tEngineTestKit.engine(\"junit-jupiter\")\n\t\t\t.selectors(selectClass(ATestCase.class))\n\t\t\t.execute()\n\t\t\t.allEvents()\n\t\t\t.assertEventsMatchExactly(\n\t\t\t\tevent(engine(), started()),\n\t\t\t\t\tevent(container(ATestCase.class), started()),\n\t\t\t\t\t\tevent(test(\"test_a\"), started()),\n\t\t\t\t\t\tevent(test(\"test_a\"), finishedSuccessfully()),\n\t\t\t\t\t\tevent(nestedContainer(ATestCase.BTestCase.class, displayName(\"Test case B\")), started()),\n\t\t\t\t\t\t\tevent(test(\"test_b\"), started()),\n\t\t\t\t\t\t\tevent(test(\"test_b\"), finishedSuccessfully()),\n\t\t\t\t\t\t\tevent(nestedContainer(ATestCase.BTestCase.CTestCase.class), started()),\n\t\t\t\t\t\t\t\tevent(test(\"test_c\"), started()),\n\t\t\t\t\t\t\t\tevent(test(\"test_c\"), finishedSuccessfully()),\n\t\t\t\t\t\t\tevent(nestedContainer(ATestCase.BTestCase.CTestCase.class), finishedSuccessfully()),\n\t\t\t\t\t\tevent(nestedContainer(ATestCase.BTestCase.class, displayName(\"Test case B\")), finishedSuccessfully()),\n\t\t\t\t\tevent(container(ATestCase.class), finishedSuccessfully()),\n\t\t\t\tevent(engine(), finishedSuccessfully())\n\t\t\t);\n\t\t// @formatter:on\n\t}\n\n\tstatic class ATestCase {\n\n\t\t@Test\n\t\tvoid test_a() {\n\t\t}\n\n\t\t@Nested\n\t\t@DisplayName(\"Test case B\")\n\t\tclass BTestCase {\n\t\t\t@Test\n\t\t\tvoid test_b() {\n\t\t\t}\n\n\t\t\t@Nested\n\t\t\tclass CTestCase {\n\t\t\t\t@Test\n\t\t\t\tvoid test_c() {\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/java/org/junit/platform/testkit/engine/TestExecutionResultConditionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.testkit.engine;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationFor;\nimport static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.message;\nimport static org.junit.platform.testkit.engine.TestExecutionResultConditions.rootCause;\n\nimport org.assertj.core.api.Condition;\nimport org.junit.jupiter.api.Test;\n\n/**\n * Tests for {@link TestExecutionResultConditions}.\n *\n * @since 1.11\n */\nclass TestExecutionResultConditionsTests {\n\n\tprivate static final String EXPECTED = \"expected\";\n\n\tprivate static final String UNEXPECTED = \"unexpected\";\n\n\tprivate static final Condition<Throwable> rootCauseCondition = rootCause(message(EXPECTED));\n\n\t@Test\n\tvoid rootCauseFailsForNullThrowable() {\n\t\tassertPreconditionViolationNotNullFor(\"Throwable\", () -> rootCauseCondition.matches(null));\n\t}\n\n\t@Test\n\tvoid rootCauseFailsForThrowableWithoutCause() {\n\t\tThrowable throwable = new Throwable();\n\n\t\tassertPreconditionViolationFor(() -> rootCauseCondition.matches(throwable))//\n\t\t\t\t.withMessage(\"Throwable does not have a cause\");\n\t}\n\n\t@Test\n\tvoid rootCauseMatchesForDirectCauseWithExpectedMessage() {\n\t\tRuntimeException cause = new RuntimeException(EXPECTED);\n\t\tThrowable throwable = new Throwable(cause);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isTrue();\n\t}\n\n\t@Test\n\tvoid rootCauseDoesNotMatchForDirectCauseWithDifferentMessage() {\n\t\tRuntimeException cause = new RuntimeException(UNEXPECTED);\n\t\tThrowable throwable = new Throwable(cause);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isFalse();\n\t}\n\n\t@Test\n\tvoid rootCauseMatchesForRootCauseWithExpectedMessage() {\n\t\tRuntimeException rootCause = new RuntimeException(EXPECTED);\n\t\tRuntimeException intermediateCause = new RuntimeException(\"intermediate cause\", rootCause);\n\t\tThrowable throwable = new Throwable(intermediateCause);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isTrue();\n\t}\n\n\t@Test\n\tvoid rootCauseDoesNotMatchForRootCauseWithDifferentMessage() {\n\t\tRuntimeException rootCause = new RuntimeException(UNEXPECTED);\n\t\tRuntimeException intermediateCause = new RuntimeException(\"intermediate cause\", rootCause);\n\t\tThrowable throwable = new Throwable(intermediateCause);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isFalse();\n\t}\n\n\t@Test\n\tvoid rootCauseMatchesForRootCauseWithExpectedMessageAndSingleLevelRecursiveCauseChain() {\n\t\tRuntimeException rootCause = new RuntimeException(EXPECTED);\n\t\tThrowable throwable = new Throwable(rootCause);\n\t\trootCause.initCause(throwable);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isTrue();\n\t}\n\n\t@Test\n\tvoid rootCauseDoesNotMatchForRootCauseWithDifferentMessageAndSingleLevelRecursiveCauseChain() {\n\t\tRuntimeException rootCause = new RuntimeException(UNEXPECTED);\n\t\tThrowable throwable = new Throwable(rootCause);\n\t\trootCause.initCause(throwable);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isFalse();\n\t}\n\n\t@Test\n\tvoid rootCauseMatchesForRootCauseWithExpectedMessageAndDoubleLevelRecursiveCauseChain() {\n\t\tRuntimeException rootCause = new RuntimeException(EXPECTED);\n\t\tException intermediateCause = new Exception(\"intermediate cause\", rootCause);\n\t\tThrowable throwable = new Throwable(intermediateCause);\n\t\trootCause.initCause(throwable);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isTrue();\n\t}\n\n\t@Test\n\tvoid rootCauseDoesNotMatchForRootCauseWithDifferentMessageAndDoubleLevelRecursiveCauseChain() {\n\t\tRuntimeException rootCause = new RuntimeException(UNEXPECTED);\n\t\tException intermediateCause = new Exception(\"intermediate cause\", rootCause);\n\t\tThrowable throwable = new Throwable(intermediateCause);\n\t\trootCause.initCause(throwable);\n\n\t\tassertThat(rootCauseCondition.matches(throwable)).isFalse();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tests/src/test/kotlin/org/junit/platform/commons/util/KotlinReflectionUtilsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage org.junit.platform.commons.util\n\nimport org.junit.jupiter.api.Assertions.assertFalse\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Test\nimport org.junit.platform.commons.support.ModifierSupport\nimport kotlin.coroutines.Continuation\n\nclass KotlinReflectionUtilsTests {\n    @Test\n    fun recognizesSuspendFunction() {\n        val method =\n            OpenTestMethodTestCase::class.java.getDeclaredMethod(\n                \"test\",\n                Continuation::class.java\n            )\n\n        assertFalse(ModifierSupport.isStatic(method))\n        assertFalse(method.isSynthetic)\n\n        assertTrue(KotlinReflectionUtils.isKotlinSuspendingFunction(method))\n    }\n\n    @Test\n    fun doesNotRecognizeSyntheticMethodAsSuspendFunction() {\n        val method =\n            OpenTestMethodTestCase::class.java.getDeclaredMethod(\n                \"test\\$suspendImpl\",\n                OpenTestMethodTestCase::class.java,\n                Continuation::class.java\n            )\n\n        assertTrue(ModifierSupport.isStatic(method))\n        assertTrue(method.isSynthetic)\n\n        assertFalse(KotlinReflectionUtils.isKotlinSuspendingFunction(method))\n    }\n\n    @Suppress(\"JUnitMalformedDeclaration\")\n    open class OpenTestMethodTestCase {\n        @Test\n        open suspend fun test() {\n        }\n    }\n}\n"
  },
  {
    "path": "platform-tests/src/test/resources/META-INF/services/org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser",
    "content": "org.junit.platform.suite.engine.error.ErrorSelectorIdentifierParser\n"
  },
  {
    "path": "platform-tests/src/test/resources/config-test-override.properties",
    "content": "# Used in tests, don't delete me\ncom.example.prop.first=first value from override file\n"
  },
  {
    "path": "platform-tests/src/test/resources/config-test.properties",
    "content": "# Used in tests, don't delete me\ncom.example.prop.first=first value\ncom.example.prop.second=second value\ncom.example.prop.third=third value\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nStarted:     .oO fancy display name Oo. ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()])\nFinished:    .oO fancy display name Oo. ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()])\nFinished:    Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nStarted:     .oO fancy display name Oo. ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()])\nFinished:    .oO fancy display name Oo. ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()])\nFinished:    Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Basic > .oO fancy display name Oo. :: STARTED\nJUnit Jupiter > Basic > .oO fancy display name Oo. :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Basic > .oO fancy display name Oo. :: STARTED\nJUnit Jupiter > Basic > .oO fancy display name Oo. :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Basic [OK]\n    '-- .oO fancy display name Oo. [OK]\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Basic ✔\n      └─ .oO fancy display name Oo. ✔\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Basic\n| | +-- .oO fancy display name Oo.\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$BasicTestCase', methodName = 'changeDisplayName', methodParameterTypes = '']\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Basic finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-changeDisplayName-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Basic\n│  │  ├─ .oO fancy display name Oo.\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:changeDisplayName()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$BasicTestCase', methodName = 'changeDisplayName', methodParameterTypes = '']\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Basic finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nStarted:     empty() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()])\nFinished:    empty() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()])\nFinished:    Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nStarted:     empty() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()])\nFinished:    empty() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()])\nFinished:    Basic ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Basic > empty() :: STARTED\nJUnit Jupiter > Basic > empty() :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Basic > empty() :: STARTED\nJUnit Jupiter > Basic > empty() :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Basic [OK]\n    '-- empty() [OK]\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Basic ✔\n      └─ empty() ✔\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Basic\n| | +-- empty()\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$BasicTestCase', methodName = 'empty', methodParameterTypes = '']\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Basic finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/basic/Basic-empty-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Basic\n│  │  ├─ empty()\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]/[method:empty()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$BasicTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$BasicTestCase', methodName = 'empty', methodParameterTypes = '']\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Basic finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nStarted:     failWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()])\nFinished:    failWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()])\n             => Exception: org.opentest4j.AssertionFailedError: multi\n             line\n             fail\n             message\n>> S T A C K T R A C E >>\nFinished:    Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nStarted:     failWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()])\nFinished:    failWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()])\n             => Exception: org.opentest4j.AssertionFailedError: multi\n             line\n             fail\n             message\n>> S T A C K T R A C E >>\nFinished:    Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-none-ascii.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-none-unicode.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-summary-ascii.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-summary-unicode.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Fail > failWithMultiLineMessage() :: STARTED\nJUnit Jupiter > Fail > failWithMultiLineMessage() :: FAILED\n\torg.opentest4j.AssertionFailedError: multi\n\tline\n\tfail\n\tmessage\n    \t>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Fail > failWithMultiLineMessage() :: STARTED\nJUnit Jupiter > Fail > failWithMultiLineMessage() :: FAILED\n\torg.opentest4j.AssertionFailedError: multi\n\tline\n\tfail\n\tmessage\n    \t>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Fail [OK]\n    '-- failWithMultiLineMessage() [X] multi\n          line\n          fail\n          message\n\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Fail ✔\n      └─ failWithMultiLineMessage() ✘ multi\n               line\n               fail\n               message\n\nFailures (1):\n  JUnit Jupiter:Fail:failWithMultiLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: multi\nline\nfail\nmessage\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Fail\n| | +-- failWithMultiLineMessage()\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n| | |    caught: org.opentest4j.AssertionFailedError: multi\n| | |              line\n| | |              fail\n| | |              message\n>> S T A C K T R A C E >>\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [X] FAILED\n\\| '-- Fail finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithMultiLineMessage-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Fail\n│  │  ├─ failWithMultiLineMessage()\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithMultiLineMessage()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithMultiLineMessage', methodParameterTypes = '']\n│  │  │     caught: org.opentest4j.AssertionFailedError: multi\n│  │  │               line\n│  │  │               fail\n│  │  │               message\n>> S T A C K T R A C E >>\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✘ FAILED\n│  └─ Fail finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nStarted:     failWithSingleLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()])\nFinished:    failWithSingleLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()])\n             => Exception: org.opentest4j.AssertionFailedError: single line fail message\n>> S T A C K T R A C E >>\nFinished:    Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nStarted:     failWithSingleLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()])\nFinished:    failWithSingleLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()])\n             => Exception: org.opentest4j.AssertionFailedError: single line fail message\n>> S T A C K T R A C E >>\nFinished:    Fail ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-none-ascii.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-none-unicode.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-summary-ascii.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-summary-unicode.out.txt",
    "content": "\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Fail > failWithSingleLineMessage() :: STARTED\nJUnit Jupiter > Fail > failWithSingleLineMessage() :: FAILED\n\torg.opentest4j.AssertionFailedError: single line fail message\n    \t>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Fail > failWithSingleLineMessage() :: STARTED\nJUnit Jupiter > Fail > failWithSingleLineMessage() :: FAILED\n\torg.opentest4j.AssertionFailedError: single line fail message\n    \t>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Fail [OK]\n    '-- failWithSingleLineMessage() [X] single line fail message\n\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Fail ✔\n      └─ failWithSingleLineMessage() ✘ single line fail message\n\nFailures (1):\n  JUnit Jupiter:Fail:failWithSingleLineMessage()\n    MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n    => org.opentest4j.AssertionFailedError: single line fail message\n>> STACKTRACE >>\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Fail\n| | +-- failWithSingleLineMessage()\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n| | |    caught: org.opentest4j.AssertionFailedError: single line fail message\n>> S T A C K T R A C E >>\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [X] FAILED\n\\| '-- Fail finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/fail/Fail-failWithSingleLineMessage-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Fail\n│  │  ├─ failWithSingleLineMessage()\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]/[method:failWithSingleLineMessage()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$FailTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$FailTestCase', methodName = 'failWithSingleLineMessage', methodParameterTypes = '']\n│  │  │     caught: org.opentest4j.AssertionFailedError: single line fail message\n>> S T A C K T R A C E >>\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✘ FAILED\n│  └─ Fail finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         1 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'dk38', award year = '1974'\\]\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, single = 'mapping'\\]\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'st77', award year = '1977', last seen = '2001'\\]\nFinished:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'dk38', award year = '1974'\\]\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, single = 'mapping'\\]\nReported:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'st77', award year = '1977', last seen = '2001'\\]\nFinished:    reportMultiEntriesWithMultiMappings(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Report > reportMultiEntriesWithMultiMappings(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultiEntriesWithMultiMappings(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Report > reportMultiEntriesWithMultiMappings(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultiEntriesWithMultiMappings(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Report [OK]\n    '-- reportMultiEntriesWithMultiMappings(TestReporter) [OK]\n        ....-..-..T..:...*\n          user name = `dk38`\n          award year = `1974`\n        ....-..-..T..:...* single = `mapping`\n        ....-..-..T..:...*\n          user name = `st77`\n          award year = `1977`\n          last seen = `2001`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Report ✔\n      └─ reportMultiEntriesWithMultiMappings(TestReporter) ✔\n            ....-..-..T..:...*\n               user name = `dk38`\n               award year = `1974`\n            ....-..-..T..:...* single = `mapping`\n            ....-..-..T..:...*\n               user name = `st77`\n               award year = `1977`\n               last seen = `2001`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Report\n| | +-- reportMultiEntriesWithMultiMappings(TestReporter)\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultiEntriesWithMultiMappings', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'dk38', award year = '1974'\\]\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, single = 'mapping'\\]\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'st77', award year = '1977', last seen = '2001'\\]\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Report finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithMultiMappings-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Report\n│  │  ├─ reportMultiEntriesWithMultiMappings(TestReporter)\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithMultiMappings(org.junit.jupiter.api.TestReporter)]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultiEntriesWithMultiMappings', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'dk38', award year = '1974'\\]\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, single = 'mapping'\\]\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, user name = 'st77', award year = '1977', last seen = '2001'\\]\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Report finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\nReported:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, far = 'boo'\\]\nFinished:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\nReported:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, far = 'boo'\\]\nFinished:    reportMultiEntriesWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Report > reportMultiEntriesWithSingleMapping(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultiEntriesWithSingleMapping(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Report > reportMultiEntriesWithSingleMapping(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultiEntriesWithSingleMapping(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Report [OK]\n    '-- reportMultiEntriesWithSingleMapping(TestReporter) [OK]\n        ....-..-..T..:...* foo = `bar`\n        ....-..-..T..:...* far = `boo`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Report ✔\n      └─ reportMultiEntriesWithSingleMapping(TestReporter) ✔\n            ....-..-..T..:...* foo = `bar`\n            ....-..-..T..:...* far = `boo`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Report\n| | +-- reportMultiEntriesWithSingleMapping(TestReporter)\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultiEntriesWithSingleMapping', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, far = 'boo'\\]\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Report finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultiEntriesWithSingleMapping-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Report\n│  │  ├─ reportMultiEntriesWithSingleMapping(TestReporter)\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultiEntriesWithSingleMapping(org.junit.jupiter.api.TestReporter)]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultiEntriesWithSingleMapping', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, far = 'boo'\\]\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Report finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\nReported:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'bar'\\]\nFinished:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\nReported:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\nReported:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'bar'\\]\nFinished:    reportMultipleMessages(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Report > reportMultipleMessages(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultipleMessages(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Report > reportMultipleMessages(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportMultipleMessages(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Report [OK]\n    '-- reportMultipleMessages(TestReporter) [OK]\n        ....-..-..T..:...* value = `foo`\n        ....-..-..T..:...* value = `bar`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Report ✔\n      └─ reportMultipleMessages(TestReporter) ✔\n            ....-..-..T..:...* value = `foo`\n            ....-..-..T..:...* value = `bar`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Report\n| | +-- reportMultipleMessages(TestReporter)\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultipleMessages', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'bar'\\]\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Report finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportMultipleMessages-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Report\n│  │  ├─ reportMultipleMessages(TestReporter)\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportMultipleMessages(org.junit.jupiter.api.TestReporter)]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportMultipleMessages', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'bar'\\]\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Report finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nReported:    reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\nFinished:    reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nReported:    reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\nFinished:    reportSingleEntryWithSingleMapping(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Report > reportSingleEntryWithSingleMapping(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportSingleEntryWithSingleMapping(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Report > reportSingleEntryWithSingleMapping(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportSingleEntryWithSingleMapping(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Report [OK]\n    '-- reportSingleEntryWithSingleMapping(TestReporter) [OK]\n        ....-..-..T..:...* foo = `bar`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Report ✔\n      └─ reportSingleEntryWithSingleMapping(TestReporter) ✔\n            ....-..-..T..:...* foo = `bar`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Report\n| | +-- reportSingleEntryWithSingleMapping(TestReporter)\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportSingleEntryWithSingleMapping', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Report finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleEntryWithSingleMapping-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Report\n│  │  ├─ reportSingleEntryWithSingleMapping(TestReporter)\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleEntryWithSingleMapping(org.junit.jupiter.api.TestReporter)]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportSingleEntryWithSingleMapping', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, foo = 'bar'\\]\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Report finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\nReported:    reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\nFinished:    reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nStarted:     reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\nReported:    reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\n             => Reported values: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\nFinished:    reportSingleMessage(TestReporter) ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)])\nFinished:    Report ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Report > reportSingleMessage(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportSingleMessage(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Report > reportSingleMessage(TestReporter) :: STARTED\nJUnit Jupiter > Report > reportSingleMessage(TestReporter) :: SUCCESSFUL\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Report [OK]\n    '-- reportSingleMessage(TestReporter) [OK]\n        ....-..-..T..:...* value = `foo`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Report ✔\n      └─ reportSingleMessage(TestReporter) ✔\n            ....-..-..T..:...* value = `foo`\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Report\n| | +-- reportSingleMessage(TestReporter)\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportSingleMessage', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n\\| \\| \\|   reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\n\\| \\| \\|  duration: [\\d]+ ms\n| | |    status: [OK] SUCCESSFUL\n\\| '-- Report finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/report/Report-reportSingleMessage-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Report\n│  │  ├─ reportSingleMessage(TestReporter)\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]/[method:reportSingleMessage(org.junit.jupiter.api.TestReporter)]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$ReportTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$ReportTestCase', methodName = 'reportSingleMessage', methodParameterTypes = 'org.junit.jupiter.api.TestReporter']\n│  │  │    reports: ReportEntry \\[timestamp = ....-..-..T..:...*, value = 'foo'\\]\n│  │  │   duration: [\\d]+ ms\n│  │  │     status: ✔ SUCCESSFUL\n│  └─ Report finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         0 tests skipped         ]\n[         1 tests started         ]\n[         0 tests aborted         ]\n[         1 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nSkipped:     skipWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithMultiLineMessage()])\n             => Reason: multi\n             line\n             fail\n             message\nFinished:    Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nSkipped:     skipWithMultiLineMessage() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithMultiLineMessage()])\n             => Reason: multi\n             line\n             fail\n             message\nFinished:    Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Skip > skipWithMultiLineMessage() :: SKIPPED\n\tReason: multi\n\tline\n\tfail\n\tmessage\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Skip > skipWithMultiLineMessage() :: SKIPPED\n\tReason: multi\n\tline\n\tfail\n\tmessage\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Skip [OK]\n    '-- skipWithMultiLineMessage() [S] multi\n          line\n          fail\n          message\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Skip ✔\n      └─ skipWithMultiLineMessage() ↷ multi\n               line\n               fail\n               message\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Skip\n| | +-- skipWithMultiLineMessage()\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithMultiLineMessage()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$SkipTestCase', methodName = 'skipWithMultiLineMessage', methodParameterTypes = '']\n| | |    reason: multi\n| | |              line\n| | |              fail\n| | |              message\n| | |    status: [S] SKIPPED\n\\| '-- Skip finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithMultiLineMessage-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Skip\n│  │  ├─ skipWithMultiLineMessage()\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithMultiLineMessage()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$SkipTestCase', methodName = 'skipWithMultiLineMessage', methodParameterTypes = '']\n│  │  │     reason: multi\n│  │  │               line\n│  │  │               fail\n│  │  │               message\n│  │  │     status: ↷ SKIPPED\n│  └─ Skip finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-flat-ascii.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nSkipped:     skipWithSingleLineReason() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithSingleLineReason()])\n             => Reason: single line skip reason\nFinished:    Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-flat-unicode.out.txt",
    "content": "Test execution started. Number of static tests: 1\nStarted:     JUnit Jupiter ([engine:junit-jupiter])\nStarted:     Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nSkipped:     skipWithSingleLineReason() ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithSingleLineReason()])\n             => Reason: single line skip reason\nFinished:    Skip ([engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase])\nFinished:    JUnit Jupiter ([engine:junit-jupiter])\nTest execution finished.\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-none-ascii.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-none-unicode.out.txt",
    "content": "\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-summary-ascii.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-summary-unicode.out.txt",
    "content": "\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-testfeed-ascii.out.txt",
    "content": "JUnit Jupiter > Skip > skipWithSingleLineReason() :: SKIPPED\n\tReason: single line skip reason\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-testfeed-unicode.out.txt",
    "content": "JUnit Jupiter > Skip > skipWithSingleLineReason() :: SKIPPED\n\tReason: single line skip reason\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-tree-ascii.out.txt",
    "content": ".\n'-- JUnit Jupiter [OK]\n  '-- Skip [OK]\n    '-- skipWithSingleLineReason() [S] single line skip reason\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-tree-unicode.out.txt",
    "content": "╷\n└─ JUnit Jupiter ✔\n   └─ Skip ✔\n      └─ skipWithSingleLineReason() ↷ single line skip reason\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-verbose-ascii.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n.\n+-- JUnit Jupiter\n| +-- Skip\n| | +-- skipWithSingleLineReason()\n| | |      tags: []\n| | |  uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithSingleLineReason()]\n| | |    parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]\n| | |    source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$SkipTestCase', methodName = 'skipWithSingleLineReason', methodParameterTypes = '']\n| | |    reason: single line skip reason\n| | |    status: [S] SKIPPED\n\\| '-- Skip finished after [\\d]+ ms\\.\n'-- JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/console/details/skip/Skip-skipWithSingleLineReason-verbose-unicode.out.txt",
    "content": "Test plan execution started. Number of static tests: 1\n╷\n├─ JUnit Jupiter\n│  ├─ Skip\n│  │  ├─ skipWithSingleLineReason()\n│  │  │       tags: []\n│  │  │   uniqueId: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]/[method:skipWithSingleLineReason()]\n│  │  │     parent: [engine:junit-jupiter]/[class:org.junit.platform.console.ConsoleDetailsTests$SkipTestCase]\n│  │  │     source: MethodSource [className = 'org.junit.platform.console.ConsoleDetailsTests$SkipTestCase', methodName = 'skipWithSingleLineReason', methodParameterTypes = '']\n│  │  │     reason: single line skip reason\n│  │  │     status: ↷ SKIPPED\n│  └─ Skip finished after [\\d]+ ms\\.\n└─ JUnit Jupiter finished after [\\d]+ ms\\.\nTest plan execution finished. Number of all tests: 1\n\nTest run finished after [\\d]+ ms\n[         2 containers found      ]\n[         0 containers skipped    ]\n[         2 containers started    ]\n[         0 containers aborted    ]\n[         2 containers successful ]\n[         0 containers failed     ]\n[         1 tests found           ]\n[         1 tests skipped         ]\n[         0 tests started         ]\n[         0 tests aborted         ]\n[         0 tests successful      ]\n[         0 tests failed          ]\n"
  },
  {
    "path": "platform-tests/src/test/resources/default-package.resource",
    "content": "This file was unintentionally left blank.\n"
  },
  {
    "path": "platform-tests/src/test/resources/do_not_delete_me.txt",
    "content": "I am used by tests, so...\n\nDo NOT delete me!"
  },
  {
    "path": "platform-tests/src/test/resources/error-engine/META-INF/services/org.junit.platform.engine.TestEngine",
    "content": "org.junit.platform.suite.engine.error.SelectorProcessingErrorCausingEngine\n"
  },
  {
    "path": "platform-tests/src/test/resources/intercepted-testservices/META-INF/services/org.junit.platform.engine.TestEngine",
    "content": "org.junit.platform.launcher.InterceptedTestEngine\n"
  },
  {
    "path": "platform-tests/src/test/resources/intercepted-testservices/META-INF/services/org.junit.platform.launcher.LauncherSessionListener",
    "content": "org.junit.platform.launcher.InterceptorInjectedLauncherSessionListener\n"
  },
  {
    "path": "platform-tests/src/test/resources/jenkins-junit.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!--\n Source: https://svn.jenkins-ci.org/trunk/hudson/dtkit/dtkit-format/dtkit-junit-model/src/main/resources/com/thalesgroup/dtkit/junit/model/xsd/junit-4.xsd\n \n This file available under the terms of the MIT License as follows:\n \n *******************************************************************************\n * Copyright (c) 2010 Thales Corporate Services SAS                             *\n *                                                                              *\n * Permission is hereby granted, free of charge, to any person obtaining a copy *\n * of this software and associated documentation files (the \"Software\"), to deal*\n * in the Software without restriction, including without limitation the rights *\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell    *\n * copies of the Software, and to permit persons to whom the Software is        *\n * furnished to do so, subject to the following conditions:                     *\n *                                                                              *\n * The above copyright notice and this permission notice shall be included in   *\n * all copies or substantial portions of the Software.                          *\n *                                                                              *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR   *\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,     *\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  *\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER       *\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,*\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN    *\n * THE SOFTWARE.                                                                *\n ********************************************************************************\n -->\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n\n    <xs:element name=\"failure\">\n        <xs:complexType mixed=\"true\">\n            <xs:attribute name=\"type\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"message\" type=\"xs:string\" use=\"optional\"/>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"error\">\n        <xs:complexType mixed=\"true\">\n            <xs:attribute name=\"type\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"message\" type=\"xs:string\" use=\"optional\"/>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"properties\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element ref=\"property\" maxOccurs=\"unbounded\"/>\n            </xs:sequence>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"property\">\n        <xs:complexType>\n            <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>\n            <xs:attribute name=\"value\" type=\"xs:string\" use=\"required\"/>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"skipped\" type=\"xs:string\"/>\n    <xs:element name=\"system-err\" type=\"xs:string\"/>\n    <xs:element name=\"system-out\" type=\"xs:string\"/>\n\n    <xs:element name=\"testcase\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element ref=\"skipped\" minOccurs=\"0\" maxOccurs=\"1\"/>\n                <xs:element ref=\"error\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n                <xs:element ref=\"failure\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n                <xs:element ref=\"system-out\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n                <xs:element ref=\"system-err\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n            </xs:sequence>\n            <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>\n            <xs:attribute name=\"assertions\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"time\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"classname\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"status\" type=\"xs:string\" use=\"optional\"/>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"testsuite\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element ref=\"properties\" minOccurs=\"0\" maxOccurs=\"1\"/>\n                <xs:element ref=\"testcase\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n                <xs:element ref=\"system-out\" minOccurs=\"0\" maxOccurs=\"1\"/>\n                <xs:element ref=\"system-err\" minOccurs=\"0\" maxOccurs=\"1\"/>\n            </xs:sequence>\n            <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>\n            <xs:attribute name=\"tests\" type=\"xs:string\" use=\"required\"/>\n            <xs:attribute name=\"failures\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"errors\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"time\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"disabled\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"skipped\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"timestamp\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"hostname\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"id\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"package\" type=\"xs:string\" use=\"optional\"/>\n        </xs:complexType>\n    </xs:element>\n\n    <xs:element name=\"testsuites\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element ref=\"testsuite\" minOccurs=\"0\" maxOccurs=\"unbounded\"/>\n            </xs:sequence>\n            <xs:attribute name=\"name\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"time\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"tests\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"failures\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"disabled\" type=\"xs:string\" use=\"optional\"/>\n            <xs:attribute name=\"errors\" type=\"xs:string\" use=\"optional\"/>\n        </xs:complexType>\n    </xs:element>\n\n\n</xs:schema>\n"
  },
  {
    "path": "platform-tests/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.extensions.autodetection.enabled=true\njunit.platform.stacktrace.pruning.enabled=false\n"
  },
  {
    "path": "platform-tests/src/test/resources/log4j2-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\"\n\t\t\t   xmlns=\"https://logging.apache.org/xml/ns\"\n\t\t\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t   xsi:schemaLocation=\"https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd\">\n\t<Appenders>\n\t\t<Console name=\"Console\" target=\"SYSTEM_OUT\">\n\t\t\t<PatternLayout pattern=\"%d{HH:mm:ss.SSSSSS} [%-18t] %-5level %logger{1.} - %msg%n\"/>\n\t\t</Console>\n\t</Appenders>\n\t<Loggers>\n\t\t<Logger name=\"org.junit\" level=\"WARN\"/>\n\t\t<Logger name=\"org.junit.platform.commons.util.DefaultClasspathScanner\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.engine.support.descriptor.DemoClassTestDescriptor\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.CompositeEngineExecutionListener\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.CompositeTestExecutionListener\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.DiscoveryIssueCollector\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.DiscoveryIssueNotifier\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.EngineIdValidator\" level=\"FATAL\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.EngineFilterer\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.InternalTestPlan\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.core.LauncherConfigurationParameters\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.platform.launcher.listeners.discovery.LoggingLauncherDiscoveryListener\" level=\"OFF\"/>\n\t\t<Logger name=\"org.junit.platform.engine.support.hierarchical.WorkerThreadPoolHierarchicalTestExecutorService\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.vintage.engine\" level=\"ERROR\"/>\n\t\t<Logger name=\"org.junit.jupiter.engine\" level=\"ERROR\"/>\n\t\t<Root level=\"ERROR\">\n\t\t\t<AppenderRef ref=\"Console\"/>\n\t\t</Root>\n\t</Loggers>\n</Configuration>\n"
  },
  {
    "path": "platform-tests/src/test/resources/modules-2500/foo/Foo.java",
    "content": "package foo;\n\npublic class Foo {}\n"
  },
  {
    "path": "platform-tests/src/test/resources/modules-2500/foo/module-info.java",
    "content": "module foo {\n  exports foo;\n}\n"
  },
  {
    "path": "platform-tests/src/test/resources/modules-2500/foo.bar/FooBar.java",
    "content": "package foo.bar;\n\npublic class FooBar {}\n"
  },
  {
    "path": "platform-tests/src/test/resources/modules-2500/foo.bar/module-info.java",
    "content": "open module foo.bar {\n  requires foo;\n}\n"
  },
  {
    "path": "platform-tests/src/test/resources/org/junit/platform/commons/example.resource",
    "content": "This file was unintentionally left blank.\n"
  },
  {
    "path": "platform-tests/src/test/resources/org/junit/platform/commons/other-example.resource",
    "content": "This file was unintentionally left blank.\n"
  },
  {
    "path": "platform-tests/src/test/resources/test-junit-platform.properties",
    "content": "org.junit.platform.launcher.core.LauncherConfigurationParametersTests = from config file\n"
  },
  {
    "path": "platform-tests/src/test/resources/testservices/META-INF/services/org.junit.platform.launcher.LauncherDiscoveryListener",
    "content": "org.junit.platform.launcher.TestLauncherDiscoveryListener\n"
  },
  {
    "path": "platform-tests/src/test/resources/testservices/META-INF/services/org.junit.platform.launcher.LauncherInterceptor",
    "content": "org.junit.platform.launcher.TestLauncherInterceptor1\norg.junit.platform.launcher.TestLauncherInterceptor2\n"
  },
  {
    "path": "platform-tests/src/test/resources/testservices/META-INF/services/org.junit.platform.launcher.LauncherSessionListener",
    "content": "org.junit.platform.launcher.TestLauncherSessionListener\n"
  },
  {
    "path": "platform-tests/src/test/resources/testservices/META-INF/services/org.junit.platform.launcher.PostDiscoveryFilter",
    "content": "org.junit.platform.launcher.TestPostDiscoveryTagFilter\n"
  },
  {
    "path": "platform-tests/src/test/resources/testservices/META-INF/services/org.junit.platform.launcher.TestExecutionListener",
    "content": "org.junit.platform.launcher.listeners.NoopTestExecutionListener\norg.junit.platform.launcher.listeners.UnusedTestExecutionListener\norg.junit.platform.launcher.listeners.AnotherUnusedTestExecutionListener"
  },
  {
    "path": "platform-tooling-support-tests/platform-tooling-support-tests.gradle.kts",
    "content": "import com.gradle.develocity.agent.gradle.internal.test.TestDistributionConfigurationInternal\nimport junitbuild.extensions.capitalized\nimport junitbuild.extensions.dependencyProject\nimport junitbuild.extensions.javaModuleName\nimport net.ltgt.gradle.errorprone.errorprone\nimport org.gradle.api.tasks.PathSensitivity.RELATIVE\nimport org.gradle.kotlin.dsl.support.listFilesOrdered\nimport java.time.Duration\n\nplugins {\n\tid(\"junitbuild.build-parameters\")\n\tid(\"junitbuild.kotlin-library-conventions\")\n\tid(\"junitbuild.testing-conventions\")\n}\n\njavaLibrary {\n\tmainJavaVersion = JavaVersion.VERSION_25\n}\n\nspotless {\n\tjava {\n\t\ttarget(files(project.java.sourceSets.map { it.allJava }), \"projects/**/*.java\")\n\t\tfileTree(\"projects/junit-start\") {\n\t\t\tinclude(\"**/*.java\")\n\t\t}.forEach { file ->\n\t\t\tsuppressLintsFor { // due to compact source files and module imports\n\t\t\t\tpath = file.toRelativeString(project.projectDir).replace('\\\\', '/')\n\t\t\t}\n\t\t}\n\t}\n\tformat(\"moduleAndPackageInfo\") {\n\t\ttarget(\"projects/**/module-info.java\", \"projects/**/package-info.java\")\n\t}\n\tkotlin {\n\t\ttarget(\"projects/**/*.kt\")\n\t}\n\tformat(\"projects\") {\n\t\ttarget(\"projects/**/*.gradle.kts\", \"projects/**/*.md\")\n\t\ttrimTrailingWhitespace()\n\t\tendWithNewline()\n\t}\n}\n\nval thirdPartyJars = configurations.dependencyScope(\"thirdPartyJars\")\nval thirdPartyJarsClasspath = configurations.resolvable(\"thirdPartyJarsClasspath\") {\n\textendsFrom(thirdPartyJars.get())\n}\nval antJars = configurations.dependencyScope(\"antJars\")\nval antJarsClasspath = configurations.resolvable(\"antJarsClasspath\") {\n\textendsFrom(antJars.get())\n}\nval mavenDistribution = configurations.dependencyScope(\"mavenDistribution\")\nval mavenDistributionClasspath = configurations.resolvable(\"mavenDistributionClasspath\") {\n\textendsFrom(mavenDistribution.get())\n}\n\nval modularProjects: List<Project> by rootProject\n\ndependencies {\n\timplementation(libs.commons.io) {\n\t\tbecause(\"moving/deleting directory trees\")\n\t}\n\timplementation(projects.platformTests) {\n\t\tcapabilities {\n\t\t\trequireFeature(\"process-starter\")\n\t\t}\n\t}\n\timplementation(projects.junitJupiterApi) {\n\t\tbecause(\"it uses the OS enum to support Windows\")\n\t}\n\n\tthirdPartyJars(libs.junit4) {\n\t\texclude(group = \"org.hamcrest\")\n\t}\n\tthirdPartyJars(libs.assertj)\n\tthirdPartyJars(libs.apiguardian)\n\tthirdPartyJars(libs.fastcsv)\n\tthirdPartyJars(libs.hamcrest)\n\tthirdPartyJars(libs.jimfs)\n\tthirdPartyJars(libs.jspecify)\n\tthirdPartyJars(kotlin(\"stdlib\"))\n\tthirdPartyJars(kotlin(\"reflect\"))\n\tthirdPartyJars(libs.kotlinx.coroutines.core)\n\tthirdPartyJars(libs.opentest4j)\n\tthirdPartyJars(libs.openTestReporting.events)\n\tthirdPartyJars(libs.openTestReporting.tooling.spi)\n\tthirdPartyJars(libs.picocli)\n\n\tantJars(platform(projects.junitBom))\n\tantJars(libs.bundles.ant)\n\tantJars(projects.junitPlatformConsoleStandalone)\n\tantJars(projects.junitPlatformLauncher)\n\tantJars(projects.junitPlatformReporting)\n\n\tmavenDistribution(libs.maven) {\n\t\tartifact {\n\t\t\tclassifier = \"bin\"\n\t\t\ttype = \"zip\"\n\t\t\tisTransitive = false\n\t\t}\n\t}\n}\n\nval mavenDistributionDir = layout.buildDirectory.dir(\"maven-distribution\")\n\nval unzipMavenDistribution by tasks.registering(Sync::class) {\n\tfrom(zipTree(mavenDistributionClasspath.flatMap { d -> d.elements.map { e -> e.single() } }))\n\tinto(mavenDistributionDir)\n}\n\nval normalizeMavenRepo by tasks.registering(Sync::class) {\n\n\tval mavenizedProjects: List<Project> by rootProject\n\tval tempRepoDir: File by rootProject\n\tval tempRepoName: String by rootProject\n\n\t// All maven-aware projects must be published to the local temp repository\n\t(mavenizedProjects + dependencyProject(projects.junitBom))\n\t\t.map { project -> project.tasks.named(\"publishAllPublicationsTo${tempRepoName.capitalized()}Repository\") }\n\t\t.forEach { dependsOn(it) }\n\n\tfrom(tempRepoDir) {\n\t\texclude(\"**/maven-metadata.xml*\")\n\t\texclude(\"**/*.md5\")\n\t\texclude(\"**/*.sha*\")\n\t\texclude(\"**/*.module\")\n\t}\n\tfrom(tempRepoDir) {\n\t\tinclude(\"**/*.module\")\n\t\tval regex = \"\\\"(sha\\\\d+|md5|size)\\\": (?:\\\".+\\\"|\\\\d+)(,)?\".toRegex()\n\t\tfilter { line -> regex.replace(line, \"\\\"normalized-$1\\\": \\\"normalized-value\\\"$2\") }\n\t}\n\trename(\"(.*\\\\W)\\\\d{8}\\\\.\\\\d{6}-\\\\d+(\\\\W.*)\", \"$1SNAPSHOT$2\")\n\tinto(layout.buildDirectory.dir(\"normalized-repo\"))\n}\n\nval archUnit by testing.suites.registering(JvmTestSuite::class) {\n\tdependencies {\n\t\timplementation(libs.archunit) {\n\t\t\tbecause(\"checking the architecture\")\n\t\t}\n\t\timplementation(libs.apiguardian) {\n\t\t\tbecause(\"we validate that public classes are annotated\")\n\t\t}\n\t\timplementation(libs.jspecify) {\n\t\t\tbecause(\"we validate that packages are annotated\")\n\t\t}\n\t\timplementation(libs.assertj)\n\t\truntimeOnly.bundle(libs.bundles.log4j)\n\t\tmodularProjects.forEach {\n\t\t\timplementation(project(it.path))\n\t\t}\n\t}\n\n\ttargets {\n\t\tall {\n\t\t\ttestTask.configure {\n\t\t\t\tuseJUnitPlatform()\n\t\t\t\t(options as JUnitPlatformOptions).apply {\n\t\t\t\t\tincludeEngines(\"archunit\")\n\t\t\t\t\texcludeEngines(\"junit-jupiter\")\n\t\t\t\t}\n\t\t\t\tdevelocity {\n\t\t\t\t\ttestRetry.maxRetries = 0\n\t\t\t\t\ttestDistribution.enabled = false\n\t\t\t\t\tpredictiveTestSelection.enabled = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\ntasks.compileJava {\n\toptions.errorprone {\n\t\tdisableAllChecks = true\n\t}\n}\n\ntasks.named<Checkstyle>(\"checkstyle${archUnit.name.capitalized()}\").configure {\n\tconfig = resources.text.fromFile(checkstyle.configDirectory.file(\"checkstyleTest.xml\"))\n}\n\ntasks.check {\n\tdependsOn(archUnit)\n}\n\nval test by testing.suites.getting(JvmTestSuite::class) {\n\tdependencies {\n\t\timplementation(libs.bndlib) {\n\t\t\tbecause(\"parsing OSGi metadata\")\n\t\t}\n\t\truntimeOnly(libs.slf4j.julBinding) {\n\t\t\tbecause(\"provide appropriate SLF4J binding\")\n\t\t}\n\t\timplementation(libs.ant) {\n\t\t\tbecause(\"we reference Ant's main class\")\n\t\t}\n\t\timplementation.bundle(libs.bundles.xmlunit)\n\t\timplementation(testFixtures(projects.junitJupiterApi))\n\t\timplementation(testFixtures(projects.junitPlatformReporting))\n\t\timplementation(libs.snapshotTests.junit5)\n\t\timplementation(libs.snapshotTests.xml)\n\t}\n\n\ttargets {\n\t\tall {\n\t\t\ttestTask.configure {\n\t\t\t\tshouldRunAfter(archUnit)\n\n\t\t\t\t// Opt-out via system property: '-Dplatform.tooling.support.tests.enabled=false'\n\t\t\t\tenabled = System.getProperty(\"platform.tooling.support.tests.enabled\")?.toBoolean() ?: true\n\n\t\t\t\t// The following if-block is necessary since Gradle will otherwise\n\t\t\t\t// always publish all mavenizedProjects even if this \"test\" task\n\t\t\t\t// is not executed.\n\t\t\t\tif (enabled) {\n\t\t\t\t\tdependsOn(normalizeMavenRepo)\n\t\t\t\t\tjvmArgumentProviders += MavenRepo(project, normalizeMavenRepo.map { it.destinationDir })\n\t\t\t\t}\n\t\t\t\tenvironment.remove(\"JAVA_TOOL_OPTIONS\")\n\n\t\t\t\tjvmArgumentProviders += JarPath(project, thirdPartyJarsClasspath.get(), \"thirdPartyJars\")\n\t\t\t\tjvmArgumentProviders += JarPath(project, antJarsClasspath.get(), \"antJars\")\n\t\t\t\tjvmArgumentProviders += MavenDistribution(project, unzipMavenDistribution, mavenDistributionDir)\n\n\t\t\t\tsystemProperty(\"junit.modules\", modularProjects.map { it.javaModuleName }.joinToString(\",\"))\n\n\t\t\t\tjvmArgumentProviders += CommandLineArgumentProvider {\n\t\t\t\t\tmodularProjects.map { \"-Djunit.moduleSourcePath.${it.javaModuleName}=${it.sourceSets[\"main\"].allJava.sourceDirectories.filter { it.exists() }.asPath}\" }\n\t\t\t\t}\n\n\t\t\t\tinputs.apply {\n\t\t\t\t\tdir(\"projects\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tfile(\"${rootDir}/gradle.properties\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tfile(\"${rootDir}/settings.gradle.kts\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tfile(\"${rootDir}/gradlew\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tfile(\"${rootDir}/gradlew.bat\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tdir(\"${rootDir}/gradle/wrapper\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tdir(\"${rootDir}/documentation/src/main\").withPathSensitivity(RELATIVE)\n\t\t\t\t\tdir(\"${rootDir}/documentation/src/test\").withPathSensitivity(RELATIVE)\n\t\t\t\t}\n\n\t\t\t\t// Disable capturing output since parallel execution is enabled and output of\n\t\t\t\t// external processes happens on non-test threads which can't reliably be\n\t\t\t\t// attributed to the test that started the process.\n\t\t\t\tsystemProperty(\"junit.platform.output.capture.stdout\", \"false\")\n\t\t\t\tsystemProperty(\"junit.platform.output.capture.stderr\", \"false\")\n\n\t\t\t\tdevelocity {\n\t\t\t\t\ttestDistribution {\n\t\t\t\t\t\trequirements.add(\"jdk=17\")\n\t\t\t\t\t\tthis as TestDistributionConfigurationInternal\n\t\t\t\t\t\tpreferredMaxDuration = Duration.ofMillis(500)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tjvmArgumentProviders += JavaHomeDir(project, 17, develocity.testDistribution.enabled)\n\n\t\t\t\tval gradleJavaVersion = JavaVersion.current().majorVersion.toInt()\n\t\t\t\tjvmArgumentProviders += JavaHomeDir(project, gradleJavaVersion, develocity.testDistribution.enabled)\n\t\t\t\tsystemProperty(\"gradle.java.version\", gradleJavaVersion)\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass MavenRepo(project: Project, @get:Internal val repoDir: Provider<File>) : CommandLineArgumentProvider {\n\n\t// Track jars and non-jars separately to benefit from runtime classpath normalization\n\t// which ignores timestamp manifest attributes.\n\n\t@InputFiles\n\t@Classpath\n\tval jarFiles: ConfigurableFileTree = project.fileTree(repoDir) {\n\t\tinclude(\"**/*.jar\")\n\t}\n\n\t@InputFiles\n\t@PathSensitive(RELATIVE)\n\tval nonJarFiles: ConfigurableFileTree = project.fileTree(repoDir) {\n\t\texclude(\"**/*.jar\")\n\t}\n\n\toverride fun asArguments() = listOf(\"-Dmaven.repo=${repoDir.get().absolutePath}\")\n}\n\nclass JavaHomeDir(project: Project, @Input val version: Int, testDistributionEnabled: Provider<Boolean>) : CommandLineArgumentProvider {\n\n\t@Internal\n\tval javaLauncher: Property<JavaLauncher> = project.objects.property<JavaLauncher>()\n\t\t\t.value(project.provider {\n\t\t\t\ttry {\n\t\t\t\t\tproject.javaToolchains.launcherFor {\n\t\t\t\t\t\tlanguageVersion = JavaLanguageVersion.of(version)\n\t\t\t\t\t}.get()\n\t\t\t\t} catch (e: Exception) {\n\t\t\t\t\tnull\n\t\t\t\t}\n\t\t\t})\n\n\t@Internal\n\tval enabled: Property<Boolean> = project.objects.property<Boolean>().convention(testDistributionEnabled.map { !it })\n\n\toverride fun asArguments(): List<String> {\n\t\tif (!enabled.get()) {\n\t\t\treturn emptyList()\n\t\t}\n\t\tval metadata = javaLauncher.map { it.metadata }\n\t\tval javaHome = metadata.map { it.installationPath.asFile.absolutePath }.orNull\n\t\treturn javaHome?.let { listOf(\"-Djava.home.$version=$it\") } ?: emptyList()\n\t}\n}\n\nclass JarPath(project: Project, configuration: Configuration, @Input val key: String = configuration.name) : CommandLineArgumentProvider {\n\t@get:Classpath\n\tval files: ConfigurableFileCollection = project.objects.fileCollection().from(configuration)\n\n\toverride fun asArguments() = listOf(\"-D${key}=${files.asPath}\")\n}\n\nclass MavenDistribution(project: Project, sourceTask: TaskProvider<*>, distributionDir: Provider<Directory>) : CommandLineArgumentProvider {\n\t@InputDirectory\n\t@PathSensitive(RELATIVE)\n\tval mavenDistribution: DirectoryProperty = project.objects.directoryProperty()\n\t\t.fileProvider(project.files(distributionDir).builtBy(sourceTask).elements.map { it.single().asFile.listFilesOrdered().single() })\n\n\toverride fun asArguments() = listOf(\"-DmavenDistribution=${mavenDistribution.get().asFile.absolutePath}\")\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/build.gradle.kts",
    "content": "plugins {\n\tjava\n\tid(\"org.graalvm.buildtools.native\")\n}\n\nval junitVersion: String by project\n\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:$junitVersion\")\n\ttestImplementation(\"junit:junit:4.13.2\")\n\ttestImplementation(\"org.junit.platform:junit-platform-suite:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.vintage:junit-vintage-engine:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-reporting:$junitVersion\")\n}\n\ntasks.withType<JavaCompile>().configureEach {\n\toptions.release = 21\n}\n\ntasks.test {\n\tuseJUnitPlatform {\n\t\tincludeEngines(\"junit-platform-suite\")\n\t}\n\n\tval outputDir = reports.junitXml.outputLocation\n\tjvmArgumentProviders += CommandLineArgumentProvider {\n\t\tlistOf(\n\t\t\t\"-Djunit.platform.reporting.open.xml.enabled=true\",\n\t\t\t\"-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}\"\n\t\t)\n\t}\n}\n\nval initializeAtBuildTime = mapOf(\n\t// These need to be added to native-build-tools\n\t\"5.14.1\" to listOf(\n\t\t// https://github.com/graalvm/native-build-tools/pull/794\n\t\t\"org.junit.jupiter.engine.discovery.MethodSegmentResolver\"\n\t),\n\t\"6.1\" to listOf(\n\t\t// https://github.com/graalvm/native-build-tools/pull/867\n\t\t\"org.junit.platform.launcher.core.DiscoveryIssueReportingDiscoveryListener\",\n\t),\n)\n\ngraalvmNative {\n\tmetadataRepository {\n\t\tenabled = false\n\t}\n\tbinaries {\n\t\tnamed(\"test\") {\n\t\t\tbuildArgs.add(\"-H:+ReportExceptionStackTraces\")\n\t\t\tval classNames = initializeAtBuildTime.values.flatten()\n\t\t\tif (classNames.isNotEmpty()) {\n\t\t\t\tbuildArgs.add(\"--initialize-at-build-time=${classNames.joinToString(\",\")}\")\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/settings.gradle.kts",
    "content": "pluginManagement {\n\tplugins {\n\t\t// TODO Check if classes can be removed from `initializeAtBuildTime` in build.gradle.kts when upgrading\n\t\tid(\"org.graalvm.buildtools.native\") version \"1.1.0\"\n\t}\n\trepositories {\n\t\tmavenCentral()\n\t\tgradlePluginPortal()\n\t\tmaven(url = \"https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots\") {\n\t\t\tmavenContent {\n\t\t\t\tsnapshotsOnly()\n\t\t\t}\n\t\t}\n\t}\n}\n\nplugins {\n\tid(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\ndependencyResolutionManagement {\n\trepositories {\n\t\trepositories {\n\t\t\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\t\t\tmavenCentral()\n\t\t\tmaven(url = \"https://raw.githubusercontent.com/graalvm/native-build-tools/snapshots\") {\n\t\t\t\tmavenContent {\n\t\t\t\t\tsnapshotsOnly()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nrootProject.name = \"graalvm-starter\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/main/java/com/example/project/Calculator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\npublic class Calculator {\n\n\tpublic int add(int a, int b) {\n\t\treturn a + b;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorParameterizedClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n@ParameterizedClass\n@ValueSource(ints = { 1, 2 })\nclass CalculatorParameterizedClassTests {\n\n\t@Parameter\n\tint i;\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { 1, 2 })\n\tvoid parameterizedTest(int j) {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(i + j, calculator.add(i, j));\n\t}\n\n\t@Nested\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\t@Disabled(\"https://github.com/junit-team/junit-framework/issues/4440\")\n\tclass Inner {\n\n\t\tfinal int j;\n\n\t\tInner(int j) {\n\t\t\tthis.j = j;\n\t\t}\n\n\t\t@Test\n\t\tvoid regularTest() {\n\t\t\tCalculator calculator = new Calculator();\n\t\t\tassertEquals(i + j, calculator.add(i, j));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/CalculatorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nclass CalculatorTests {\n\n\t@Test\n\t@DisplayName(\"1 + 1 = 2\")\n\tvoid addsTwoNumbers() {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(2, calculator.add(1, 1), \"1 + 1 should equal 2\");\n\t}\n\n\t@ParameterizedTest(name = \"{0} + {1} = {2}\", quoteTextArguments = false)\n\t@CsvSource({ //\n\t\t\t\"0,    1,   1\", //\n\t\t\t\"1,    2,   3\", //\n\t\t\t\"49,  51, 100\", //\n\t\t\t\"1,  100, 101\" //\n\t})\n\tvoid add(int first, int second, int expectedResult) {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(expectedResult, calculator.add(first, second),\n\t\t\t() -> first + \" + \" + second + \" should equal \" + expectedResult);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/ClassLevelAnnotationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport org.junit.jupiter.api.DisplayNameGenerator;\nimport org.junit.jupiter.api.IndicativeSentencesGeneration;\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledInNativeImage;\n\n@EnabledInNativeImage\n@IndicativeSentencesGeneration(generator = DisplayNameGenerator.ReplaceUnderscores.class)\nclass ClassLevelAnnotationTests {\n\t@Nested\n\tclass Inner {\n\t\t@Test\n\t\tvoid test() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/GraalvmSuite.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport org.junit.platform.suite.api.*;\n\n@Suite\n@SelectPackages(\"com.example.project\")\npublic class GraalvmSuite {\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/java/com/example/project/VintageTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport org.junit.Test;\n\npublic class VintageTests {\n\t@Test\n\tpublic void test() {\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/graalvm-starter/src/test/resources/junit-platform.properties",
    "content": "junit.platform.stacktrace.pruning.enabled=false\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-kotlin-extensions/build.gradle.kts",
    "content": "import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\nimport org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17\nimport org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_1\n\nplugins {\n\tkotlin(\"jvm\") version \"2.3.21\"\n}\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\nval junitVersion: String by project\n\ndependencies {\n\ttestImplementation(kotlin(\"stdlib\"))\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher:$junitVersion\")\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t}\n}\n\ntasks.withType<KotlinCompile>().configureEach {\n\tcompilerOptions {\n\t\tjvmTarget = JVM_17\n\t\tapiVersion = KOTLIN_2_1\n\t\tlanguageVersion = KOTLIN_2_1\n\t\tfreeCompilerArgs.addAll(\"-Xskip-prerelease-check\")\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n\ttestLogging {\n\t\tevents(\"passed\", \"skipped\", \"failed\")\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-kotlin-extensions/gradle.properties",
    "content": "org.gradle.java.installations.fromEnv=JDK17\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-kotlin-extensions/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"gradle-kotlin-extensions\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-kotlin-extensions/src/test/kotlin/com/example/project/ExtensionFunctionsTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project\n\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertAll\nimport org.junit.jupiter.api.assertDoesNotThrow\nimport org.junit.jupiter.api.assertThrows\nimport org.junit.jupiter.api.fail\nimport org.junit.jupiter.params.ParameterizedTest\nimport org.junit.jupiter.params.aggregator.ArgumentsAccessor\nimport org.junit.jupiter.params.aggregator.get\nimport org.junit.jupiter.params.provider.CsvSource\nimport org.opentest4j.AssertionFailedError\n\nclass ExtensionFunctionsTests {\n    @Test\n    fun `assertDoesNotThrow() and assertAll`() {\n        assertDoesNotThrow {\n            assertAll(setOf {})\n        }\n        assertDoesNotThrow(\"message\") {\n            assertAll(\"header\", setOf {})\n        }\n        assertDoesNotThrow({ \"message\" }) {\n            assertAll({})\n            assertAll(\"header\", {})\n        }\n    }\n\n    @Test\n    fun `fail() and assertThrows`() {\n        assertThrows<AssertionFailedError> {\n            fail(\"message\")\n        }\n        assertThrows<AssertionFailedError>(\"message\") {\n            fail { \"message\" }\n        }\n        assertThrows<AssertionFailedError>({ \"message\" }) {\n            fail(IllegalArgumentException())\n        }\n    }\n\n    @ParameterizedTest\n    @CsvSource(\"1\")\n    fun accessor(accessor: ArgumentsAccessor) {\n        val value = accessor.get<Int>(0)\n        assertEquals(1, value)\n    }\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-missing-engine/build.gradle.kts",
    "content": "plugins {\n\tjava\n}\n\nval junitVersion: String by project\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter-api:$junitVersion\") {\n\t\texclude(group = \"org.junit.jupiter\", module = \"junit-jupiter-engine\")\n\t}\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher:$junitVersion\")}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-missing-engine/gradle.properties",
    "content": "org.gradle.java.installations.fromEnv=JDK17\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-missing-engine/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"gradle-missing-engine\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/gradle-missing-engine/src/test/java/FooTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport org.junit.jupiter.api.Test;\n\nclass FooTests {\n\n\t@Test\n\tvoid test() {\n\t\tfail(\"This test must not be executed!\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-api.expected.txt",
    "content": "org.junit.jupiter.api@${version} jar:file:.+/junit-jupiter-api-\\d.+\\.jar..module-info\\.class\nexports org.junit.jupiter.api\nexports org.junit.jupiter.api.condition\nexports org.junit.jupiter.api.extension\nexports org.junit.jupiter.api.extension.support\nexports org.junit.jupiter.api.function\nexports org.junit.jupiter.api.io\nexports org.junit.jupiter.api.parallel\nexports org.junit.jupiter.api.util\nrequires java.base mandated\nrequires kotlin.stdlib static\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons transitive\nrequires org.opentest4j transitive\nqualified exports org.junit.jupiter.api.timeout to org.junit.jupiter.engine\nqualified opens org.junit.jupiter.api.condition to org.junit.platform.commons\nqualified opens org.junit.jupiter.api.util to org.junit.platform.commons\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-engine.expected.txt",
    "content": "org.junit.jupiter.engine@${version} jar:file:.+/junit-jupiter-engine-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires org.apiguardian.api static\nrequires org.jspecify static transitive\nrequires org.junit.jupiter.api\nrequires org.junit.platform.commons\nrequires org.junit.platform.engine\nrequires org.opentest4j\nuses org.junit.jupiter.api.extension.Extension\nprovides org.junit.platform.engine.TestEngine with org.junit.jupiter.engine.JupiterTestEngine\nqualified opens org.junit.jupiter.engine.extension to org.junit.platform.commons\ncontains org.junit.jupiter.engine\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-migrationsupport.expected.txt",
    "content": "org.junit.jupiter.migrationsupport@${version} jar:file:.+/junit-jupiter-migrationsupport-\\d.+\\.jar..module-info\\.class\nexports org.junit.jupiter.migrationsupport\nexports org.junit.jupiter.migrationsupport.conditions\nexports org.junit.jupiter.migrationsupport.rules\nexports org.junit.jupiter.migrationsupport.rules.adapter\nexports org.junit.jupiter.migrationsupport.rules.member\nrequires java.base mandated\nrequires junit transitive\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.jupiter.api transitive\nrequires org.junit.platform.commons\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter-params.expected.txt",
    "content": "org.junit.jupiter.params@${version} jar:file:.+/junit-jupiter-params-\\d.+\\.jar..module-info\\.class\nexports org.junit.jupiter.params\nexports org.junit.jupiter.params.aggregator\nexports org.junit.jupiter.params.converter\nexports org.junit.jupiter.params.provider\nexports org.junit.jupiter.params.support\nrequires java.base mandated\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.jupiter.api transitive\nrequires org.junit.platform.commons transitive\nqualified opens org.junit.jupiter.params to org.junit.platform.commons\nqualified opens org.junit.jupiter.params.converter to org.junit.platform.commons\nqualified opens org.junit.jupiter.params.provider to org.junit.platform.commons\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-jupiter.expected.txt",
    "content": "org.junit.jupiter@${version} jar:file:.+/junit-jupiter-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires org.junit.jupiter.api transitive\nrequires org.junit.jupiter.engine transitive\nrequires org.junit.jupiter.params transitive\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-commons.expected.txt",
    "content": "org.junit.platform.commons@${version} jar:file:.+/junit-platform-commons-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.commons\nexports org.junit.platform.commons.annotation\nexports org.junit.platform.commons.function\nexports org.junit.platform.commons.io\nexports org.junit.platform.commons.support\nexports org.junit.platform.commons.support.conversion\nexports org.junit.platform.commons.support.scanning\nrequires java.base mandated\nrequires java.logging\nrequires java.management\nrequires kotlin.reflect static\nrequires kotlin.stdlib static\nrequires kotlinx.coroutines.core static\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nuses org.junit.platform.commons.support.scanning.ClasspathScanner\nqualified exports org.junit.platform.commons.logging to org.junit.jupiter.api org.junit.jupiter.engine org.junit.jupiter.migrationsupport org.junit.jupiter.params org.junit.platform.console org.junit.platform.engine org.junit.platform.launcher org.junit.platform.reporting org.junit.platform.suite.api org.junit.platform.suite.engine org.junit.platform.testkit org.junit.vintage.engine\nqualified exports org.junit.platform.commons.util to org.junit.jupiter.api org.junit.jupiter.engine org.junit.jupiter.migrationsupport org.junit.jupiter.params org.junit.platform.console org.junit.platform.engine org.junit.platform.launcher org.junit.platform.reporting org.junit.platform.suite.api org.junit.platform.suite.engine org.junit.platform.testkit org.junit.vintage.engine\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-console.expected.txt",
    "content": "org.junit.platform.console@${version} jar:file:.+/junit-platform-console-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires org.apiguardian.api static\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons\nrequires org.junit.platform.engine\nrequires org.junit.platform.launcher\nrequires org.junit.platform.reporting\nprovides java.util.spi.ToolProvider with org.junit.platform.console.ConsoleLauncherToolProvider\nqualified exports org.junit.platform.console.output to org.junit.start\ncontains org.junit.platform.console\ncontains org.junit.platform.console.command\ncontains org.junit.platform.console.options\ncontains org.junit.platform.console.shadow.picocli\nmain-class org.junit.platform.console.ConsoleLauncher\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-engine.expected.txt",
    "content": "org.junit.platform.engine@${version} jar:file:.+/junit-platform-engine-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.engine\nexports org.junit.platform.engine.discovery\nexports org.junit.platform.engine.reporting\nexports org.junit.platform.engine.support.config\nexports org.junit.platform.engine.support.descriptor\nexports org.junit.platform.engine.support.discovery\nexports org.junit.platform.engine.support.hierarchical\nexports org.junit.platform.engine.support.store\nrequires java.base mandated\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons transitive\nrequires org.opentest4j transitive\nuses org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser\nprovides org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser with org.junit.platform.engine.discovery.ClassSelector$IdentifierParser org.junit.platform.engine.discovery.ClasspathResourceSelector$IdentifierParser org.junit.platform.engine.discovery.ClasspathRootSelector$IdentifierParser org.junit.platform.engine.discovery.DirectorySelector$IdentifierParser org.junit.platform.engine.discovery.FileSelector$IdentifierParser org.junit.platform.engine.discovery.IterationSelector$IdentifierParser org.junit.platform.engine.discovery.MethodSelector$IdentifierParser org.junit.platform.engine.discovery.ModuleSelector$IdentifierParser org.junit.platform.engine.discovery.NestedClassSelector$IdentifierParser org.junit.platform.engine.discovery.NestedMethodSelector$IdentifierParser org.junit.platform.engine.discovery.PackageSelector$IdentifierParser org.junit.platform.engine.discovery.UniqueIdSelector$IdentifierParser org.junit.platform.engine.discovery.UriSelector$IdentifierParser\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-launcher.expected.txt",
    "content": "org.junit.platform.launcher@${version} jar:file:.+/junit-platform-launcher-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.launcher\nexports org.junit.platform.launcher.core\nexports org.junit.platform.launcher.listeners\nexports org.junit.platform.launcher.listeners.discovery\nrequires java.base mandated\nrequires java.logging transitive\nrequires jdk.jfr static\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons transitive\nrequires org.junit.platform.engine transitive\nuses org.junit.platform.engine.TestEngine\nuses org.junit.platform.launcher.LauncherDiscoveryListener\nuses org.junit.platform.launcher.LauncherInterceptor\nuses org.junit.platform.launcher.LauncherSessionListener\nuses org.junit.platform.launcher.PostDiscoveryFilter\nuses org.junit.platform.launcher.TestExecutionListener\nprovides org.junit.platform.launcher.TestExecutionListener with org.junit.platform.launcher.listeners.UniqueIdTrackingListener\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-reporting.expected.txt",
    "content": "org.junit.platform.reporting@${version} jar:file:.+/junit-platform-reporting-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.reporting.legacy\nexports org.junit.platform.reporting.legacy.xml\nexports org.junit.platform.reporting.open.xml\nrequires java.base mandated\nrequires java.xml\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons\nrequires org.junit.platform.engine transitive\nrequires org.junit.platform.launcher transitive\nrequires org.opentest4j.reporting.tooling.spi\nprovides org.junit.platform.launcher.TestExecutionListener with org.junit.platform.reporting.open.xml.OpenTestReportGeneratingListener\nprovides org.opentest4j.reporting.tooling.spi.htmlreport.Contributor with org.junit.platform.reporting.open.xml.JUnitContributor\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-suite-api.expected.txt",
    "content": "org.junit.platform.suite.api@${version} jar:file:.+/junit-platform-suite-api-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.suite.api\nrequires java.base mandated\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons transitive\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-suite-engine.expected.txt",
    "content": "org.junit.platform.suite.engine@${version} jar:file:.+/junit-platform-suite-engine-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires org.apiguardian.api static\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons\nrequires org.junit.platform.engine\nrequires org.junit.platform.launcher\nrequires org.junit.platform.suite.api\nprovides org.junit.platform.engine.TestEngine with org.junit.platform.suite.engine.SuiteTestEngine\ncontains org.junit.platform.suite.engine\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-suite.expected.txt",
    "content": "org.junit.platform.suite@${version} jar:file:.+/junit-platform-suite-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires org.junit.platform.suite.api transitive\nrequires org.junit.platform.suite.engine transitive\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-platform-testkit.expected.txt",
    "content": "org.junit.platform.testkit@${version} jar:file:.+/junit-platform-testkit-\\d.+\\.jar..module-info\\.class\nexports org.junit.platform.testkit.engine\nrequires java.base mandated\nrequires org.apiguardian.api static transitive\nrequires org.assertj.core transitive\nrequires org.jspecify static transitive\nrequires org.junit.platform.commons\nrequires org.junit.platform.engine transitive\nrequires org.junit.platform.launcher transitive\nrequires org.opentest4j transitive\nuses org.junit.platform.engine.TestEngine\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-start.expected.txt",
    "content": "org.junit.start@${version} jar:file:.+/junit-start-\\d.+\\.jar..module-info\\.class\nexports org.junit.start\nrequires java.base mandated\nrequires org.apiguardian.api static transitive\nrequires org.jspecify static transitive\nrequires org.junit.jupiter transitive\nrequires org.junit.platform.console\nrequires org.junit.platform.launcher\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jar-describe-module/junit-vintage-engine.expected.txt",
    "content": "org.junit.vintage.engine@${version} jar:file:.+/junit-vintage-engine-\\d.+\\.jar..module-info\\.class\nrequires java.base mandated\nrequires junit\nrequires org.apiguardian.api static\nrequires org.jspecify static transitive\nrequires org.junit.platform.engine\nprovides org.junit.platform.engine.TestEngine with org.junit.vintage.engine.VintageTestEngine\ncontains org.junit.vintage.engine\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/junit-start/compact/JUnitRun.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport module org.junit.start;\n\nvoid main() {\n\tJUnit.run();\n}\n\n@Test\nvoid addition() {\n\tAssertions.assertEquals(2, 1 + 1, \"Addition error detected!\");\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/junit-start/compact/JUnitRunClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport module org.junit.start;\n\nvoid main() {\n\tJUnit.run(getClass());\n}\n\n@Test\nvoid substraction() {\n\tAssertions.assertEquals(2, 3 - 1, \"Subtraction error detected!\");\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/junit-start/modular/module-info.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nopen module m {\n\trequires org.junit.start;\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/junit-start/modular/p/JUnitRunModule.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage p;\n\nimport module org.junit.start;\n\nclass JUnitRunModule {\n\tvoid main() {\n\t\tJUnit.run(getClass().getModule());\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/junit-start/modular/p/MultiplicationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage p;\n\nimport module org.junit.jupiter.api;\n\nclass MultiplicationTests {\n\n\t@Test\n\tvoid multiplication() {\n\t\tAssertions.assertEquals(4, 2 * 2, \"Multiplication error detected!\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/build.gradle.kts",
    "content": "plugins {\n\tjava\n}\n\nval junitVersion: String by project\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-reporting:$junitVersion\")\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(System.getProperty(\"java.toolchain.version\"))\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n\n\ttestLogging {\n\t\tevents(\"passed\", \"skipped\", \"failed\", \"standardOut\")\n\t\texceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL\n\t}\n\n\treports {\n\t\thtml.required = true\n\t}\n\n\tval outputDir = reports.junitXml.outputLocation\n\tjvmArgumentProviders += CommandLineArgumentProvider {\n\t\tlistOf(\n\t\t\t\"-Djunit.platform.reporting.open.xml.enabled=true\",\n\t\t\t\"-Djunit.platform.reporting.output.dir=${outputDir.get().asFile.absolutePath}\"\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/build.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project name=\"ant-starter\" default=\"build\" basedir=\".\">\n\n    <fail message=\"Ant 1.10.6+ is required!\">\n        <condition>\n            <not>\n                <antversion atleast=\"1.10.6\"/>\n            </not>\n        </condition>\n    </fail>\n\n    <property name=\"build.sysclasspath\" value=\"last\"/>\n\n    <path id=\"test.classpath\">\n        <pathelement path=\"build/test\"/>\n        <pathelement path=\"build/main\"/>\n    </path>\n\n    <target name=\"build\" description=\"clean build\" depends=\"clean, test\" />\n\n    <target name=\"clean\">\n        <delete dir=\"build\"/>\n    </target>\n\n    <target name=\"init\">\n        <echo message=\"Using Java version: ${java.version}\"/>\n        <mkdir dir=\"build/main\"/>\n        <mkdir dir=\"build/test\"/>\n        <mkdir dir=\"build/test-report\"/>\n    </target>\n\n    <target name=\"compile\" depends=\"init\">\n        <javac destdir=\"build/main\" srcdir=\"src/main/java\" includeantruntime=\"false\"/>\n        <javac destdir=\"build/test\" classpathref=\"test.classpath\" srcdir=\"src/test/java\" includeantruntime=\"true\"/>\n    </target>\n\n    <!-- https://docs.junit.org/current/running-tests/build-support.html#ant -->\n    <target name=\"test.junit.launcher\" depends=\"compile\">\n        <junitlauncher haltOnFailure=\"true\" printSummary=\"true\">\n            <classpath refid=\"test.classpath\"/>\n            <testclasses outputdir=\"build/test-report\">\n                <fileset dir=\"build/test\">\n                    <include name=\"**/*Tests.class\"/>\n                </fileset>\n                <listener type=\"legacy-xml\" sendSysOut=\"true\" sendSysErr=\"true\"/>\n                <listener type=\"legacy-plain\" sendSysOut=\"true\" />\n            </testclasses>\n        </junitlauncher>\n    </target>\n\n    <!-- https://docs.junit.org/current/running-tests/console-launcher.html -->\n    <target name=\"test.console.launcher\" depends=\"compile\">\n        <java classpathref=\"test.classpath\" classname=\"org.junit.platform.console.ConsoleLauncher\" fork=\"true\">\n            <arg value=\"execute\"/>\n            <arg value=\"--scan-classpath\"/>\n            <arg value=\"--include-engine=junit-jupiter\"/>\n            <arg value=\"--disable-ansi-colors\"/>\n            <arg line=\"--reports-dir build/test-report\"/>\n            <arg value=\"--config=junit.platform.reporting.open.xml.enabled=true\"/>\n            <arg value=\"--config=junit.platform.reporting.output.dir=build/test-report\"/>\n        </java>\n        <junitreport todir=\"build/test-report\">\n            <fileset dir=\"build/test-report\">\n                <include name=\"TEST-*.xml\"/>\n            </fileset>\n            <report format=\"frames\" todir=\"build/test-report/html\"/>\n        </junitreport>\n    </target>\n\n    <target name=\"test\" depends=\"test.junit.launcher, test.console.launcher\" />\n\n</project>\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/gradle.properties",
    "content": "org.gradle.java.installations.fromEnv=JDK17\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>maven-starter</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <maven.compiler.release>17</maven.compiler.release>\n        <junit.platform.commons.version>${junit.version}</junit.platform.commons.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.junit.platform</groupId>\n            <artifactId>junit-platform-commons</artifactId>\n            <version>${junit.platform.commons.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.junit.platform</groupId>\n            <artifactId>junit-platform-reporting</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.junit</groupId>\n                <artifactId>junit-bom</artifactId>\n                <version>${junit.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.15.0</version>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>3.5.5</version>\n                <configuration>\n                    <properties>\n                        <configurationParameters>\n                            junit.platform.reporting.open.xml.enabled = true\n                            junit.platform.reporting.output.dir = target/surefire-reports\n                        </configurationParameters>\n                    </properties>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>local-temp</id>\n            <url>file://${maven.repo}</url>\n            <snapshots>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </snapshots>\n            <releases>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </releases>\n        </repository>\n        <repository>\n            <id>snapshots-repo</id>\n            <url>${snapshot.repo.url}</url>\n            <snapshots>\n                <enabled>true</enabled>\n            </snapshots>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"gradle-starter\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/src/main/java/com/example/project/Calculator.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\npublic class Calculator {\n\n\tpublic int add(int a, int b) {\n\t\treturn a + b;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/src/test/java/com/example/project/CalculatorParameterizedClassTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.Nested;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.Parameter;\nimport org.junit.jupiter.params.ParameterizedClass;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n@ParameterizedClass\n@ValueSource(ints = { 1, 2 })\nclass CalculatorParameterizedClassTests {\n\n\t@Parameter\n\tint i;\n\n\t@ParameterizedTest\n\t@ValueSource(ints = { 1, 2 })\n\tvoid parameterizedTest(int j) {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(i + j, calculator.add(i, j));\n\t}\n\n\t@Nested\n\t@ParameterizedClass\n\t@ValueSource(ints = { 1, 2 })\n\tclass Inner {\n\n\t\tfinal int j;\n\n\t\tInner(int j) {\n\t\t\tthis.j = j;\n\t\t}\n\n\t\t@Test\n\t\tvoid regularTest() {\n\t\t\tCalculator calculator = new Calculator();\n\t\t\tassertEquals(i + j, calculator.add(i, j));\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/src/test/java/com/example/project/CalculatorTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DisplayName;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.CsvSource;\n\nclass CalculatorTests {\n\n\t@BeforeAll\n\tstatic void printJavaVersion() {\n\t\tSystem.out.println(\"Using Java version: \" + System.getProperty(\"java.specification.version\"));\n\t}\n\n\t@Test\n\t@DisplayName(\"1 + 1 = 2\")\n\tvoid addsTwoNumbers() {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(2, calculator.add(1, 1), \"1 + 1 should equal 2\");\n\t}\n\n\t@ParameterizedTest(name = \"{0} + {1} = {2}\", quoteTextArguments = false)\n\t@CsvSource({ //\n\t\t\t\"0,    1,   1\", //\n\t\t\t\"1,    2,   3\", //\n\t\t\t\"49,  51, 100\", //\n\t\t\t\"1,  100, 101\" //\n\t})\n\tvoid add(int first, int second, int expectedResult) {\n\t\tCalculator calculator = new Calculator();\n\t\tassertEquals(expectedResult, calculator.add(first, second),\n\t\t\t() -> first + \" + \" + second + \" should equal \" + expectedResult);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/jupiter-starter/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.testclass.order.default = \\\n  org.junit.jupiter.api.ClassOrderer$ClassName\n\njunit.platform.stacktrace.pruning.enabled = false\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/kotlin-coroutines/build.gradle.kts",
    "content": "plugins {\n\tkotlin(\"jvm\") version \"2.3.21\"\n}\n\nval junitVersion: String by project\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")\n\n\tif (!project.hasProperty(\"withoutKotlinReflect\")) {\n\t\ttestImplementation(kotlin(\"reflect\"))\n\t}\n\n\tif (!project.hasProperty(\"withoutKotlinxCoroutines\")) {\n\t\ttestImplementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0\")\n\t}\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n\ttestLogging {\n\t\texceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL\n\t}\n\tsystemProperty(\"junit.platform.stacktrace.pruning.enabled\", \"false\")\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/kotlin-coroutines/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"kotlin-coroutines\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/kotlin-coroutines/src/test/kotlin/com/example/project/SuspendFunctionTests.kt",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project\n\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.fail\n\nclass SuspendFunctionTests {\n    @Test\n    suspend fun test() {\n        fail(\"expected\")\n    }\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/maven-surefire-compatibility/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>maven-surefire-compatibility</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <maven.compiler.release>17</maven.compiler.release>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.junit.jupiter</groupId>\n            <artifactId>junit-jupiter</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.junit</groupId>\n                <artifactId>junit-bom</artifactId>\n                <version>${junit.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.15.0</version>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>${surefire.version}</version>\n                <configuration>\n                    <properties>\n                        <configurationParameters>\n                            junit.platform.listeners.uid.tracking.enabled = true\n                        </configurationParameters>\n                    </properties>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>local-temp</id>\n            <url>file://${maven.repo}</url>\n            <snapshots>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </snapshots>\n            <releases>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </releases>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/maven-surefire-compatibility/src/test/java/com/example/project/DummyTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.project;\n\nimport org.junit.jupiter.api.Test;\n\nclass DummyTests {\n\n\t@Test\n\tvoid test() {\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/memory-cleanup/src/OneMillionTests.java",
    "content": "/*\n * Copyright 2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.stream.IntStream;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.DynamicTest;\nimport org.junit.jupiter.api.TestFactory;\n\nclass OneMillionTests {\n\n\t@TestFactory\n\tStream<DynamicTest> tests() {\n\t\treturn IntStream.range(0, 1_000_000) //\n\t\t\t\t.mapToObj(i -> dynamicTest(\"test \" + i, () -> {\n\t\t\t\t\tassertTrue(i + 1 < 1_000_000); // fail last test\n\t\t\t\t}));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/reflection-tests/build.gradle.kts",
    "content": "plugins {\n\tjava\n}\n\nval junitVersion: String by project\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter:$junitVersion\")\n\ttestImplementation(\"org.junit.jupiter:junit-jupiter-engine:$junitVersion\")\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher:$junitVersion\")\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n\n\ttestLogging {\n\t\tevents(\"failed\", \"standardOut\")\n\t}\n\n\treports {\n\t\thtml.required = true\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/reflection-tests/gradle.properties",
    "content": "org.gradle.java.installations.fromEnv=JDK17\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/reflection-tests/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"reflection-tests\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/reflection-tests/src/test/java/ReflectionTestCase.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage standalone;\n\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.DynamicContainer.dynamicContainer;\nimport static org.junit.jupiter.api.DynamicTest.dynamicTest;\n\nimport java.util.Arrays;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.DynamicNode;\nimport org.junit.jupiter.api.TestFactory;\nimport org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.ClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.JupiterTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateInvocationTestDescriptor;\nimport org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor;\n\nclass ReflectionTestCase {\n\n\t@BeforeAll\n\tstatic void printJavaVersion() {\n\t\tSystem.out.println(\"Using Java version: \" + System.getProperty(\"java.specification.version\"));\n\t}\n\n\t@TestFactory\n\tStream<DynamicNode> canReadParameters() {\n\t\treturn Stream.of(JupiterTestDescriptor.class, ClassBasedTestDescriptor.class, ClassTestDescriptor.class,\n\t\t\tMethodBasedTestDescriptor.class, TestMethodTestDescriptor.class, TestTemplateTestDescriptor.class,\n\t\t\tTestTemplateInvocationTestDescriptor.class, TestFactoryTestDescriptor.class,\n\t\t\tNestedClassTestDescriptor.class) //\n\t\t\t\t.map(descriptorClass -> dynamicContainer(descriptorClass.getSimpleName(),\n\t\t\t\t\tArrays.stream(descriptorClass.getDeclaredMethods()) //\n\t\t\t\t\t\t\t.map(method -> dynamicTest(method.getName(),\n\t\t\t\t\t\t\t\t() -> assertDoesNotThrow(method::getParameters)))));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/expected-err.txt",
    "content": ".+ org.junit.platform.launcher.core.ServiceLoaderRegistry load\n.+ Loaded LauncherInterceptor instances: ..\n.+ org.junit.platform.launcher.core.ServiceLoaderRegistry load\n.+ Loaded LauncherSessionListener instances: ..\n.+ org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines\n.+ Discovered TestEngines:\n- junit-platform-suite .+\n- junit-jupiter .+\n- junit-vintage .+\n.+ org.junit.platform.launcher.core.ServiceLoaderRegistry load\n.+ Loaded PostDiscoveryFilter instances: ..\n.+ org.junit.platform.launcher.core.ServiceLoaderRegistry load\n.+ Loaded LauncherDiscoveryListener instances: ..\n.+ org.junit.platform.launcher.core.ServiceLoaderRegistry load\n.+ Loaded TestExecutionListener instances: .+ \\Q(excluded classes: [])\\E\n.+ org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry loadTestEngines\n.+ Discovered TestEngines:\n- junit-platform-suite .+\n- junit-jupiter .+\n- junit-vintage .+\n>> LOGGER >>\nINFO: TestEngine with ID 'junit-vintage' encountered a non-critical issue during test discovery:\n\n(1) [INFO] The JUnit Vintage engine is deprecated and should only be used temporarily while migrating tests to JUnit Jupiter or another testing framework with native JUnit Platform support.\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/expected-out.txt",
    "content": ">> JAVA VERSION + TREE >>\n\nFailures (2):\n>> STACKTRACE >>\n\nTest run finished after \\d+ ms\n[        11 containers found      ]\n[         0 containers skipped    ]\n[        11 containers started    ]\n[         0 containers aborted    ]\n[        11 containers successful ]\n[         0 containers failed     ]\n[        10 tests found           ]\n[         2 tests skipped         ]\n[         8 tests started         ]\n[         1 tests aborted         ]\n[         5 tests successful      ]\n[         2 tests failed          ]\n\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/logging.properties",
    "content": "handlers=java.util.logging.ConsoleHandler\njava.util.logging.ConsoleHandler.level=CONFIG\norg.junit.level=CONFIG\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/src/other/OtherwiseNotReferencedClass.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage other;\n\npublic class OtherwiseNotReferencedClass {\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/src/standalone/JupiterIntegration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage standalone;\n\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.Disabled;\nimport org.junit.jupiter.api.Test;\n\nclass JupiterIntegration {\n\n\t@Test\n\tvoid successful() {\n\t\tRuntime.getRuntime().addShutdownHook(new Thread(() -> {\n\t\t\tnew other.OtherwiseNotReferencedClass();\n\t\t}));\n\t}\n\n\t@Test\n\t@Disabled(\"integration-test-disabled\")\n\tvoid disabled() {\n\t}\n\n\t@Test\n\tvoid abort() {\n\t\tAssumptions.assumeTrue(false, \"integration-test-abort\");\n\t}\n\n\t@Test\n\tvoid fail() {\n\t\tAssertions.fail(\"integration-test-fail\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/src/standalone/JupiterParamsIntegration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage standalone;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\nclass JupiterParamsIntegration {\n\n\t@ParameterizedTest(name = \"[{index}] argument={0}\", quoteTextArguments = false)\n\t@ValueSource(strings = \"test\")\n\tvoid parameterizedTest(String argument) {\n\t\tassertEquals(\"test\", argument);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/src/standalone/SuiteIntegration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage standalone;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.platform.suite.api.SelectClasses;\nimport org.junit.platform.suite.api.Suite;\n\n@Suite\n@SelectClasses(SuiteIntegration.SingleTestContainer.class)\nclass SuiteIntegration {\n\n\tstatic class SingleTestContainer {\n\t\t@Test\n\t\tvoid successful() {\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/standalone/src/standalone/VintageIntegration.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage standalone;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\nimport static org.junit.runners.MethodSorters.NAME_ASCENDING;\n\nimport org.junit.FixMethodOrder;\nimport org.junit.Ignore;\nimport org.junit.Test;\n\n@FixMethodOrder(NAME_ASCENDING)\npublic class VintageIntegration {\n\n\t@Test\n\t@Ignore(\"integr4tion test\")\n\tpublic void ignored() {\n\t\tfail(\"this test should be ignored\");\n\t}\n\n\t@Test\n\tpublic void succ3ssful() {\n\t\tassertEquals(3, 1 + 2);\n\t}\n\n\t@Test\n\tpublic void f4il() {\n\t\tfail(\"f4iled\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/build.gradle.kts",
    "content": "import org.gradle.api.tasks.testing.logging.TestLogEvent\n\nplugins {\n\tjava\n}\n\nrepositories {\n\tmaven { url = uri(file(System.getProperty(\"maven.repo\"))) }\n\tmavenCentral()\n}\n\nval junitVersion: String by project\n\ndependencies {\n\tval junit4Version = System.getProperty(\"junit4Version\", \"4.12\")\n\ttestImplementation(\"junit:junit:$junit4Version\")\n\n\ttestImplementation(\"org.junit.vintage:junit-vintage-engine:$junitVersion\") {\n\t\texclude(group = \"junit\")\n\t\tbecause(\"we want to override it to test against different versions\")\n\t}\n\n\ttestRuntimeOnly(\"org.junit.platform:junit-platform-launcher:$junitVersion\")\n}\n\njava {\n\ttoolchain {\n\t\tlanguageVersion = JavaLanguageVersion.of(17)\n\t}\n}\n\ntasks.test {\n\tuseJUnitPlatform()\n\n\ttestLogging {\n\t\tevents = setOf(TestLogEvent.STARTED, TestLogEvent.PASSED, TestLogEvent.FAILED)\n\t\tafterSuite(KotlinClosure2<TestDescriptor, TestResult, Any>({ _, result ->\n\t\t\tresult.exception?.printStackTrace(System.out)\n\t\t}))\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/gradle.properties",
    "content": "org.gradle.java.installations.fromEnv=JDK17\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>com.example</groupId>\n    <artifactId>vintage</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <maven.compiler.release>17</maven.compiler.release>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.junit.vintage</groupId>\n            <artifactId>junit-vintage-engine</artifactId>\n            <version>${junit.version}</version>\n            <scope>test</scope>\n            <exclusions>\n                <exclusion>\n                    <groupId>junit</groupId>\n                    <artifactId>junit</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>${junit4Version}</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.15.0</version>\n            </plugin>\n            <plugin>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>3.5.5</version>\n            </plugin>\n        </plugins>\n    </build>\n\n    <repositories>\n        <repository>\n            <id>local-temp</id>\n            <url>file://${maven.repo}</url>\n            <snapshots>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </snapshots>\n            <releases>\n                <enabled>true</enabled>\n                <checksumPolicy>ignore</checksumPolicy>\n            </releases>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/settings.gradle.kts",
    "content": "plugins {\n    id(\"org.gradle.toolchains.foojay-resolver-convention\") version \"1.0.0\"\n}\n\nrootProject.name = \"vintage\"\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/src/test/java/DefaultPackageTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\nimport com.example.vintage.VintageTest;\n\nimport org.junit.Ignore;\n\n/**\n * Reproducer for https://github.com/junit-team/junit-framework/issues/4076\n */\n@Ignore\npublic class DefaultPackageTest extends VintageTest {\n\tvoid packagePrivateMethod() {\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/projects/vintage/src/test/java/com/example/vintage/VintageTest.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage com.example.vintage;\n\nimport static org.junit.Assert.*;\n\nimport org.junit.Test;\n\npublic class VintageTest {\n\tvoid packagePrivateMethod() {\n\t}\n\n\t@Test\n\tpublic void success() {\n\t\t// pass\n\t}\n\n\t@Test\n\tpublic void failure() {\n\t\tfail(\"expected to fail\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/archUnit/java/platform/tooling/support/tests/ArchUnitTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static com.tngtech.archunit.base.DescribedPredicate.and;\nimport static com.tngtech.archunit.base.DescribedPredicate.describe;\nimport static com.tngtech.archunit.base.DescribedPredicate.not;\nimport static com.tngtech.archunit.core.domain.JavaClass.Predicates.ANONYMOUS_CLASSES;\nimport static com.tngtech.archunit.core.domain.JavaClass.Predicates.TOP_LEVEL_CLASSES;\nimport static com.tngtech.archunit.core.domain.JavaClass.Predicates.resideInAPackage;\nimport static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleName;\nimport static com.tngtech.archunit.core.domain.JavaClass.Predicates.simpleNameEndingWith;\nimport static com.tngtech.archunit.core.domain.JavaMember.Predicates.declaredIn;\nimport static com.tngtech.archunit.core.domain.JavaModifier.PUBLIC;\nimport static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;\nimport static com.tngtech.archunit.core.domain.properties.HasModifiers.Predicates.modifier;\nimport static com.tngtech.archunit.core.domain.properties.HasName.Predicates.name;\nimport static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameContaining;\nimport static com.tngtech.archunit.core.domain.properties.HasName.Predicates.nameStartingWith;\nimport static com.tngtech.archunit.lang.conditions.ArchConditions.onlyBeAccessedByClassesThat;\nimport static com.tngtech.archunit.lang.conditions.ArchPredicates.are;\nimport static com.tngtech.archunit.lang.conditions.ArchPredicates.have;\nimport static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;\nimport static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.members;\nimport static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices;\nimport static org.apiguardian.api.API.Status.DEPRECATED;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.annotation.Repeatable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\nimport java.util.Arrays;\nimport java.util.Objects;\nimport java.util.function.BiPredicate;\nimport java.util.stream.Stream;\n\nimport com.tngtech.archunit.base.DescribedPredicate;\nimport com.tngtech.archunit.core.domain.JavaClass;\nimport com.tngtech.archunit.core.domain.JavaClasses;\nimport com.tngtech.archunit.core.domain.JavaPackage;\nimport com.tngtech.archunit.core.domain.PackageMatcher;\nimport com.tngtech.archunit.core.domain.properties.HasAnnotations;\nimport com.tngtech.archunit.core.domain.properties.HasSourceCodeLocation;\nimport com.tngtech.archunit.junit.AnalyzeClasses;\nimport com.tngtech.archunit.junit.ArchTest;\nimport com.tngtech.archunit.lang.ArchCondition;\nimport com.tngtech.archunit.lang.ArchRule;\nimport com.tngtech.archunit.library.GeneralCodingRules;\n\nimport org.apiguardian.api.API;\nimport org.jspecify.annotations.NullMarked;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Assumptions;\nimport org.junit.jupiter.api.TestReporter;\nimport org.junit.platform.commons.support.scanning.ClasspathScanner;\nimport org.junit.platform.engine.OutputDirectoryCreator;\nimport org.junit.platform.engine.TestDescriptor;\n\n@AnalyzeClasses(packages = { \"org.junit.platform\", \"org.junit.jupiter\", \"org.junit.vintage\" })\nclass ArchUnitTests {\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest\n\tprivate final ArchRule allClassesAreInJUnitPackage = classes() //\n\t\t\t.should().haveNameMatching(\"org\\\\.junit\\\\..+\");\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest\n\tprivate final ArchRule allPublicTopLevelTypesHaveApiAnnotations = classes() //\n\t\t\t.that(have(modifier(PUBLIC))) //\n\t\t\t.and(TOP_LEVEL_CLASSES) //\n\t\t\t.and(not(ANONYMOUS_CLASSES)) //\n\t\t\t.and(not(describe(\"are Kotlin SAM type implementations\", simpleName(\"\")))) //\n\t\t\t.and(not(describe(\"are Kotlin-generated classes that contain only top-level functions\",\n\t\t\t\tsimpleNameEndingWith(\"Kt\")))) //\n\t\t\t.and(notShadowed()) //\n\t\t\t.should().beAnnotatedWith(API.class);\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest // Consistency of @Documented and @Inherited is checked by the compiler but not for @Retention and @Target\n\tprivate final ArchRule repeatableAnnotationsShouldHaveMatchingContainerAnnotations = classes() //\n\t\t\t.that(nameStartingWith(\"org.junit.\")) //\n\t\t\t.and().areAnnotations() //\n\t\t\t.and().areAnnotatedWith(Repeatable.class) //\n\t\t\t.should(haveContainerAnnotationWithSameRetentionPolicy()) //\n\t\t\t.andShould(haveContainerAnnotationWithSameTargetTypes());\n\n\tprivate final DescribedPredicate<? super JavaClass> jupiterAssertions = name(Assertions.class.getName()) //\n\t\t\t.or(name(Assumptions.class.getName())).or(name(\"org.junit.jupiter.api.AssertionsKt\"));\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest // https://github.com/junit-team/junit-framework/issues/4604\n\tprivate final ArchRule jupiterAssertionsShouldBeSelfContained = classes().that(jupiterAssertions) //\n\t\t\t.should(onlyBeAccessedByClassesThat(jupiterAssertions));\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest\n\tprivate final ArchRule deprecatedAnnotationOnMembersShouldBeDeclaredConsistently = members() //\n\t\t\t.that(annotatedWith(Deprecated.class)) //\n\t\t\t.or(haveApiAnnotationWithDeprecatedStatus()) //\n\t\t\t.and(declaredIn(notShadowed())) //\n\t\t\t.should(haveBothAnnotationsWithMatchingSinceAttributes());\n\n\t@SuppressWarnings(\"unused\")\n\t@ArchTest\n\tprivate final ArchRule deprecatedAnnotationOnClassesShouldBeDeclaredConsistently = classes() //\n\t\t\t.that(annotatedWith(Deprecated.class)) //\n\t\t\t.or(haveApiAnnotationWithDeprecatedStatus()) //\n\t\t\t.and(notShadowed()) //\n\t\t\t.should(haveBothAnnotationsWithMatchingSinceAttributes());\n\n\t@ArchTest\n\tvoid packagesShouldBeNullMarked(JavaClasses classes) {\n\t\tvar exclusions = Stream.of( //\n\t\t\t\"..shadow..\" //\n\t\t).map(PackageMatcher::of).toList();\n\n\t\tvar subpackages = Stream.of(\"org.junit.platform\", \"org.junit.jupiter\", \"org.junit.vintage\") //\n\t\t\t\t.map(classes::getPackage) //\n\t\t\t\t.flatMap(rootPackage -> rootPackage.getSubpackagesInTree().stream()) //\n\t\t\t\t.filter(pkg -> exclusions.stream().noneMatch(it -> it.matches(pkg.getName()))) //\n\t\t\t\t.filter(pkg -> !pkg.getClasses().isEmpty()) //\n\t\t\t\t.toList();\n\t\tassertThat(subpackages).isNotEmpty();\n\n\t\tvar violations = subpackages.stream() //\n\t\t\t\t.filter(pkg -> !pkg.isAnnotatedWith(NullMarked.class)) //\n\t\t\t\t.map(JavaPackage::getName) //\n\t\t\t\t.sorted();\n\t\tassertThat(violations).describedAs(\"The following packages are missing the @NullMarked annotation\").isEmpty();\n\t}\n\n\t@ArchTest\n\tvoid allAreIn(JavaClasses classes) {\n\t\t// about 928 classes found in all jars\n\t\tassertTrue(classes.size() > 800, \"expected more than 800 classes, got: \" + classes.size());\n\t}\n\n\t@ArchTest\n\tvoid freeOfGroupCycles(JavaClasses classes) {\n\t\tslices().matching(\"org.junit.(*)..\").should().beFreeOfCycles().check(classes);\n\t}\n\n\t@ArchTest\n\t@SuppressWarnings(\"removal\")\n\tvoid freeOfPackageCycles(JavaClasses classes) throws Exception {\n\t\tslices().matching(\"org.junit.(**)\").should().beFreeOfCycles() //\n\n\t\t\t\t// Ignore shadowed packages\n\t\t\t\t.ignoreDependency(nameContaining(\".shadow.\"), nameContaining(\".shadow.\")) //\n\n\t\t\t\t// https://github.com/junit-team/junit-framework/issues/4886\n\t\t\t\t.ignoreDependency(TestReporter.class, org.junit.jupiter.api.extension.MediaType.class) //\n\n\t\t\t\t// https://github.com/junit-team/junit-framework/issues/4885\n\t\t\t\t.ignoreDependency(ClasspathScanner.class, org.junit.platform.commons.support.Resource.class) //\n\n\t\t\t\t// https://github.com/junit-team/junit-framework/issues/4919\n\t\t\t\t.ignoreDependency(org.junit.jupiter.params.support.ParameterInfo.class,\n\t\t\t\t\torg.junit.jupiter.params.ParameterInfo.class)\n\n\t\t\t\t// https://github.com/junit-team/junit-framework/issues/4923\n\t\t\t\t.ignoreDependency(org.junit.platform.engine.reporting.OutputDirectoryProvider.class,\n\t\t\t\t\tOutputDirectoryCreator.class) //\n\t\t\t\t.ignoreDependency(Class.forName(\"org.junit.platform.engine.reporting.OutputDirectoryProviderAdapter\"),\n\t\t\t\t\tOutputDirectoryCreator.class) //\n\t\t\t\t.ignoreDependency(Class.forName(\"org.junit.platform.engine.reporting.OutputDirectoryProviderAdapter\"),\n\t\t\t\t\tTestDescriptor.class) //\n\n\t\t\t\t.check(classes);\n\t}\n\n\t@ArchTest\n\tvoid avoidJavaUtilLogging(JavaClasses classes) {\n\t\t// LoggerFactory.java:80 -> sets field LoggerFactory$DelegatingLogger.julLogger\n\t\tvar subset = classes.that(are(not(name(\"org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger\"))));\n\t\tGeneralCodingRules.NO_CLASSES_SHOULD_USE_JAVA_UTIL_LOGGING.check(subset);\n\t}\n\n\t@ArchTest\n\tvoid avoidThrowingGenericExceptions(JavaClasses classes) {\n\t\t// LoggerFactory.java:155 -> new Throwable()\n\t\tvar subset = classes.that(are(not(\n\t\t\tname(\"org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger\").or(nameContaining(\".shadow.\")))));\n\t\tGeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS.check(subset);\n\t}\n\n\t@ArchTest\n\tvoid avoidAccessingStandardStreams(JavaClasses classes) {\n\t\t// ConsoleLauncher, StreamInterceptor, Picocli et al...\n\t\tvar subset = classes //\n\t\t\t\t.that(are(not(name(\"org.junit.platform.console.ConsoleLauncher\")))) //\n\t\t\t\t.that(are(not(name(\"org.junit.platform.console.command.ConsoleTestExecutor\")))) //\n\t\t\t\t.that(are(not(name(\"org.junit.platform.launcher.core.StreamInterceptor\")))) //\n\t\t\t\t.that(are(not(name(\"org.junit.platform.testkit.engine.Events\")))) //\n\t\t\t\t.that(are(not(name(\"org.junit.platform.testkit.engine.Executions\")))) //\n\t\t\t\t//The PreInterruptThreadDumpPrinter writes to StdOut by contract to dump threads\n\t\t\t\t.that(are(not(name(\"org.junit.jupiter.engine.extension.PreInterruptThreadDumpPrinter\")))) //\n\t\t\t\t.that(are(not(nameContaining(\".shadow.\"))));\n\t\tGeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS.check(subset);\n\t}\n\n\tprivate static ArchCondition<? super JavaClass> haveContainerAnnotationWithSameRetentionPolicy() {\n\t\treturn ArchCondition.from(new RepeatableAnnotationPredicate<>(Retention.class,\n\t\t\t(expectedTarget, actualTarget) -> expectedTarget.value() == actualTarget.value()));\n\t}\n\n\tprivate static ArchCondition<? super JavaClass> haveContainerAnnotationWithSameTargetTypes() {\n\t\treturn ArchCondition.from(new RepeatableAnnotationPredicate<>(Target.class,\n\t\t\t(expectedTarget, actualTarget) -> Arrays.equals(expectedTarget.value(), actualTarget.value())));\n\t}\n\n\tprivate static <T extends HasAnnotations<?> & HasSourceCodeLocation> ArchCondition<T> haveBothAnnotationsWithMatchingSinceAttributes() {\n\t\treturn ArchCondition.from( //\n\t\t\tDescribedPredicate.and( //\n\t\t\t\tannotatedWith(Deprecated.class), haveApiAnnotationWithDeprecatedStatus(), //\n\t\t\t\thaveSinceAttributeMatchingApiAnnotation()));\n\t}\n\n\tprivate static <T extends HasAnnotations<?> & HasSourceCodeLocation> DescribedPredicate<T> haveApiAnnotationWithDeprecatedStatus() {\n\t\treturn and(annotatedWith(API.class), describe(\"status() is DEPRECATED\",\n\t\t\telement -> element.getAnnotationOfType(API.class).status() == DEPRECATED));\n\t}\n\n\tprivate static <T extends HasAnnotations<?> & HasSourceCodeLocation> DescribedPredicate<T> haveSinceAttributeMatchingApiAnnotation() {\n\t\treturn describe(\"@API(since) equals @Deprecated(since)\", element -> {\n\t\t\tvar deprecatedAnnotation = element.getAnnotationOfType(Deprecated.class);\n\t\t\tvar apiAnnotation = element.getAnnotationOfType(API.class);\n\t\t\treturn deprecatedAnnotation.since() != null //\n\t\t\t\t\t&& Objects.equals(deprecatedAnnotation.since(), apiAnnotation.since());\n\t\t});\n\t}\n\n\tprivate static DescribedPredicate<JavaClass> notShadowed() {\n\t\treturn not(describe(\"are shadowed\", resideInAPackage(\"..shadow..\")));\n\t}\n\n\tprivate static class RepeatableAnnotationPredicate<T extends Annotation> extends DescribedPredicate<JavaClass> {\n\n\t\tprivate final Class<T> annotationType;\n\t\tprivate final BiPredicate<T, T> predicate;\n\n\t\tRepeatableAnnotationPredicate(Class<T> annotationType, BiPredicate<T, T> predicate) {\n\t\t\tsuper(\"have identical @%s annotation as container annotation\", annotationType.getSimpleName());\n\t\t\tthis.annotationType = annotationType;\n\t\t\tthis.predicate = predicate;\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean test(JavaClass annotationClass) {\n\t\t\tvar containerAnnotationClass = (JavaClass) annotationClass.getAnnotationOfType(\n\t\t\t\tRepeatable.class.getName()).get(\"value\").orElseThrow();\n\t\t\tvar expectedAnnotation = annotationClass.tryGetAnnotationOfType(annotationType);\n\t\t\tvar actualAnnotation = containerAnnotationClass.tryGetAnnotationOfType(annotationType);\n\t\t\treturn expectedAnnotation.map(expectedTarget -> actualAnnotation //\n\t\t\t\t\t.map(actualTarget -> predicate.test(expectedTarget, actualTarget)) //\n\t\t\t\t\t.orElse(false)) //\n\t\t\t\t\t.orElse(actualAnnotation.isEmpty());\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/main/java/platform/tooling/support/Helper.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Optional;\nimport java.util.Properties;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Stream;\n\n/**\n * @since 1.3\n */\npublic class Helper {\n\n\tprivate static final Path ROOT = Path.of(\"..\");\n\tprivate static final Path GRADLE_PROPERTIES = ROOT.resolve(\"gradle.properties\");\n\tprivate static final Path SETTINGS_GRADLE = ROOT.resolve(\"settings.gradle.kts\");\n\n\tprivate static final Properties gradleProperties = new Properties();\n\n\tstatic {\n\t\ttry {\n\t\t\tgradleProperties.load(Files.newInputStream(GRADLE_PROPERTIES));\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"loading gradle.properties failed\", e);\n\t\t}\n\t}\n\n\tpublic static String version() {\n\t\treturn gradleProperties.getProperty(\"version\");\n\t}\n\n\tpublic static List<String> loadModuleDirectoryNames() {\n\t\tvar moduleLinePattern = Pattern.compile(\"include\\\\(\\\"(.+)\\\"\\\\)\");\n\t\ttry (var stream = Files.lines(SETTINGS_GRADLE)) {\n\t\t\treturn stream.map(moduleLinePattern::matcher) //\n\t\t\t\t\t.filter(Matcher::matches) //\n\t\t\t\t\t.map(matcher -> matcher.group(1)) //\n\t\t\t\t\t.filter(name -> name.startsWith(\"junit-\")) //\n\t\t\t\t\t.filter(name -> !\"junit-bom\".equals(name)) //\n\t\t\t\t\t.filter(name -> !\"junit-platform-console-standalone\".equals(name)).toList();\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"loading module directory names failed: \" + SETTINGS_GRADLE);\n\t\t}\n\t}\n\n\tpublic static Optional<Path> getJavaHome(int version) {\n\t\t// First, try various system sources...\n\t\tvar sources = Stream.of( //\n\t\t\tSystem.getProperty(\"java.home.\" + version), //\n\t\t\tSystem.getProperty(\"java.\" + version), //\n\t\t\tSystem.getProperty(\"jdk.home.\" + version), //\n\t\t\tSystem.getProperty(\"jdk.\" + version), //\n\t\t\tSystem.getenv(\"JAVA_HOME_\" + version), //\n\t\t\tSystem.getenv(\"JAVA_\" + version), //\n\t\t\tSystem.getenv(\"JDK\" + version) //\n\t\t);\n\t\treturn sources.filter(Objects::nonNull).findFirst().map(Path::of);\n\t}\n\n\tprivate Helper() {\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/main/java/platform/tooling/support/MavenRepo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.function.Predicate;\nimport java.util.stream.Stream;\n\npublic class MavenRepo {\n\n\tprivate MavenRepo() {\n\t}\n\n\tpublic static Path dir() {\n\t\tvar candidates = Stream.of(Path.of(\"../build/repo\"));\n\t\tvar candidate = candidates.filter(Files::isDirectory).findFirst().orElse(Path.of(\"build/repo\"));\n\t\treturn Path.of(System.getProperty(\"maven.repo\", candidate.toString()));\n\t}\n\n\tpublic static Path jar(String artifactId) {\n\t\treturn artifact(artifactId, fileName -> fileName.endsWith(\".jar\") //\n\t\t\t\t&& !fileName.endsWith(\"-sources.jar\") //\n\t\t\t\t&& !fileName.endsWith(\"-javadoc.jar\"));\n\t}\n\n\tpublic static Path gradleModuleMetadata(String artifactId) {\n\t\treturn artifact(artifactId, fileName -> fileName.endsWith(\".module\"));\n\t}\n\n\tpublic static Path pom(String artifactId) {\n\t\treturn artifact(artifactId, fileName -> fileName.endsWith(\".pom\"));\n\t}\n\n\tprivate static Path artifact(String artifactId, Predicate<String> fileNamePredicate) {\n\t\tvar parentDir = dir() //\n\t\t\t\t.resolve(groupId(artifactId).replace('.', File.separatorChar)) //\n\t\t\t\t.resolve(artifactId) //\n\t\t\t\t.resolve(Helper.version());\n\t\ttry (var files = Files.list(parentDir)) {\n\t\t\treturn files.filter(\n\t\t\t\tfile -> fileNamePredicate.test(file.getFileName().toString())).findFirst().orElseThrow();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t}\n\n\tprivate static String groupId(String artifactId) {\n\t\tif (artifactId.startsWith(\"junit-jupiter\")) {\n\t\t\treturn \"org.junit.jupiter\";\n\t\t}\n\t\tif (artifactId.startsWith(\"junit-platform\")) {\n\t\t\treturn \"org.junit.platform\";\n\t\t}\n\t\tif (artifactId.startsWith(\"junit-vintage\")) {\n\t\t\treturn \"org.junit.vintage\";\n\t\t}\n\t\treturn \"org.junit\";\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/main/java/platform/tooling/support/ProcessStarters.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support;\n\nimport java.nio.file.Path;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.platform.tests.process.ProcessStarter;\nimport org.opentest4j.TestAbortedException;\n\npublic class ProcessStarters {\n\n\tpublic static ProcessStarter java() {\n\t\treturn javaCommand(currentJdkHome(), \"java\");\n\t}\n\n\tpublic static Path currentJdkHome() {\n\t\tvar executable = ProcessHandle.current().info().command().map(Path::of).orElseThrow();\n\t\t// path element count is 3 or higher: \"<JAVA_HOME>/bin/java[.exe]\"\n\t\treturn executable.getParent().getParent().toAbsolutePath();\n\t}\n\n\tpublic static ProcessStarter java(Path javaHome) {\n\t\treturn javaCommand(javaHome, \"java\");\n\t}\n\n\tpublic static ProcessStarter javaCommand(String commandName) {\n\t\treturn javaCommand(currentJdkHome(), commandName);\n\t}\n\n\tpublic static ProcessStarter javaCommand(Path javaHome, String commandName) {\n\t\treturn new ProcessStarter() //\n\t\t\t\t.executable(javaHome.resolve(\"bin\").resolve(commandName)) //\n\t\t\t\t.putEnvironment(\"JAVA_HOME\", javaHome);\n\t}\n\n\tpublic static ProcessStarter gradlew() {\n\t\treturn new ProcessStarter() //\n\t\t\t\t.executable(Path.of(\"..\").resolve(windowsOrOtherExecutable(\"gradlew.bat\", \"gradlew\")).toAbsolutePath()) //\n\t\t\t\t.putEnvironment(\"JAVA_HOME\", getGradleJavaHome().orElseThrow(TestAbortedException::new)) //\n\t\t\t\t.addArguments(\"-PjunitVersion=\" + Helper.version());\n\t}\n\n\tpublic static ProcessStarter maven(Path javaHome) {\n\t\treturn new ProcessStarter() //\n\t\t\t\t.executable(Path.of(System.getProperty(\"mavenDistribution\")).resolve(\"bin\").resolve(\n\t\t\t\t\twindowsOrOtherExecutable(\"mvn.cmd\", \"mvn\")).toAbsolutePath()) //\n\t\t\t\t.putEnvironment(\"JAVA_HOME\", javaHome) //\n\t\t\t\t.addArguments(\"-Djunit.version=\" + Helper.version());\n\t}\n\n\tprivate static String windowsOrOtherExecutable(String cmdOrExe, String other) {\n\t\treturn OS.current() == OS.WINDOWS ? cmdOrExe : other;\n\t}\n\n\tpublic static Optional<Path> getGradleJavaHome() {\n\t\treturn Helper.getJavaHome(getGradleJavaVersion());\n\t}\n\n\tpublic static int getGradleJavaVersion() {\n\t\treturn Integer.parseInt(System.getProperty(\"gradle.java.version\"));\n\t}\n\n\tprivate ProcessStarters() {\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/main/java/platform/tooling/support/ThirdPartyJars.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support;\n\nimport static java.nio.file.StandardCopyOption.REPLACE_EXISTING;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.stream.Stream;\n\npublic class ThirdPartyJars {\n\n\tprivate ThirdPartyJars() {\n\t}\n\n\tpublic static void copy(Path targetDir, String group, String artifact) {\n\t\tPath source = find(group, artifact);\n\t\tcopy(source, targetDir);\n\t}\n\n\tpublic static void copyAll(Path targetDir) {\n\t\tthirdPartyJars().forEach(path -> copy(path, targetDir));\n\t}\n\n\tprivate static void copy(Path source, Path targetDir) {\n\t\ttry {\n\t\t\tFiles.copy(source, targetDir.resolve(source.getFileName()), REPLACE_EXISTING);\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(\"Failed to copy %s to %s\".formatted(source, targetDir), e);\n\t\t}\n\t}\n\n\tpublic static Path find(String group, String artifact) {\n\t\treturn thirdPartyJars() //\n\t\t\t\t.filter(it -> it.toAbsolutePath().toString().replace(File.separator, \"/\").contains(\n\t\t\t\t\t\"/\" + group + \"/\" + artifact + \"/\")) //\n\t\t\t\t.findFirst() //\n\t\t\t\t.orElseThrow(() -> new AssertionError(\"Failed to find JAR file for \" + group + \":\" + artifact));\n\t}\n\n\tprivate static Stream<Path> thirdPartyJars() {\n\t\treturn Stream.of(System.getProperty(\"thirdPartyJars\").split(File.pathSeparator)) //\n\t\t\t\t.map(Path::of);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/main/java/platform/tooling/support/package-info.java",
    "content": "/**\n * Tooling support helper.\n *\n * @since 1.3\n */\n\npackage platform.tooling.support;\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/HelperTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.Assumptions.assumeTrue;\n\nimport java.nio.file.Files;\nimport java.util.List;\nimport java.util.Optional;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\n\n@Order(Integer.MAX_VALUE)\nclass HelperTests {\n\n\t@Test\n\tvoid loadModuleDirectoryNames() {\n\t\tassertLinesMatch(List.of( //\n\t\t\t\"junit-jupiter\", //\n\t\t\t\"junit-jupiter-api\", //\n\t\t\t\"junit-jupiter-engine\", //\n\t\t\t\"junit-jupiter-migrationsupport\", //\n\t\t\t\"junit-jupiter-params\", //\n\t\t\t\"junit-start\", //\n\t\t\t\"junit-platform-commons\", //\n\t\t\t\"junit-platform-console\", //\n\t\t\t\"junit-platform-engine\", //\n\t\t\t\"junit-platform-launcher\", //\n\t\t\t\"junit-platform-reporting\", //\n\t\t\t\"junit-platform-suite\", //\n\t\t\t\"junit-platform-suite-api\", //\n\t\t\t\"junit-platform-suite-engine\", //\n\t\t\t\"junit-platform-testkit\", //\n\t\t\t\"junit-vintage-engine\"//\n\t\t), Helper.loadModuleDirectoryNames());\n\t}\n\n\t@Test\n\tvoid version() {\n\t\tassertNotNull(Helper.version());\n\t}\n\n\t@Test\n\tvoid nonExistingJdkVersionYieldsAnEmptyOptional() {\n\t\tassertEquals(Optional.empty(), Helper.getJavaHome(-1));\n\t}\n\n\t@ParameterizedTest\n\t@ValueSource(ints = 17)\n\tvoid checkJavaHome(int version) {\n\t\tvar home = Helper.getJavaHome(version);\n\t\tassumeTrue(home.isPresent(), \"No 'jdk' element found in Maven toolchain for: \" + version);\n\t\tassertTrue(Files.isDirectory(home.get()));\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/AntStarterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\nimport static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport;\n\nimport java.nio.file.Path;\nimport java.util.List;\n\nimport de.skuzzle.test.snapshots.Snapshot;\nimport de.skuzzle.test.snapshots.junit5.EnableSnapshotTests;\n\nimport org.apache.tools.ant.Main;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\n@EnableSnapshotTests\n//@SnapshotTestOptions(alwaysPersistActualResult = true)\nclass AntStarterTests {\n\n\t@Test\n\t@Timeout(60)\n\tvoid ant_starter(@TempDir Path workspace, @FilePrefix(\"ant\") OutputFiles outputFiles, Snapshot snapshot)\n\t\t\tthrows Exception {\n\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.JUPITER_STARTER, workspace)) //\n\t\t\t\t.addArguments(\"-cp\", System.getProperty(\"antJars\"), Main.class.getName()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertEquals(\"\", result.stdErr(), \"error log isn't empty\");\n\t\tassertLinesMatch(List.of(\">> HEAD >>\", //\n\t\t\t\"test.junit.launcher:\", //\n\t\t\t\">>>>\", //\n\t\t\t\"\\\\[junitlauncher\\\\] Tests run: 8, Failures: 0, Aborted: 0, Skipped: 0, Time elapsed: .+ sec\", //\n\t\t\t\"\\\\[junitlauncher\\\\] Running com.example.project.CalculatorTests\", //\n\t\t\t\"\\\\[junitlauncher\\\\] Tests run: 5, Failures: 0, Aborted: 0, Skipped: 0, Time elapsed: .+ sec\", //\n\t\t\t\">>>>\", //\n\t\t\t\"test.console.launcher:\", //\n\t\t\t\">>>>\", //\n\t\t\t\"     \\\\[java\\\\] Test run finished after [\\\\d]+ ms\", //\n\t\t\t\">>>>\", //\n\t\t\t\"     \\\\[java\\\\] \\\\[        13 tests successful      \\\\]\", //\n\t\t\t\"     \\\\[java\\\\] \\\\[         0 tests failed          \\\\]\", //\n\t\t\t\">> TAIL >>\"), //\n\t\t\tresult.stdOutLines());\n\n\t\tvar testResultsDir = workspace.resolve(\"build/test-report\");\n\t\tverifyContainsExpectedStartedOpenTestReport(testResultsDir, snapshot);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/FilePrefix.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n@Target(ElementType.PARAMETER)\n@Retention(RetentionPolicy.RUNTIME)\n@ExtendWith(OutputAttachingExtension.class)\n@interface FilePrefix {\n\tString value();\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GraalVmStarterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static java.util.concurrent.TimeUnit.MINUTES;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.9.1\n */\n@Order(Integer.MIN_VALUE)\n@DisabledOnOpenJ9\n@EnabledIfEnvironmentVariable(named = \"GRAALVM_HOME\", matches = \".+\")\nclass GraalVmStarterTests {\n\n\t@Test\n\t@Timeout(value = 10, unit = MINUTES)\n\tvoid runsTestsInNativeImage(@TempDir Path workspace, @FilePrefix(\"gradle\") OutputFiles outputFiles)\n\t\t\tthrows Exception {\n\t\tvar result = ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.GRAALVM_STARTER, workspace)) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"javaToolchains\", \"nativeTest\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\",\n\t\t\t\t\t\"--warning-mode=fail\", \"--refresh-dependencies\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertThat(result.stdOutLines()) //\n\t\t\t\t.anyMatch(line -> line.contains(\"CalculatorTests > 1 + 1 = 2 SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(line -> line.contains(\"CalculatorTests > 1 + 100 = 101 SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(line -> line.contains(\n\t\t\t\t\t\"ClassLevelAnnotationTests$Inner > ClassLevelAnnotationTests, Inner, test SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(\n\t\t\t\t\tline -> line.contains(\"com.example.project.CalculatorParameterizedClassTests > [1] 1 SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(\n\t\t\t\t\tline -> line.contains(\"com.example.project.CalculatorParameterizedClassTests > [2] 2 SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(line -> line.contains(\"com.example.project.VintageTests > test SUCCESSFUL\")) //\n\t\t\t\t.anyMatch(line -> line.contains(\"BUILD SUCCESSFUL\"));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleKotlinExtensionsTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\nclass GradleKotlinExtensionsTests {\n\n\t@Test\n\tvoid gradle_wrapper(@TempDir Path workspace, @FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.GRADLE_KOTLIN_EXTENSIONS, workspace)) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"build\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.putEnvironment(\"JDK17\", Helper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode(), \"result=\" + result);\n\t\tassertTrue(result.stdOut().lines().anyMatch(line -> line.contains(\"BUILD SUCCESSFUL\")));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleMissingEngineTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\nclass GradleMissingEngineTests {\n\n\t@Test\n\tvoid gradle_wrapper(@TempDir Path workspace, @FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.GRADLE_MISSING_ENGINE, workspace)) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"build\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.putEnvironment(\"JDK17\", Helper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.redirectOutput(outputFiles).startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\t\tassertThat(result.stdErr()) //\n\t\t\t\t.contains(\"FAILURE: Build failed with an exception.\") //\n\t\t\t\t.contains(\"Cannot create Launcher without at least one TestEngine\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleModuleFileTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\n\nimport java.nio.file.Files;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\n\nimport platform.tooling.support.MavenRepo;\n\n/**\n * @since 1.6\n */\n@Order(Integer.MAX_VALUE)\nclass GradleModuleFileTests {\n\n\t@Test\n\tvoid jupiterAggregatorGradleModuleMetadataVariants() throws Exception {\n\t\tvar expected = List.of(\">> HEAD >>\", //\n\t\t\t\"{\", //\n\t\t\t\"  \\\"formatVersion\\\": \\\"1.1\\\",\", //\n\t\t\t\"  \\\"component\\\": {\", //\n\t\t\t\"    \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"    \\\"module\\\": \\\"junit-jupiter\\\",\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"    \\\"attributes\\\": {\", //\n\t\t\t\">> STATUS >>\", //\n\t\t\t\"    }\", //\n\t\t\t\"  },\", //\n\t\t\t\">> CREATED_BY >>\", //\n\t\t\t\"  \\\"variants\\\": [\", //\n\t\t\t\"    {\", //\n\t\t\t\"      \\\"name\\\": \\\"apiElements\\\",\", //\n\t\t\t\"      \\\"attributes\\\": {\", //\n\t\t\t\"        \\\"org.gradle.category\\\": \\\"library\\\",\", //\n\t\t\t\"        \\\"org.gradle.dependency.bundling\\\": \\\"external\\\",\", //\n\t\t\t\"        \\\"org.gradle.jvm.version\\\": 17,\", //\n\t\t\t\"        \\\"org.gradle.libraryelements\\\": \\\"jar\\\",\", //\n\t\t\t\"        \\\"org.gradle.usage\\\": \\\"java-api\\\"\", //\n\t\t\t\"      },\", //\n\t\t\t\"      \\\"dependencies\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-bom\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          },\", //\n\t\t\t\"          \\\"attributes\\\": {\", //\n\t\t\t\"            \\\"org.gradle.category\\\": \\\"platform\\\"\", //\n\t\t\t\"          },\", //\n\t\t\t\"          \\\"endorseStrictVersions\\\": true\", //\n\t\t\t\"        },\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-jupiter-api\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          }\", //\n\t\t\t\"        },\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-jupiter-params\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          }\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ],\", //\n\t\t\t\"      \\\"files\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\">> JAR_FILE_DETAILS >>\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ]\", //\n\t\t\t\"    },\", //\n\t\t\t\"    {\", //\n\t\t\t\"      \\\"name\\\": \\\"runtimeElements\\\",\", //\n\t\t\t\"      \\\"attributes\\\": {\", //\n\t\t\t\"        \\\"org.gradle.category\\\": \\\"library\\\",\", //\n\t\t\t\"        \\\"org.gradle.dependency.bundling\\\": \\\"external\\\",\", //\n\t\t\t\"        \\\"org.gradle.jvm.version\\\": 17,\", //\n\t\t\t\"        \\\"org.gradle.libraryelements\\\": \\\"jar\\\",\", //\n\t\t\t\"        \\\"org.gradle.usage\\\": \\\"java-runtime\\\"\", //\n\t\t\t\"      },\", //\n\t\t\t\"      \\\"dependencies\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-jupiter-engine\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          }\", //\n\t\t\t\"        },\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-bom\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          },\", //\n\t\t\t\"          \\\"attributes\\\": {\", //\n\t\t\t\"            \\\"org.gradle.category\\\": \\\"platform\\\"\", //\n\t\t\t\"          },\", //\n\t\t\t\"          \\\"endorseStrictVersions\\\": true\", //\n\t\t\t\"        },\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-jupiter-api\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          }\", //\n\t\t\t\"        },\", //\n\t\t\t\"        {\", //\n\t\t\t\"          \\\"group\\\": \\\"org.junit.jupiter\\\",\", //\n\t\t\t\"          \\\"module\\\": \\\"junit-jupiter-params\\\",\", //\n\t\t\t\"          \\\"version\\\": {\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"          }\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ],\", //\n\t\t\t\"      \\\"files\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\">> JAR_FILE_DETAILS >>\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ]\", //\n\t\t\t\"    },\", //\n\t\t\t\"    {\", //\n\t\t\t\"      \\\"name\\\": \\\"javadocElements\\\",\", //\n\t\t\t\"      \\\"attributes\\\": {\", //\n\t\t\t\"        \\\"org.gradle.category\\\": \\\"documentation\\\",\", //\n\t\t\t\"        \\\"org.gradle.dependency.bundling\\\": \\\"external\\\",\", //\n\t\t\t\"        \\\"org.gradle.docstype\\\": \\\"javadoc\\\",\", //\n\t\t\t\"        \\\"org.gradle.usage\\\": \\\"java-runtime\\\"\", //\n\t\t\t\"      },\", //\n\t\t\t\"      \\\"files\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\">> JAR_FILE_DETAILS >>\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ]\", //\n\t\t\t\"    },\", //\n\t\t\t\"    {\", //\n\t\t\t\"      \\\"name\\\": \\\"sourcesElements\\\",\", //\n\t\t\t\"      \\\"attributes\\\": {\", //\n\t\t\t\"        \\\"org.gradle.category\\\": \\\"documentation\\\",\", //\n\t\t\t\"        \\\"org.gradle.dependency.bundling\\\": \\\"external\\\",\", //\n\t\t\t\"        \\\"org.gradle.docstype\\\": \\\"sources\\\",\", //\n\t\t\t\"        \\\"org.gradle.usage\\\": \\\"java-runtime\\\"\", //\n\t\t\t\"      },\", //\n\t\t\t\"      \\\"files\\\": [\", //\n\t\t\t\"        {\", //\n\t\t\t\">> JAR_FILE_DETAILS >>\", //\n\t\t\t\"        }\", //\n\t\t\t\"      ]\", //\n\t\t\t\"    }\", //\n\t\t\t\"  ]\", //\n\t\t\t\"}\");\n\n\t\tassertLinesMatch(expected, Files.readAllLines(MavenRepo.gradleModuleMetadata(\"junit-jupiter\")));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/GradleStarterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\nimport static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport;\n\nimport java.nio.file.Path;\n\nimport de.skuzzle.test.snapshots.Snapshot;\nimport de.skuzzle.test.snapshots.junit5.EnableSnapshotTests;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\n@EnableSnapshotTests\n//@SnapshotTestOptions(alwaysPersistActualResult = true)\nclass GradleStarterTests {\n\n\t@TempDir\n\tPath workspace;\n\n\t@BeforeEach\n\tvoid prepareWorkspace() throws Exception {\n\t\tcopyToWorkspace(Projects.JUPITER_STARTER, workspace);\n\t}\n\n\t@Test\n\tvoid buildJupiterStarterProject(@FilePrefix(\"gradle\") OutputFiles outputFiles, Snapshot snapshot) throws Exception {\n\n\t\tvar result = runGradle(outputFiles, \"build\");\n\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains( //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > parameterizedTest(int)\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > Inner > [1] 1 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > Inner > [2] 2 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > parameterizedTest(int)\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > Inner > [1] 1 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > Inner > [2] 2 > regularTest() PASSED\", //\n\t\t\t\t\t\"Using Java version: 17\", //\n\t\t\t\t\t\"CalculatorTests > 1 + 1 = 2 PASSED\", //\n\t\t\t\t\t\"CalculatorTests > add(int, int, int) > 0 + 1 = 1 PASSED\", //\n\t\t\t\t\t\"CalculatorTests > add(int, int, int) > 1 + 2 = 3 PASSED\", //\n\t\t\t\t\t\"CalculatorTests > add(int, int, int) > 49 + 51 = 100 PASSED\", //\n\t\t\t\t\t\"CalculatorTests > add(int, int, int) > 1 + 100 = 101 PASSED\" //\n\t\t\t\t);\n\n\t\tvar testResultsDir = workspace.resolve(\"build/test-results/test\");\n\t\tverifyContainsExpectedStartedOpenTestReport(testResultsDir, snapshot);\n\t}\n\n\t@Test\n\tvoid runOnlyOneMethodInClassTemplate(@FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\n\t\tvar result = runGradle(outputFiles, \"test\", \"--tests\", \"CalculatorParameterized*.regular*\");\n\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains( //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > Inner > [1] 1 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > Inner > [2] 2 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > Inner > [1] 1 > regularTest() PASSED\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > Inner > [2] 2 > regularTest() PASSED\" //\n\t\t\t\t) //\n\t\t\t\t.doesNotContain(\"parameterizedTest(int)\", \"CalculatorTests\");\n\n\t\tresult = runGradle(outputFiles, \"test\", \"--tests\", \"*ParameterizedClassTests.parameterized*\");\n\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains( //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [1] i = 1 > parameterizedTest(int)\", //\n\t\t\t\t\t\"CalculatorParameterizedClassTests > [2] i = 2 > parameterizedTest(int)\" //\n\t\t\t\t) //\n\t\t\t\t.doesNotContain(\"regularTest()\", \"CalculatorTests\");\n\t}\n\n\tprivate ProcessResult runGradle(OutputFiles outputFiles, String... extraArgs) throws InterruptedException {\n\t\tvar result = ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Djava.toolchain.version=17\") //\n\t\t\t\t.addArguments(\"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.addArguments(extraArgs).putEnvironment(\"JDK17\",\n\t\t\t\t\tHelper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().lines().anyMatch(line -> line.contains(\"BUILD SUCCESSFUL\")));\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JUnitStartTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.EnabledOnJre;\nimport org.junit.jupiter.api.condition.JRE;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\nimport platform.tooling.support.ThirdPartyJars;\n\n/**\n * @since 6.1\n */\nclass JUnitStartTests {\n\n\t@TempDir\n\tstatic Path workspace;\n\n\t@BeforeAll\n\tstatic void prepareLocalLibraryDirectoryWithJUnitModules() throws Exception {\n\t\tcopyToWorkspace(Projects.JUNIT_START, workspace);\n\t\tvar lib = workspace.resolve(\"lib\");\n\t\ttry {\n\t\t\tFiles.createDirectories(lib);\n\t\t\ttry (var directoryStream = Files.newDirectoryStream(lib, \"*.jar\")) {\n\t\t\t\tfor (Path jarFile : directoryStream) {\n\t\t\t\t\tFiles.delete(jarFile);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (var module : Helper.loadModuleDirectoryNames()) {\n\t\t\t\tif (module.startsWith(\"junit-platform\") || module.startsWith(\"junit-jupiter\")\n\t\t\t\t\t\t|| module.equals(\"junit-start\")) {\n\t\t\t\t\tif (module.equals(\"junit-jupiter-migrationsupport\"))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (module.startsWith(\"junit-platform-suite\"))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (module.equals(\"junit-platform-testkit\"))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tvar jar = MavenRepo.jar(module);\n\t\t\t\t\tFiles.copy(jar, lib.resolve(module + \".jar\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tThirdPartyJars.copy(lib, \"org.apiguardian\", \"apiguardian-api\");\n\t\t\tThirdPartyJars.copy(lib, \"org.jspecify\", \"jspecify\");\n\t\t\tThirdPartyJars.copy(lib, \"org.opentest4j\", \"opentest4j\");\n\t\t\tThirdPartyJars.copy(lib, \"org.opentest4j.reporting\", \"open-test-reporting-tooling-spi\");\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"Preparing local library folder failed\", e);\n\t\t}\n\t}\n\n\t@Test\n\t@EnabledOnJre(JRE.JAVA_25)\n\tvoid junitRun(@FilePrefix(\"junit-run\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"--module-path\", \"lib\") // relative to workspace\n\t\t\t\t.addArguments(\"--add-modules\", \"org.junit.start\") // configure root module\n\t\t\t\t.addArguments(\"compact/JUnitRun.java\") // leverage Java's source mode\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().contains(\"addition()\"), result.stdOut());\n\t}\n\n\t@Test\n\t@EnabledOnJre(JRE.JAVA_25)\n\tvoid junitRunClass(@FilePrefix(\"junit-run-class\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"--module-path\", \"lib\") // relative to workspace\n\t\t\t\t.addArguments(\"--add-modules\", \"org.junit.start\") // configure root module\n\t\t\t\t.addArguments(\"compact/JUnitRunClass.java\") // leverage Java's source mode\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().contains(\"substraction()\"), result.stdOut());\n\t}\n\n\t@Test\n\t@EnabledOnJre(JRE.JAVA_25)\n\tvoid junitRunModule(@FilePrefix(\"junit-run-module\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.putEnvironment(\"NO_COLOR\", \"1\") // --disable-ansi-colors\n\t\t\t\t.addArguments(\"--module-path\", \"lib\") // relative to workspace\n\t\t\t\t.addArguments(\"modular/p/JUnitRunModule.java\") // leverage Java's source mode\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().contains(\"multiplication()\"), result.stdOut());\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarContainsManifestFirstTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport java.io.FileInputStream;\nimport java.nio.file.Files;\nimport java.util.jar.JarInputStream;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\n\nimport platform.tooling.support.MavenRepo;\n\n/**\n * @since 1.8\n */\n@Order(Integer.MAX_VALUE)\nclass JarContainsManifestFirstTests {\n\n\t@ParameterizedTest(quoteTextArguments = false)\n\t@MethodSource(\"platform.tooling.support.Helper#loadModuleDirectoryNames\")\n\tvoid manifestFirst(String module) throws Exception {\n\t\tvar modulePath = MavenRepo.jar(module);\n\n\t\tif (Files.notExists(modulePath)) {\n\t\t\tfail(\"No such file: \" + modulePath);\n\t\t}\n\n\t\t// JarInputStream expects the META-INF/MANIFEST.MF to be at the start of the JAR archive\n\t\ttry (var jarInputStream = new JarInputStream(new FileInputStream(modulePath.toFile()))) {\n\t\t\tassertNotNull(jarInputStream.getManifest(), \"MANIFEST.MF should be available via JarInputStream\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/JarDescribeModuleTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.getSourceDirectory;\n\nimport java.lang.module.ModuleFinder;\nimport java.nio.file.Files;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\n@Order(Integer.MAX_VALUE)\nclass JarDescribeModuleTests {\n\n\t@ParameterizedTest(quoteTextArguments = false)\n\t@MethodSource(\"platform.tooling.support.Helper#loadModuleDirectoryNames\")\n\tvoid describeModule(String module, @FilePrefix(\"jar\") OutputFiles outputFiles) throws Exception {\n\t\tvar sourceDirectory = getSourceDirectory(Projects.JAR_DESCRIBE_MODULE);\n\t\tvar modulePath = MavenRepo.jar(module);\n\n\t\tvar result = ProcessStarters.javaCommand(\"jar\") //\n\t\t\t\t.workingDir(sourceDirectory) //\n\t\t\t\t.addArguments(\"--describe-module\", \"--file\", modulePath.toAbsolutePath().toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertEquals(\"\", result.stdErr(), \"error log isn't empty\");\n\n\t\tvar expectedLines = replaceVersionPlaceholders(\n\t\t\tFiles.readString(sourceDirectory.resolve(module + \".expected.txt\")).strip());\n\t\tassertLinesMatch(expectedLines.lines().toList(), result.stdOut().strip().lines().toList());\n\t}\n\n\t@ParameterizedTest(quoteTextArguments = false)\n\t@MethodSource(\"platform.tooling.support.Helper#loadModuleDirectoryNames\")\n\tvoid packageNamesStartWithNameOfTheModule(String module) {\n\t\tvar modulePath = MavenRepo.jar(module);\n\t\tvar moduleDescriptor = ModuleFinder.of(modulePath).findAll().iterator().next().descriptor();\n\t\tvar moduleName = moduleDescriptor.name();\n\t\tfor (var packageName : moduleDescriptor.packages()) {\n\t\t\tassertTrue(packageName.startsWith(moduleName));\n\t\t}\n\t}\n\n\tprivate static String replaceVersionPlaceholders(String line) {\n\t\tline = line.replace(\"${version}\", Helper.version());\n\t\treturn line;\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/KotlinCoroutinesTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 6.0\n */\n@Order(Integer.MIN_VALUE + 1)\nclass KotlinCoroutinesTests {\n\n\t@Test\n\tvoid failsExpectedlyWhenAllOptionalDependenciesArePresent(@TempDir Path workspace,\n\t\t\t@FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = runBuild(workspace, outputFiles);\n\n\t\tassertEquals(1, result.exitCode(), \"result=\" + result);\n\t\tassertThat(result.stdOut()).contains(\"AssertionFailedError: expected\");\n\t\tassertThat(result.stdErr()).contains(\"BUILD FAILED\");\n\t}\n\n\t@Test\n\tvoid failsWithHelpfulErrorMessageWhenKotlinxCoroutinesIsMissing(@TempDir Path workspace,\n\t\t\t@FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = runBuild(workspace, outputFiles, \"-PwithoutKotlinxCoroutines\");\n\n\t\tassertEquals(1, result.exitCode(), \"result=\" + result);\n\t\tassertThat(result.stdOut()).contains(\"PreconditionViolationException: Kotlin suspending function \"\n\t\t\t\t+ \"[public final java.lang.Object com.example.project.SuspendFunctionTests.test(kotlin.coroutines.Continuation<? super kotlin.Unit>)] \"\n\t\t\t\t+ \"requires org.jetbrains.kotlinx:kotlinx-coroutines-core to be on the classpath or module path. \"\n\t\t\t\t+ \"Please add a corresponding dependency.\");\n\t\tassertThat(result.stdErr()).contains(\"BUILD FAILED\");\n\t}\n\n\t@Test\n\tvoid failsWithHelpfulErrorMessageWhenKotlinReflectIsMissing(@TempDir Path workspace,\n\t\t\t@FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = runBuild(workspace, outputFiles, \"-PwithoutKotlinReflect\");\n\n\t\tassertEquals(1, result.exitCode(), \"result=\" + result);\n\t\tassertThat(result.stdOut()).contains(\"PreconditionViolationException: Kotlin suspending function \"\n\t\t\t\t+ \"[public final java.lang.Object com.example.project.SuspendFunctionTests.test(kotlin.coroutines.Continuation<? super kotlin.Unit>)] \"\n\t\t\t\t+ \"requires org.jetbrains.kotlin:kotlin-reflect to be on the classpath or module path. \"\n\t\t\t\t+ \"Please add a corresponding dependency.\");\n\t\tassertThat(result.stdErr()).contains(\"BUILD FAILED\");\n\t}\n\n\tprivate static ProcessResult runBuild(Path workspace, OutputFiles outputFiles, String... extraArgs)\n\t\t\tthrows InterruptedException, IOException {\n\n\t\treturn ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.KOTLIN_COROUTINES, workspace)) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"build\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.addArguments(extraArgs).putEnvironment(\"JDK17\",\n\t\t\t\t\tHelper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/LocalMavenRepo.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static platform.tooling.support.tests.ManagedResource.Scope.GLOBAL;\nimport static platform.tooling.support.tests.ManagedResource.Scope.PER_CONTEXT;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Comparator;\n\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\n\n@ManagedResource.Scoped(LocalMavenRepo.ScopeProvider.class)\npublic class LocalMavenRepo implements AutoCloseable {\n\n\tpublic static class ScopeProvider implements ManagedResource.Scoped.Provider {\n\n\t\tprivate static final Namespace NAMESPACE = Namespace.create(LocalMavenRepo.class);\n\n\t\t@Override\n\t\tpublic ManagedResource.Scope determineScope(ExtensionContext extensionContext) {\n\t\t\tvar store = extensionContext.getRoot().getStore(NAMESPACE);\n\t\t\tvar fileSystemType = store.computeIfAbsent(\"tempFileSystemType\", key -> {\n\t\t\t\tvar type = getFileSystemType(Path.of(System.getProperty(\"java.io.tmpdir\")));\n\t\t\t\textensionContext.getRoot().publishReportEntry(\"tempFileSystemType\", type);\n\t\t\t\treturn type;\n\t\t\t}, String.class);\n\t\t\t// Writing to the same file from multiple Maven processes may fail the build on Windows\n\t\t\treturn \"NTFS\".equalsIgnoreCase(fileSystemType) ? PER_CONTEXT : GLOBAL;\n\t\t}\n\n\t\tprivate static String getFileSystemType(Path path) {\n\t\t\ttry {\n\t\t\t\treturn Files.getFileStore(path).type();\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new UncheckedIOException(e);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate final Path tempDir;\n\n\tpublic LocalMavenRepo() {\n\t\ttry {\n\t\t\ttempDir = Files.createTempDirectory(\"local-maven-repo-\");\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\tpublic String toCliArgument() {\n\t\treturn \"-Dmaven.repo.local=\" + tempDir;\n\t}\n\n\t@Override\n\tpublic void close() throws IOException {\n\t\ttry (var files = Files.walk(tempDir)) {\n\t\t\tfiles.sorted(Comparator.<Path> naturalOrder().reversed()) //\n\t\t\t\t\t.forEach(path -> {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tFiles.delete(path);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (IOException e) {\n\t\t\t\t\t\t\tthrow new UncheckedIOException(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManagedResource.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static java.lang.annotation.RetentionPolicy.RUNTIME;\nimport static org.junit.platform.commons.support.ReflectionSupport.streamFields;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Target;\n\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.jupiter.api.extension.TestInstancePostProcessor;\nimport org.junit.platform.commons.support.AnnotationSupport;\nimport org.junit.platform.commons.support.HierarchyTraversalMode;\nimport org.junit.platform.commons.support.ReflectionSupport;\nimport org.junit.platform.commons.util.Preconditions;\n\n@Target({ ElementType.PARAMETER, ElementType.FIELD })\n@Retention(RUNTIME)\n@ExtendWith(ManagedResource.Extension.class)\npublic @interface ManagedResource {\n\n\t@Target(ElementType.TYPE)\n\t@Retention(RUNTIME)\n\t@interface Scoped {\n\n\t\tClass<? extends Provider> value();\n\n\t\tinterface Provider {\n\t\t\tScope determineScope(ExtensionContext extensionContext);\n\t\t}\n\t}\n\n\tenum Scope {\n\t\tGLOBAL, PER_CONTEXT\n\t}\n\n\tclass Extension implements ParameterResolver, TestInstancePostProcessor {\n\n\t\t@Override\n\t\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\treturn parameterContext.isAnnotated(ManagedResource.class);\n\t\t}\n\n\t\t@Override\n\t\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)\n\t\t\t\tthrows ParameterResolutionException {\n\t\t\tClass<?> type = parameterContext.getParameter().getType();\n\t\t\treturn getOrCreateResource(extensionContext, type).get();\n\t\t}\n\n\t\t@Override\n\t\tpublic ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) {\n\t\t\treturn ExtensionContextScope.TEST_METHOD;\n\t\t}\n\n\t\t@Override\n\t\tpublic void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext) {\n\t\t\tstreamFields(testInstance.getClass(), field -> AnnotationSupport.isAnnotated(field, ManagedResource.class),\n\t\t\t\tHierarchyTraversalMode.BOTTOM_UP) //\n\t\t\t\t\t\t.forEach(field -> {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tfield.set(testInstance, getOrCreateResource(extensionContext, field.getType()).get());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (IllegalAccessException e) {\n\t\t\t\t\t\t\t\tthrow new RuntimeException(\"Failed to inject resource into field: \" + field, e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t}\n\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tprivate <T> Resource<T> getOrCreateResource(ExtensionContext extensionContext, Class<T> type) {\n\t\t\tvar scope = AnnotationSupport.findAnnotation(type, Scoped.class) //\n\t\t\t\t\t.map(Scoped::value) //\n\t\t\t\t\t.map(ReflectionSupport::newInstance) //\n\t\t\t\t\t.map(provider -> provider.determineScope(extensionContext)) //\n\t\t\t\t\t.orElse(Scope.GLOBAL);\n\t\t\tvar storingContext = switch (scope) {\n\t\t\t\tcase GLOBAL -> extensionContext.getRoot();\n\t\t\t\tcase PER_CONTEXT -> extensionContext;\n\t\t\t};\n\t\t\treturn storingContext.getStore(Namespace.GLOBAL) //\n\t\t\t\t\t.computeIfAbsent(type, Resource::new, Resource.class);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"try\")\n\tclass Resource<T> implements AutoCloseable {\n\n\t\tprivate final T value;\n\n\t\tprivate Resource(Class<T> type) {\n\t\t\tPreconditions.condition(AutoCloseable.class.isAssignableFrom(type),\n\t\t\t\t() -> \"Resource type must implement AutoCloseable: \" + type.getName());\n\t\t\tthis.value = ReflectionSupport.newInstance(type);\n\t\t}\n\n\t\tprivate T get() {\n\t\t\treturn value;\n\t\t}\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\t((AutoCloseable) value).close();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ManifestTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static aQute.bnd.osgi.Constants.VERSION_ATTRIBUTE;\nimport static org.junit.jupiter.api.Assertions.assertDoesNotThrow;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertNotNull;\n\nimport java.util.jar.Attributes;\n\nimport aQute.bnd.osgi.Domain;\nimport aQute.bnd.osgi.Jar;\nimport aQute.bnd.version.MavenVersion;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.osgi.framework.Version;\nimport org.osgi.framework.VersionRange;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\n\n/**\n * @since 1.5\n */\n@Order(Integer.MAX_VALUE)\nclass ManifestTests {\n\n\t@ParameterizedTest(quoteTextArguments = false)\n\t@MethodSource(\"platform.tooling.support.Helper#loadModuleDirectoryNames\")\n\tvoid manifestEntriesAdhereToConventions(String module) throws Exception {\n\t\tvar version = Helper.version();\n\t\tvar jarFile = MavenRepo.jar(module).toFile();\n\t\ttry (var jar = new Jar(jarFile)) {\n\t\t\tvar manifest = jar.getManifest();\n\t\t\tvar attributes = manifest.getMainAttributes();\n\t\t\tassertValue(attributes, \"Built-By\", \"JUnit Team\");\n\t\t\tassertValue(attributes, \"Specification-Title\", module);\n\t\t\tassertValue(attributes, \"Specification-Version\", specificationVersion(version));\n\t\t\tassertValue(attributes, \"Specification-Vendor\", \"junit.org\");\n\t\t\tassertValue(attributes, \"Implementation-Title\", module);\n\t\t\tassertValue(attributes, \"Implementation-Version\", version);\n\t\t\tassertValue(attributes, \"Implementation-Vendor\", \"junit.org\");\n\t\t\tassertValue(attributes, \"Automatic-Module-Name\", null);\n\t\t\tassertValue(attributes, \"Bundle-ManifestVersion\", \"2\");\n\t\t\tassertValue(attributes, \"Bundle-SymbolicName\", module);\n\t\t\tassertValue(attributes, \"Bundle-Version\",\n\t\t\t\tMavenVersion.parseMavenString(version).getOSGiVersion().toString());\n\t\t\tif (module.equals(\"junit-platform-console\")) {\n\t\t\t\tassertValue(attributes, \"Main-Class\", \"org.junit.platform.console.ConsoleLauncher\");\n\t\t\t}\n\t\t\tvar domain = Domain.domain(manifest);\n\t\t\tdomain.getExportPackage().forEach((pkg, attrs) -> {\n\t\t\t\tfinal var stringVersion = attrs.get(VERSION_ATTRIBUTE);\n\t\t\t\tassertNotNull(stringVersion);\n\t\t\t\tassertDoesNotThrow(() -> new Version(stringVersion));\n\t\t\t});\n\t\t\tdomain.getImportPackage().forEach((pkg, attrs) -> {\n\t\t\t\tfinal var stringVersionRange = attrs.get(VERSION_ATTRIBUTE);\n\t\t\t\tif (stringVersionRange == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tassertDoesNotThrow(() -> new VersionRange(stringVersionRange));\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate static String specificationVersion(String version) {\n\t\tvar dash = version.indexOf('-');\n\t\tif (dash < 0) {\n\t\t\treturn version;\n\t\t}\n\t\treturn version.substring(0, dash);\n\t}\n\n\tprivate static void assertValue(Attributes attributes, String name, String expected) {\n\t\tvar actual = attributes.getValue(name);\n\t\tassertEquals(expected, actual,\n\t\t\t\"Manifest attribute %s expected to be %s, but is: %s\".formatted(name, expected, actual));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenEnvVars.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.junit.jupiter.api.condition.JRE;\n\nfinal class MavenEnvVars {\n\n\tprivate static final List<String> FOR_JDK24_AND_LATER = List.of( //\n\t\t\"--enable-native-access=ALL-UNNAMED\", // https://issues.apache.org/jira/browse/MNG-8248\n\t\t\"--sun-misc-unsafe-memory-access=allow\" // https://issues.apache.org/jira/browse/MNG-8399\n\t);\n\tprivate static final List<String> FOR_JDK26_AND_LATER = List.of( //\n\t\t\"--enable-final-field-mutation=ALL-UNNAMED\" // https://github.com/junit-team/junit-framework/issues/5173\n\t);\n\n\tstatic Map<String, String> forJre(JRE jre) {\n\t\tvar list = new ArrayList<String>();\n\t\tif (jre.compareTo(JRE.JAVA_24) >= 0)\n\t\t\tlist.addAll(FOR_JDK24_AND_LATER);\n\t\tif (jre == JRE.JAVA_26) {\n\t\t\t// exclude \"leyden\" and \"valhalla\" builds\n\t\t\tif (Runtime.version().build().orElse(0) >= 25) {\n\t\t\t\tlist.addAll(FOR_JDK26_AND_LATER);\n\t\t\t}\n\t\t}\n\t\tif (jre.compareTo(JRE.JAVA_27) >= 0) {\n\t\t\tlist.addAll(FOR_JDK26_AND_LATER);\n\t\t}\n\t\treturn list.isEmpty() ? Map.of() : Map.of(\"MAVEN_OPTS\", String.join(\" \", list));\n\t}\n\n\tprivate MavenEnvVars() {\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenPomFileTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\n\nimport java.nio.file.Files;\nimport java.util.List;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\n\nimport platform.tooling.support.MavenRepo;\n\n/**\n * @since 1.4\n */\n@Order(Integer.MAX_VALUE)\nclass MavenPomFileTests {\n\n\t@Test\n\tvoid jupiterAggregatorPomDependencies() throws Exception {\n\n\t\tvar expected = List.of(\">> HEAD >>\", //\n\t\t\t\"  <dependencyManagement>\", //\n\t\t\t\"    <dependencies>\", //\n\t\t\t\"      <dependency>\", //\n\t\t\t\"        <groupId>org.junit</groupId>\", //\n\t\t\t\"        <artifactId>junit-bom</artifactId>\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"        <type>pom</type>\", //\n\t\t\t\"        <scope>import</scope>\", //\n\t\t\t\"      </dependency>\", //\n\t\t\t\"    </dependencies>\", //\n\t\t\t\"  </dependencyManagement>\", //\n\t\t\t\"  <dependencies>\", //\n\t\t\t\"    <dependency>\", //\n\t\t\t\"      <groupId>org.junit.jupiter</groupId>\", //\n\t\t\t\"      <artifactId>junit-jupiter-api</artifactId>\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"      <scope>compile</scope>\", //\n\t\t\t\"    </dependency>\", //\n\t\t\t\"    <dependency>\", //\n\t\t\t\"      <groupId>org.junit.jupiter</groupId>\", //\n\t\t\t\"      <artifactId>junit-jupiter-params</artifactId>\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"      <scope>compile</scope>\", //\n\t\t\t\"    </dependency>\", //\n\t\t\t\"    <dependency>\", //\n\t\t\t\"      <groupId>org.junit.jupiter</groupId>\", //\n\t\t\t\"      <artifactId>junit-jupiter-engine</artifactId>\", //\n\t\t\t\">> VERSION >>\", //\n\t\t\t\"      <scope>runtime</scope>\", //\n\t\t\t\"    </dependency>\", //\n\t\t\t\"  </dependencies>\", //\n\t\t\t\">> TAIL >>\");\n\n\t\tassertLinesMatch(expected, Files.readAllLines(MavenRepo.pom(\"junit-jupiter\")));\n\t}\n\n\t@Test\n\tvoid jupiterAggregatorGradleMetadataMarker() throws Exception {\n\n\t\tvar expected = List.of(\">> HEAD >>\", //\n\t\t\t\"  <!-- do_not_remove: published-with-gradle-metadata -->\", //\n\t\t\t\">> TAIL >>\");\n\n\t\tassertLinesMatch(expected, Files.readAllLines(MavenRepo.pom(\"junit-jupiter\")));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenRepoProxy.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.URI;\nimport java.util.List;\n\nimport com.sun.net.httpserver.HttpServer;\n\npublic class MavenRepoProxy implements AutoCloseable {\n\n\t// Forbid downloading JUnit artifacts since we want to use the local ones\n\tprivate static final List<String> FORBIDDEN_PATHS = List.of(\"/org/junit\");\n\n\tprivate final HttpServer httpServer;\n\n\t@SuppressWarnings(\"unused\")\n\tpublic MavenRepoProxy() throws IOException {\n\t\tthis(0);\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\tpublic MavenRepoProxy(int port) throws IOException {\n\t\tthis(\"https://central.sonatype.com/repository/maven-snapshots\", port);\n\t}\n\n\tprivate MavenRepoProxy(String proxiedUrl, int port) throws IOException {\n\t\thttpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), port), 0);\n\t\thttpServer.createContext(\"/\", exchange -> {\n\t\t\ttry (exchange) {\n\t\t\t\tswitch (exchange.getRequestMethod()) {\n\t\t\t\t\tcase \"HEAD\", \"GET\" -> {\n\t\t\t\t\t\tif (FORBIDDEN_PATHS.stream().anyMatch(exchange.getRequestURI().getPath()::startsWith)) {\n\t\t\t\t\t\t\texchange.sendResponseHeaders(404, -1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tvar redirectUrl = proxiedUrl + exchange.getRequestURI().getPath();\n\t\t\t\t\t\texchange.getResponseHeaders().add(\"Location\", redirectUrl);\n\t\t\t\t\t\texchange.sendResponseHeaders(302, -1);\n\t\t\t\t\t}\n\t\t\t\t\tdefault -> exchange.sendResponseHeaders(405, -1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t});\n\t\thttpServer.start();\n\t}\n\n\tURI getBaseUri() {\n\t\tvar address = httpServer.getAddress();\n\t\treturn URI.create(\"http://\" + address.getAddress().getHostName() + \":\" + address.getPort());\n\t}\n\n\t@Override\n\tpublic void close() {\n\t\thttpServer.stop(0);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenStarterTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\nimport static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport;\n\nimport java.nio.file.Path;\n\nimport de.skuzzle.test.snapshots.Snapshot;\nimport de.skuzzle.test.snapshots.junit5.EnableSnapshotTests;\n\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\n@EnableSnapshotTests\n//@SnapshotTestOptions(alwaysPersistActualResult = true)\nclass MavenStarterTests {\n\n\t@ManagedResource\n\tLocalMavenRepo localMavenRepo;\n\n\t@ManagedResource\n\tMavenRepoProxy mavenRepoProxy;\n\n\t@TempDir\n\tPath workspace;\n\n\t@BeforeEach\n\tvoid prepareWorkspace() throws Exception {\n\t\tcopyToWorkspace(Projects.JUPITER_STARTER, workspace);\n\t}\n\n\t@Test\n\tvoid verifyJupiterStarterProject(@FilePrefix(\"maven\") OutputFiles outputFiles, Snapshot snapshot) throws Exception {\n\n\t\tvar result = runMaven(outputFiles, \"verify\");\n\n\t\tassertThat(result.stdOutLines()).contains(\"[INFO] Tests run: 13, Failures: 0, Errors: 0, Skipped: 0\");\n\t\tassertThat(result.stdOut()).contains(\"Using Java version: 17\");\n\n\t\tvar testResultsDir = workspace.resolve(\"target/surefire-reports\");\n\t\tverifyContainsExpectedStartedOpenTestReport(testResultsDir, snapshot);\n\t}\n\n\t@Test\n\tvoid runOnlyOneMethodInClassTemplate(@FilePrefix(\"maven\") OutputFiles outputFiles) throws Exception {\n\n\t\tvar result = runMaven(outputFiles, \"test\", \"-Dtest=CalculatorParameterizedClassTests$Inner#regularTest\");\n\n\t\tassertThat(result.stdOutLines()) //\n\t\t\t\t.doesNotContain(\"CalculatorTests\") //\n\t\t\t\t.contains(\"[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0\");\n\n\t\tresult = runMaven(outputFiles, \"test\", \"-Dtest=CalculatorParameterizedClassTests#parameterizedTest\");\n\n\t\tassertThat(result.stdOutLines()) //\n\t\t\t\t.doesNotContain(\"CalculatorTests\") //\n\t\t\t\t.contains(\"[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0\");\n\t}\n\n\tprivate ProcessResult runMaven(OutputFiles outputFiles, String... extraArgs) throws InterruptedException {\n\t\tvar result = ProcessStarters.maven(Helper.getJavaHome(17).orElseThrow(TestAbortedException::new)) //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(localMavenRepo.toCliArgument(), \"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Dsnapshot.repo.url=\" + mavenRepoProxy.getBaseUri()) //\n\t\t\t\t.addArguments(\"--update-snapshots\", \"--batch-mode\") //\n\t\t\t\t.addArguments(extraArgs).redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertEquals(\"\", result.stdErr());\n\t\tassertThat(result.stdOutLines()).contains(\"[INFO] BUILD SUCCESS\");\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MavenSurefireCompatibilityTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.9.2\n */\nclass MavenSurefireCompatibilityTests {\n\n\tstatic final String MINIMUM_SUPPORTED_SUREFIRE_VERSION = \"3.0.0\";\n\n\t@ManagedResource\n\tLocalMavenRepo localMavenRepo;\n\n\t@Test\n\tvoid testMavenSurefireCompatibilityProject(@TempDir Path workspace, @FilePrefix(\"maven\") OutputFiles outputFiles)\n\t\t\tthrows Exception {\n\t\tvar result = ProcessStarters.maven(Helper.getJavaHome(17).orElseThrow(TestAbortedException::new)) //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.MAVEN_SUREFIRE_COMPATIBILITY, workspace)) //\n\t\t\t\t.addArguments(localMavenRepo.toCliArgument(), \"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Dsurefire.version=\" + MINIMUM_SUPPORTED_SUREFIRE_VERSION) //\n\t\t\t\t.addArguments(\"--update-snapshots\", \"--batch-mode\", \"test\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertEquals(\"\", result.stdErr());\n\n\t\tvar output = result.stdOutLines();\n\t\tassertTrue(output.contains(\"[INFO] BUILD SUCCESS\"));\n\t\tassertTrue(output.contains(\"[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0\"));\n\n\t\tvar targetDir = workspace.resolve(\"target\");\n\t\ttry (var stream = Files.list(targetDir)) {\n\t\t\tassertThat(stream.filter(file -> file.getFileName().toString().startsWith(\"junit-platform-unique-ids\"))) //\n\t\t\t\t\t.hasSize(1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/MemoryCleanupTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;\nimport static org.junit.platform.launcher.LauncherConstants.MEMORY_CLEANUP_ENABLED_PROPERTY_NAME;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\nimport java.time.Duration;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\n\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 6.1\n */\nclass MemoryCleanupTests {\n\n\t@TempDir\n\tPath workspace;\n\n\t@Test\n\t@DisabledOnOpenJ9\n\tvoid runsWithSmallHeapSize(@FilePrefix(\"javac\") OutputFiles javacOutputFiles,\n\t\t\t@FilePrefix(\"java\") OutputFiles javaOutputFiles) throws Exception {\n\n\t\tcopyToWorkspace(Projects.MEMORY_CLEANUP, workspace);\n\t\tcompile(javacOutputFiles);\n\n\t\tvar timeout = Duration.ofSeconds(OS.WINDOWS.isCurrentOs() ? 20 : 10);\n\t\tvar result = assertTimeoutPreemptively(timeout, () -> executeWithSmallHeapSize(javaOutputFiles));\n\n\t\tassertThat(result).isNotNull();\n\t\tassertThat(result.exitCode()).isOne();\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains(\"1000000 tests found\") //\n\t\t\t\t.contains(\" 999999 tests successful\") //\n\t\t\t\t.contains(\"      1 tests failed\");\n\t}\n\n\tvoid compile(OutputFiles javacOutputFiles) throws Exception {\n\t\tvar result = ProcessStarters.javaCommand(\"javac\") //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-Xlint:all\") //\n\t\t\t\t.addArguments(\"--release\", \"17\") //\n\t\t\t\t.addArguments(\"-proc:none\") //\n\t\t\t\t.addArguments(\"-d\", workspace.resolve(\"bin\").toString()) //\n\t\t\t\t.addArguments(\"--class-path\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/OneMillionTests.java\").toString()) //\n\t\t\t\t.redirectOutput(javacOutputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertThat(result.exitCode()).isZero();\n\t\tassertThat(result.stdOut()).isEmpty();\n\t\tassertThat(result.stdErr()).isEmpty();\n\t}\n\n\tprivate ProcessResult executeWithSmallHeapSize(OutputFiles outputFiles) throws Exception {\n\t\treturn ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-Xmx16m\") //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--scan-class-path\") //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--classpath\", \"bin\") //\n\t\t\t\t.addArguments(\"--details=summary\") //\n\t\t\t\t.addArguments(\"--config=%s=%s\".formatted(MEMORY_CLEANUP_ENABLED_PROPERTY_NAME, true)) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularCompilationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static java.util.Objects.requireNonNull;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Arrays;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.ProcessStarters;\nimport platform.tooling.support.ThirdPartyJars;\n\nclass ModularCompilationTests {\n\n\t@Test\n\tvoid compileAllJUnitModules(@TempDir Path workspace, @FilePrefix(\"javac\") OutputFiles javacOutputFiles)\n\t\t\tthrows Exception {\n\t\tvar lib = Files.createDirectories(workspace.resolve(\"lib\"));\n\t\tThirdPartyJars.copyAll(lib);\n\n\t\tvar moduleNames = Arrays.asList(System.getProperty(\"junit.modules\").split(\",\"));\n\n\t\tvar outputDir = workspace.resolve(\"classes\").toAbsolutePath();\n\t\tvar processStarter = ProcessStarters.javaCommand(\"javac\") //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-d\", outputDir.toString()) //\n\t\t\t\t.addArguments(\"-Xlint:all\", \"-Werror\") //\n\t\t\t\t.addArguments(\"-Xlint:-requires-automatic,-requires-transitive-automatic\") // JUnit 4\n\t\t\t\t// external modules\n\t\t\t\t.addArguments(\"--module-path\", lib.toAbsolutePath().toString());\n\n\t\t// source locations in module-specific form\n\t\tmoduleNames.forEach(\n\t\t\tmoduleName -> processStarter.addArguments(\"--module-source-path\", moduleSourcePath(moduleName)));\n\n\t\tvar result = processStarter\n\t\t\t\t// un-shadow\n\t\t\t\t.addArguments(\"--add-modules\", \"info.picocli\") //\n\t\t\t\t.addArguments(\"--add-reads\", \"org.junit.platform.console=info.picocli\") //\n\t\t\t\t.addArguments(\"--add-modules\", \"org.opentest4j.reporting.events\") //\n\t\t\t\t.addArguments(\"--add-reads\", \"org.junit.platform.reporting=org.opentest4j.reporting.events\") //\n\t\t\t\t.addArguments(\"--add-modules\", \"de.siegmar.fastcsv\") //\n\t\t\t\t.addArguments(\"--add-reads\", \"org.junit.jupiter.params=de.siegmar.fastcsv\")\n\t\t\t\t// modules to compile\n\t\t\t\t.addArguments(\"--module\", String.join(\",\", moduleNames)) //\n\t\t\t\t.redirectOutput(javacOutputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertThat(outputDir).isNotEmptyDirectory();\n\t}\n\n\tstatic String moduleSourcePath(String moduleName) {\n\t\treturn \"%s=%s\".formatted(moduleName,\n\t\t\trequireNonNull(System.getProperty(\"junit.moduleSourcePath.\" + moduleName)));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ModularUserGuideTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.Helper.loadModuleDirectoryNames;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.io.Writer;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.function.Consumer;\nimport java.util.function.Predicate;\nimport java.util.spi.ToolProvider;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.launcher.LauncherConstants;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\nimport platform.tooling.support.ThirdPartyJars;\n\n/**\n * @since 1.5\n */\n@DisabledOnOpenJ9\nclass ModularUserGuideTests {\n\n\tprivate static final String DOCUMENTATION_MODULE_DESCRIPTOR = \"\"\"\n\t\t\t@SuppressWarnings(\"removal\")\n\t\t\topen module documentation {\n\t\t\t  exports example.testkit; // just here to ensure documentation example sources are compiled\n\n\t\t\t  requires org.junit.jupiter.api;\n\t\t\t  requires org.junit.jupiter.migrationsupport;\n\t\t\t  requires org.junit.jupiter.params;\n\n\t\t\t  requires org.junit.platform.engine;\n\t\t\t  requires org.junit.platform.reporting;\n\t\t\t  requires org.junit.platform.suite;\n\t\t\t  requires org.junit.platform.testkit;\n\n\t\t\t  // Byte Buddy is used by AssertJ's soft assertions which are used by the Engine Test Kit\n\t\t\t  requires net.bytebuddy;\n\n\t\t\t  requires java.desktop;\n\t\t\t  requires java.logging;\n\t\t\t  requires java.scripting;\n\t\t\t  requires jdk.httpserver;\n\n\t\t\t  provides org.junit.platform.launcher.LauncherSessionListener\n\t\t\t    with example.session.GlobalSetupTeardownListener;\n\t\t\t}\n\t\t\t\"\"\";\n\n\tprivate static List<String> compile(Path temp, Writer out, Writer err) throws Exception {\n\t\tvar documentation = Files.createDirectories(temp.resolve(\"src/documentation\"));\n\t\tFiles.writeString(documentation.resolve(\"module-info.java\"), DOCUMENTATION_MODULE_DESCRIPTOR);\n\n\t\tvar args = new ArrayList<String>();\n\t\targs.add(\"-Xlint\"); // enable all default warnings\n\t\targs.add(\"-proc:none\"); // disable annotation processing\n\t\targs.add(\"-cp\");\n\t\targs.add(\"\"); // set empty class path, otherwise system property \"java.class.path\" is read\n\n\t\targs.add(\"-d\");\n\t\targs.add(temp.resolve(\"destination\").toString());\n\n\t\tvar lib = Files.createDirectories(temp.resolve(\"lib\"));\n\t\tThirdPartyJars.copyAll(lib);\n\t\tloadAllJUnitModules(lib);\n\t\targs.add(\"--module-path\");\n\t\targs.add(lib.toString());\n\n\t\targs.add(\"--patch-module\");\n\t\targs.add(\"documentation=\" + Path.of(\"../documentation/src/main/java\") + File.pathSeparator\n\t\t\t\t+ Path.of(\"../documentation/src/test/java\"));\n\n\t\targs.add(\"--module-source-path\");\n\t\targs.add(temp.resolve(\"src\").toString());\n\n\t\targs.add(documentation.resolve(\"module-info.java\").toString());\n\t\ttry (var walk = Files.walk(Path.of(\"../documentation/src/test/java\"))) {\n\t\t\twalk.map(Path::toString) //\n\t\t\t\t\t.filter(s -> s.endsWith(\".java\")) //\n\t\t\t\t\t// TypeError: systemProperty.get is not a function ?!?!\n\t\t\t\t\t.filter(s -> !s.endsWith(\"ConditionalTestExecutionDemo.java\")) //\n\t\t\t\t\t// Don't include command-line tools that \"require io.github.classgraph\"\n\t\t\t\t\t.filter(s -> !s.contains(\"tools\")).forEach(args::add);\n\t\t}\n\n\t\tvar javac = ToolProvider.findFirst(\"javac\").orElseThrow();\n\t\tvar code = javac.run(new PrintWriter(out), new PrintWriter(err), args.toArray(String[]::new));\n\n\t\tassertEquals(0, code, err.toString());\n\t\tassertTrue(out.toString().isBlank(), out.toString());\n\t\treturn args;\n\t}\n\n\tprivate static void junit(Path temp, OutputFiles outputFiles) throws Exception {\n\t\tvar projectDir = Path.of(\"../documentation\").toAbsolutePath();\n\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(projectDir) //\n\t\t\t\t.addArguments(\"-XX:StartFlightRecording:filename=\" + temp.resolve(\"user-guide.jfr\")) //\n\t\t\t\t.addArguments(\"--show-version\", \"--show-module-resolution\") //\n\t\t\t\t.addArguments(\"--module-path\", String.join(File.pathSeparator, //\n\t\t\t\t\ttemp.resolve(\"destination\").toString(), //\n\t\t\t\t\ttemp.resolve(\"lib\").toString() //\n\t\t\t\t)) //\n\t\t\t\t.addArguments(\"--add-modules\", \"documentation\") //\n\t\t\t\t.addArguments(\"--patch-module\", \"documentation=\" + projectDir.resolve(\"src/test/resources\")) //\n\t\t\t\t.addArguments(\"--add-modules\", \"com.google.common\") //\n\t\t\t\t.addArguments(\"--module\", \"org.junit.platform.console\") //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--scan-modules\") //\n\t\t\t\t.addArguments(\"--config\", \"enableHttpServer=true\") //\n\t\t\t\t.addArguments(\"--config\", LauncherConstants.OUTPUT_DIR_PROPERTY_NAME + \"=\" + temp.resolve(\"reports\")) //\n\t\t\t\t.addArguments(\"--fail-if-no-tests\") //\n\t\t\t\t.addArguments(\"--include-classname\", \".*Tests\") //\n\t\t\t\t.addArguments(\"--include-classname\", \".*Demo\") //\n\t\t\t\t.addArguments(\"--exclude-tag\", \"exclude\") //\n\t\t\t\t.addArguments(\"--exclude-tag\", \"exclude\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t}\n\n\t@Test\n\tvoid runTestsFromUserGuideWithinModularBoundaries(@TempDir Path temp,\n\t\t\t@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar out = new StringWriter();\n\t\tvar err = new StringWriter();\n\n\t\tvar args = compile(temp, out, err);\n\n\t\tassertTrue(err.toString().isBlank(), () -> err + \"\\n\\n\" + String.join(\"\\n\", args));\n\t\tvar listing = treeWalk(temp);\n\t\tassertLinesMatch(List.of( //\n\t\t\t\"destination\", //\n\t\t\t\">> CLASSES AND JARS >>\", //\n\t\t\t\"src\", //\n\t\t\t\"src/documentation\", //\n\t\t\t\"src/documentation/module-info.java\" //\n\t\t), listing);\n\n\t\tjunit(temp, outputFiles);\n\t}\n\n\tprivate static void loadAllJUnitModules(Path target) throws Exception {\n\t\tfor (var module : loadModuleDirectoryNames()) {\n\t\t\tvar jar = MavenRepo.jar(module);\n\t\t\tFiles.copy(jar, target.resolve(jar.getFileName()));\n\t\t}\n\t}\n\n\tprivate static List<String> treeWalk(Path root) {\n\t\tvar lines = new ArrayList<String>();\n\t\ttreeWalk(root, lines::add);\n\t\treturn lines;\n\t}\n\n\tprivate static void treeWalk(Path root, Consumer<String> out) {\n\t\ttry (var stream = Files.walk(root)) {\n\t\t\tstream.map(root::relativize) //\n\t\t\t\t\t.map(path -> path.toString().replace('\\\\', '/')) //\n\t\t\t\t\t.sorted().filter(Predicate.not(String::isEmpty)) //\n\t\t\t\t\t.forEach(out);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new Error(\"Walking tree failed: \" + root, e);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/OutputAttachingExtension.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport java.io.IOException;\nimport java.io.UncheckedIOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\nimport java.util.Comparator;\n\nimport org.junit.jupiter.api.MediaType;\nimport org.junit.jupiter.api.extension.AfterTestExecutionCallback;\nimport org.junit.jupiter.api.extension.ExtensionContext;\nimport org.junit.jupiter.api.extension.ExtensionContext.Namespace;\nimport org.junit.jupiter.api.extension.ParameterContext;\nimport org.junit.jupiter.api.extension.ParameterResolutionException;\nimport org.junit.jupiter.api.extension.ParameterResolver;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessStarter;\n\nclass OutputAttachingExtension implements ParameterResolver, AfterTestExecutionCallback {\n\n\tprivate static final Namespace NAMESPACE = Namespace.create(OutputAttachingExtension.class);\n\n\tprivate static final MediaType MEDIA_TYPE = MediaType.create(\"text\", \"plain\", ProcessStarter.OUTPUT_ENCODING);\n\n\t@Override\n\tpublic boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\treturn parameterContext.isAnnotated(FilePrefix.class);\n\t}\n\n\t@Override\n\tpublic Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {\n\t\tvar outputDir = extensionContext.getStore(NAMESPACE).computeIfAbsent(\"outputDir\", __ -> {\n\t\t\ttry {\n\t\t\t\treturn new OutputDir(Files.createTempDirectory(\"output\"));\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tthrow new ParameterResolutionException(\"Failed to create temp directory\", e);\n\t\t\t}\n\t\t}, OutputDir.class);\n\t\tvar prefix = parameterContext.findAnnotation(FilePrefix.class) //\n\t\t\t\t.map(FilePrefix::value) //\n\t\t\t\t.orElseThrow();\n\t\treturn outputDir.toOutputFiles(prefix);\n\t}\n\n\t@Override\n\tpublic void afterTestExecution(ExtensionContext context) throws Exception {\n\t\tvar outputDir = context.getStore(NAMESPACE).get(\"outputDir\", OutputDir.class);\n\t\tif (outputDir != null) {\n\t\t\ttry (var stream = Files.list(outputDir.root()).filter(Files::isRegularFile).sorted()) {\n\t\t\t\tstream.filter(OutputAttachingExtension::notEmpty).forEach(file -> {\n\t\t\t\t\tvar fileName = file.getFileName().toString();\n\t\t\t\t\tcontext.publishFile(fileName, MEDIA_TYPE,\n\t\t\t\t\t\ttarget -> Files.copy(file, target, StandardCopyOption.REPLACE_EXISTING));\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate static boolean notEmpty(Path file) {\n\t\ttry {\n\t\t\treturn Files.size(file) > 0;\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t}\n\n\t@SuppressWarnings(\"try\")\n\trecord OutputDir(Path root) implements AutoCloseable {\n\n\t\t@Override\n\t\tpublic void close() throws Exception {\n\t\t\ttry (var stream = Files.walk(root).sorted(Comparator.<Path> naturalOrder().reversed())) {\n\t\t\t\tstream.forEach(path -> {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tFiles.delete(path);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (IOException e) {\n\t\t\t\t\t\tthrow new UncheckedIOException(\"Failed to delete \" + path, e);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tprivate OutputFiles toOutputFiles(String prefix) {\n\t\t\treturn new OutputFiles(root.resolve(prefix + \"-stdout.txt\"), root.resolve(prefix + \"-stderr.txt\"));\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/Projects.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport java.io.IOException;\nimport java.nio.file.Path;\n\nimport org.apache.commons.io.file.PathUtils;\n\npublic class Projects {\n\n\tpublic static final String GRAALVM_STARTER = \"graalvm-starter\";\n\tpublic static final String GRADLE_KOTLIN_EXTENSIONS = \"gradle-kotlin-extensions\";\n\tpublic static final String GRADLE_MISSING_ENGINE = \"gradle-missing-engine\";\n\tpublic static final String JAR_DESCRIBE_MODULE = \"jar-describe-module\";\n\tpublic static final String JUNIT_START = \"junit-start\";\n\tpublic static final String JUPITER_STARTER = \"jupiter-starter\";\n\tpublic static final String KOTLIN_COROUTINES = \"kotlin-coroutines\";\n\tpublic static final String MAVEN_SUREFIRE_COMPATIBILITY = \"maven-surefire-compatibility\";\n\tpublic static final String MEMORY_CLEANUP = \"memory-cleanup\";\n\tpublic static final String REFLECTION_TESTS = \"reflection-tests\";\n\tpublic static final String STANDALONE = \"standalone\";\n\tpublic static final String VINTAGE = \"vintage\";\n\n\tprivate Projects() {\n\t}\n\n\tstatic Path copyToWorkspace(String project, Path workspace) throws IOException {\n\t\tPathUtils.copyDirectory(getSourceDirectory(project), workspace);\n\t\treturn workspace;\n\t}\n\n\tstatic Path getSourceDirectory(String project) {\n\t\treturn Path.of(\"projects\").resolve(project);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ReflectionCompatibilityTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.11\n */\nclass ReflectionCompatibilityTests {\n\n\t@Test\n\tvoid gradle_wrapper(@TempDir Path workspace, @FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.REFLECTION_TESTS, workspace)) //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"build\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.putEnvironment(\"JDK17\", Helper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().lines().anyMatch(line -> line.contains(\"BUILD SUCCESSFUL\")));\n\t\tassertThat(result.stdOut()).contains(\"Using Java version: 17\");\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/StandaloneTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static java.util.stream.Collectors.joining;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\nimport static platform.tooling.support.tests.Projects.getSourceDirectory;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.MethodOrderer;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestMethodOrder;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\nimport platform.tooling.support.ThirdPartyJars;\n\n/**\n * @since 1.4\n */\n@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n@Execution(CONCURRENT)\nclass StandaloneTests {\n\n\t@TempDir\n\tstatic Path workspace;\n\n\t@BeforeAll\n\tstatic void prepareWorkspace() throws IOException {\n\t\tcopyToWorkspace(Projects.STANDALONE, workspace);\n\t}\n\n\t@Test\n\tvoid jarFileWithoutCompiledModuleDescriptorClass() throws Exception {\n\t\tvar jar = MavenRepo.jar(\"junit-platform-console-standalone\");\n\t\tvar name = \"module-info.class\";\n\t\tvar found = new ArrayList<Path>();\n\t\ttry (var fileSystem = FileSystems.newFileSystem(jar)) {\n\t\t\tfor (var rootDirectory : fileSystem.getRootDirectories()) {\n\t\t\t\ttry (var stream = Files.walk(rootDirectory)) {\n\t\t\t\t\tstream.filter(path -> path.getNameCount() > 0) // skip root entry\n\t\t\t\t\t\t\t.filter(path -> path.getFileName().toString().equals(name)).forEach(found::add);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tassertTrue(found.isEmpty(), jar + \" must not contain any \" + name + \" files: \" + found);\n\t}\n\n\t@Test\n\tvoid listAllObservableEngines(@FilePrefix(\"java\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(getSourceDirectory(Projects.STANDALONE)) //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"engines\", \"--disable-ansi-colors\", \"--disable-banner\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\n\t\tassertLinesMatch(\"\"\"\n\t\t\t\tjunit-jupiter (org.junit.jupiter:junit-jupiter-engine:%1$s)\n\t\t\t\tjunit-platform-suite (org.junit.platform:junit-platform-suite-engine:%1$s)\n\t\t\t\tjunit-vintage (org.junit.vintage:junit-vintage-engine:%1$s)\n\t\t\t\t\"\"\".formatted(Helper.version()).lines(), //\n\t\t\tresult.stdOut().lines());\n\t}\n\n\t@Test\n\tvoid printVersionViaJar(@FilePrefix(\"java\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(getSourceDirectory(Projects.STANDALONE)) //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"--version\", \"--disable-ansi-colors\") //\n\t\t\t\t.putEnvironment(\"CLICOLOR_FORCE\", \"1\") // enable ANSI colors by default (see https://picocli.info/#_heuristics_for_enabling_ansi)\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\n\t\tvar version = Helper.version();\n\t\tassertLinesMatch(\"\"\"\n\t\t\t\tJUnit Platform Console Launcher %s\n\t\t\t\tJVM: .*\n\t\t\t\tOS: .*\n\t\t\t\t\"\"\".formatted(version).lines(), //\n\t\t\tresult.stdOut().lines());\n\t}\n\n\t@Test\n\tvoid printVersionViaModule(@FilePrefix(\"java\") OutputFiles outputFiles) throws Exception {\n\t\tvar junitJars = Stream.of(\"junit-platform-console\", \"junit-platform-reporting\", \"junit-platform-engine\",\n\t\t\t\"junit-platform-launcher\", \"junit-platform-commons\") //\n\t\t\t\t.map(MavenRepo::jar);\n\t\tvar thirdPartyJars = Stream.of( //\n\t\t\tThirdPartyJars.find(\"org.opentest4j\", \"opentest4j\"), //\n\t\t\tThirdPartyJars.find(\"org.opentest4j.reporting\", \"open-test-reporting-tooling-spi\") //\n\t\t);\n\t\tvar modulePath = Stream.concat(junitJars, thirdPartyJars) //\n\t\t\t\t.map(String::valueOf) //\n\t\t\t\t.collect(joining(File.pathSeparator));\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(getSourceDirectory(Projects.STANDALONE)) //\n\t\t\t\t.addArguments(\"--module-path\", modulePath) //\n\t\t\t\t.addArguments(\"--module\", \"org.junit.platform.console\") //\n\t\t\t\t.addArguments(\"--version\", \"--disable-ansi-colors\") //\n\t\t\t\t.putEnvironment(\"CLICOLOR_FORCE\", \"1\") // enable ANSI colors by default (see https://picocli.info/#_heuristics_for_enabling_ansi)\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\n\t\tvar version = Helper.version();\n\t\tassertLinesMatch(\"\"\"\n\t\t\t\tJUnit Platform Console Launcher %s\n\t\t\t\tJVM: .*\n\t\t\t\tOS: .*\n\t\t\t\t\"\"\".formatted(version).lines(), //\n\t\t\tresult.stdOut().lines());\n\t}\n\n\t@Test\n\t@Order(1)\n\t@Execution(SAME_THREAD)\n\tvoid compile(@FilePrefix(\"javac\") OutputFiles javacOutputFiles) throws Exception {\n\t\tvar result = ProcessStarters.javaCommand(\"javac\") //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-Xlint:-options\") //\n\t\t\t\t.addArguments(\"--release\", \"17\") //\n\t\t\t\t.addArguments(\"-proc:none\") //\n\t\t\t\t.addArguments(\"-d\", workspace.resolve(\"bin\").toString()) //\n\t\t\t\t.addArguments(\"--class-path\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/other/OtherwiseNotReferencedClass.java\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/standalone/JupiterIntegration.java\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/standalone/JupiterParamsIntegration.java\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/standalone/SuiteIntegration.java\").toString()) //\n\t\t\t\t.addArguments(workspace.resolve(\"src/standalone/VintageIntegration.java\").toString()) //\n\t\t\t\t.redirectOutput(javacOutputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\tassertTrue(result.stdOut().isEmpty());\n\t\tassertTrue(result.stdErr().isEmpty());\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverTree(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details-theme=ascii\");\n\n\t\tvar expected = \"\"\"\n\t\t\t\t.\n\t\t\t\t+-- JUnit Platform Suite\n\t\t\t\t| '-- SuiteIntegration\n\t\t\t\t|   '-- JUnit Jupiter\n\t\t\t\t|     '-- SuiteIntegration$SingleTestContainer\n\t\t\t\t|       '-- successful()\n\t\t\t\t+-- JUnit Jupiter\n\t\t\t\t| +-- JupiterIntegration\n\t\t\t\t| | +-- successful()\n\t\t\t\t| | +-- fail()\n\t\t\t\t| | +-- abort()\n\t\t\t\t| | '-- disabled()\n\t\t\t\t| +-- SuiteIntegration$SingleTestContainer\n\t\t\t\t| | '-- successful()\n\t\t\t\t| '-- JupiterParamsIntegration\n\t\t\t\t|   '-- parameterizedTest(String)\n\t\t\t\t'-- JUnit Vintage\n\t\t\t\t  '-- VintageIntegration\n\t\t\t\t    +-- f4il\n\t\t\t\t    +-- ignored\n\t\t\t\t    '-- succ3ssful\n\n\t\t\t\t[        11 containers found ]\n\t\t\t\t[         9 tests found      ]\n\n\t\t\t\t\"\"\".stripIndent();\n\t\tassertLinesMatch(expected.lines(), result.stdOut().lines());\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverFlat(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details=flat\");\n\n\t\tvar expected = \"\"\"\n\t\t\t\tJUnit Platform Suite ([engine:junit-platform-suite])\n\t\t\t\tSuiteIntegration ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration])\n\t\t\t\tJUnit Jupiter ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter])\n\t\t\t\tSuiteIntegration$SingleTestContainer ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer])\n\t\t\t\tsuccessful() ([engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()])\n\t\t\t\tJUnit Jupiter ([engine:junit-jupiter])\n\t\t\t\tJupiterIntegration ([engine:junit-jupiter]/[class:standalone.JupiterIntegration])\n\t\t\t\tsuccessful() ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:successful()])\n\t\t\t\tfail() ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:fail()])\n\t\t\t\tabort() ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:abort()])\n\t\t\t\tdisabled() ([engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:disabled()])\n\t\t\t\tSuiteIntegration$SingleTestContainer ([engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer])\n\t\t\t\tsuccessful() ([engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()])\n\t\t\t\tJupiterParamsIntegration ([engine:junit-jupiter]/[class:standalone.JupiterParamsIntegration])\n\t\t\t\tparameterizedTest(String) ([engine:junit-jupiter]/[class:standalone.JupiterParamsIntegration]/[test-template:parameterizedTest(java.lang.String)])\n\t\t\t\tJUnit Vintage ([engine:junit-vintage])\n\t\t\t\tVintageIntegration ([engine:junit-vintage]/[runner:standalone.VintageIntegration])\n\t\t\t\tf4il ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:f4il(standalone.VintageIntegration)])\n\t\t\t\tignored ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:ignored(standalone.VintageIntegration)])\n\t\t\t\tsucc3ssful ([engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:succ3ssful(standalone.VintageIntegration)])\n\n\t\t\t\t[        11 containers found ]\n\t\t\t\t[         9 tests found      ]\n\n\t\t\t\t\"\"\".stripIndent();\n\t\tassertLinesMatch(expected.lines(), result.stdOut().lines());\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverVerbose(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details=verbose\", \"--details-theme=ascii\");\n\n\t\tvar expected = \"\"\"\n\t\t\t\t+-- JUnit Platform Suite\n\t\t\t\t| +-- SuiteIntegration\n\t\t\t\t| | +-- JUnit Jupiter\n\t\t\t\t| | | +-- SuiteIntegration$SingleTestContainer\n\t\t\t\t| | | | +-- successful()\n\t\t\t\t| | | | |      tags: []\n\t\t\t\t| | | | |  uniqueId: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()]\n\t\t\t\t| | | | |    parent: [engine:junit-platform-suite]/[suite:standalone.SuiteIntegration]/[engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]\n\t\t\t\t| | | | |    source: MethodSource [className = 'standalone.SuiteIntegration$SingleTestContainer', methodName = 'successful', methodParameterTypes = '']\n\t\t\t\t| | | '-- SuiteIntegration$SingleTestContainer\n\t\t\t\t| | '-- JUnit Jupiter\n\t\t\t\t| '-- SuiteIntegration\n\t\t\t\t'-- JUnit Platform Suite\n\t\t\t\t+-- JUnit Jupiter\n\t\t\t\t| +-- JupiterIntegration\n\t\t\t\t| | +-- successful()\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:successful()]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.JupiterIntegration', methodName = 'successful', methodParameterTypes = '']\n\t\t\t\t| | +-- fail()\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:fail()]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.JupiterIntegration', methodName = 'fail', methodParameterTypes = '']\n\t\t\t\t| | +-- abort()\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:abort()]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.JupiterIntegration', methodName = 'abort', methodParameterTypes = '']\n\t\t\t\t| | +-- disabled()\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]/[method:disabled()]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.JupiterIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.JupiterIntegration', methodName = 'disabled', methodParameterTypes = '']\n\t\t\t\t| '-- JupiterIntegration\n\t\t\t\t| +-- SuiteIntegration$SingleTestContainer\n\t\t\t\t| | +-- successful()\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]/[method:successful()]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.SuiteIntegration$SingleTestContainer]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.SuiteIntegration$SingleTestContainer', methodName = 'successful', methodParameterTypes = '']\n\t\t\t\t| '-- SuiteIntegration$SingleTestContainer\n\t\t\t\t| +-- JupiterParamsIntegration\n\t\t\t\t| | +-- parameterizedTest(String)\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-jupiter]/[class:standalone.JupiterParamsIntegration]/[test-template:parameterizedTest(java.lang.String)]\n\t\t\t\t| | |    parent: [engine:junit-jupiter]/[class:standalone.JupiterParamsIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.JupiterParamsIntegration', methodName = 'parameterizedTest', methodParameterTypes = 'java.lang.String']\n\t\t\t\t| '-- JupiterParamsIntegration\n\t\t\t\t'-- JUnit Jupiter\n\t\t\t\t+-- JUnit Vintage\n\t\t\t\t| +-- VintageIntegration\n\t\t\t\t| | +-- f4il\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:f4il(standalone.VintageIntegration)]\n\t\t\t\t| | |    parent: [engine:junit-vintage]/[runner:standalone.VintageIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.VintageIntegration', methodName = 'f4il', methodParameterTypes = '']\n\t\t\t\t| | +-- ignored\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:ignored(standalone.VintageIntegration)]\n\t\t\t\t| | |    parent: [engine:junit-vintage]/[runner:standalone.VintageIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.VintageIntegration', methodName = 'ignored', methodParameterTypes = '']\n\t\t\t\t| | +-- succ3ssful\n\t\t\t\t| | |      tags: []\n\t\t\t\t| | |  uniqueId: [engine:junit-vintage]/[runner:standalone.VintageIntegration]/[test:succ3ssful(standalone.VintageIntegration)]\n\t\t\t\t| | |    parent: [engine:junit-vintage]/[runner:standalone.VintageIntegration]\n\t\t\t\t| | |    source: MethodSource [className = 'standalone.VintageIntegration', methodName = 'succ3ssful', methodParameterTypes = '']\n\t\t\t\t| '-- VintageIntegration\n\t\t\t\t'-- JUnit Vintage\n\n\t\t\t\t[        11 containers found ]\n\t\t\t\t[         9 tests found      ]\n\n\t\t\t\t\"\"\".stripIndent();\n\t\tassertLinesMatch(expected.lines(), result.stdOut().lines());\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverNone(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details=none\");\n\n\t\tassertThat(result.stdOut()).isEmpty();\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverSummary(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details=summary\");\n\n\t\tvar expected = \"\"\"\n\n\t\t\t\t[        11 containers found ]\n\t\t\t\t[         9 tests found      ]\n\n\t\t\t\t\"\"\".stripIndent();\n\t\tassertLinesMatch(expected.lines(), result.stdOut().lines());\n\t}\n\n\t@Test\n\t@Order(2)\n\t@Execution(SAME_THREAD)\n\tvoid discoverTestFeed(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = discover(outputFiles, \"--details=testfeed\");\n\t\tvar expected = \"\"\"\n\t\t\t\tJUnit Platform Suite > SuiteIntegration > JUnit Jupiter > SuiteIntegration$SingleTestContainer > successful()\n\t\t\t\tJUnit Jupiter > JupiterIntegration > successful()\n\t\t\t\tJUnit Jupiter > JupiterIntegration > fail()\n\t\t\t\tJUnit Jupiter > JupiterIntegration > abort()\n\t\t\t\tJUnit Jupiter > JupiterIntegration > disabled()\n\t\t\t\tJUnit Jupiter > SuiteIntegration$SingleTestContainer > successful()\n\t\t\t\tJUnit Vintage > VintageIntegration > f4il\n\t\t\t\tJUnit Vintage > VintageIntegration > ignored\n\t\t\t\tJUnit Vintage > VintageIntegration > succ3ssful\n\n\t\t\t\t[        11 containers found ]\n\t\t\t\t[         9 tests found      ]\n\n\t\t\t\t\"\"\".stripIndent();\n\n\t\tassertLinesMatch(expected.lines(), result.stdOut().lines());\n\t}\n\n\tprivate static ProcessResult discover(OutputFiles outputFiles, String... args) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.putEnvironment(\"NO_COLOR\", \"1\") // --disable-ansi-colors\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"discover\") //\n\t\t\t\t.addArguments(\"--scan-class-path\") //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--include-classname\", \"standalone.*\") //\n\t\t\t\t.addArguments(\"--classpath\", \"bin\") //\n\t\t\t\t.addArguments(args) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, result.exitCode());\n\t\treturn result;\n\t}\n\n\t@Test\n\t@Order(3)\n\t@Execution(SAME_THREAD)\n\tvoid execute(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.putEnvironment(\"NO_COLOR\", \"1\") // --disable-ansi-colors\n\t\t\t\t.addArguments(\"--show-version\") //\n\t\t\t\t.addArguments(\"-enableassertions\") //\n\t\t\t\t.addArguments(\"-Djava.util.logging.config.file=logging.properties\") //\n\t\t\t\t.addArguments(\"-Djunit.platform.launcher.interceptors.enabled=true\") //\n\t\t\t\t.addArguments(\"-Duser.language=en\", \"-Duser.country=US\") //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--scan-class-path\") //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--include-classname\", \"standalone.*\") //\n\t\t\t\t.addArguments(\"--classpath\", \"bin\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\n\t\tassertOutputOnCurrentJvm(result);\n\t}\n\n\t@Test\n\t@Order(4)\n\t@Execution(SAME_THREAD)\n\tvoid executeOnJava17(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar javaHome = Helper.getJavaHome(17).orElseThrow(TestAbortedException::new);\n\t\tvar result = ProcessStarters.java(javaHome) //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"-showversion\") //\n\t\t\t\t.addArguments(\"-enableassertions\") //\n\t\t\t\t.addArguments(\"-Djava.util.logging.config.file=logging.properties\") //\n\t\t\t\t.addArguments(\"-Djunit.platform.launcher.interceptors.enabled=true\") //\n\t\t\t\t.addArguments(\"-Duser.language=en\", \"-Duser.country=US\") //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--scan-class-path\") //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--include-classname\", \"standalone.*\") //\n\t\t\t\t.addArguments(\"--classpath\", \"bin\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\n\t\tvar expectedOutLines = Files.readAllLines(workspace.resolve(\"expected-out.txt\"));\n\t\tvar expectedErrLines = getExpectedErrLinesOnJava17(workspace);\n\t\tassertLinesMatch(expectedOutLines, result.stdOutLines());\n\t\tassertLinesMatch(expectedErrLines, result.stdErrLines());\n\n\t\tassertTrue(result.stdErr().contains(\"junit-jupiter\"\n\t\t\t\t+ \" (group ID: org.junit.jupiter, artifact ID: junit-jupiter-engine, version: \" + Helper.version()));\n\t\tassertTrue(result.stdErr().contains(\"junit-vintage\"\n\t\t\t\t+ \" (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: \" + Helper.version()));\n\t}\n\n\t@Test\n\t@Order(5)\n\t@Execution(SAME_THREAD)\n\t// https://github.com/junit-team/junit-framework/issues/2600\n\tvoid executeOnJava17SelectPackage(@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar javaHome = Helper.getJavaHome(17).orElseThrow(TestAbortedException::new);\n\t\tvar result = ProcessStarters.java(javaHome) //\n\t\t\t\t.workingDir(workspace).addArguments(\"-showversion\") //\n\t\t\t\t.addArguments(\"-enableassertions\") //\n\t\t\t\t.addArguments(\"-Djava.util.logging.config.file=logging.properties\") //\n\t\t\t\t.addArguments(\"-Djunit.platform.launcher.interceptors.enabled=true\") //\n\t\t\t\t.addArguments(\"-Duser.language=en\", \"-Duser.country=US\") //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--select-package\", Projects.STANDALONE) //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--include-classname\", \"standalone.*\") //\n\t\t\t\t.addArguments(\"--classpath\", \"bin\") //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\n\t\tvar expectedOutLines = Files.readAllLines(workspace.resolve(\"expected-out.txt\"));\n\t\tvar expectedErrLines = getExpectedErrLinesOnJava17(workspace);\n\t\tassertLinesMatch(expectedOutLines, result.stdOutLines());\n\t\tassertLinesMatch(expectedErrLines, result.stdErrLines());\n\n\t\tassertTrue(result.stdErr().contains(\"junit-jupiter\"\n\t\t\t\t+ \" (group ID: org.junit.jupiter, artifact ID: junit-jupiter-engine, version: \" + Helper.version()));\n\t\tassertTrue(result.stdErr().contains(\"junit-vintage\"\n\t\t\t\t+ \" (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: \" + Helper.version()));\n\t}\n\n\tprivate static List<String> getExpectedErrLinesOnJava17(Path workspace) throws IOException {\n\t\tvar expectedErrLines = new ArrayList<String>();\n\t\texpectedErrLines.add(\">> JAVA VERSION >>\");\n\t\texpectedErrLines.addAll(Files.readAllLines(workspace.resolve(\"expected-err.txt\")));\n\t\treturn expectedErrLines;\n\t}\n\n\t@Test\n\t@Order(6)\n\t@Execution(SAME_THREAD)\n\tvoid executeWithJarredTestClasses(@FilePrefix(\"jar\") OutputFiles jarOutputFiles,\n\t\t\t@FilePrefix(\"console-launcher\") OutputFiles outputFiles) throws Exception {\n\t\tvar jar = workspace.resolve(\"tests.jar\");\n\t\tvar jarResult = ProcessStarters.javaCommand(\"jar\") //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.addArguments(\"--create\") //\n\t\t\t\t.addArguments(\"--file\", jar.toAbsolutePath().toString()) //\n\t\t\t\t.addArguments(\"-C\", workspace.resolve(\"bin\").toString(), \".\") //\n\t\t\t\t.redirectOutput(jarOutputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(0, jarResult.exitCode());\n\n\t\tvar result = ProcessStarters.java() //\n\t\t\t\t.workingDir(workspace) //\n\t\t\t\t.putEnvironment(\"NO_COLOR\", \"1\") // --disable-ansi-colors\n\t\t\t\t.addArguments(\"--show-version\") //\n\t\t\t\t.addArguments(\"-enableassertions\") //\n\t\t\t\t.addArguments(\"-Djava.util.logging.config.file=logging.properties\") //\n\t\t\t\t.addArguments(\"-Djunit.platform.launcher.interceptors.enabled=true\") //\n\t\t\t\t.addArguments(\"-Duser.language=en\", \"-Duser.country=US\") //\n\t\t\t\t.addArguments(\"-jar\", MavenRepo.jar(\"junit-platform-console-standalone\").toString()) //\n\t\t\t\t.addArguments(\"execute\") //\n\t\t\t\t.addArguments(\"--scan-class-path\") //\n\t\t\t\t.addArguments(\"--disable-banner\") //\n\t\t\t\t.addArguments(\"--include-classname\", \"standalone.*\") //\n\t\t\t\t.addArguments(\"--classpath\", jar.toAbsolutePath().toString()) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\n\t\tassertOutputOnCurrentJvm(result);\n\t}\n\n\tprivate static void assertOutputOnCurrentJvm(ProcessResult result) throws IOException {\n\t\tvar expectedOutLines = Files.readAllLines(workspace.resolve(\"expected-out.txt\"));\n\t\tvar expectedErrLines = Files.readAllLines(workspace.resolve(\"expected-err.txt\"));\n\t\tassertLinesMatch(expectedOutLines, result.stdOutLines());\n\t\tvar actualErrLines = result.stdErrLines();\n\t\tif (actualErrLines.getFirst().contains(\"stty: /dev/tty: No such device or address\")) {\n\t\t\t// Happens intermittently on GitHub Actions on Windows\n\t\t\tactualErrLines = new ArrayList<>(actualErrLines);\n\t\t\tactualErrLines.removeFirst();\n\t\t}\n\t\tassertLinesMatch(expectedErrLines, actualErrLines);\n\n\t\tassertTrue(result.stdErr().contains(\"junit-jupiter\"\n\t\t\t\t+ \" (group ID: org.junit.jupiter, artifact ID: junit-jupiter-engine, version: \" + Helper.version()));\n\t\tassertTrue(result.stdErr().contains(\"junit-vintage\"\n\t\t\t\t+ \" (group ID: org.junit.vintage, artifact ID: junit-vintage-engine, version: \" + Helper.version()));\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/ToolProviderTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.junit.jupiter.api.Assertions.assertAll;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertLinesMatch;\nimport static org.junit.jupiter.api.Assertions.assertTrue;\n\nimport java.io.IOException;\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.lang.module.ModuleDescriptor;\nimport java.lang.module.ModuleFinder;\nimport java.lang.module.ModuleReference;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.Set;\nimport java.util.spi.ToolProvider;\nimport java.util.stream.StreamSupport;\n\nimport org.junit.jupiter.api.AfterAll;\nimport org.junit.jupiter.api.BeforeAll;\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.condition.OS;\nimport org.junit.jupiter.api.extension.DisabledOnOpenJ9;\nimport org.junit.jupiter.api.io.TempDir;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ThirdPartyJars;\n\n/**\n * @since 1.6\n */\n@Order(Integer.MAX_VALUE)\nclass ToolProviderTests {\n\n\t@TempDir\n\tstatic Path lib;\n\n\t@BeforeAll\n\tstatic void prepareLocalLibraryDirectoryWithJUnitPlatformModules() {\n\t\ttry {\n\t\t\tFiles.createDirectories(lib);\n\t\t\ttry (var directoryStream = Files.newDirectoryStream(lib, \"*.jar\")) {\n\t\t\t\tfor (Path jarFile : directoryStream) {\n\t\t\t\t\tFiles.delete(jarFile);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (var module : Helper.loadModuleDirectoryNames()) {\n\t\t\t\tif (module.startsWith(\"junit-platform\")) {\n\t\t\t\t\tvar jar = MavenRepo.jar(module);\n\t\t\t\t\tFiles.copy(jar, lib.resolve(module + \".jar\"));\n\t\t\t\t}\n\t\t\t}\n\t\t\tThirdPartyJars.copy(lib, \"org.apiguardian\", \"apiguardian-api\");\n\t\t\tThirdPartyJars.copy(lib, \"org.opentest4j\", \"opentest4j\");\n\t\t\tThirdPartyJars.copy(lib, \"org.opentest4j.reporting\", \"open-test-reporting-tooling-spi\");\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"Preparing local library folder failed\", e);\n\t\t}\n\t}\n\n\t@AfterAll\n\tstatic void triggerReleaseOfFileHandlesOnWindows() throws Exception {\n\t\tif (OS.current() == OS.WINDOWS) {\n\t\t\tSystem.gc();\n\t\t\tThread.sleep(1_000);\n\t\t}\n\t}\n\n\t@Test\n\tvoid findAndRunJUnitOnTheClassPath() {\n\t\ttry (var loader = new URLClassLoader(\"junit\", urls(lib), ClassLoader.getPlatformClassLoader())) {\n\t\t\tvar sl = ServiceLoader.load(ToolProvider.class, loader);\n\t\t\tvar junit = StreamSupport.stream(sl.spliterator(), false).filter(p -> p.name().equals(\"junit\")).findFirst();\n\n\t\t\tassertTrue(junit.isPresent(), \"Tool 'junit' not found in: \" + lib);\n\t\t\tassertJUnitPrintsHelpMessage(junit.get());\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new AssertionError(\"Closing URLClassLoader failed: \" + e, e);\n\t\t}\n\t}\n\n\t@Test\n\t@DisabledOnOpenJ9\n\tvoid findAndRunJUnitOnTheModulePath() {\n\t\tvar finder = ModuleFinder.of(lib);\n\t\tvar modules = finder.findAll().stream() //\n\t\t\t\t.map(ModuleReference::descriptor) //\n\t\t\t\t.map(ModuleDescriptor::toNameAndVersion) //\n\t\t\t\t.sorted() //\n\t\t\t\t.toList();\n\t\t// modules.forEach(System.out::println);\n\n\t\tvar bootLayer = ModuleLayer.boot();\n\t\tvar configuration = bootLayer.configuration().resolveAndBind(finder, ModuleFinder.of(), Set.of());\n\t\tvar layer = bootLayer.defineModulesWithOneLoader(configuration, ClassLoader.getPlatformClassLoader());\n\n\t\tvar sl = ServiceLoader.load(layer, ToolProvider.class);\n\t\tvar junit = StreamSupport.stream(sl.spliterator(), false).filter(p -> p.name().equals(\"junit\")).findFirst();\n\n\t\tassertTrue(junit.isPresent(), \"Tool 'junit' not found in modules: \" + modules);\n\t\tassertJUnitPrintsHelpMessage(junit.get());\n\t}\n\n\tprivate static URL[] urls(Path directory) {\n\t\ttry (var stream = Files.newDirectoryStream(directory, \"*.jar\")) {\n\t\t\tvar paths = new ArrayList<URL>();\n\t\t\tstream.forEach(path -> paths.add(url(path)));\n\t\t\treturn paths.toArray(URL[]::new);\n\t\t}\n\t\tcatch (Exception e) {\n\t\t\tthrow new AssertionError(\"Creating URL[] failed: \" + e, e);\n\t\t}\n\t}\n\n\tprivate static URL url(Path path) {\n\t\ttry {\n\t\t\treturn path.toUri().toURL();\n\t\t}\n\t\tcatch (MalformedURLException e) {\n\t\t\tthrow new AssertionError(\"Converting path to URL failed: \" + e, e);\n\t\t}\n\t}\n\n\tprivate static void assertJUnitPrintsHelpMessage(ToolProvider junit) {\n\t\tvar out = new StringWriter();\n\t\tvar err = new StringWriter();\n\t\tvar code = junit.run(new PrintWriter(out), new PrintWriter(err), \"--help\");\n\t\tassertAll(() -> assertLinesMatch(List.of( //\n\t\t\t\">> USAGE >>\", //\n\t\t\t\"Launches the JUnit Platform for test discovery and execution.\", //\n\t\t\t\">> OPTIONS >>\"), //\n\t\t\tout.toString().lines().toList()), //\n\t\t\t() -> assertEquals(\"\", err.toString()), //\n\t\t\t() -> assertEquals(0, code, \"Expected exit of 0, but got: \" + code) //\n\t\t);\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/UnalignedClasspathTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD;\nimport static platform.tooling.support.ProcessStarters.currentJdkHome;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\nimport java.util.stream.Stream;\n\nimport org.junit.jupiter.api.condition.JRE;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.api.parallel.Execution;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.Arguments;\nimport org.junit.jupiter.params.provider.MethodSource;\nimport org.junit.platform.tests.process.OutputFiles;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n/**\n * @since 1.3\n */\nclass UnalignedClasspathTests {\n\n\t@ManagedResource\n\tLocalMavenRepo localMavenRepo;\n\n\t@ManagedResource\n\tMavenRepoProxy mavenRepoProxy;\n\n\t@ParameterizedTest(name = \"[{index}] {0}\")\n\t@MethodSource(\"javaVersions\")\n\t@Execution(SAME_THREAD)\n\tvoid verifyErrorMessageForUnalignedClasspath(JRE jre, Path javaHome, @TempDir Path workspace,\n\t\t\t@FilePrefix(\"maven\") OutputFiles outputFiles) throws Exception {\n\t\tvar starter = ProcessStarters.maven(javaHome) //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.JUPITER_STARTER, workspace)) //\n\t\t\t\t.addArguments(localMavenRepo.toCliArgument(), \"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Dsnapshot.repo.url=\" + mavenRepoProxy.getBaseUri()) //\n\t\t\t\t.addArguments(\"-Djunit.platform.commons.version=1.11.4\") //\n\t\t\t\t.addArguments(\"--update-snapshots\", \"--batch-mode\", \"verify\") //\n\t\t\t\t.putEnvironment(MavenEnvVars.forJre(jre)) //\n\t\t\t\t.redirectOutput(outputFiles);\n\t\tvar result = starter.startAndWait();\n\n\t\tassertEquals(1, result.exitCode());\n\t\tassertEquals(\"\", result.stdErr());\n\t\tassertThat(result.stdOutLines()).contains(\"[INFO] BUILD FAILURE\");\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains(\"The wrapped NoClassDefFoundError is likely caused by the versions of JUnit jars \"\n\t\t\t\t\t\t+ \"on the classpath/module path not being properly aligned\");\n\t}\n\n\tstatic Stream<Arguments> javaVersions() {\n\t\treturn Stream.concat( //\n\t\t\tHelper.getJavaHome(17).map(path -> Arguments.of(JRE.JAVA_17, path)).stream(), //\n\t\t\tStream.of(Arguments.of(JRE.currentJre(), currentJdkHome())) //\n\t\t);\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageGradleIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Order;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\n@Order(Integer.MIN_VALUE + 1)\nclass VintageGradleIntegrationTests {\n\n\t@TempDir\n\tPath workspace;\n\n\t@Test\n\tvoid unsupportedVersion(@FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = run(outputFiles, \"4.11\");\n\n\t\tassertThat(result.exitCode()).isGreaterThan(0);\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.doesNotContain(\"STARTED\") //\n\t\t\t\t.contains(\"Unsupported version of junit:junit: 4.11\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\", quoteTextArguments = false)\n\t@ValueSource(strings = { \"4.12\", \"4.13.2\" })\n\tvoid supportedVersions(String version, @FilePrefix(\"gradle\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = run(outputFiles, version);\n\n\t\tassertThat(result.exitCode()).isGreaterThan(0);\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains(\"VintageTest > success PASSED\") //\n\t\t\t\t.contains(\"VintageTest > failure FAILED\");\n\n\t\tvar testResultsDir = workspace.resolve(\"build/test-results/test\");\n\t\tassertThat(testResultsDir.resolve(\"TEST-com.example.vintage.VintageTest.xml\")).isRegularFile();\n\t}\n\n\tprivate ProcessResult run(OutputFiles outputFiles, String version) throws Exception {\n\t\treturn ProcessStarters.gradlew() //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.VINTAGE, workspace)) //\n\t\t\t\t.putEnvironment(\"JDK17\", Helper.getJavaHome(17).orElseThrow(TestAbortedException::new).toString()) //\n\t\t\t\t.addArguments(\"build\", \"--no-daemon\", \"--stacktrace\", \"--no-build-cache\", \"--warning-mode=fail\") //\n\t\t\t\t.addArguments(\"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Djunit4Version=\" + version) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/VintageMavenIntegrationTests.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static platform.tooling.support.tests.Projects.copyToWorkspace;\n\nimport java.nio.file.Path;\n\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.io.TempDir;\nimport org.junit.jupiter.params.ParameterizedTest;\nimport org.junit.jupiter.params.provider.ValueSource;\nimport org.junit.platform.tests.process.OutputFiles;\nimport org.junit.platform.tests.process.ProcessResult;\nimport org.opentest4j.TestAbortedException;\n\nimport platform.tooling.support.Helper;\nimport platform.tooling.support.MavenRepo;\nimport platform.tooling.support.ProcessStarters;\n\nclass VintageMavenIntegrationTests {\n\n\t@ManagedResource\n\tLocalMavenRepo localMavenRepo;\n\n\t@TempDir\n\tPath workspace;\n\n\t@Test\n\tvoid unsupportedVersion(@FilePrefix(\"maven\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = run(outputFiles, \"4.11\");\n\n\t\tassertThat(result.exitCode()).isEqualTo(1);\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains(\"TestEngine with ID 'junit-vintage' failed to discover tests\") //\n\t\t\t\t.contains(\"Tests run: 0, Failures: 0, Errors: 0, Skipped: 0\");\n\t}\n\n\t@ParameterizedTest(name = \"{0}\", quoteTextArguments = false)\n\t@ValueSource(strings = { \"4.12\", \"4.13.2\" })\n\tvoid supportedVersions(String version, @FilePrefix(\"maven\") OutputFiles outputFiles) throws Exception {\n\t\tvar result = run(outputFiles, version);\n\n\t\tassertThat(result.exitCode()).isGreaterThan(0);\n\t\tassertThat(result.stdOut()) //\n\t\t\t\t.contains(\"Running com.example.vintage.VintageTest\") //\n\t\t\t\t.contains(\"VintageTest.failure:\") //\n\t\t\t\t.contains(\"Tests run: 2, Failures: 1, Errors: 0, Skipped: 0\");\n\n\t\tvar surefireReportsDir = workspace.resolve(\"target/surefire-reports\");\n\t\tassertThat(surefireReportsDir.resolve(\"com.example.vintage.VintageTest.txt\")).isRegularFile();\n\t\tassertThat(surefireReportsDir.resolve(\"TEST-com.example.vintage.VintageTest.xml\")).isRegularFile();\n\t}\n\n\tprivate ProcessResult run(OutputFiles outputFiles, String version) throws Exception {\n\t\treturn ProcessStarters.maven(Helper.getJavaHome(17).orElseThrow(TestAbortedException::new)) //\n\t\t\t\t.workingDir(copyToWorkspace(Projects.VINTAGE, workspace)) //\n\t\t\t\t.addArguments(\"clean\", \"test\", \"--update-snapshots\", \"--batch-mode\") //\n\t\t\t\t.addArguments(localMavenRepo.toCliArgument(), \"-Dmaven.repo=\" + MavenRepo.dir()) //\n\t\t\t\t.addArguments(\"-Djunit4Version=\" + version) //\n\t\t\t\t.redirectOutput(outputFiles) //\n\t\t\t\t.startAndWait();\n\t}\n\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/java/platform/tooling/support/tests/XmlAssertions.java",
    "content": "/*\n * Copyright 2015-2026 the original author or authors.\n *\n * All rights reserved. This program and the accompanying materials are\n * made available under the terms of the Eclipse Public License v2.0 which\n * accompanies this distribution and is available at\n *\n * https://www.eclipse.org/legal/epl-v20.html\n */\n\npackage platform.tooling.support.tests;\n\nimport static de.skuzzle.test.snapshots.data.xml.XmlSnapshot.xml;\nimport static org.junit.platform.reporting.testutil.FileUtils.findPath;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Map;\nimport java.util.function.UnaryOperator;\nimport java.util.regex.Pattern;\n\nimport de.skuzzle.test.snapshots.Snapshot;\nimport de.skuzzle.test.snapshots.SnapshotSerializer;\nimport de.skuzzle.test.snapshots.StructuredData;\nimport de.skuzzle.test.snapshots.StructuredDataProvider;\n\nclass XmlAssertions {\n\n\tstatic void verifyContainsExpectedStartedOpenTestReport(Path testResultsDir, Snapshot snapshot) throws IOException {\n\t\tvar xmlFile = findPath(testResultsDir, \"glob:**/open-test-report.xml\");\n\t\tverifyContent(xmlFile, snapshot);\n\t}\n\n\tprivate static void verifyContent(Path xmlFile, Snapshot snapshot) throws IOException {\n\t\tsnapshot.named(\"open-test-report.xml\") //\n\t\t\t\t.assertThat(Files.readString(xmlFile)) //\n\t\t\t\t.as(obfuscated( //\n\t\t\t\t\txml() //\n\t\t\t\t\t\t\t.withXPathNamespaceContext(Map.of( //\n\t\t\t\t\t\t\t\t\"c\", \"https://schemas.opentest4j.org/reporting/core/0.2.0\", //\n\t\t\t\t\t\t\t\t\"e\", \"https://schemas.opentest4j.org/reporting/events/0.2.0\", //\n\t\t\t\t\t\t\t\t\"java\", \"https://schemas.opentest4j.org/reporting/java/0.2.0\" //\n\t\t\t\t\t\t\t)) //\n\t\t\t\t\t\t\t.withComparisonRules(rules -> rules //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//@time\").mustMatch(\n\t\t\t\t\t\t\t\t\t\tPattern.compile(\"\\\\d{4}-\\\\d{2}-\\\\d{2}T\\\\d{2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d+)?Z\")) //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/c:hostName/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/c:userName/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/c:operatingSystem/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/c:cpuCores/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/java:javaVersion/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/java:fileEncoding/text()\").ignore() //\n\t\t\t\t\t\t\t\t\t.pathAt(\"//c:infrastructure/java:heapSize/@max\").ignore() //\n\t\t\t\t\t\t\t), //\n\t\t\t\t\ttext -> text //\n\t\t\t\t\t\t\t.replaceAll(\"<hostName>.+?</hostName>\", \"<hostName>obfuscated</hostName>\") //\n\t\t\t\t\t\t\t.replaceAll(\"<userName>.+?</userName>\", \"<userName>obfuscated</userName>\") //\n\t\t\t\t)) //\n\t\t\t\t.matchesSnapshotStructure();\n\t}\n\n\tprivate static StructuredDataProvider obfuscated(StructuredDataProvider provider,\n\t\t\tUnaryOperator<String> obfuscator) {\n\t\treturn () -> {\n\t\t\tvar structuredData = provider.build();\n\t\t\tvar snapshotSerializer = obfuscatingSnapshotSerializer(structuredData.snapshotSerializer(), obfuscator);\n\t\t\treturn StructuredData.with(snapshotSerializer, structuredData.structuralAssertions());\n\t\t};\n\t}\n\n\tprivate static SnapshotSerializer obfuscatingSnapshotSerializer(SnapshotSerializer delegate,\n\t\t\tUnaryOperator<String> obfuscator) {\n\t\treturn testResult -> {\n\t\t\tObject obfuscatedTestResult = testResult;\n\t\t\tif (testResult instanceof String string) {\n\t\t\t\tobfuscatedTestResult = obfuscator.apply(string);\n\t\t\t}\n\t\t\treturn delegate.serialize(obfuscatedTestResult);\n\t\t};\n\t}\n}\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/resources/junit-platform.properties",
    "content": "junit.jupiter.execution.parallel.enabled=true\njunit.jupiter.execution.parallel.mode.default=concurrent\njunit.jupiter.execution.parallel.config.executor-service=worker_thread_pool\njunit.jupiter.execution.parallel.config.strategy=dynamic\njunit.jupiter.execution.parallel.config.dynamic.factor=0.25\njunit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor=1\n\njunit.jupiter.testclass.order.default = \\\n    org.junit.jupiter.api.ClassOrderer$OrderAnnotation\n\njunit.jupiter.execution.timeout.default = 3m\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/resources/log4j2-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\"\n\t\t\t   xmlns=\"https://logging.apache.org/xml/ns\"\n\t\t\t   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\t\t\t   xsi:schemaLocation=\"https://logging.apache.org/xml/ns https://logging.apache.org/xml/ns/log4j-config-2.xsd\">\n\t<Appenders>\n\t\t<Console name=\"Console\" target=\"SYSTEM_OUT\">\n\t\t\t<PatternLayout pattern=\"%d{HH:mm:ss.SSSSSS} [%-18t] %-5level %logger{1.} - %msg%n\"/>\n\t\t</Console>\n\t</Appenders>\n\t<Loggers>\n\t\t<Logger name=\"com.tngtech.archunit\" level=\"WARN\"/>\n\t\t<Logger name=\"org.junit\" level=\"WARN\"/>\n\t\t<Root level=\"ERROR\">\n\t\t\t<AppenderRef ref=\"Console\"/>\n\t\t</Root>\n\t</Loggers>\n</Configuration>\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/resources/platform/tooling/support/tests/AntStarterTests_snapshots/open-test-report.xml.snapshot",
    "content": "dynamic-directory: false\nsnapshot-name: open-test-report.xml\nsnapshot-number: 0\ntest-class: platform.tooling.support.tests.AntStarterTests\ntest-method: ant_starter\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<e:events xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\" xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\"\n\t\t\txmlns:git=\"https://schemas.opentest4j.org/reporting/git/0.2.0\"\n\t\t\txmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\txmlns:junit=\"https://schemas.junit.org/open-test-reporting\"\n\t\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n  \n  <infrastructure>\n    <hostName>obfuscated</hostName>\n    <userName>obfuscated</userName>\n    <operatingSystem>Linux</operatingSystem>\n    <cpuCores>16</cpuCores>\n    <java:javaVersion>25.0.2</java:javaVersion>\n    <java:fileEncoding>UTF-8</java:fileEncoding>\n    <java:heapSize max=\"16282288128\"/>\n  </infrastructure>\n  \n  <e:started id=\"1\" name=\"JUnit Jupiter\" time=\"2026-03-06T12:56:11.394645169Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]</junit:uniqueId>\n      <junit:legacyReportingName>JUnit Jupiter</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n  </e:started>\n  \n  <e:started id=\"2\" name=\"CalculatorParameterizedClassTests\" parentId=\"1\" time=\"2026-03-06T12:56:11.411441424Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"3\" name=\"[1] i = 1\" parentId=\"2\" time=\"2026-03-06T12:56:11.445667319Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"4\" name=\"parameterizedTest(int)\" parentId=\"3\" time=\"2026-03-06T12:56:11.448051230Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"5\" name=\"[1] 1\" parentId=\"4\" time=\"2026-03-06T12:56:11.459649542Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"5\" time=\"2026-03-06T12:56:11.474034024Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"6\" name=\"[2] 2\" parentId=\"4\" time=\"2026-03-06T12:56:11.477526975Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"6\" time=\"2026-03-06T12:56:11.479124134Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"4\" time=\"2026-03-06T12:56:11.479582087Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"7\" name=\"Inner\" parentId=\"3\" time=\"2026-03-06T12:56:11.480173962Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"8\" name=\"[1] 1\" parentId=\"7\" time=\"2026-03-06T12:56:11.482461572Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"9\" name=\"regularTest()\" parentId=\"8\" time=\"2026-03-06T12:56:11.484582498Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"9\" time=\"2026-03-06T12:56:11.487167999Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"8\" time=\"2026-03-06T12:56:11.488391484Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"10\" name=\"[2] 2\" parentId=\"7\" time=\"2026-03-06T12:56:11.489315756Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"11\" name=\"regularTest()\" parentId=\"10\" time=\"2026-03-06T12:56:11.490603522Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"11\" time=\"2026-03-06T12:56:11.491933798Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"10\" time=\"2026-03-06T12:56:11.492385289Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"7\" time=\"2026-03-06T12:56:11.493671933Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"3\" time=\"2026-03-06T12:56:11.494156065Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"12\" name=\"[2] i = 2\" parentId=\"2\" time=\"2026-03-06T12:56:11.495412663Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"13\" name=\"parameterizedTest(int)\" parentId=\"12\" time=\"2026-03-06T12:56:11.496611451Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"14\" name=\"[1] 1\" parentId=\"13\" time=\"2026-03-06T12:56:11.498812819Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"14\" time=\"2026-03-06T12:56:11.500261348Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"15\" name=\"[2] 2\" parentId=\"13\" time=\"2026-03-06T12:56:11.501299624Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"15\" time=\"2026-03-06T12:56:11.502554118Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"13\" time=\"2026-03-06T12:56:11.503025937Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"16\" name=\"Inner\" parentId=\"12\" time=\"2026-03-06T12:56:11.503615728Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"17\" name=\"[1] 1\" parentId=\"16\" time=\"2026-03-06T12:56:11.505478157Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"18\" name=\"regularTest()\" parentId=\"17\" time=\"2026-03-06T12:56:11.506758820Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"18\" time=\"2026-03-06T12:56:11.507977315Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"17\" time=\"2026-03-06T12:56:11.508409349Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"19\" name=\"[2] 2\" parentId=\"16\" time=\"2026-03-06T12:56:11.509104880Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"20\" name=\"regularTest()\" parentId=\"19\" time=\"2026-03-06T12:56:11.510159978Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"20\" time=\"2026-03-06T12:56:11.511071455Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"19\" time=\"2026-03-06T12:56:11.511350431Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"16\" time=\"2026-03-06T12:56:11.511675593Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"12\" time=\"2026-03-06T12:56:11.511962824Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"2\" time=\"2026-03-06T12:56:11.512295040Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"21\" name=\"CalculatorTests\" parentId=\"1\" time=\"2026-03-06T12:56:11.512599113Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"22\" name=\"1 + 1 = 2\" parentId=\"21\" time=\"2026-03-06T12:56:11.514707866Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[method:addsTwoNumbers()]</junit:uniqueId>\n      <junit:legacyReportingName>addsTwoNumbers()</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"addsTwoNumbers\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"22\" time=\"2026-03-06T12:56:11.515853124Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"23\" name=\"add(int, int, int)\" parentId=\"21\" time=\"2026-03-06T12:56:11.516322578Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"24\" name=\"0 + 1 = 1\" parentId=\"23\" time=\"2026-03-06T12:56:11.523443375Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"24\" time=\"2026-03-06T12:56:11.530444826Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"25\" name=\"1 + 2 = 3\" parentId=\"23\" time=\"2026-03-06T12:56:11.531569354Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"25\" time=\"2026-03-06T12:56:11.534187327Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"26\" name=\"49 + 51 = 100\" parentId=\"23\" time=\"2026-03-06T12:56:11.535099916Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#3]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[3]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"26\" time=\"2026-03-06T12:56:11.536816531Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"27\" name=\"1 + 100 = 101\" parentId=\"23\" time=\"2026-03-06T12:56:11.537572234Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#4]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[4]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"27\" time=\"2026-03-06T12:56:11.538418338Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"23\" time=\"2026-03-06T12:56:11.538669101Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"21\" time=\"2026-03-06T12:56:11.538874608Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"1\" time=\"2026-03-06T12:56:11.555264267Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n</e:events>\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/resources/platform/tooling/support/tests/GradleStarterTests_snapshots/open-test-report.xml.snapshot",
    "content": "dynamic-directory: false\nsnapshot-name: open-test-report.xml\nsnapshot-number: 0\ntest-class: platform.tooling.support.tests.GradleStarterTests\ntest-method: buildJupiterStarterProject\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<e:events xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\" xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\"\n\t\t\txmlns:git=\"https://schemas.opentest4j.org/reporting/git/0.2.0\"\n\t\t\txmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\txmlns:junit=\"https://schemas.junit.org/open-test-reporting\"\n\t\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n  \n  <infrastructure>\n    <hostName>obfuscated</hostName>\n    <userName>obfuscated</userName>\n    <operatingSystem>Linux</operatingSystem>\n    <cpuCores>16</cpuCores>\n    <java:javaVersion>17.0.18</java:javaVersion>\n    <java:fileEncoding>UTF-8</java:fileEncoding>\n    <java:heapSize max=\"536870912\"/>\n  </infrastructure>\n  \n  <e:started id=\"1\" name=\"JUnit Jupiter\" time=\"2026-03-06T12:56:36.258554411Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]</junit:uniqueId>\n      <junit:legacyReportingName>JUnit Jupiter</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n  </e:started>\n  \n  <e:started id=\"2\" name=\"CalculatorParameterizedClassTests\" parentId=\"1\" time=\"2026-03-06T12:56:36.278911645Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"3\" name=\"[1] i = 1\" parentId=\"2\" time=\"2026-03-06T12:56:36.316562802Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"4\" name=\"parameterizedTest(int)\" parentId=\"3\" time=\"2026-03-06T12:56:36.319098890Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"5\" name=\"[1] 1\" parentId=\"4\" time=\"2026-03-06T12:56:36.329635582Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"5\" time=\"2026-03-06T12:56:36.339664868Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"6\" name=\"[2] 2\" parentId=\"4\" time=\"2026-03-06T12:56:36.344033829Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"6\" time=\"2026-03-06T12:56:36.345962703Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"4\" time=\"2026-03-06T12:56:36.346634880Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"7\" name=\"Inner\" parentId=\"3\" time=\"2026-03-06T12:56:36.347336632Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"8\" name=\"[1] 1\" parentId=\"7\" time=\"2026-03-06T12:56:36.349547397Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"9\" name=\"regularTest()\" parentId=\"8\" time=\"2026-03-06T12:56:36.350890578Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"9\" time=\"2026-03-06T12:56:36.352488759Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"8\" time=\"2026-03-06T12:56:36.353032654Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"10\" name=\"[2] 2\" parentId=\"7\" time=\"2026-03-06T12:56:36.353692306Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"11\" name=\"regularTest()\" parentId=\"10\" time=\"2026-03-06T12:56:36.354640924Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"11\" time=\"2026-03-06T12:56:36.355563412Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"10\" time=\"2026-03-06T12:56:36.355854059Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"7\" time=\"2026-03-06T12:56:36.356830379Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"3\" time=\"2026-03-06T12:56:36.357197721Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"12\" name=\"[2] i = 2\" parentId=\"2\" time=\"2026-03-06T12:56:36.358755005Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"13\" name=\"parameterizedTest(int)\" parentId=\"12\" time=\"2026-03-06T12:56:36.360178256Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"14\" name=\"[1] 1\" parentId=\"13\" time=\"2026-03-06T12:56:36.362595671Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"14\" time=\"2026-03-06T12:56:36.363944242Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"15\" name=\"[2] 2\" parentId=\"13\" time=\"2026-03-06T12:56:36.364894312Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"15\" time=\"2026-03-06T12:56:36.366101797Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"13\" time=\"2026-03-06T12:56:36.366601318Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"16\" name=\"Inner\" parentId=\"12\" time=\"2026-03-06T12:56:36.367150733Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"17\" name=\"[1] 1\" parentId=\"16\" time=\"2026-03-06T12:56:36.368954561Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"18\" name=\"regularTest()\" parentId=\"17\" time=\"2026-03-06T12:56:36.370306198Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"18\" time=\"2026-03-06T12:56:36.371341879Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"17\" time=\"2026-03-06T12:56:36.371700245Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"19\" name=\"[2] 2\" parentId=\"16\" time=\"2026-03-06T12:56:36.372506273Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"20\" name=\"regularTest()\" parentId=\"19\" time=\"2026-03-06T12:56:36.373707426Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"20\" time=\"2026-03-06T12:56:36.374715355Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"19\" time=\"2026-03-06T12:56:36.375282494Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"16\" time=\"2026-03-06T12:56:36.375660276Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"12\" time=\"2026-03-06T12:56:36.376013090Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"2\" time=\"2026-03-06T12:56:36.376387055Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"21\" name=\"CalculatorTests\" parentId=\"1\" time=\"2026-03-06T12:56:36.376852341Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"22\" name=\"1 + 1 = 2\" parentId=\"21\" time=\"2026-03-06T12:56:36.380117834Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[method:addsTwoNumbers()]</junit:uniqueId>\n      <junit:legacyReportingName>addsTwoNumbers()</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"addsTwoNumbers\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"22\" time=\"2026-03-06T12:56:36.381494467Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"23\" name=\"add(int, int, int)\" parentId=\"21\" time=\"2026-03-06T12:56:36.382103054Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"24\" name=\"0 + 1 = 1\" parentId=\"23\" time=\"2026-03-06T12:56:36.389078717Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"24\" time=\"2026-03-06T12:56:36.396408927Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"25\" name=\"1 + 2 = 3\" parentId=\"23\" time=\"2026-03-06T12:56:36.397536442Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"25\" time=\"2026-03-06T12:56:36.398699413Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"26\" name=\"49 + 51 = 100\" parentId=\"23\" time=\"2026-03-06T12:56:36.399549835Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#3]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[3]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"26\" time=\"2026-03-06T12:56:36.400426076Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"27\" name=\"1 + 100 = 101\" parentId=\"23\" time=\"2026-03-06T12:56:36.400983716Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#4]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[4]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"27\" time=\"2026-03-06T12:56:36.401801437Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"23\" time=\"2026-03-06T12:56:36.402117112Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"21\" time=\"2026-03-06T12:56:36.402354078Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"1\" time=\"2026-03-06T12:56:36.402783938Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n</e:events>\n"
  },
  {
    "path": "platform-tooling-support-tests/src/test/resources/platform/tooling/support/tests/MavenStarterTests_snapshots/open-test-report.xml.snapshot",
    "content": "dynamic-directory: false\nsnapshot-name: open-test-report.xml\nsnapshot-number: 0\ntest-class: platform.tooling.support.tests.MavenStarterTests\ntest-method: verifyJupiterStarterProject\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<e:events xmlns:e=\"https://schemas.opentest4j.org/reporting/events/0.2.0\" xmlns=\"https://schemas.opentest4j.org/reporting/core/0.2.0\"\n\t\t\txmlns:git=\"https://schemas.opentest4j.org/reporting/git/0.2.0\"\n\t\t\txmlns:java=\"https://schemas.opentest4j.org/reporting/java/0.2.0\"\n\t\t\txmlns:junit=\"https://schemas.junit.org/open-test-reporting\"\n\t\t\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"https://schemas.junit.org/open-test-reporting https://schemas.junit.org/open-test-reporting/junit-1.9.xsd\">\n  \n  <infrastructure>\n    <hostName>obfuscated</hostName>\n    <userName>obfuscated</userName>\n    <operatingSystem>Linux</operatingSystem>\n    <cpuCores>16</cpuCores>\n    <java:javaVersion>17.0.18</java:javaVersion>\n    <java:fileEncoding>UTF-8</java:fileEncoding>\n    <java:heapSize max=\"16282288128\"/>\n  </infrastructure>\n  \n  <e:started id=\"1\" name=\"JUnit Jupiter\" time=\"2026-03-06T12:56:57.131176017Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]</junit:uniqueId>\n      <junit:legacyReportingName>JUnit Jupiter</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n  </e:started>\n  \n  <e:started id=\"2\" name=\"CalculatorParameterizedClassTests\" parentId=\"1\" time=\"2026-03-06T12:56:57.153878429Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"3\" name=\"[1] i = 1\" parentId=\"2\" time=\"2026-03-06T12:56:57.198278341Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"4\" name=\"parameterizedTest(int)\" parentId=\"3\" time=\"2026-03-06T12:56:57.202791173Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"5\" name=\"[1] 1\" parentId=\"4\" time=\"2026-03-06T12:56:57.214614018Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"5\" time=\"2026-03-06T12:56:57.236202632Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"6\" name=\"[2] 2\" parentId=\"4\" time=\"2026-03-06T12:56:57.241224083Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"6\" time=\"2026-03-06T12:56:57.245552127Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"4\" time=\"2026-03-06T12:56:57.246684320Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"7\" name=\"Inner\" parentId=\"3\" time=\"2026-03-06T12:56:57.247689715Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"8\" name=\"[1] 1\" parentId=\"7\" time=\"2026-03-06T12:56:57.251968115Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"9\" name=\"regularTest()\" parentId=\"8\" time=\"2026-03-06T12:56:57.254446395Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"9\" time=\"2026-03-06T12:56:57.258567559Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"8\" time=\"2026-03-06T12:56:57.262413234Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"10\" name=\"[2] 2\" parentId=\"7\" time=\"2026-03-06T12:56:57.263837448Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[1][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"11\" name=\"regularTest()\" parentId=\"10\" time=\"2026-03-06T12:56:57.265884194Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#1]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[1][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"11\" time=\"2026-03-06T12:56:57.271925947Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"10\" time=\"2026-03-06T12:56:57.274748074Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"7\" time=\"2026-03-06T12:56:57.279463218Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"3\" time=\"2026-03-06T12:56:57.282321232Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"12\" name=\"[2] i = 2\" parentId=\"2\" time=\"2026-03-06T12:56:57.284224889Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"13\" name=\"parameterizedTest(int)\" parentId=\"12\" time=\"2026-03-06T12:56:57.286553716Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"14\" name=\"[1] 1\" parentId=\"13\" time=\"2026-03-06T12:56:57.292693254Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"14\" time=\"2026-03-06T12:56:57.294968500Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"15\" name=\"[2] 2\" parentId=\"13\" time=\"2026-03-06T12:56:57.296147862Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[test-template:parameterizedTest(int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>parameterizedTest(int)[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests\" methodName=\"parameterizedTest\" methodParameterTypes=\"int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"15\" time=\"2026-03-06T12:56:57.298321277Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"13\" time=\"2026-03-06T12:56:57.299033559Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"16\" name=\"Inner\" parentId=\"12\" time=\"2026-03-06T12:56:57.299696688Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"17\" name=\"[1] 1\" parentId=\"16\" time=\"2026-03-06T12:56:57.302301416Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][1]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"18\" name=\"regularTest()\" parentId=\"17\" time=\"2026-03-06T12:56:57.304309509Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#1]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"18\" time=\"2026-03-06T12:56:57.307184235Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"17\" time=\"2026-03-06T12:56:57.308533678Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"19\" name=\"[2] 2\" parentId=\"16\" time=\"2026-03-06T12:56:57.309584127Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorParameterizedClassTests$Inner[2][2]</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"20\" name=\"regularTest()\" parentId=\"19\" time=\"2026-03-06T12:56:57.312726698Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class-template:com.example.project.CalculatorParameterizedClassTests]/[class-template-invocation:#2]/[nested-class-template:Inner]/[class-template-invocation:#2]/[method:regularTest()]</junit:uniqueId>\n      <junit:legacyReportingName>regularTest()[2][2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorParameterizedClassTests$Inner\" methodName=\"regularTest\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"20\" time=\"2026-03-06T12:56:57.314827646Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"19\" time=\"2026-03-06T12:56:57.315654153Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"16\" time=\"2026-03-06T12:56:57.316876837Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"12\" time=\"2026-03-06T12:56:57.317922817Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"2\" time=\"2026-03-06T12:56:57.318833233Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"21\" name=\"CalculatorTests\" parentId=\"1\" time=\"2026-03-06T12:56:57.319392436Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]</junit:uniqueId>\n      <junit:legacyReportingName>com.example.project.CalculatorTests</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:classSource className=\"com.example.project.CalculatorTests\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"22\" name=\"1 + 1 = 2\" parentId=\"21\" time=\"2026-03-06T12:56:57.321959292Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[method:addsTwoNumbers()]</junit:uniqueId>\n      <junit:legacyReportingName>addsTwoNumbers()</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"addsTwoNumbers\" methodParameterTypes=\"\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"22\" time=\"2026-03-06T12:56:57.324482677Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"23\" name=\"add(int, int, int)\" parentId=\"21\" time=\"2026-03-06T12:56:57.325159692Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)</junit:legacyReportingName>\n      <junit:type>CONTAINER</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:started id=\"24\" name=\"0 + 1 = 1\" parentId=\"23\" time=\"2026-03-06T12:56:57.334112900Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#1]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[1]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"24\" time=\"2026-03-06T12:56:57.342139413Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"25\" name=\"1 + 2 = 3\" parentId=\"23\" time=\"2026-03-06T12:56:57.343385981Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#2]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[2]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"25\" time=\"2026-03-06T12:56:57.345114047Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"26\" name=\"49 + 51 = 100\" parentId=\"23\" time=\"2026-03-06T12:56:57.345933371Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#3]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[3]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"26\" time=\"2026-03-06T12:56:57.347382581Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:started id=\"27\" name=\"1 + 100 = 101\" parentId=\"23\" time=\"2026-03-06T12:56:57.348140098Z\">\n    <metadata>\n      <junit:uniqueId>[engine:junit-jupiter]/[class:com.example.project.CalculatorTests]/[test-template:add(int, int, int)]/[test-template-invocation:#4]</junit:uniqueId>\n      <junit:legacyReportingName>add(int, int, int)[4]</junit:legacyReportingName>\n      <junit:type>TEST</junit:type>\n    </metadata>\n    <sources>\n      <java:methodSource className=\"com.example.project.CalculatorTests\" methodName=\"add\" methodParameterTypes=\"int, int, int\"/>\n    </sources>\n  </e:started>\n  \n  <e:finished id=\"27\" time=\"2026-03-06T12:56:57.349531610Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"23\" time=\"2026-03-06T12:56:57.349831415Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"21\" time=\"2026-03-06T12:56:57.350393704Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n  <e:finished id=\"1\" time=\"2026-03-06T12:56:57.350926939Z\">\n    <result status=\"SUCCESSFUL\"/>\n  </e:finished>\n  \n</e:events>\n"
  },
  {
    "path": "settings.gradle.kts",
    "content": "import buildparameters.BuildParametersExtension\n\npluginManagement {\n\tincludeBuild(\"gradle/plugins\")\n\trepositories {\n\t\tgradlePluginPortal()\n\t}\n}\n\nplugins {\n\tid(\"junitbuild.build-parameters\")\n\tid(\"junitbuild.maven-central-publishing\")\n\tid(\"junitbuild.settings-conventions\")\n}\n\ndependencyResolutionManagement {\n\trepositories {\n\t\tmavenCentral()\n\t}\n}\n\nval buildParameters = the<BuildParametersExtension>()\nval develocityServer = \"https://ge.junit.org\"\nval useDevelocityInstance = !gradle.startParameter.isBuildScan\n\ndevelocity {\n\tif (useDevelocityInstance) {\n\t\t// Publish to scans.gradle.com when `--scan` is used explicitly\n\t\tserver = develocityServer\n\t}\n\tbuildScan {\n\t\tuploadInBackground = !buildParameters.ci\n\n\t\tpublishing {\n\t\t\tonlyIf { it.isAuthenticated }\n\t\t}\n\n\t\tobfuscation {\n\t\t\tif (buildParameters.ci) {\n\t\t\t\tusername { \"github\" }\n\t\t\t} else {\n\t\t\t\thostname { null }\n\t\t\t\tipAddresses { emptyList() }\n\t\t\t}\n\t\t}\n\n\t\tif (buildParameters.junit.develocity.testDistribution.enabled) {\n\t\t\ttag(\"test-distribution\")\n\t\t}\n\t}\n}\n\nbuildCache {\n\tlocal {\n\t\tisEnabled = !buildParameters.ci\n\t}\n\tval buildCacheServer = buildParameters.junit.develocity.buildCache.server\n\tif (useDevelocityInstance) {\n\t\tremote(develocity.buildCache) {\n\t\t\tserver = buildCacheServer.orNull\n\t\t\tisPush = buildParameters.junit.develocity.buildCache.pushEnabled\n\t\t}\n\t} else {\n\t\tremote<HttpBuildCache> {\n\t\t\turl = uri(buildCacheServer.getOrElse(develocityServer)).resolve(\"/cache/\")\n\t\t}\n\t}\n}\n\nincludeBuild(\"gradle/base\")\n\nrootProject.name = \"junit-framework\"\n\ninclude(\"documentation\")\ninclude(\"junit-jupiter\")\ninclude(\"junit-jupiter-api\")\ninclude(\"junit-jupiter-engine\")\ninclude(\"junit-jupiter-migrationsupport\")\ninclude(\"junit-jupiter-params\")\ninclude(\"junit-start\")\ninclude(\"junit-platform-commons\")\ninclude(\"junit-platform-console\")\ninclude(\"junit-platform-console-standalone\")\ninclude(\"junit-platform-engine\")\ninclude(\"junit-platform-launcher\")\ninclude(\"junit-platform-reporting\")\ninclude(\"junit-platform-suite\")\ninclude(\"junit-platform-suite-api\")\ninclude(\"junit-platform-suite-engine\")\ninclude(\"junit-platform-testkit\")\ninclude(\"junit-vintage-engine\")\ninclude(\"jupiter-tests\")\ninclude(\"platform-tests\")\ninclude(\"platform-tooling-support-tests\")\ninclude(\"junit-bom\")\n\n// check that every subproject has a custom build file\n// based on the project name\nrootProject.children.forEach { project ->\n\tproject.buildFileName = \"${project.name}.gradle.kts\"\n\trequire(project.buildFile.isFile) {\n\t\t\"${project.buildFile} must exist\"\n\t}\n}\n\nenableFeaturePreview(\"STABLE_CONFIGURATION_CACHE\")\nenableFeaturePreview(\"TYPESAFE_PROJECT_ACCESSORS\")\n"
  }
]